WheelDialog.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. import { _decorator, Component, Node, tween, Vec3 } from "cc";
  2. import BaseUI from "../../scripts/base/BaseUI";
  3. import { Hall } from "../hall/Hall";
  4. import { RewardLayer } from "../layer/RewardLayer";
  5. import { GoodsId } from "../../scripts/api/GoodsId";
  6. import { Label } from "cc";
  7. import UserM from "../../scripts/api/UserM";
  8. import Utils from "../../scripts/utils/Utils";
  9. import { CritLayer } from "../layer/CritLayer";
  10. import { UIOpacity } from "cc";
  11. import UIUtils from "../../scripts/base/UIUtils";
  12. import { Animation } from "cc";
  13. import { isValid } from "cc";
  14. import { Sprite } from "cc";
  15. import { FlyContainer } from "../hall/FlyContainer";
  16. import GameM, { GameSpinResult } from "../../scripts/api/GameM";
  17. import ItemsM from "../../scripts/mgr/ItemsM";
  18. import { Widget } from "cc";
  19. const { ccclass, property } = _decorator;
  20. @ccclass("WheelDialog")
  21. export class WheelDialog extends BaseUI {
  22. static async show(gameId: number) {
  23. let dialog = await Hall.ins.showLayer("prefab/play/WheelDialog");
  24. dialog.getComponent(WheelDialog).init(gameId);
  25. }
  26. private gameId: number;
  27. init(gameId: number) {
  28. this.gameId = gameId;
  29. this.requestAndStartSpin();
  30. }
  31. private static mock = 0;
  32. private data: GameSpinResult;
  33. async requestAndStartSpin() {
  34. let result: GameSpinResult = await GameM.ins.gameSpin(this.gameId);
  35. if (!result) {
  36. return;
  37. }
  38. this.data = result;
  39. let pog = this.data.spinPog;
  40. let index = 0;
  41. for (let i = 0; i < WheelDialog.REWARDS.length; i++) {
  42. let reward = WheelDialog.REWARDS[i];
  43. if (reward == pog) {
  44. index = i;
  45. break;
  46. }
  47. }
  48. this.spinToIndex(index);
  49. }
  50. private rotateSprite: Node;
  51. private isSpinning: boolean = false;
  52. private currentAngle: number = 0;
  53. @property(Number)
  54. public wheelRewardAllCount: number = 8;
  55. @property(Number)
  56. public spinDuration: number = 3.0; // 轉動持續時間
  57. @property(Number)
  58. public spinRounds: number = 5; // 轉動圈數
  59. private static readonly REWARDS = [
  60. 25, 50, 100, 200, 500, 1200, 2500, 5000, 10000, 0,
  61. ];
  62. private btn_use_crit_card: Node;
  63. private _rewardIndex: number = 0;
  64. protected onLoad(): void {
  65. super.onLoad();
  66. this.btn_use_crit_card = this.FindNode("btn_use_crit_card");
  67. this.btn_use_crit_card.active = false;
  68. this.rotateSprite = this.FindNode("rotateSprite");
  69. this.FindNode("wheel3").active = true;
  70. this.setText(
  71. "lbl_crit_card_count",
  72. "x" + UserM.ins.getGoodsCount(GoodsId.POG_CRITICAL_CARD)
  73. );
  74. }
  75. protected simpleOnBtnClick(name: string): void {
  76. if (name.startsWith("btn_start_")) {
  77. let to = parseInt(name.replace("btn_start_", "")) - 1;
  78. console.warn("to", to);
  79. this.spinToIndex(to);
  80. return;
  81. }
  82. switch (name) {
  83. case "btn_spin":
  84. if (!this.isSpinning) {
  85. // this.spinToIndex(2);
  86. }
  87. break;
  88. case "btn_use_crit_card":
  89. this.useCritCard();
  90. break;
  91. }
  92. }
  93. useCritCard() {
  94. if (!this.canUserCrit()) {
  95. return;
  96. }
  97. let reward = this.data?.totalPog;
  98. if (reward > 0) {
  99. CritLayer.show(this.gameId, this.data.spinId, reward);
  100. this.resetWheel();
  101. this.closePage();
  102. }
  103. }
  104. /**
  105. * 轉動到指定索引的獎品
  106. * @param index 獎品索引 (0 到 wheelRewardAllCount-1)
  107. */
  108. onStartSpin() {
  109. this.btn_use_crit_card.active = false;
  110. let anim_layout = this.FindNode("anim_layout");
  111. for (let i = 0; i < anim_layout.children.length; i++) {
  112. let child = anim_layout.children[i];
  113. child.active = false;
  114. }
  115. }
  116. spinToIndex(index: number) {
  117. this._rewardIndex = index;
  118. if (this.isSpinning) {
  119. console.warn("轉盤正在轉動中,請稍後再試");
  120. return;
  121. }
  122. this.onStartSpin();
  123. this.resetWheel();
  124. // 確保索引在有效範圍內
  125. if (index < 0 || index >= this.wheelRewardAllCount) {
  126. console.error(
  127. `無效的獎品索引: ${index}, 有效範圍: 0-${this.wheelRewardAllCount - 1}`
  128. );
  129. return;
  130. }
  131. this.isSpinning = true;
  132. // 計算每個獎品的角度
  133. const anglePerReward = 360 / this.wheelRewardAllCount;
  134. // 計算目標角度
  135. // 轉盤順時針轉動,索引0在頂部12點鐘方向
  136. // 每個獎品區域的中心角度
  137. const targetAngle = anglePerReward * index + anglePerReward / 2;
  138. // 計算總轉動角度(多轉幾圈然後停在目標位置)
  139. // 從當前角度開始轉動
  140. const totalRotation =
  141. this.currentAngle + this.spinRounds * 360 + targetAngle;
  142. // 停止之前的動畫
  143. tween(this.rotateSprite).stop();
  144. // 開始新的轉動動畫
  145. tween(this.rotateSprite)
  146. .to(
  147. this.spinDuration,
  148. { angle: totalRotation },
  149. {
  150. easing: "expoOut", // 使用指數緩出效果,快速開始然後急劇減速
  151. }
  152. )
  153. .call(() => {
  154. // 動畫完成後的回調
  155. this.currentAngle = totalRotation;
  156. this.onSpinComplete(index);
  157. })
  158. .start();
  159. }
  160. /**
  161. * 轉動完成後的回調
  162. * @param index 最終停止的獎品索引
  163. */
  164. private onSpinComplete(index: number) {
  165. this.isSpinning = false;
  166. console.log(`轉盤停止在獎品索引: ${index}`);
  167. this.btn_use_crit_card.active = true;
  168. if (this.canUserCrit()) {
  169. this.FindAs("btn_use_crit_card", Sprite).grayscale = false;
  170. } else {
  171. this.FindAs("btn_use_crit_card", Sprite).grayscale = true;
  172. }
  173. // 這裡可以添加獲獎邏輯
  174. this.onReward(index);
  175. }
  176. canUserCrit() {
  177. let can =
  178. !this.isSpinning &&
  179. this._rewardIndex != 9 &&
  180. UserM.ins.getGoodsCount(GoodsId.POG_CRITICAL_CARD) > 0;
  181. return can;
  182. }
  183. protected update(dt: number): void {
  184. if (this.FindNode("bottom") == null) {
  185. return;
  186. }
  187. this.FindAs("bottom", Widget).updateAlignment();
  188. }
  189. /**
  190. * 獲獎處理
  191. * @param index 獎品索引
  192. */
  193. private onReward(index: number) {
  194. let self = this;
  195. console.log(`恭喜獲得獎品索引: ${index}`);
  196. let first_value = this.data.spinPog;
  197. this.setText("lbl_add_value_1", "+" + first_value);
  198. // let rank_percent = this.data.ranKAddition / 100;
  199. this.setText("lbl_add_value_rank_percent", this.data.ranKAddition+ "%");
  200. // let pass_percent = this.data.gamePassAddition / 100;
  201. this.setText("lbl_add_value_pass_percent", this.data.gamePassAddition + "%");
  202. this.setText(
  203. "lbl_add_value_2",
  204. "+" + Utils.formatNumber(this.data.rankPog, 2)
  205. );
  206. this.setText(
  207. "lbl_add_value_3",
  208. "+" + Utils.formatNumber(this.data.gamePassPog, 2)
  209. );
  210. this.setText(
  211. "lbl_add_value_4",
  212. "+" + Utils.formatNumber(this.data.totalPog, 2)
  213. );
  214. let layout = this.FindNode("anim_layout");
  215. for (let i = 0; i < layout.children.length - 1; i++) {
  216. let child = layout.children[i];
  217. child.active = true;
  218. child.getComponent(UIOpacity).opacity = 255;
  219. UIUtils.fadeIn(child, 0.4 * i);
  220. }
  221. setTimeout(() => {
  222. if (!isValid(self.node)) {
  223. return;
  224. }
  225. let final = layout.children[layout.children.length - 1].children[0];
  226. final.parent.active = true;
  227. final.setPosition(0, -300, 0);
  228. UIUtils.AnimBezierMoveAndScale(final, new Vec3(0, 0, 0), 1.2, 0.5, () => {
  229. if (!isValid(self.node)) {
  230. return;
  231. }
  232. if (self.data.totalPog <= 0) {
  233. self.closePage();
  234. return;
  235. }
  236. // let changeGoodList = [
  237. // ];
  238. // UserM.ins.data.goodList.forEach((item) => {
  239. // if (item.id == GoodsId.POG) {
  240. // item.count += self.data.totalPog;
  241. // }
  242. // });
  243. UserM.ins.refreshGoods(self.data.goodList);
  244. // ItemsM.ins.itemChange(changeGoodList,self.data.goodList );
  245. });
  246. }, 1000);
  247. }
  248. /**
  249. * 重置轉盤角度
  250. */
  251. public resetWheel() {
  252. tween(this.rotateSprite).stop();
  253. this.rotateSprite.angle = 0;
  254. this.currentAngle = 0;
  255. this.isSpinning = false;
  256. }
  257. /**
  258. * 檢查轉盤是否正在轉動
  259. */
  260. public getIsSpinning(): boolean {
  261. return this.isSpinning;
  262. }
  263. /**
  264. * 開始轉動到指定索引
  265. * @param index 要轉到的獎品索引
  266. */
  267. public startSpin(index: number) {
  268. if (!this.isSpinning) {
  269. this.spinToIndex(index);
  270. }
  271. }
  272. }