feat: 完善七日活动交互、奖励动画与猫形象支持

This commit is contained in:
keke
2026-09-08 20:25:28 +08:00
parent f2f12d480c
commit 79e4a25039
67 changed files with 2461 additions and 287 deletions
+12 -6
View File
@@ -118,11 +118,13 @@ export default class Avatar extends cc.Component {
this.content.children[this.avatarData[j] - 4].getChildByName("lock").active = false;
}
if (this.catArr.length > 0) {
for (let k = 0; k < this.catArr.length; k++) {
this.content4.children[this.catArr[k] - 1].getChildByName("lock").active = false;
}
}
// 按节点名匹配,避免新增猫 ID 越界;复用窗口时重新计算锁状态。
const ownedCats = [1, 2, 3].concat(this.catArr.map(id => Number(id)));
this.content4.children.forEach(cat => {
const id = Number(cat.name.replace("cat_", ""));
const lock = cat.getChildByName("lock");
if (lock) lock.active = ownedCats.indexOf(id) < 0;
});
}
@@ -420,11 +422,15 @@ export default class Avatar extends cc.Component {
//改变头像框
changeCat(event, catData) {
const cat = this.content4.getChildByName(catData);
if (!cat) { cc.warn("[Avatar] 缺少猫形象节点", catData); return; }
const lock = cat.getChildByName("lock");
if (event && lock && lock.active) return;
this.closeCatKuang();
this.avatarCat = catData;
console.log("谁应该为true111", catData);
console.log(this.content4.children);
this.content4.getChildByName(catData).getChildByName("select").active = true;
cat.getChildByName("select").active = true;
this.changeBtnState("kuang");
}
+51 -18
View File
@@ -7,6 +7,8 @@
import MapConroler from "./Map";
import { DRAWABLE_CAT_IDS, getOwnedDrawableCats, getCatDrawCost } from "./module/Tool/CatDrawRules";
import HomePopupQueue from "../home_popup_queue/HomePopupQueue";
import NumberToImage from "./NumberToImage";
import { MiniGameSdk } from "./Sdk/MiniGameSdk";
import { LQCollideSystem } from "./lq_collide_system/lq_collide_system";
@@ -1050,23 +1052,12 @@ export default class JiaZai extends cc.Component {
if (!redNode) {
return;
}
const drawableCatIds = [4, 5, 6, 7, 8, 9, 10, 11];
const filmConsumeTable = [5, 10, 25, 35, 55, 70, 100, 120];
const catArr = Array.isArray(cc.fx.GameConfig.GM_INFO.catArr)
? cc.fx.GameConfig.GM_INFO.catArr
: [];
const ownedCatIds: { [catId: number]: boolean } = {};
for (let i = 0; i < catArr.length; i++) {
const catId = Number(catArr[i]);
if (drawableCatIds.indexOf(catId) !== -1) {
ownedCatIds[catId] = true;
}
}
const ownedCount = Object.keys(ownedCatIds).length;
const allCatsOwned = ownedCount >= drawableCatIds.length;
const filmCost = filmConsumeTable[Math.min(ownedCount, filmConsumeTable.length - 1)];
const ownedCount = getOwnedDrawableCats(catArr).length;
const allCatsOwned = ownedCount >= DRAWABLE_CAT_IDS.length;
const filmCost = getCatDrawCost(ownedCount);
const currentFilm = Number(cc.fx.GameConfig.GM_INFO.film) || 0;
// drawCat 在 film < filmCost 时判定不足,因此相等时也可以抽取。
@@ -2721,7 +2712,25 @@ export default class JiaZai extends cc.Component {
// - customEventData:自定义事件数据
// ============================================
// 打开通行证弹窗
openStarter_pack(event?: cc.Event, customEventData?: string) {
openStarter_pack(event?: cc.Event, customEventData?: string, fromQueue: boolean = false) {
if (!event && customEventData === undefined && !fromQueue) {
HomePopupQueue.get(this.node).enqueue("starterPack", 40, done => {
const show = () => {
if (!cc.isValid(this.node, true)) { done(); return; }
try { this.openStarter_pack(undefined, undefined, true); } finally { done(); }
};
if (JiaZai.cachedActionPrefab) { show(); return; }
cc.assetManager.loadBundle("action_bundle", (error, bundle) => {
if (error) { cc.error(error); done(); return; }
bundle.load("prefab/newbieGift", cc.Prefab, (loadError, prefab: cc.Prefab) => {
if (loadError) { cc.error(loadError); done(); return; }
JiaZai.cachedActionPrefab = prefab;
show();
});
});
});
return;
}
this.vibrateButtonClick(event);
if (!JiaZai.cachedActionPrefab) {
@@ -3841,6 +3850,7 @@ export default class JiaZai extends cc.Component {
console.log("未到期已领取");
}
if (data.code == 1) {
HomePopupQueue.get(this.node).enqueue("monthlyReward", 20, done => {
this.getcard.active = true;
this.getcard.zIndex = 999;
// 外层 getcard 整体弹入动画(三次回弹)
@@ -3869,6 +3879,8 @@ export default class JiaZai extends cc.Component {
compensate: false
}
cc.fx.GameTool.shushu_Track("resource_get", dataTemp);
done();
});
}
})
@@ -3901,8 +3913,11 @@ export default class JiaZai extends cc.Component {
if (cachedDateStr !== dateStr) {
this.winStreakShow = false;
// 更新缓存
cc.fx.StorageMessage.setStorage("monthlyCardDate", dateStr);
this.openMonthCard();
HomePopupQueue.get(this.node).enqueue("monthlyCard", 30, done => {
this.openMonthCard();
if (this.monthlyCardNode && this.monthlyCardNode.active) cc.fx.StorageMessage.setStorage("monthlyCardDate", dateStr);
done();
});
}
else {
this.openWin();
@@ -4316,7 +4331,25 @@ export default class JiaZai extends cc.Component {
// 功能:创建并显示连胜活动预制体
// 何时调用:点击连胜活动按钮时调用
// ============================================
openWinStreak(event?: cc.Event, customEventData?: string) {
openWinStreak(event?: cc.Event, customEventData?: string, fromQueue: boolean = false) {
if (!event && customEventData === undefined && !fromQueue) {
HomePopupQueue.get(this.node).enqueue("winStreak", 50, done => {
const show = () => {
if (!cc.isValid(this.node, true)) { done(); return; }
try { this.openWinStreak(undefined, undefined, true); } finally { done(); }
};
if (JiaZai.cachedWinStreakPrefab) { show(); return; }
cc.assetManager.loadBundle("action_bundle", (error, bundle) => {
if (error) { cc.error(error); done(); return; }
bundle.load("prefab/winStreak", cc.Prefab, (loadError, prefab: cc.Prefab) => {
if (loadError) { cc.error(loadError); done(); return; }
JiaZai.cachedWinStreakPrefab = prefab;
show();
});
});
});
return;
}
this.vibrateButtonClick(event);
let eventName = "";
let btnName = ""
+3 -1
View File
@@ -12,6 +12,7 @@ export default class WinCat extends cc.Component {
@property(cc.SpriteAtlas)
catUI: cc.SpriteAtlas = null;
@property(cc.SpriteFrame) cat12: cc.SpriteFrame = null;
start() {
this.refreshUserCat();
@@ -30,7 +31,7 @@ export default class WinCat extends cc.Component {
const useravatarCat = cc.fx.GameConfig.GM_INFO.useravatarCat;
const catNumber = String(useravatarCat || "").match(/\d+/);
const frameName = catNumber ? "cat" + parseInt(catNumber[0], 10) : "cat1";
const spriteFrame = this.catUI.getSpriteFrame(frameName)
const spriteFrame = (frameName === "cat12" ? this.cat12 : this.catUI.getSpriteFrame(frameName))
|| this.catUI.getSpriteFrame("cat1");
if (!spriteFrame) {
@@ -57,6 +58,7 @@ export default class WinCat extends cc.Component {
cc.warn("WinCat: 找不到猫节点或 Sprite", nodePath);
return;
}
sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM;
sprite.spriteFrame = spriteFrame;
}
}
+13
View File
@@ -0,0 +1,13 @@
/** 普通抽猫池;签到等活动猫不参与费用、进度和集齐判断。 */
export const DRAWABLE_CAT_IDS: ReadonlyArray<number> = [4, 5, 6, 7, 8, 9, 10, 11];
const FILM_COSTS = [5, 10, 25, 35, 55, 70, 100, 120];
export function getOwnedDrawableCats(catArr: any): number[] {
const owned = Array.isArray(catArr) ? catArr.map(id => Number(id)) : [];
return DRAWABLE_CAT_IDS.filter(id => owned.indexOf(id) !== -1);
}
/** 集齐时保留最后一档展示费用;是否可抽由拥有数量单独判断。 */
export function getCatDrawCost(ownedCount: number): number {
return FILM_COSTS[Math.min(Math.max(0, ownedCount), FILM_COSTS.length - 1)];
}
@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "4d23795e-6b93-46f0-8ef0-1e0cb8e4f1a6",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}
@@ -1,5 +1,6 @@
import Utils from "../module/Pay/Utils";
// 主包用于判断是否展示的活动快照,具体奖励由分包解析。
export interface SevenDayActivityInfo {
serverNow: string | number;
activityId: string;
@@ -17,15 +18,17 @@ export interface SevenDayActivityInfo {
interface SevenDayInfoResponse {
code: number;
data: SevenDayActivityInfo;
data: SevenDayActivityInfo | false;
msg?: string;
}
/** 七日活动查询入口,与分包内的资产同步接口分开。 */
export default class SevenDayGiftBootstrapApi {
// 主包只查询活动信息,不提前加载活动资源。
public static getInfo(callback: (response: SevenDayInfoResponse) => void) {
const endpoint = "signInActivityInfo";
const params = { uid: this.getUid() };
let completed = false;
let completed = false; // 超时后到达的响应不再处理。
const timeoutHandle = setTimeout(() => {
if (completed) return;
@@ -35,6 +38,7 @@ export default class SevenDayGiftBootstrapApi {
}, 5500);
Utils.POST(endpoint, params, response => {
console.log("[signInActivityInfo] 回复" + (completed ? "(已超时或重复,不再处理)" : ""), JSON.stringify(response, null, 2));
if (completed) return;
completed = true;
clearTimeout(timeoutHandle);
@@ -42,6 +46,7 @@ export default class SevenDayGiftBootstrapApi {
});
}
// 优先使用本地登录 UID,并同步到运行时账号信息。
public static getUid(): string {
const storedUid = cc.fx.StorageMessage.getStorage("uid");
if (storedUid !== undefined && storedUid !== null && storedUid !== "") {
@@ -1,41 +1,79 @@
import SevenDayGiftBootstrapApi, { SevenDayActivityInfo } from "./SevenDayGiftBootstrapApi";
import HomePopupQueue from "../../home_popup_queue/HomePopupQueue";
const { ccclass } = cc._decorator;
const AUTO_OPEN_KEY = "seven_day_gift_auto_open_v1";
const BUNDLE_NAME = "seven_day_gift";
const GIFT_PREFAB_PATH = "prefab/sevenDayGift";
// 主包通过接口调用分包,避免静态引入活动脚本和资源。
interface SevenDayGiftView {
initializeRuntime(info: SevenDayActivityInfo, closeHandler: () => void): void;
initializeRuntime(info: SevenDayActivityInfo, closeHandler: () => void): boolean;
updateRuntimeInfo(info: SevenDayActivityInfo): void;
show(autoClaim?: boolean): void;
show(autoClaim?: boolean): boolean;
}
/** 主页入口:查询活动、安排自动展示、管理分包生命周期。 */
@ccclass
export default class SevenDayGiftHost extends cc.Component {
private giftNode: cc.Node = null;
private giftView: SevenDayGiftView = null;
private giftBundle: cc.AssetManager.Bundle = null;
private giftLoading = false;
private giftLoadId = 0;
private giftLoadId = 0; // 加载批次号,用于忽略过期回调。
private giftLoadCallbacks: Array<(error?: Error) => void> = [];
private info: SevenDayActivityInfo = null;
private querying = false;
private retryCount = 0;
private testPanelNode: cc.Node = null;
// 监听回到前台事件,并在正式环境查询活动。
onLoad() {
if (this.node.parent) this.node.setContentSize(this.node.parent.getContentSize());
cc.game.on(cc.game.EVENT_SHOW, this.onGameShow, this);
const gameTool = cc.fx && cc.fx.GameTool;
if (CC_PREVIEW || (gameTool && gameTool.getWechatGameVersion() === "开发版")) this.loadTestPanel();
if (CC_PREVIEW) return;
this.queryActivity(true);
}
// 移除监听,同时清理弹窗和分包资源。
onDestroy() {
if (this.testPanelNode && cc.isValid(this.testPanelNode)) this.testPanelNode.destroy();
cc.game.off(cc.game.EVENT_SHOW, this.onGameShow, this);
this.releaseGiftAssets(true);
}
// 开发环境按需加载接口测试面板。
private loadTestPanel() {
// 主包不静态引用测试脚本;发布构建可以直接排除 testSeven。
cc.assetManager.loadBundle("testSeven", error => {
if (!cc.isValid(this.node, true)) return;
if (error) {
cc.warn("[SevenDayGiftHost] 测试面板未加载(需要包含 testSeven Bundle)", error);
return;
}
const canvas = cc.find("Canvas");
if (!canvas) return;
const node = new cc.Node("SevenApiTestPanel");
canvas.addChild(node);
node.setPosition(0, 0);
node.zIndex = 3000;
this.testPanelNode = node;
const panel: any = node.addComponent("SevenApiPanel");
panel.testHome = (info: SevenDayActivityInfo) => {
if (this.giftLoading || (this.giftNode && this.giftNode.active)) {
cc.warn("[SevenDayGiftHost] 请先关闭当前七日活动,再测试自动弹窗");
return;
}
this.info = info;
if (this.shouldAutoOpen()) this.openGift(true);
};
});
}
// 等待登录 UID 就绪后查询,避免并发重复请求。
private queryActivity(allowAutoOpen: boolean) {
if (this.querying) return;
if (!SevenDayGiftBootstrapApi.getUid()) {
@@ -60,8 +98,18 @@ export default class SevenDayGiftHost extends cc.Component {
});
}
// 把七日活动加入主页弹窗队列。
private openGift(autoClaim: boolean) {
// 保留旧参数名;现在表示自动展示来源,不会自动领取。
const canvas = cc.find("Canvas");
if (!canvas) return;
HomePopupQueue.get(canvas).enqueue("sevenDayGift", 10, done => this.openQueuedGift(autoClaim, done));
}
// 轮到本活动时加载并显示,成功显示后才记录当天已弹。
private openQueuedGift(autoClaim: boolean, done: () => void) {
this.ensureGiftLoaded((error?: Error) => {
done();
if (error) {
cc.error("[SevenDayGiftHost] 七日活动分包加载失败", error);
return;
@@ -69,17 +117,23 @@ export default class SevenDayGiftHost extends cc.Component {
if (!this.info || !this.giftView) return;
if (autoClaim && !this.shouldAutoOpen()) return;
this.initializeGiftView();
if (!this.initializeGiftView() || !this.giftView.show(autoClaim)) {
cc.error("[SevenDayGiftHost] 弹窗初始化或显示失败,未记录当天已弹");
this.releaseGiftAssets();
return;
}
if (autoClaim) this.markAutoOpened();
this.giftView.show(autoClaim);
console.log("[SevenDayGiftHost] 弹窗已打开", { autoClaim });
});
}
private initializeGiftView() {
if (!this.giftView) return;
this.giftView.initializeRuntime(this.info, () => this.releaseGiftAssets());
// 把活动快照和关闭后的资源清理回调交给分包。
private initializeGiftView(): boolean {
if (!this.giftView) return false;
return this.giftView.initializeRuntime(this.info, () => this.releaseGiftAssets());
}
// 复用已加载视图;多个加载请求共用一次分包加载。
private ensureGiftLoaded(callback: (error?: Error) => void) {
if (this.giftView && this.giftNode && cc.isValid(this.giftNode)) {
callback();
@@ -145,12 +199,14 @@ export default class SevenDayGiftHost extends cc.Component {
});
}
// 统一通知本次等待加载的调用方。
private finishGiftLoad(error?: Error) {
this.giftLoading = false;
const callbacks = this.giftLoadCallbacks.splice(0);
callbacks.forEach(callback => callback(error));
}
// 使旧加载回调失效,并在节点销毁后释放资源。
private releaseGiftAssets(immediate: boolean = false) {
this.giftLoadId++;
this.giftLoadCallbacks.length = 0;
@@ -176,6 +232,7 @@ export default class SevenDayGiftHost extends cc.Component {
else this.scheduleOnce(release, 0);
}
// 仅释放当前仍登记在资源管理器中的活动分包。
private releaseBundle(bundle: cc.AssetManager.Bundle) {
if (!bundle || cc.assetManager.getBundle(BUNDLE_NAME) !== bundle) return;
bundle.releaseAll();
@@ -184,6 +241,7 @@ export default class SevenDayGiftHost extends cc.Component {
}
}
// 根据触发条件、活动状态和服务端时间判断是否可展示。
private isActivityVisible(): boolean {
if (!this.info || !this.info.activityId || !this.info.triggerReached) return false;
if (this.info.status === "expired" || this.info.status === "completed") return false;
@@ -192,28 +250,44 @@ export default class SevenDayGiftHost extends cc.Component {
return !serverNow || !endAt || serverNow < endAt;
}
// 今天可领且当前账号当天未弹出过,才自动展示。
private shouldAutoOpen(): boolean {
if (!this.isActivityVisible() || !this.info.canClaim) return false;
return cc.fx.StorageMessage.getStorage(this.getAutoOpenStorageKey()) !== this.getServerDayKey();
if (!this.isActivityVisible() || !this.info.canClaim || this.info.todayClaimed) {
console.log("[SevenDayGiftHost] 跳过弹窗:活动不可见或今天不可领取");
return false;
}
const key = this.getAutoOpenStorageKey();
const recordedDay = cc.fx.StorageMessage.getStorage(key);
const serverDay = this.getServerDayKey();
const shouldOpen = recordedDay !== serverDay;
console.log("[SevenDayGiftHost] 自动弹窗判断", JSON.stringify({ key, recordedDay, serverDay, shouldOpen }));
return shouldOpen;
}
// 只记录已展示,不代表奖励已经领取。
private markAutoOpened() {
cc.fx.StorageMessage.setStorage(this.getAutoOpenStorageKey(), this.getServerDayKey());
}
// 按账号和活动隔离自动弹窗记录。
private getAutoOpenStorageKey(): string {
return AUTO_OPEN_KEY + ":" + SevenDayGiftBootstrapApi.getUid() + ":" + this.info.activityId;
}
// 按北京时间划分服务端日期,避免设备时区影响。
private getServerDayKey(): string {
const time = this.toTime(this.info.serverNow) || Date.now();
return Math.floor((time + 8 * 60 * 60 * 1000) / (24 * 60 * 60 * 1000)).toString();
}
// 回到前台时刷新活动;正在展示时继续使用原快照。
private onGameShow() {
// 弹窗正在使用本次查询结果,切前后台不重新查询或重复展示。
if (this.giftLoading || (this.giftNode && cc.isValid(this.giftNode) && this.giftNode.active)) return;
if (!CC_PREVIEW) this.queryActivity(true);
}
// 兼容秒、毫秒时间戳和日期字符串。
private toTime(value: string | number): number {
if (typeof value === "number") return value < 100000000000 ? value * 1000 : value;
if (!value) return 0;