ShopController.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. import { _decorator, Component, Node, Label, Button, JsonAsset, Sprite, Color, SpriteFrame } from 'cc';
  2. import { SaveDataManager } from '../../LevelSystem/SaveDataManager';
  3. import EventBus, { GameEvents } from '../../Core/EventBus';
  4. import { TopBarController } from '../TopBarController';
  5. const { ccclass, property } = _decorator;
  6. interface ShopConfig {
  7. dailyRewards: {
  8. money: {
  9. baseAmount: number;
  10. maxClaimsPerDay: number;
  11. maxAdsPerDay: number;
  12. firstClaimFree: boolean;
  13. adRequiredAfterFirst: boolean;
  14. };
  15. diamond: {
  16. baseAmount: number;
  17. maxClaimsPerDay: number;
  18. maxAdsPerDay: number;
  19. firstClaimFree: boolean;
  20. adRequiredAfterFirst: boolean;
  21. };
  22. // 新增Cost按钮配置
  23. costButtons: {
  24. maxAdsPerDay: number; // Cost按钮每日最大广告观看次数
  25. };
  26. }
  27. }
  28. interface DailyRewardData {
  29. lastResetDate: string;
  30. moneyFreeCount: number;
  31. moneyAdCount: number;
  32. diamondsFreeCount: number;
  33. diamondsAdCount: number;
  34. // Cost按钮相关
  35. costAdCount: number;
  36. // Cost按钮免费使用状态 - 分别管理
  37. billCostFreeUsed: boolean;
  38. diamondCostFreeUsed: boolean;
  39. }
  40. @ccclass('ShopController')
  41. export class ShopController extends Component {
  42. @property(Node)
  43. moneyRewardNode: Node = null;
  44. @property(Node)
  45. diamondRewardNode: Node = null;
  46. @property(Button)
  47. moneyButton: Button = null;
  48. @property(Button)
  49. diamondButton: Button = null;
  50. @property(Label)
  51. moneyCountLabel: Label = null;
  52. @property(Label)
  53. diamondCountLabel: Label = null;
  54. @property(Label)
  55. moneyAmountLabel: Label = null;
  56. @property(Label)
  57. diamondAmountLabel: Label = null;
  58. @property(JsonAsset)
  59. shopConfigAsset: JsonAsset = null;
  60. // 新增Cost按钮装饰器
  61. @property(Button)
  62. billCostButton: Button = null;
  63. @property(Button)
  64. diamondCostButton: Button = null;
  65. // 按钮置灰图片资源
  66. @property(SpriteFrame)
  67. disabledButtonSprite: SpriteFrame = null;
  68. // 保存原始按钮图片
  69. private originalBillButtonSprite: SpriteFrame = null;
  70. private originalDiamondButtonSprite: SpriteFrame = null;
  71. private shopConfig: ShopConfig = null;
  72. private dailyRewardData: DailyRewardData = null;
  73. private saveDataManager: SaveDataManager = null;
  74. private readonly DAILY_REWARD_KEY = 'daily_reward_data';
  75. /**
  76. * 格式化数字显示,在数字之间添加空格
  77. * @param num 要格式化的数字
  78. * @returns 格式化后的字符串,如 "50" -> "5 0"
  79. */
  80. private formatNumberWithSpaces(num: number): string {
  81. return num.toString().split('').join(' ');
  82. }
  83. onLoad() {
  84. this.saveDataManager = SaveDataManager.getInstance();
  85. this.loadShopConfig();
  86. this.loadDailyRewardData();
  87. this.setupEventListeners();
  88. }
  89. start() {
  90. // 检查disabledButtonSprite是否已设置
  91. console.log('[ShopController] disabledButtonSprite是否已设置:', !!this.disabledButtonSprite);
  92. // 保存原始按钮图片
  93. if (this.billCostButton) {
  94. const sprite = this.billCostButton.node.getComponent(Sprite);
  95. if (sprite) {
  96. this.originalBillButtonSprite = sprite.spriteFrame;
  97. console.log('[ShopController] 保存钞票按钮原始图片:', !!this.originalBillButtonSprite);
  98. } else {
  99. console.log('[ShopController] 警告:无法获取钞票按钮的Sprite组件');
  100. }
  101. }
  102. if (this.diamondCostButton) {
  103. const sprite = this.diamondCostButton.node.getComponent(Sprite);
  104. if (sprite) {
  105. this.originalDiamondButtonSprite = sprite.spriteFrame;
  106. console.log('[ShopController] 保存钻石按钮原始图片:', !!this.originalDiamondButtonSprite);
  107. } else {
  108. console.log('[ShopController] 警告:无法获取钻石按钮的Sprite组件');
  109. }
  110. }
  111. this.updateUI();
  112. }
  113. private loadShopConfig() {
  114. if (this.shopConfigAsset) {
  115. this.shopConfig = this.shopConfigAsset.json as ShopConfig;
  116. console.log('[ShopController] shop.json加载成功:', this.shopConfig);
  117. this.updateUI();
  118. } else {
  119. console.error('[ShopController] shopConfigAsset未设置,请在编辑器中拖拽shop.json文件到shopConfigAsset属性');
  120. }
  121. }
  122. private loadDailyRewardData() {
  123. const savedData = localStorage.getItem(this.DAILY_REWARD_KEY);
  124. const today = this.getCurrentDateString();
  125. if (savedData) {
  126. const data = JSON.parse(savedData) as DailyRewardData;
  127. // 兼容旧数据:如果存在旧的costFreeUsed字段,迁移到新字段
  128. if (data.hasOwnProperty('costFreeUsed')) {
  129. const oldCostFreeUsed = (data as any).costFreeUsed;
  130. data.billCostFreeUsed = oldCostFreeUsed || false;
  131. data.diamondCostFreeUsed = oldCostFreeUsed || false;
  132. delete (data as any).costFreeUsed;
  133. console.log('[ShopController] 迁移旧的costFreeUsed数据到新字段');
  134. }
  135. // 确保新字段存在,设置默认值
  136. if (data.billCostFreeUsed === undefined) {
  137. data.billCostFreeUsed = true; // 默认为true(可用)
  138. console.log('[ShopController] 设置billCostFreeUsed默认值: true');
  139. }
  140. if (data.diamondCostFreeUsed === undefined) {
  141. data.diamondCostFreeUsed = true; // 默认为true(可用)
  142. console.log('[ShopController] 设置diamondCostFreeUsed默认值: true');
  143. }
  144. // 检查是否需要重置(新的一天)
  145. if (data.lastResetDate !== today) {
  146. console.log('[ShopController] 检测到新的一天,重置每日奖励数据');
  147. this.resetDailyRewardData(today);
  148. } else {
  149. this.dailyRewardData = data;
  150. console.log('[ShopController] 加载已有的每日奖励数据');
  151. }
  152. } else {
  153. console.log('[ShopController] 首次运行,创建新的每日奖励数据');
  154. this.resetDailyRewardData(today);
  155. }
  156. console.log('[ShopController] 每日奖励数据:', this.dailyRewardData);
  157. }
  158. private resetDailyRewardData(date: string) {
  159. this.dailyRewardData = {
  160. lastResetDate: date,
  161. moneyFreeCount: 0,
  162. moneyAdCount: 0,
  163. diamondsFreeCount: 0,
  164. diamondsAdCount: 0,
  165. costAdCount: 0,
  166. billCostFreeUsed: true,
  167. diamondCostFreeUsed: true
  168. };
  169. this.saveDailyRewardData();
  170. }
  171. private saveDailyRewardData() {
  172. localStorage.setItem(this.DAILY_REWARD_KEY, JSON.stringify(this.dailyRewardData));
  173. }
  174. private getCurrentDateString(): string {
  175. const now = new Date();
  176. const month = (now.getMonth() + 1).toString();
  177. const day = now.getDate().toString();
  178. const paddedMonth = month.length === 1 ? '0' + month : month;
  179. const paddedDay = day.length === 1 ? '0' + day : day;
  180. return `${now.getFullYear()}-${paddedMonth}-${paddedDay}`;
  181. }
  182. private setupEventListeners() {
  183. // 监听货币变化事件,更新UI
  184. EventBus.getInstance().on(GameEvents.CURRENCY_CHANGED, this.updateUI, this);
  185. }
  186. private updateUI() {
  187. if (!this.shopConfig || !this.dailyRewardData) return;
  188. // 更新钞票奖励UI
  189. this.updateMoneyRewardUI();
  190. // 更新钻石奖励UI
  191. this.updateDiamondRewardUI();
  192. // 更新Cost按钮UI
  193. this.updateBillCostButtonUI();
  194. this.updateDiamondCostButtonUI();
  195. }
  196. private updateMoneyRewardUI() {
  197. if (!this.shopConfig || !this.dailyRewardData) return;
  198. const config = this.shopConfig.dailyRewards.money;
  199. const freeCount = this.dailyRewardData.moneyFreeCount;
  200. const adCount = this.dailyRewardData.moneyAdCount;
  201. const totalCount = freeCount + adCount;
  202. const maxCount = config.maxClaimsPerDay;
  203. // 更新次数显示
  204. if (this.moneyCountLabel) {
  205. this.moneyCountLabel.string = `${totalCount}/${maxCount}`;
  206. }
  207. // 更新金额显示
  208. if (this.moneyAmountLabel) {
  209. this.moneyAmountLabel.string = this.formatNumberWithSpaces(config.baseAmount);
  210. }
  211. // 更新按钮状态(带广告标识的按钮)
  212. if (this.moneyButton) {
  213. const canClaim = totalCount < maxCount;
  214. this.moneyButton.interactable = canClaim;
  215. // 更新按钮文本
  216. const buttonLabel = this.moneyButton.node.getChildByName('Label')?.getComponent(Label);
  217. if (buttonLabel) {
  218. if (!canClaim) {
  219. buttonLabel.string = "今日已达上限";
  220. } else {
  221. buttonLabel.string = "观看广告";
  222. }
  223. }
  224. }
  225. }
  226. private updateDiamondRewardUI() {
  227. if (!this.shopConfig || !this.dailyRewardData) return;
  228. const config = this.shopConfig.dailyRewards.diamond;
  229. const freeCount = this.dailyRewardData.diamondsFreeCount;
  230. const adCount = this.dailyRewardData.diamondsAdCount;
  231. const totalCount = freeCount + adCount;
  232. const maxCount = config.maxClaimsPerDay;
  233. // 更新次数显示
  234. if (this.diamondCountLabel) {
  235. this.diamondCountLabel.string = `${totalCount}/${maxCount}`;
  236. }
  237. // 更新金额显示
  238. if (this.diamondAmountLabel) {
  239. this.diamondAmountLabel.string = this.formatNumberWithSpaces(config.baseAmount);
  240. }
  241. // 更新按钮状态(带广告标识的按钮)
  242. if (this.diamondButton) {
  243. const canClaim = totalCount < maxCount;
  244. this.diamondButton.interactable = canClaim;
  245. // 更新按钮文本
  246. const buttonLabel = this.diamondButton.node.getChildByName('Label')?.getComponent(Label);
  247. if (buttonLabel) {
  248. if (!canClaim) {
  249. buttonLabel.string = "今日已达上限";
  250. } else {
  251. buttonLabel.string = "观看广告";
  252. }
  253. }
  254. }
  255. }
  256. // 钞票奖励按钮点击事件(带广告标识的按钮)
  257. public onMoneyRewardClick() {
  258. if (!this.shopConfig || !this.dailyRewardData) return;
  259. const config = this.shopConfig.dailyRewards.money;
  260. const freeCount = this.dailyRewardData.moneyFreeCount;
  261. const adCount = this.dailyRewardData.moneyAdCount;
  262. const totalCount = freeCount + adCount;
  263. const maxCount = config.maxClaimsPerDay;
  264. if (totalCount >= maxCount) {
  265. console.log('[ShopController] 钞票奖励已达每日上限');
  266. return;
  267. }
  268. // 看广告增加次数
  269. this.showAdForMoneyReward();
  270. }
  271. // 钻石奖励按钮点击事件(带广告标识的按钮)
  272. public onDiamondRewardClick() {
  273. if (!this.shopConfig || !this.dailyRewardData) return;
  274. const config = this.shopConfig.dailyRewards.diamond;
  275. const freeCount = this.dailyRewardData.diamondsFreeCount;
  276. const adCount = this.dailyRewardData.diamondsAdCount;
  277. const totalCount = freeCount + adCount;
  278. const maxCount = config.maxClaimsPerDay;
  279. if (totalCount >= maxCount) {
  280. console.log('[ShopController] 钻石奖励已达每日上限');
  281. return;
  282. }
  283. // 看广告增加次数
  284. this.showAdForDiamondReward();
  285. }
  286. private claimMoneyReward(isFromAd: boolean) {
  287. const amount = this.shopConfig.dailyRewards.money.baseAmount;
  288. // 添加钞票
  289. const success = this.saveDataManager.addMoney(amount, isFromAd ? 'shop_ad_reward' : 'shop_free_reward');
  290. if (success) {
  291. // 更新领取次数
  292. if (isFromAd) {
  293. this.dailyRewardData.moneyAdCount++;
  294. } else {
  295. this.dailyRewardData.moneyFreeCount++;
  296. }
  297. this.saveDailyRewardData();
  298. this.updateUI();
  299. console.log(`[ShopController] 成功领取钞票奖励: ${amount}, 来源: ${isFromAd ? '广告' : '免费'}`);
  300. // 可以在这里添加奖励动画或提示
  301. this.showRewardEffect('money', amount);
  302. } else {
  303. console.error('[ShopController] 领取钞票奖励失败');
  304. }
  305. }
  306. private claimDiamondReward(isFromAd: boolean) {
  307. const config = this.shopConfig.dailyRewards.diamond;
  308. const amount = config.baseAmount;
  309. // 添加钻石
  310. const success = this.saveDataManager.addDiamonds(amount, isFromAd ? 'shop_ad_reward' : 'shop_free_reward');
  311. if (success) {
  312. // 更新领取次数
  313. if (isFromAd) {
  314. this.dailyRewardData.diamondsAdCount++;
  315. } else {
  316. this.dailyRewardData.diamondsFreeCount++;
  317. }
  318. this.saveDailyRewardData();
  319. this.updateUI();
  320. console.log(`[ShopController] 成功领取钻石奖励: ${amount}, 来源: ${isFromAd ? '广告' : '免费'}`);
  321. // 可以在这里添加奖励动画或提示
  322. this.showRewardEffect('diamonds', amount);
  323. } else {
  324. console.error('[ShopController] 领取钻石奖励失败');
  325. }
  326. }
  327. private showAdForMoneyReward() {
  328. console.log('[ShopController] 显示钞票奖励广告');
  329. // 这里应该调用广告SDK显示广告
  330. // 立即发放奖励,无延迟
  331. console.log('[ShopController] 广告观看完成,发放钞票奖励');
  332. this.claimMoneyReward(true);
  333. }
  334. private showAdForDiamondReward() {
  335. console.log('[ShopController] 显示钻石奖励广告');
  336. // 这里应该调用广告SDK显示广告
  337. // 立即发放奖励,无延迟
  338. console.log('[ShopController] 广告观看完成,发放钻石奖励');
  339. this.claimDiamondReward(true);
  340. }
  341. private showRewardEffect(type: 'money' | 'diamonds', amount: number) {
  342. // 这里可以添加奖励特效
  343. console.log(`[ShopController] 显示奖励特效: ${type} +${amount}`);
  344. // 可以触发TopBarController的货币动画
  345. const topBarController = this.node.parent?.getComponentInChildren(TopBarController);
  346. if (topBarController) {
  347. // 如果TopBarController有显示奖励动画的方法,可以在这里调用
  348. }
  349. }
  350. // 更新钞票Cost按钮UI状态(底部免费按钮)
  351. public updateBillCostButtonUI() {
  352. if (!this.dailyRewardData) {
  353. console.log('[ShopController] dailyRewardData为空,跳过更新');
  354. return;
  355. }
  356. const costFreeUsed = this.dailyRewardData.billCostFreeUsed !== undefined ? this.dailyRewardData.billCostFreeUsed : true;
  357. console.log('[ShopController] 更新钞票Cost按钮UI状态,billCostFreeUsed:', costFreeUsed);
  358. if (this.billCostButton) {
  359. const buttonLabel = this.billCostButton.node.getChildByName('Label')?.getComponent(Label);
  360. const buttonSprite = this.billCostButton.node.getComponent(Sprite);
  361. console.log('[ShopController] 按钮组件检查 - Label:', !!buttonLabel, 'Sprite:', !!buttonSprite);
  362. if (costFreeUsed) {
  363. // 每天第一次,显示"免费"
  364. this.billCostButton.interactable = true;
  365. if (buttonLabel) {
  366. buttonLabel.string = "免费";
  367. }
  368. // 恢复原始图片
  369. if (buttonSprite && this.originalBillButtonSprite) {
  370. buttonSprite.spriteFrame = this.originalBillButtonSprite;
  371. console.log('[ShopController] 恢复按钮原始图片');
  372. }
  373. } else {
  374. // 免费已使用,按钮置灰,并更新对应的数值显示
  375. this.billCostButton.interactable = false;
  376. if (buttonLabel) {
  377. buttonLabel.string = "已使用";
  378. }
  379. // 设置置灰图片
  380. console.log('[ShopController] 尝试设置置灰图片 - buttonSprite:', !!buttonSprite, 'disabledButtonSprite:', !!this.disabledButtonSprite);
  381. if (buttonSprite && this.disabledButtonSprite) {
  382. buttonSprite.spriteFrame = this.disabledButtonSprite;
  383. console.log('[ShopController] 设置按钮为置灰图片成功');
  384. } else {
  385. console.log('[ShopController] 警告:无法设置置灰图片 - buttonSprite:', !!buttonSprite, 'disabledButtonSprite:', !!this.disabledButtonSprite);
  386. }
  387. // 更新钞票数值显示
  388. this.updateMoneyAmountFromConfig();
  389. }
  390. }
  391. }
  392. // 更新钻石Cost按钮UI状态(底部免费按钮)
  393. public updateDiamondCostButtonUI() {
  394. if (!this.dailyRewardData) {
  395. console.log('[ShopController] dailyRewardData为空,跳过更新');
  396. return;
  397. }
  398. const costFreeUsed = this.dailyRewardData.diamondCostFreeUsed !== undefined ? this.dailyRewardData.diamondCostFreeUsed : true;
  399. console.log('[ShopController] 更新钻石Cost按钮UI状态,diamondCostFreeUsed:', costFreeUsed);
  400. if (this.diamondCostButton) {
  401. const buttonLabel = this.diamondCostButton.node.getChildByName('Label')?.getComponent(Label);
  402. const buttonSprite = this.diamondCostButton.node.getComponent(Sprite);
  403. if (costFreeUsed) {
  404. // 每天第一次,显示"免费"
  405. this.diamondCostButton.interactable = true;
  406. if (buttonLabel) {
  407. buttonLabel.string = "免费";
  408. }
  409. // 恢复原始图片
  410. if (buttonSprite && this.originalDiamondButtonSprite) {
  411. buttonSprite.spriteFrame = this.originalDiamondButtonSprite;
  412. }
  413. } else {
  414. // 免费已使用,按钮置灰,并更新对应的数值显示
  415. this.diamondCostButton.interactable = false;
  416. if (buttonLabel) {
  417. buttonLabel.string = "已使用";
  418. }
  419. // 设置置灰图片
  420. if (buttonSprite && this.disabledButtonSprite) {
  421. buttonSprite.spriteFrame = this.disabledButtonSprite;
  422. }
  423. // 更新钻石数值显示
  424. this.updateDiamondAmountFromConfig();
  425. }
  426. }
  427. }
  428. // 从配置JSON更新钞票数值显示
  429. private updateMoneyAmountFromConfig() {
  430. if (this.moneyAmountLabel && this.shopConfig) {
  431. // 从配置中读取下一次的钞票数值
  432. const nextMoneyAmount = this.shopConfig.dailyRewards.money.baseAmount || 100;
  433. this.moneyAmountLabel.string = this.formatNumberWithSpaces(nextMoneyAmount);
  434. console.log('[ShopController] 从配置更新钞票数值:', nextMoneyAmount);
  435. }
  436. }
  437. // 从配置JSON更新钻石数值显示
  438. private updateDiamondAmountFromConfig() {
  439. if (this.diamondAmountLabel && this.shopConfig) {
  440. // 从配置中读取下一次的钻石数值
  441. const nextDiamondAmount = this.shopConfig.dailyRewards.diamond.baseAmount || 10;
  442. this.diamondAmountLabel.string = this.formatNumberWithSpaces(nextDiamondAmount);
  443. console.log('[ShopController] 从配置更新钻石数值:', nextDiamondAmount);
  444. }
  445. }
  446. // 钞票Cost按钮点击事件(底部免费按钮)
  447. public onBillCostClick() {
  448. console.log('[ShopController] 钞票Cost按钮被点击');
  449. if (!this.dailyRewardData) {
  450. console.log('[ShopController] dailyRewardData为空,无法处理点击');
  451. return;
  452. }
  453. const costFreeUsed = this.dailyRewardData.billCostFreeUsed;
  454. console.log('[ShopController] 当前billCostFreeUsed状态:', costFreeUsed);
  455. if (costFreeUsed) {
  456. // 第一次免费点击
  457. this.dailyRewardData.billCostFreeUsed = false;
  458. // 增加钞票计数
  459. this.dailyRewardData.moneyFreeCount++;
  460. this.saveDailyRewardData();
  461. console.log('[ShopController] 钞票免费按钮使用完毕,更新UI');
  462. this.updateBillCostButtonUI(); // 立即更新按钮UI
  463. this.updateUI(); // 更新整体UI
  464. return;
  465. }
  466. console.log('[ShopController] 钞票免费按钮今日已使用');
  467. }
  468. // 钻石Cost按钮点击事件(底部免费按钮)
  469. public onDiamondCostClick() {
  470. if (!this.dailyRewardData) return;
  471. const costFreeUsed = this.dailyRewardData.diamondCostFreeUsed;
  472. if (costFreeUsed) {
  473. // 第一次免费点击
  474. this.dailyRewardData.diamondCostFreeUsed = false;
  475. // 增加钻石计数
  476. this.dailyRewardData.diamondsFreeCount++;
  477. this.saveDailyRewardData();
  478. this.updateUI();
  479. console.log('[ShopController] 钻石免费按钮使用完毕,钻石计数增加');
  480. return;
  481. }
  482. console.log('[ShopController] 钻石免费按钮今日已使用');
  483. }
  484. onDestroy() {
  485. // 移除事件监听
  486. EventBus.getInstance().off(GameEvents.CURRENCY_CHANGED, this.updateUI, this);
  487. }
  488. // 调试方法:重置每日奖励数据
  489. public resetDailyRewards() {
  490. const today = this.getCurrentDateString();
  491. this.resetDailyRewardData(today);
  492. this.updateUI();
  493. console.log('[ShopController] 每日奖励数据已重置');
  494. }
  495. // 获取当前每日奖励状态(用于调试)
  496. public getDailyRewardStatus() {
  497. return {
  498. config: this.shopConfig,
  499. data: this.dailyRewardData,
  500. today: this.getCurrentDateString()
  501. };
  502. }
  503. }