import { _decorator, Component, Node, Label, Button, JsonAsset, Sprite, Color, SpriteFrame, director } from 'cc'; import { SaveDataManager } from '../../LevelSystem/SaveDataManager'; import EventBus, { GameEvents } from '../../Core/EventBus'; import { TopBarController } from '../TopBarController'; import { MoneyAni } from '../../Animations/MoneyAni'; import { AdManager } from '../../Ads/AdManager'; import { JsonConfigLoader } from '../../Core/JsonConfigLoader'; import { AnalyticsManager, OpenShopProperties, ViewMallContentProperties } from '../../Utils/AnalyticsManager'; const { ccclass, property } = _decorator; interface ShopConfig { dailyRewards: { money: { rewards: number[]; maxClaimsPerDay: number; }; diamond: { rewards: number[]; maxClaimsPerDay: number; }; } } interface DailyRewardData { lastResetDate: string; moneyFreeCount: number; moneyAdCount: number; diamondsFreeCount: number; diamondsAdCount: number; // 每日免费机会是否已使用 moneyFreeUsed: boolean; diamondFreeUsed: boolean; } @ccclass('ShopController') export class ShopController extends Component { @property(Node) moneyRewardNode: Node = null; @property(Node) diamondRewardNode: Node = null; @property(Button) moneyButton: Button = null; @property(Button) diamondButton: Button = null; @property(Label) moneyCountLabel: Label = null; @property(Label) diamondCountLabel: Label = null; @property(Label) moneyAmountLabel: Label = null; @property(Label) diamondAmountLabel: Label = null; // @property(JsonAsset) shopConfigAsset: JsonAsset = null; // 已改为动态加载 @property(Node) billSpriteNode: Node = null; // Canvas/ShopUI/ScrollView/view/content/bill/Sprite/Sprite @property(Node) diamondSpriteNode: Node = null; // Canvas/ShopUI/ScrollView/view/content/diamond/Sprite/Sprite @property(Node) moneyIconNode: Node = null; // Canvas/ShopUI/ScrollView/view/content/bill/Sprite/次数/antb-02 @property(Node) diamondIconNode: Node = null; // Canvas/ShopUI/ScrollView/view/content/diamond/Sprite/次数/antb-02 private shopConfig: ShopConfig = null; private dailyRewardData: DailyRewardData = null; private saveDataManager: SaveDataManager = null; private readonly DAILY_REWARD_KEY = 'daily_reward_data'; /** * 格式化数字显示,在数字之间添加空格 * @param num 要格式化的数字 * @returns 格式化后的字符串,如 "50" -> "5 0" */ private formatNumberWithSpaces(num: number): string { return num.toString().split('').join(' '); } async onLoad() { this.saveDataManager = SaveDataManager.getInstance(); // 用户已手动隐藏图标,代码只在非免费时显示图标 this.loadDailyRewardData(); await this.loadShopConfig(); this.setupEventListeners(); this.updateUI(); // 追踪商店打开事件 this.trackOpenShopEvent(); // 追踪查看商城内容事件 this.trackViewMallContentEvent(); } start() { // start方法保留为空,所有初始化逻辑已在onLoad中完成 } private async loadShopConfig() { try { const shopData = await JsonConfigLoader.getInstance().loadConfig('shop'); if (shopData) { this.shopConfig = shopData as ShopConfig; console.log('[ShopController] shop.json加载成功:', this.shopConfig); this.updateUI(); } else { console.warn('[ShopController] shop.json加载失败,使用默认配置'); this.useDefaultConfig(); } } catch (error) { console.error('[ShopController] 加载shop.json时出错:', error); this.useDefaultConfig(); } } /** * 使用默认商店配置 */ private useDefaultConfig() { this.shopConfig = { dailyRewards: { money: { rewards: [100, 200, 300, 400, 500], maxClaimsPerDay: 5 }, diamond: { rewards: [10, 20, 30, 40, 50], maxClaimsPerDay: 5 } } }; console.log('[ShopController] 使用默认商店配置:', this.shopConfig); this.updateUI(); } private loadDailyRewardData() { const savedData = localStorage.getItem(this.DAILY_REWARD_KEY); const today = this.getCurrentDateString(); if (savedData) { const data = JSON.parse(savedData) as DailyRewardData; // 兼容旧数据:如果存在旧的costFreeUsed字段,迁移到新字段 if (data.hasOwnProperty('costFreeUsed')) { const oldCostFreeUsed = (data as any).costFreeUsed; data.moneyFreeUsed = oldCostFreeUsed || false; data.diamondFreeUsed = oldCostFreeUsed || false; delete (data as any).costFreeUsed; console.log('[ShopController] 迁移旧的costFreeUsed数据到新字段'); } // 确保新字段存在,设置默认值 if (data.moneyFreeUsed === undefined) { data.moneyFreeUsed = false; // 默认为false(未使用) console.log('[ShopController] 设置moneyFreeUsed默认值: false'); } if (data.diamondFreeUsed === undefined) { data.diamondFreeUsed = false; // 默认为false(未使用) console.log('[ShopController] 设置diamondFreeUsed默认值: false'); } // 检查是否需要重置(新的一天) if (data.lastResetDate !== today) { console.log('[ShopController] 检测到新的一天,重置每日奖励数据'); this.resetDailyRewardData(today); } else { this.dailyRewardData = data; console.log('[ShopController] 加载已有的每日奖励数据'); } } else { console.log('[ShopController] 首次运行,创建新的每日奖励数据'); this.resetDailyRewardData(today); } console.log('[ShopController] 每日奖励数据:', this.dailyRewardData); } private resetDailyRewardData(date: string) { this.dailyRewardData = { lastResetDate: date, moneyFreeCount: 0, moneyAdCount: 0, diamondsFreeCount: 0, diamondsAdCount: 0, moneyFreeUsed: false, diamondFreeUsed: false }; this.saveDailyRewardData(); } private saveDailyRewardData() { localStorage.setItem(this.DAILY_REWARD_KEY, JSON.stringify(this.dailyRewardData)); } private getCurrentDateString(): string { const now = new Date(); const month = (now.getMonth() + 1).toString(); const day = now.getDate().toString(); const paddedMonth = month.length === 1 ? '0' + month : month; const paddedDay = day.length === 1 ? '0' + day : day; return `${now.getFullYear()}-${paddedMonth}-${paddedDay}`; } private setupEventListeners() { // 监听货币变化事件,更新UI EventBus.getInstance().on(GameEvents.CURRENCY_CHANGED, this.updateUI, this); } private updateUI() { if (!this.shopConfig || !this.dailyRewardData) { console.log('[ShopController] 配置或数据未准备好,跳过UI更新'); return; } console.log('[ShopController] 开始更新UI,数据状态:', { moneyFreeUsed: this.dailyRewardData.moneyFreeUsed, diamondFreeUsed: this.dailyRewardData.diamondFreeUsed }); // 更新钞票奖励UI this.updateMoneyRewardUI(); // 更新钻石奖励UI this.updateDiamondRewardUI(); } private updateMoneyRewardUI() { if (!this.shopConfig || !this.dailyRewardData) return; const config = this.shopConfig.dailyRewards.money; const freeCount = this.dailyRewardData.moneyFreeCount; const adCount = this.dailyRewardData.moneyAdCount; const totalCount = freeCount + adCount; const maxCount = config.maxClaimsPerDay; const freeUsed = this.dailyRewardData.moneyFreeUsed; // 计算当前次数索引(免费机会未使用时为0,否则为总次数) const currentIndex = freeUsed ? totalCount : 0; const currentReward = config.rewards[currentIndex] || config.rewards[config.rewards.length - 1]; // 更新金额显示 if (this.moneyAmountLabel) { this.moneyAmountLabel.string = this.formatNumberWithSpaces(currentReward); } // 更新按钮状态和显示 if (this.moneyButton) { const canClaim = totalCount < maxCount; this.moneyButton.interactable = canClaim; // 更新按钮文本 const buttonLabel = this.moneyButton.node.getChildByName('Label')?.getComponent(Label); if (buttonLabel) { if (!canClaim) { buttonLabel.string = "今日已达上限"; } else if (!freeUsed) { buttonLabel.string = "免 费"; } else { buttonLabel.string = "观看广告"; } } // 更新次数显示和图标 if (this.moneyCountLabel) { if (!freeUsed) { this.moneyCountLabel.string = "免 费"; // 免费时保持图标隐藏(用户已手动隐藏) console.log('[ShopController] 钞票免费状态:保持图标隐藏,显示"免费"'); } else { const remainingCount = maxCount - totalCount; this.moneyCountLabel.string = `${remainingCount}/${maxCount}`; // 非免费时显示图标 if (this.moneyIconNode) { this.moneyIconNode.active = true; console.log(`[ShopController] 钞票广告状态:显示图标,剩余次数 ${remainingCount}/${maxCount}`); } } } } } private updateDiamondRewardUI() { if (!this.shopConfig || !this.dailyRewardData) return; const config = this.shopConfig.dailyRewards.diamond; const freeCount = this.dailyRewardData.diamondsFreeCount; const adCount = this.dailyRewardData.diamondsAdCount; const totalCount = freeCount + adCount; const maxCount = config.maxClaimsPerDay; const freeUsed = this.dailyRewardData.diamondFreeUsed; // 计算当前次数索引(免费机会未使用时为0,否则为总次数) const currentIndex = freeUsed ? totalCount : 0; const currentReward = config.rewards[currentIndex] || config.rewards[config.rewards.length - 1]; // 更新金额显示 if (this.diamondAmountLabel) { this.diamondAmountLabel.string = this.formatNumberWithSpaces(currentReward); } // 更新按钮状态和显示 if (this.diamondButton) { const canClaim = totalCount < maxCount; this.diamondButton.interactable = canClaim; // 更新按钮文本 const buttonLabel = this.diamondButton.node.getChildByName('Label')?.getComponent(Label); if (buttonLabel) { if (!canClaim) { buttonLabel.string = "今日已达上限"; } else if (!freeUsed) { buttonLabel.string = "免 费"; } else { buttonLabel.string = "观看广告"; } } // 更新次数显示和图标 if (this.diamondCountLabel) { if (!freeUsed) { this.diamondCountLabel.string = "免 费"; // 免费时保持图标隐藏(用户已手动隐藏) console.log('[ShopController] 钻石免费状态:保持图标隐藏,显示"免费"'); } else { const remainingCount = maxCount - totalCount; this.diamondCountLabel.string = `${remainingCount}/${maxCount}`; // 非免费时显示图标 if (this.diamondIconNode) { this.diamondIconNode.active = true; console.log(`[ShopController] 钻石广告状态:显示图标,剩余次数 ${remainingCount}/${maxCount}`); } } } } } // 钞票奖励按钮点击事件 public onMoneyRewardClick() { if (!this.shopConfig || !this.dailyRewardData) return; const config = this.shopConfig.dailyRewards.money; const freeCount = this.dailyRewardData.moneyFreeCount; const adCount = this.dailyRewardData.moneyAdCount; const totalCount = freeCount + adCount; const maxCount = config.maxClaimsPerDay; const freeUsed = this.dailyRewardData.moneyFreeUsed; if (totalCount >= maxCount) { console.log('[ShopController] 钞票奖励已达每日上限'); return; } // 如果免费机会未使用,直接发放奖励 if (!freeUsed) { console.log('[ShopController] 使用免费机会获取钞票奖励'); this.dailyRewardData.moneyFreeUsed = true; // 免费奖励不计入freeCount,只标记为已使用 this.saveDailyRewardData(); this.updateUI(); // 播放奖励动画并添加货币 const amount = config.rewards[0] || config.rewards[config.rewards.length - 1]; this.saveDataManager.addMoney(amount, 'daily_free_reward'); this.playMoneyRewardAnimation(amount, () => { console.log(`[ShopController] 钞票免费奖励动画播放完成: ${amount}`); }); } else { // 看广告增加次数 this.showAdForMoneyReward(); } } // 钻石奖励按钮点击事件 public onDiamondRewardClick() { if (!this.shopConfig || !this.dailyRewardData) return; const config = this.shopConfig.dailyRewards.diamond; const freeCount = this.dailyRewardData.diamondsFreeCount; const adCount = this.dailyRewardData.diamondsAdCount; const totalCount = freeCount + adCount; const maxCount = config.maxClaimsPerDay; const freeUsed = this.dailyRewardData.diamondFreeUsed; if (totalCount >= maxCount) { console.log('[ShopController] 钻石奖励已达每日上限'); return; } // 如果免费机会未使用,直接发放奖励 if (!freeUsed) { console.log('[ShopController] 使用免费机会获取钻石奖励'); this.dailyRewardData.diamondFreeUsed = true; // 免费奖励不计入freeCount,只标记为已使用 this.saveDailyRewardData(); this.updateUI(); // 播放奖励动画并添加货币 const amount = config.rewards[0] || config.rewards[config.rewards.length - 1]; this.saveDataManager.addDiamonds(amount, 'daily_free_reward'); this.playDiamondRewardAnimation(amount, () => { console.log(`[ShopController] 钻石免费奖励动画播放完成: ${amount}`); }); } else { // 看广告增加次数 this.showAdForDiamondReward(); } } private claimMoneyReward(isFromAd: boolean) { const config = this.shopConfig.dailyRewards.money; // 更新领取次数(先更新数据) if (isFromAd) { this.dailyRewardData.moneyAdCount++; } else { this.dailyRewardData.moneyFreeCount++; } // 计算当前次数索引和对应奖励 const totalCount = this.dailyRewardData.moneyFreeCount + this.dailyRewardData.moneyAdCount; const currentIndex = totalCount - 1; // 减1因为已经增加了次数 const amount = config.rewards[currentIndex] || config.rewards[config.rewards.length - 1]; this.saveDailyRewardData(); this.updateUI(); console.log(`[ShopController] 开始播放钞票奖励动画: ${amount}, 来源: ${isFromAd ? '广告' : '免费'}`); // 添加货币到账户并播放奖励动画 this.saveDataManager.addMoney(amount, isFromAd ? 'daily_ad_reward' : 'daily_free_reward'); this.playMoneyRewardAnimation(amount, () => { console.log(`[ShopController] 钞票奖励动画播放完成: ${amount}`); }); } private claimDiamondReward(isFromAd: boolean) { const config = this.shopConfig.dailyRewards.diamond; // 更新领取次数(先更新数据) if (isFromAd) { this.dailyRewardData.diamondsAdCount++; } else { this.dailyRewardData.diamondsFreeCount++; } // 计算当前次数索引和对应奖励 const totalCount = this.dailyRewardData.diamondsFreeCount + this.dailyRewardData.diamondsAdCount; const currentIndex = totalCount - 1; // 减1因为已经增加了次数 const amount = config.rewards[currentIndex] || config.rewards[config.rewards.length - 1]; this.saveDailyRewardData(); this.updateUI(); console.log(`[ShopController] 开始播放钻石奖励动画: ${amount}, 来源: ${isFromAd ? '广告' : '免费'}`); this.saveDataManager.addDiamonds(amount, isFromAd ? 'daily_ad_reward' : 'daily_free_reward'); this.playDiamondRewardAnimation(amount, () => { console.log(`[ShopController] 钻石奖励动画播放完成: ${amount}`); }); } private showAdForMoneyReward() { console.log('[ShopController] 显示钞票奖励广告'); // 显示激励视频广告 AdManager.getInstance().showRewardedVideoAd( () => { // 广告观看完成,发放钞票奖励 console.log('[ShopController] 广告观看完成,发放钞票奖励'); this.claimMoneyReward(true); }, (error) => { console.error('[ShopController] 钞票奖励广告显示失败:', error); // 广告失败时不给予奖励 } ); } private showAdForDiamondReward() { console.log('[ShopController] 显示钻石奖励广告'); // 显示激励视频广告 AdManager.getInstance().showRewardedVideoAd( () => { // 广告观看完成,发放钻石奖励 console.log('[ShopController] 广告观看完成,发放钻石奖励'); this.claimDiamondReward(true); }, (error) => { console.error('[ShopController] 钻石奖励广告显示失败:', error); // 广告失败时不给予奖励 } ); } // showRewardEffect方法已被MoneyAni.playReward替代,不再需要 // Cost按钮相关方法已移除,功能已整合到主按钮中 // 从配置JSON更新钞票数值显示 private updateMoneyAmountFromConfig() { if (this.moneyAmountLabel && this.shopConfig) { // 从配置中读取下一次的钞票数值 const nextMoneyAmount = this.shopConfig.dailyRewards.money.rewards[0] || 100; this.moneyAmountLabel.string = this.formatNumberWithSpaces(nextMoneyAmount); console.log('[ShopController] 从配置更新钞票数值:', nextMoneyAmount); } } // 从配置JSON更新钻石数值显示 private updateDiamondAmountFromConfig() { if (this.diamondAmountLabel && this.shopConfig) { // 从配置中读取下一次的钻石数值 const nextDiamondAmount = this.shopConfig.dailyRewards.diamond.rewards[0] || 10; this.diamondAmountLabel.string = this.formatNumberWithSpaces(nextDiamondAmount); console.log('[ShopController] 从配置更新钻石数值:', nextDiamondAmount); } } /** * 播放钞票奖励动画 */ private playMoneyRewardAnimation(amount: number, onComplete?: () => void) { // 查找场景中的MoneyAni组件 const scene = director.getScene(); if (!scene) { console.error('[ShopController] 无法获取当前场景'); if (onComplete) onComplete(); return; } const findMoneyAni = (node: Node): MoneyAni | null => { const moneyAni = node.getComponent(MoneyAni); if (moneyAni) return moneyAni; for (const child of node.children) { const result = findMoneyAni(child); if (result) return result; } return null; }; const moneyAni = findMoneyAni(scene); if (!moneyAni) { console.error('[ShopController] 场景中未找到MoneyAni组件'); if (onComplete) onComplete(); return; } // 临时设置钞票起始位置 if (this.billSpriteNode) { moneyAni.coinStartNode = this.billSpriteNode; } // 播放钞票动画 moneyAni.playRewardAnimation(amount, 0, onComplete); } /** * 播放钻石奖励动画 */ private playDiamondRewardAnimation(amount: number, onComplete?: () => void) { // 查找场景中的MoneyAni组件 const scene = director.getScene(); if (!scene) { console.error('[ShopController] 无法获取当前场景'); if (onComplete) onComplete(); return; } const findMoneyAni = (node: Node): MoneyAni | null => { const moneyAni = node.getComponent(MoneyAni); if (moneyAni) return moneyAni; for (const child of node.children) { const result = findMoneyAni(child); if (result) return result; } return null; }; const moneyAni = findMoneyAni(scene); if (!moneyAni) { console.error('[ShopController] 场景中未找到MoneyAni组件'); if (onComplete) onComplete(); return; } // 临时设置钻石起始位置 if (this.diamondSpriteNode) { moneyAni.diamondStartNode = this.diamondSpriteNode; } // 播放钻石动画 moneyAni.playRewardAnimation(0, amount, onComplete); } onDestroy() { // 移除事件监听 EventBus.getInstance().off(GameEvents.CURRENCY_CHANGED, this.updateUI, this); } // 调试方法:重置每日奖励数据 public resetDailyRewards() { const today = this.getCurrentDateString(); this.resetDailyRewardData(today); this.updateUI(); console.log('[ShopController] 每日奖励数据已重置'); } // 获取当前每日奖励状态(用于调试) public getDailyRewardStatus() { return { config: this.shopConfig, data: this.dailyRewardData, today: this.getCurrentDateString() }; } /** * 追踪商店打开事件 */ private trackOpenShopEvent(): void { try { const properties: OpenShopProperties = { shop_type: 'daily_reward', // 商店类型:每日奖励商店 entry_point: 'main_ui', // 入口点:主界面 user_level: this.saveDataManager?.getCurrentLevel() || 1, user_money: this.saveDataManager?.getMoney() || 0, user_diamonds: this.saveDataManager?.getDiamonds() || 0 }; AnalyticsManager.getInstance().trackOpenShop(properties); console.log('[ShopController] $OpenShop 事件已上报:', properties); } catch (error) { console.error('[ShopController] 追踪商店打开事件时出错:', error); } } private trackViewMallContentEvent(): void { try { const properties: ViewMallContentProperties = { content_type: 'daily_rewards', // 内容类型:每日奖励 content_id: 'shop_daily_rewards', // 内容ID:商店每日奖励 view_time: Date.now(), // 查看时间戳 user_level: this.saveDataManager?.getCurrentLevel() || 1, user_money: this.saveDataManager?.getMoney() || 0, user_diamonds: this.saveDataManager?.getDiamonds() || 0 }; AnalyticsManager.getInstance().trackViewMallContent(properties); console.log('[ShopController] $ViewMallContent 事件已上报:', properties); } catch (error) { console.error('[ShopController] 追踪查看商城内容事件时出错:', error); } } }