diff --git a/package-lock.json b/package-lock.json index b5e6e3d..bb98160 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "vue-loading-overlay": "^5.0.3" }, "devDependencies": { + "@types/node": "^17.0.24", "@vitejs/plugin-vue": "^2.3.0", "typescript": "^4.5.4", "vite": "^2.9.0", @@ -157,6 +158,12 @@ "@types/lodash": "*" } }, + "node_modules/@types/node": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", + "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==", + "dev": true + }, "node_modules/@vitejs/plugin-vue": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.1.tgz", @@ -1940,6 +1947,12 @@ "@types/lodash": "*" } }, + "@types/node": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", + "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==", + "dev": true + }, "@vitejs/plugin-vue": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.1.tgz", diff --git a/package.json b/package.json index 2846290..7d96b9c 100644 --- a/package.json +++ b/package.json @@ -23,4 +23,4 @@ "vite": "^2.9.0", "vue-tsc": "^0.29.8" } -} +} \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 49ad098..8315cad 100644 --- a/src/App.vue +++ b/src/App.vue @@ -43,6 +43,10 @@ const handleClick = (tab: TabsPaneContext, event: Event) => { float: none; } +.el-tabs__item { + font-size: 25px; +} + #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; diff --git a/src/components/BJ_Casino_Magnification.vue b/src/components/BJ_Casino_Magnification.vue index 76073d7..8acda33 100644 --- a/src/components/BJ_Casino_Magnification.vue +++ b/src/components/BJ_Casino_Magnification.vue @@ -5,19 +5,11 @@ import { BJ_Casino_Magnification } from "../script/BJ_Casino_Magnification"; const props = defineProps<{ BJ_Casino: BJ_Casino_Data }>() -// const props = defineProps({ -// list: { -// type: Array, -// default: () => [], -// }, -// }) - let Title = ref(""); let RankData = ref([]); const self = { Title: Title, RankData: RankData, - // BJ_Casino: props.list[0].value, BJ_Casino: props.BJ_Casino, } const Script = new BJ_Casino_Magnification(self); diff --git a/src/components/BJ_Casino_WinMoney.vue b/src/components/BJ_Casino_WinMoney.vue index c1e28ac..13205e4 100644 --- a/src/components/BJ_Casino_WinMoney.vue +++ b/src/components/BJ_Casino_WinMoney.vue @@ -5,19 +5,11 @@ import { BJ_Casino_WinMoney } from "../script/BJ_Casino_WinMoney"; const props = defineProps<{ BJ_Casino: BJ_Casino_Data }>() -// const props = defineProps({ -// list: { -// type: Array, -// default: () => [], -// }, -// }) - let Title = ref(""); let RankData = ref([]); const self = { Title: Title, RankData: RankData, - // BJ_Casino: props.list[0].value, BJ_Casino: props.BJ_Casino, } const Script = new BJ_Casino_WinMoney(self); @@ -32,7 +24,7 @@ const Script = new BJ_Casino_WinMoney(self); 排名 名稱 - 倍率 + 贏分 機台 桌號 日期 diff --git a/src/script/BJ_Casino_Data.ts b/src/script/BJ_Casino_Data.ts index beab4fc..bf14c6b 100644 --- a/src/script/BJ_Casino_Data.ts +++ b/src/script/BJ_Casino_Data.ts @@ -1,4 +1,12 @@ import moment from 'moment'; +import CSMessage from './Base/CSMessage'; +import { AccountLoginRequest } from './Base/Request/AccountRequest'; +import { AppRankHistory, AppRankInfo } from './Base/Request/RankRequest'; +import './Engine/CatanEngine/CSharp/String'; +import { INetResponse } from './Engine/CatanEngine/NetManagerV2/Core/INetResponse'; +import { NetConnector } from './Engine/CatanEngine/NetManagerV2/NetConnector'; +import { NetManager } from './Engine/CatanEngine/NetManagerV2/NetManager'; +import { Tools } from './Tools'; export class BJ_Casino_Data { @@ -14,12 +22,15 @@ export class BJ_Casino_Data { //#region private + private _conn: NetConnector = null!; private _ws: any; private _rankMagnificationData: any[] = []; private _rankWinMoneyData: any[] = []; - private _nowSearchContestID: number = 0; + private _nowSearchMagnificationID: number = 0; + + private _nowSearchWinMoneyID: number = 0; private _nowContestIndex: number = 0; @@ -47,6 +58,8 @@ export class BJ_Casino_Data { public get RankMagnificationData(): any[] { return this._rankMagnificationData }; + public get RankWinMoneyData(): any[] { return this._rankWinMoneyData }; + //#endregion //#region Lifecycle @@ -59,14 +72,28 @@ export class BJ_Casino_Data { this.onLoad(); } - public onLoad() { + public async onLoad() { + // CoroutineV2.Single(this.aaa()).Start(); let self: this = this; + const URL = "https://game.online-bj.com"; + const Port = "9005"; + await this.ConnectAsync(URL, +Port); + // 取得帳號資料 + let req: AccountLoginRequest = new AccountLoginRequest("ct00000691", "4lsAyoalajm7"); + await req.SendAsync(true); + let resp: INetResponse = req.Result; + if (!resp.IsValid) { + CSMessage.NetError(resp.Method, resp.Status, "Login Account Error!"); + return; + } + await this.SendRankData(); + return; try { // const URL = document.getElementById("URL").value; // const Port = document.getElementById("Port").value; - const URL = "wss://game.online-bj.com"; - const Port = "9005"; + // const URL = "wss://game.online-bj.com"; + // const Port = "9005"; const Account = { "p": 0, "device_info": ["Windows", "Windows"], "fcm_token": "", "a": "ct00000691", "pw": "4lsAyoalajm7", "ver": "1.3.0" }; // const URL = "ws://192.168.5.12"; @@ -105,11 +132,92 @@ export class BJ_Casino_Data { this.AddLog(ex); } }; + public *aaa(): IterableIterator { + console.log("aaa"); + } + + //#endregion + + //#region 網路相關 + + /**連線(目前沒有重連機制) */ + public async ConnectAsync(host: string, port: number) { + var url = "https://api.ipify.org/?format=json"; + var xhr = new XMLHttpRequest(); + let ip: string = ""; + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && (xhr.status >= 200 && xhr.status < 400)) { + ip = JSON.parse(xhr.responseText)["ip"]; + } + }; + xhr.open("GET", url, true); + xhr.send(); + console.log("[事件]準備連線..."); + while (ip == "") { + await Tools.Sleep(1); + } + this._conn = new NetConnector(host, port, ip); + this._conn.OnDataReceived.AddCallback(this._onNetDataReceived, this); + this._conn.OnDisconnected.AddCallback(this._onNetDisconnected, this); + NetManager.Initialize(this._conn); + console.log("[事件]連線中..."); + // 同個connector要再次連線, 可以不用叫CasinoNetManager.Initialize(), 但要先叫CasinoNetManager.Disconnect() + await NetManager.ConnectAsync(); + console.log(String.Format("[事件]連線狀態: {0}", NetManager.IsConnected)); + } + + private _onNetDisconnected() { + console.log("[事件] 收到連線中斷事件"); + this._conn.OnDataReceived.RemoveAllCallbacks(); + // MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.NetDisconnected]); + } + + private _onNetDataReceived(resp: INetResponse) { + console.log(`[事件] 收到server呼叫: ${resp.Method}(${JSON.stringify(resp.Data)}), 狀態: ${resp.Status}`); + // MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.ServerData, resp]); + } //#endregion //#region Custom + public async SendRankData() { + this.SendRankMagnificationData(); + this.SendRankWinMoneyData(); + } + + public async SendRankMagnificationData() { + let req: any = null; + req = new AppRankInfo(12, 2); + await req.SendAsync(true); + let resp: INetResponse = req.Result; + if (!resp.IsValid) { + if (resp.Status == 11) { + CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料"); + } else { + CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail"); + } + return; + } + await this.RankMagnificationDataCallBack(resp.Data); + } + + public async SendRankWinMoneyData() { + let req: any = null; + req = new AppRankInfo(11, 2); + await req.SendAsync(true); + let resp: INetResponse = req.Result; + if (!resp.IsValid) { + if (resp.Status == 11) { + CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料"); + } else { + CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail"); + } + return; + } + await this.RankWinMoneyDataCallBack(resp.Data); + } + public SendData(Method: string, Data: any) { let json = [Method]; if (Data != null && Data != undefined) { @@ -160,9 +268,9 @@ export class BJ_Casino_Data { } } - public RankMagnificationDataCallBack(data: any) { + public async RankMagnificationDataCallBack(data: any) { let id = +data["id"]; - this._nowSearchContestID = id; + this._nowSearchMagnificationID = id; this._nowContestID = id; this._nowContestDate = moment().format("MM/DD"); for (let i = 0; i < this.ContestData.length; i++) { @@ -185,27 +293,33 @@ export class BJ_Casino_Data { this.ParseRankMagnificationData(data); } - public ParseRankMagnificationData(data: any) { - let id = this._nowSearchContestID; - this.RankDataMagnificationAddDate(id, data["rank"]); + public async ParseRankMagnificationData(data: any) { + let id = this._nowSearchMagnificationID; + this.RankDataAddDate(id, data["rank"]); this._rankMagnificationData = this._rankMagnificationData.concat(data["rank"]); if (id !== this._nowContestStartIndex) { - this._nowSearchContestID = id - 1; - this.SendData("rank.history", { "id": this._nowSearchContestID, "t": 12, "p": 2 }); + this._nowSearchMagnificationID = id - 1; + // this.SendData("rank.history", { "id": this._nowSearchContestID, "t": 12, "p": 2 }); + let req: any = null; + req = new AppRankHistory(12, 2, this._nowSearchMagnificationID); + await req.SendAsync(true); + let resp: INetResponse = req.Result; + if (!resp.IsValid) { + if (resp.Status == 11) { + CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料"); + } else { + CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail"); + } + return; + } + this.ParseRankMagnificationData(resp.Data); + return; } else { this.OrganizeRankMagnificationData(this._rankMagnificationData); this.SetRankMagnificationData(this._rankMagnificationData); } } - public RankDataMagnificationAddDate(id: number, rankdata: any) { - let date: string = this._contestDateFormID(id); - for (let i = 0; i < rankdata.length; i++) { - rankdata[i].push(date); - } - return rankdata; - } - public OrganizeRankMagnificationData(rankdata: any) { rankdata.sort((a: any, b: any) => { return b[1] - a[1]; @@ -220,16 +334,73 @@ export class BJ_Casino_Data { } public SetRankMagnificationData(rankdata: any) { - this._ws.close(); this._isOK[0] = true; + this._checkOK(); + } + + public async RankWinMoneyDataCallBack(data: any) { + let id = +data["id"]; + this._nowSearchWinMoneyID = id; + this.ParseRankWinMoneyData(data); + } + + public async ParseRankWinMoneyData(data: any) { + let id = this._nowSearchWinMoneyID; + this.RankDataAddDate(id, data["rank"]); + this._rankWinMoneyData = this._rankWinMoneyData.concat(data["rank"]); + if (id !== this._nowContestStartIndex) { + this._nowSearchWinMoneyID = id - 1; + let req: any = null; + req = new AppRankHistory(11, 2, this._nowSearchWinMoneyID); + await req.SendAsync(true); + let resp: INetResponse = req.Result; + if (!resp.IsValid) { + if (resp.Status == 11) { + CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料"); + } else { + CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail"); + } + return; + } + this.ParseRankWinMoneyData(resp.Data); + return; + } else { + this.OrganizeRankWinMoneyData(this._rankWinMoneyData); + this.SetRankWinMoneyData(this._rankWinMoneyData); + } + } + + public OrganizeRankWinMoneyData(rankdata: any) { + rankdata.sort((a: any, b: any) => { + return b[1] - a[1]; + }); + rankdata = rankdata.filter((rankdata: any, index: any, arr: any) => { + return arr.findIndex((s: any) => rankdata[2][1] === s[2][1]) === index; + }); + for (let i = 0; i < rankdata.length; i++) { + rankdata[i][0] = i + 1; + } + this._rankWinMoneyData = rankdata; + } + + public SetRankWinMoneyData(rankdata: any) { this._isOK[1] = true; this._checkOK(); } + public RankDataAddDate(id: number, rankdata: any) { + let date: string = this._contestDateFormID(id); + for (let i = 0; i < rankdata.length; i++) { + rankdata[i].push(date); + } + return rankdata; + } + private _checkOK() { if (this._isOK.includes(false)) { return; } + NetManager.Disconnect(); this.Client.isLoading.value = false; } diff --git a/src/script/BJ_Casino_WinMoney.ts b/src/script/BJ_Casino_WinMoney.ts index 79dd3e8..87a9a5d 100644 --- a/src/script/BJ_Casino_WinMoney.ts +++ b/src/script/BJ_Casino_WinMoney.ts @@ -42,7 +42,7 @@ export class BJ_Casino_WinMoney { //#region Custom public SendData() { - this._client.RankData.value = this.BJ_Casino?.RankMagnificationData; + this._client.RankData.value = this.BJ_Casino?.RankWinMoneyData; } //#endregion diff --git a/src/script/Base/BusinessTypeSetting.ts b/src/script/Base/BusinessTypeSetting.ts new file mode 100644 index 0000000..a3d6d7b --- /dev/null +++ b/src/script/Base/BusinessTypeSetting.ts @@ -0,0 +1,237 @@ +export module BusinessEnum { + export enum BusinessType { + Type1 = "H5", + Type2 = "App" + } + export enum ServerType { + /** WEB格式 */ + Web = 1, + /** 外版 */ + Out = 2, + /** 送審環境 */ + Submit = 3, + /** 內版商業DEMO測試(B2B) */ + Internal_release = 4, + /** 內版開發(內網&4G) */ + Internal_Dev = 5, + /** 外部商業DEMO(B2B) */ + Out_B2B = 6 + } + export enum LogoType { + /** 完美(目前只有WEB) */ + WM = 1 + } +} + +/** +產品商業類別設定檔 +@explain 讀不同表就代表不同商業類別.要多開GIT並設定新測試環境 +@explain 遊戲一定都一樣不能改動介面 + */ +export default class BusinessTypeSetting { + /** 產品商業類別字串(組合判斷用) */ + public static readonly TYPE_BUSINESS: string = "App"; + /** 編譯版本 */ + public static readonly COMPILE_VERSION: string = "1.3.1"; + /** 編譯程式碼版號 */ + public static readonly SCRIPT_BUNLE_LIST: string[] = [ + "FormTableScript", "CommonScript", "ElementUIScript", "LoginScript", "LobbyScript", "SlotScript" + ]; + public static readonly ART_UI_BUNLE_LIST: string[] = [ + "Common", "CommonSound", "Login", "Lobby", "GameCommon", + "Game_BottomUI_SD", "Game_BigWinJackpot", "GameMessage", "CommonLanguageTexture", "MainControl", + "BindAccount", "SettingPanel", "Shop", "Vip", "Ad", + "Mail", "Chat", "PlayerInfo", "Rank", "Gift", + "ResourceItem", "Backpack", "GettingPanel", "Activity", "Game_BottomUI_BJ" + ]; + public static readonly DEV_ART_UI_BUNLE_LIST: string[] = [ + + ] + public static readonly ART_GAME_BUNLE_LIST: string[] = [ + "Game_1201", "Game_1202", "Game_1302" + ] + public static readonly ART_REMOTE_GAME_BUNLE_LIST: string[] = [ + "Game_1", "Game_2", "Game_5", "Game_8", "Game_9", + "Game_10", "Game_12", "Game_15", "Game_16", "Game_18", + "Game_23", "Game_24", "Game_25", "Game_26", "Game_27", + "Game_28", "Game_29", "Game_30", "Game_32", "Game_33", + "Game_34", "Game_35", "Game_37", "Game_36", "Game_40", + "Game_44", "Game_48", "Game_50", "Game_51", "Game_58", + "Game_1101", "Game_1401", "Game_1501", "Game_2001", "Game_2003", + "Game_3002", "Game_3003", "Game_3012", + ] + /** 送審旗標(讀取外版自動判斷是否送審) */ + public static IsSubmit: boolean = false; + /** 跑送審2的手動旗標(若是送審且設為true跑2號環境) */ + public static IsSubmitTestFlight: boolean = false; + /** B2B手動旗標(true寫死B2B環境) */ + public static IsB2B: boolean = false; + + /** 商業LOGO圖代碼(讀同一張表但UI有改的設定) */ + public static Logo: number = null!; + /** 連線IP(網頁版會接網址參數所以要多開變數直接指定) */ + public static UseHost: string = null!; + /** 連接阜(網頁版會接網址參數所以要多開變數直接指定) */ + public static UsePort: number = null!; + /** 資源伺服器網址 */ + public static UsePatch: string = null!; + /** 帳號 */ + public static Account: string = null!; + /** 密碼 */ + public static Password: string = null!; + + // ======================================================================================= + /** 執行環境ProductEnum.ServerType */ + public static UseServerTpye: BusinessEnum.ServerType = BusinessEnum.ServerType.Web; + /** 網頁是否在伺服器上 */ + public static readonly CheckOnServer: boolean = + window.location.href.indexOf("localhost") == -1 + && window.location.href.indexOf("/build/") == -1; + /** 網頁測試讀取對應資源的位置 */ + public static readonly FolderUrlImg: string = "shared/img/"; + public static readonly FolderUrlBg: string = "shared/bg/"; + public static readonly FolderUrlJson: string = "shared/jsons/"; + public static readonly FolderUrlTxt: string = "shared/txt/"; + public static readonly FolderUrlLoading: string = "shared/loading/"; + public static readonly FolderUrlMp3: string = "shared/"; + public static readonly FolderUrlBundle: string = `Bundle_${true ? "Debug" : "Release"}/`; + public static readonly FolderOS: string = "Android/"; + /**遠端Bundle路徑為: URL + BundleName.非遊戲資源到各自平台資料夾找BUNDLE */ + public static GetRemoteFileUrl(bundleName: string): string { + let gameNumber: string = bundleName.split("Game_")[1]; + var regExp: RegExp = /^[0-9]+$/; + let isGame: boolean = regExp.test(gameNumber); + let bundleUrl: string = `${BusinessTypeSetting.UsePatch}${BusinessTypeSetting.FolderUrlBundle}`; + if (isGame) { + bundleUrl = `${bundleUrl}${bundleName}`; + } else { + bundleUrl = `${bundleUrl}${BusinessTypeSetting.FolderOS}${bundleName}`; + } + return bundleUrl; + } + /** + * 取得PACH資原路徑 + * @param type 執行環境() + * @returns + */ + public static GetPatchUrl(type: BusinessEnum.ServerType): string { + if (this.UseServerTpye == BusinessEnum.ServerType.Web) { + // TYP2網頁版資源路路徑判斷 + if (this.CheckOnServer) { + return "../shared/"; + } else { + return "http://patch-dev.online-bj.com//shared/"; + } + } + switch (type) { + case BusinessEnum.ServerType.Out: + return "https://patch.online-bj.com/game/"; + case BusinessEnum.ServerType.Submit: + if (!this.IsSubmitTestFlight) { + return "https://patch-submit.online-bj.com/game/"; + } else { + return "https://patch-submit2.online-bj.com/game/"; + } + + case BusinessEnum.ServerType.Out_B2B: + return "https://patch-demo.online-bj.com/game/"; + case BusinessEnum.ServerType.Internal_release: + return "http://patch-release.online-bj.com/"; + + case BusinessEnum.ServerType.Internal_Dev: + return "http://patch-dev.online-bj.com/"; + + default: + console.warn("GetPatchUrl Uncheck ServerType."); + return "http://patch-dev.online-bj.com/"; + } + } + /** + * 取得連線伺服器IP + * @param type 執行環境 + * @returns + */ + public static GetHostUrl(type: BusinessEnum.ServerType): string { + if (this.UseServerTpye == BusinessEnum.ServerType.Web) { + return "app.casino.catan.com.tw"; + } + switch (type) { + case BusinessEnum.ServerType.Out: + return "https://game.online-bj.com"; + case BusinessEnum.ServerType.Submit: + if (!this.IsSubmitTestFlight) { + return "https://submit.online-bj.com"; + } else { + return "https://submit2.online-bj.com"; + } + + case BusinessEnum.ServerType.Out_B2B: + return "https://demo.online-bj.com"; + case BusinessEnum.ServerType.Internal_release: + return "https://demo.online-bj.com"; + + case BusinessEnum.ServerType.Internal_Dev: + return "http://220.134.195.1"; + + default: + console.warn("GetHostUrl Uncheck ServerType."); + // 只有內網可憐IP + return "app.casino.catan.com.tw"; + } + } + /** + * 取得伺服器連接端口 + * @param type 執行環境 + * @returns + */ + public static GetPortNum(type: BusinessEnum.ServerType): number { + if (this.UseServerTpye == BusinessEnum.ServerType.Web) { + // 網頁版測試專用.正式接網頁參數 + return 9005; + } + switch (type) { + case BusinessEnum.ServerType.Out: + return 9005; + case BusinessEnum.ServerType.Submit: + if (!this.IsSubmitTestFlight) { + return 9005; + } else { + return 9005; + } + + case BusinessEnum.ServerType.Out_B2B: + return 9005; + case BusinessEnum.ServerType.Internal_release: + return 9005; + + case BusinessEnum.ServerType.Internal_Dev: + return 19005; + + default: + console.warn("GePortNum Uncheck ServerType."); + return 9005; + } + } + public static GetDownloadUrl(type: BusinessEnum.ServerType): string { + switch (type) { + case BusinessEnum.ServerType.Internal_Dev: + return "http://static-dev.online-bj.com/"; + default: + let url: string = this.GetHostUrl(type); + url = url.replace("http://", ""); + url = url.replace("https://", ""); + return "https://static-" + url; + } + } + + public static GetUploadUrl(type: BusinessEnum.ServerType): string { + let port: string = ":9080"; + switch (type) { + case BusinessEnum.ServerType.Internal_Dev: + return "http://static-dev.online-bj.com" + port; + default: + return this.GetHostUrl(type) + port; + } + } + // ======================================================================================= +} \ No newline at end of file diff --git a/src/script/Base/CSMessage.ts b/src/script/Base/CSMessage.ts new file mode 100644 index 0000000..f7d554d --- /dev/null +++ b/src/script/Base/CSMessage.ts @@ -0,0 +1,8 @@ +/**訊息框相關 */ +export default class CSMessage { + /**網路錯誤訊息 */ + public static NetError(method: string, state: number, str: string = ""): void { + let error = String.Format("[{0}] state:{1} {2}", method, state, str); + console.warn("網路錯誤訊息: ", error); + } +} \ No newline at end of file diff --git a/src/script/Base/Config.ts b/src/script/Base/Config.ts new file mode 100644 index 0000000..97544e1 --- /dev/null +++ b/src/script/Base/Config.ts @@ -0,0 +1,22 @@ +/**放跟ProductSetting沒關係的變數 */ +export default class Config { + /**是否是連線模式 */ + public static IsOnlineMode: boolean = false; + /**內版帳號登入(目前只支援TYPE1.請從GM工具創好帳號在去DEMO場景加按鈕) */ + public static IsDemoLogin: boolean = false; + /**遊戲模式(0一般 1特色.WEB才有分) */ + public static GameMode: number = 0; + /**顯示金錢變動LOG */ + public static ShowMoneyLog: boolean = true; + /**顯示測試畫面 */ + public static ShowTest: boolean = false; + public static GetRunDevice(): number { + return 0; + } + public static IsANDROID(): boolean { + return false; + } + public static IsIOS(): boolean { + return false; + } +} diff --git a/src/script/Base/Request/AccountRequest.ts b/src/script/Base/Request/AccountRequest.ts new file mode 100644 index 0000000..9a3e665 --- /dev/null +++ b/src/script/Base/Request/AccountRequest.ts @@ -0,0 +1,237 @@ +import { NetRequest } from "../../Engine/CatanEngine/NetManagerV2/NetRequest"; +import BusinessTypeSetting from "../BusinessTypeSetting"; +import Config from "../Config"; + +// ======================================================================================= +/** 通用回傳SERVER創的帳號 */ +interface CommonAccountResponse { + a: string; + pw: string; +} + +// ======================================================================================= +interface CreateResquest { + p: number; +} +/** 直接玩(訪客給SERVER創帳號) */ +export class AccountCreateRequest extends NetRequest { + get Method(): string { + return "account.create"; + } + constructor() { + super(); + this.Data = { + p: Config.GetRunDevice(), + }; + } +} +// ======================================================================================= +interface LoginResquest { + p: number; + device_info: string[]; + fcm_token: string; + a: string; + pw: string; + ver: string; +} +interface LoginResponse { + pr: string; + cu: string; +} +/** 通用登入 */ +export class AccountLoginRequest extends NetRequest { + get Method(): string { + return "account.login"; + } + constructor(account: string, password: string) { + super(); + this.Data = { + p: Config.GetRunDevice(), + device_info: ["Windows", "Windows"], + fcm_token: "", + a: account, + pw: password, + ver: BusinessTypeSetting.COMPILE_VERSION + }; + } +} +// ======================================================================================= +interface CustomResquest { + a: string; + pw: string; +} +/** 自定帳號榜定 */ +export class CustomBindRequest extends NetRequest { + get Method(): string { + return "register.account_bind"; + } + constructor(account: string, password: string) { + super(); + this.Data = { + a: account, + pw: password, + }; + } +} +/** 自定帳號登入(回傳SERVER帳號) */ +export class CustomLoginRequest extends NetRequest { + get Method(): string { + return "register.account_login"; + } + constructor(account: string, password: string) { + super(); + this.Data = { + a: account, + pw: password, + }; + } +} +// ======================================================================================= +interface FBResquest { + t: string; +} +/** FB綁定 */ +export class FBBindRequest extends NetRequest { + get Method(): string { + return "register.fb_bind"; + } + constructor(token: string) { + super(); + this.Data = { + t: token, + }; + } +} +/** FB登入(回傳SERVER帳號) */ +export class FBLoginRequest extends NetRequest { + get Method(): string { + return "register.fb_login"; + } + constructor(token: string) { + super(); + this.Data = { + t: token, + }; + } +} +// ======================================================================================= +interface GoogleResquest { + c: string; +} +/** GOOGLE綁定 */ +export class GoogleBindRequest extends NetRequest { + get Method(): string { + return "register.google_bind"; + } + constructor(token: string) { + super(); + this.Data = { + c: token, + }; + } +} +/** GOOGLE登入(回傳SERVER帳號) */ +export class GoogleLoginRequest extends NetRequest { + get Method(): string { + return "register.google_login"; + } + constructor(token: string) { + super(); + this.Data = { + c: token, + }; + } +} +// ======================================================================================= +interface AppleResquest { + c: string; +} +/** APPEL綁定 */ +export class AppleBindRequest extends NetRequest { + get Method(): string { + return "register.apple_bind"; + } + constructor(token: string) { + super(); + this.Data = { + c: token, + }; + } +} +/** APPLE登入(回傳SERVER帳號) */ +export class AppleLoginRequest extends NetRequest { + get Method(): string { + return "register.apple_login"; + } + constructor(token: string) { + super(); + this.Data = { + c: token, + }; + } +} +// ======================================================================================= +/** 電話驗證 */ +export interface PhoneCodeRequest { + p: string; +} + +export class PhoneGet extends NetRequest { + get Method(): string { + return "register.phone_code"; + } + constructor(p: string) { + super(); + this.Data = { + p: p + }; + } +} + +export interface PhoneBindRequest { + c: string; +} + +export class PhoneBind extends NetRequest { + get Method(): string { + return "register.phone_bind"; + } + constructor(c: string) { + super(); + this.Data = { + c: c + }; + } +} + +// ======================================================================================= +/** 旗標更新 */ +export class FlagOpenAdd extends NetRequest { + get Method(): string { + return "flag.open_add"; + } + constructor(type: number) { + super(); + this.Data = type; + } +} + +// ======================================================================================== +export interface ForgotInfo { + a: string; + p: string; +} + +/** 忘記密碼 */ +export class ForgotPassword extends NetRequest { + get Method(): string { + return "register.account_forget"; + } + constructor(account: string, phone: string) { + super(); + this.Data = { + a: account, + p: phone, + }; + } +} \ No newline at end of file diff --git a/src/script/Base/Request/RankRequest.ts b/src/script/Base/Request/RankRequest.ts new file mode 100644 index 0000000..77de694 --- /dev/null +++ b/src/script/Base/Request/RankRequest.ts @@ -0,0 +1,72 @@ +import { NetRequest } from "../../Engine/CatanEngine/NetManagerV2/NetRequest"; + +export interface RankInfo { + t: number; + p?: number; + id?: number; +} + +export class AppRankInfo extends NetRequest { + get Method(): string { + return "rank.info"; + } + constructor(Type: number, Parameter?: number) { + super(); + this.Data = { + t: Type, + p: Parameter, + }; + } +} + +export class AppRankHistory extends NetRequest { + get Method(): string { + return "rank.history"; + } + constructor(Type: number, Parameter: number, DayId: number) { + super(); + this.Data = { + id: DayId, + t: Type, + p: Parameter + }; + } +} + +export interface RankReplayInfo { + id: number; + t: number; + r: number; + p: number; +} + + +export class AppRankLog extends NetRequest { + get Method(): string { + return "rank.log"; + } + constructor(DayId: number, Type: number, rank: number, Parameter: number) { + super(); + this.Data = { + id: DayId, + t: Type, + r: rank, + p: Parameter + }; + } +} + +export class TestAppRankLog extends NetRequest { + get Method(): string { + return "rank.log_test"; + } + constructor(DayId: number, Type: number, rank: number, Parameter: number) { + super(); + this.Data = { + id: DayId, + t: Type, + r: rank, + p: Parameter + }; + } +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine.meta b/src/script/Engine/CatanEngine.meta new file mode 100644 index 0000000..f0d308f --- /dev/null +++ b/src/script/Engine/CatanEngine.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "a69fe64f-177f-4e4b-83f0-1f418203d85f", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp.meta b/src/script/Engine/CatanEngine/CSharp.meta new file mode 100644 index 0000000..ad81719 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "f9edb32f-c4ab-4e5d-8270-71fa609e1db7", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/String.ts b/src/script/Engine/CatanEngine/CSharp/String.ts new file mode 100644 index 0000000..2ad222b --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/String.ts @@ -0,0 +1,20 @@ +declare global { + interface StringConstructor { + IsNullOrEmpty: (value: string) => boolean; + Format: (format: string, ...args: any[]) => string; + } +} + +String.IsNullOrEmpty = function (value: string): boolean { + return value === undefined || value === null || value.trim() === ''; +}; + +String.Format = function (format: string, ...args: any[]): string { + return format.replace(/{(\d+)}/g, (match, index) => { + let value = args[index]; + if (value === null || value === undefined) return ''; + return '' + value; + }); +} + +export { }; diff --git a/src/script/Engine/CatanEngine/CSharp/String.ts.meta b/src/script/Engine/CatanEngine/CSharp/String.ts.meta new file mode 100644 index 0000000..42955f2 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/String.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "0c3d1ca6-bdaf-4a00-b209-6ef460802cdc", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System.meta b/src/script/Engine/CatanEngine/CSharp/System.meta new file mode 100644 index 0000000..4b20ca4 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "01b35dee-e6e0-4a6e-a73c-3b49c37f1c5f", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System/Action.ts b/src/script/Engine/CatanEngine/CSharp/System/Action.ts new file mode 100644 index 0000000..e681833 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/Action.ts @@ -0,0 +1,125 @@ +/** + * 回呼函數: fnname (arg: TArg): void + */ +interface ActionCallback { + (arg: TArg): void; +} + +interface Struct { + callback: ActionCallback; + target: any; + once?: boolean; +} + +export class Action { + private _queue: Struct[] = []; + + /** + * 監聽事件 + * @param callback 回呼函數: fnname (arg: TArg): void + * @param bindTarget 回呼時this綁定的對象 + */ + AddCallback(callback: ActionCallback, bindTarget?: any) { + let q = > { + callback: callback, + target: bindTarget + }; + this._queue.push(q); + } + + /** + * 監聽事件 (一次性) + * @param callback 回呼函數: fnname (arg: TArg): void + * @param bindTarget 回呼時this綁定的對象 + */ + AddCallbackOnce(callback: ActionCallback, bindTarget?: any) { + let q = > { + callback: callback, + target: bindTarget, + once: true + }; + this._queue.push(q); + } + + /** + * 移除事件 + * @param callback + */ + RemoveByCallback(callback: ActionCallback) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || q.callback === callback) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除事件 + * @param bindTarget 回呼時this綁定的對象 + */ + RemoveByBindTarget(bindTarget: any) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || q.target === bindTarget) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除全部事件 + */ + RemoveAllCallbacks() { + this._queue.forEach(q => q.callback = undefined); + this._queue.length = 0; + } + + /** + * 發送事件 + * @param arg 參數 + */ + DispatchCallback(arg: TArg) { + let index = this._queue.length; + if (index > 0) { + let cleanRemoved = false; + this._queue.slice().forEach(q => { + if (!q.callback) { + cleanRemoved = true; + return; + } + + if (q.target) { + q.callback.call(q.target, arg); + } else { + q.callback(arg); + } + + if (q.once) { + q.callback = undefined; + cleanRemoved = true; + } + }); + + if (cleanRemoved) { + index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback) { + this._queue.splice(index, 1); + } + } + } + } + } + } +} diff --git a/src/script/Engine/CatanEngine/CSharp/System/Action.ts.meta b/src/script/Engine/CatanEngine/CSharp/System/Action.ts.meta new file mode 100644 index 0000000..a62a428 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/Action.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "ea9bf762-40a7-4bab-b949-8d5b3d4289e2", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System/ActionExample.ts b/src/script/Engine/CatanEngine/CSharp/System/ActionExample.ts new file mode 100644 index 0000000..555e498 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/ActionExample.ts @@ -0,0 +1,85 @@ +import { Action } from "./Action"; +import { ActionWithType } from "./ActionWithType"; +import { ActionWithType2 } from "./ActionWithType2"; + +const { ccclass, property } = console._decorator; + +enum CustomType { + Ex1, Ex2 +} + +class CustomEvent extends ActionWithType { } +class CustomEvent2 extends ActionWithType2 { } + +@ccclass +export default class NewClass extends console.Component { + callback: Action = new Action(); + customCallback: CustomEvent = new CustomEvent(); + customCallback2: CustomEvent2 = new CustomEvent2(); + + private num: number = 0; + + start() { + this.callback.AddCallback(this.CB, this); + this.callback.AddCallbackOnce(this.OnceCB, this); + + this.customCallback.AddCallback(CustomType.Ex1, this.CBType, this); + this.customCallback.AddCallbackOnce(CustomType.Ex2, this.OnceCBType, this); + + this.customCallback2.AddCallback(CustomType.Ex2, this.CBTypeAllin1, this); + this.customCallback2.AddCallbackOnce(CustomType.Ex1, this.CBTypeAllin1, this); + } + + DispatchClick() { + this.num++; + + this.callback.DispatchCallback(this.num); + + this.customCallback.DispatchCallback(CustomType.Ex1, this.num); + this.customCallback.DispatchCallback(CustomType.Ex2, this.num); + + this.customCallback2.DispatchCallback(CustomType.Ex1, this.num); + this.customCallback2.DispatchCallback(CustomType.Ex2, this.num); + } + + RemoveEventClick() { + this.callback.RemoveByCallback(this.CB); + // this.callback.RemoveByCallback(this.OnceCB); + // this.callback.RemoveByBindTarget(this); + // this.callback.RemoveAll(); + + // this.callbackWithType.RemoveByCallback(this.CBType); + // this.callbackWithType.RemoveByCallback(this.OnceCBType); + this.customCallback.RemoveByType(CustomType.Ex1); + // this.callbackWithType.RemoveByType(CustomType.Ex2); + // this.callbackWithType.RemoveByBindTarget(this); + // this.callbackWithType.RemoveAll(); + } + + OnceCB(x: number) { + console.log(`OnceCB [${this.num}]`); + } + + CB(x: number) { + console.log(`CB [${this.num}]`); + } + + OnceCBType(x: number) { + console.log(`OnceCBType [${this.num}]`); + } + + CBType(x: number) { + console.log(`CBType [${this.num}]`); + } + + CBTypeAllin1(type: CustomType, x: number) { + // switch (type) { + // case CustomType.Ex1: + // break; + // case CustomType.Ex2: + // break; + // } + + console.log(`CBTypeAllin1 [${CustomType[type]}][${this.num}]`); + } +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System/ActionExample.ts.meta b/src/script/Engine/CatanEngine/CSharp/System/ActionExample.ts.meta new file mode 100644 index 0000000..b2e8b66 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/ActionExample.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "cc645b73-6192-414d-a5bc-4220c24e322d", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System/ActionWithType.ts b/src/script/Engine/CatanEngine/CSharp/System/ActionWithType.ts new file mode 100644 index 0000000..1a078ee --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/ActionWithType.ts @@ -0,0 +1,166 @@ +/** + * 回呼函數: fnname (arg: TArg): void + */ +interface ActionCallback { + (arg: TArg): void; +} + +interface Struct { + callback: ActionCallback; + target: any; + type: TType; + once?: boolean; +} + +export class ActionWithType { + private _queue: Struct[] = []; + + /** + * 監聽事件 + * @param callback 回呼函數: fnname (arg: TArg): void + * @param bindTarget 回呼時this綁定的對象 + */ + AddCallback(type: TType, callback: ActionCallback, bindTarget?: any) { + let q = > { + callback: callback, + target: bindTarget, + type: type + }; + this._queue.push(q); + } + + /** + * 監聽事件 (一次性) + * @param callback 回呼函數: fnname (arg: TArg): void + * @param bindTarget 回呼時this綁定的對象 + */ + AddCallbackOnce(type: TType, callback: ActionCallback, bindTarget?: any) { + let q = > { + callback: callback, + target: bindTarget, + type: type, + once: true + }; + this._queue.push(q); + } + + /** + * 移除事件 + * @param callback + */ + RemoveByCallback(callback: ActionCallback) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || q.callback === callback) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除事件 + * @param bindTarget 回呼時this綁定的對象 + */ + RemoveByBindTarget(bindTarget: any) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || q.target === bindTarget) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除事件 + * @param type 事件類型 + */ + RemoveByType(type: TType) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || q.type === type) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除事件 + * @param type 事件類型 + * @param callback + */ + RemoveCallback(type:TType, callback: ActionCallback) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || (q.type === type && q.callback === callback)) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除全部事件 + */ + RemoveAllCallbacks() { + this._queue.forEach(q => q.callback = undefined); + this._queue.length = 0; + } + + /** + * 發送事件 + * @param type 事件類型 + * @param arg 參數 + */ + DispatchCallback(type: TType, arg: TArg) { + let index = this._queue.length; + if (index > 0) { + let cleanRemoved = false; + this._queue.slice().forEach(q => { + if (!q.callback) + { + cleanRemoved = true; + return; + } + if (q.type !== type) return; + + if (q.target) { + q.callback.call(q.target, arg); + } else { + q.callback(arg); + } + + if (q.once) { + q.callback = undefined; + cleanRemoved = true; + } + }); + + if (cleanRemoved) { + index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback) { + this._queue.splice(index, 1); + } + } + } + } + } + } +} diff --git a/src/script/Engine/CatanEngine/CSharp/System/ActionWithType.ts.meta b/src/script/Engine/CatanEngine/CSharp/System/ActionWithType.ts.meta new file mode 100644 index 0000000..e1e3d30 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/ActionWithType.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "61d770ec-24e2-425b-b66b-2b03e192e45b", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System/ActionWithType2.ts b/src/script/Engine/CatanEngine/CSharp/System/ActionWithType2.ts new file mode 100644 index 0000000..33e8c75 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/ActionWithType2.ts @@ -0,0 +1,166 @@ +/** + * 回呼函數: fnname (type: TType, arg: TArg): void + */ +interface ActionCallback { + (type: TType, arg: TArg): void; +} + +interface Struct { + callback: ActionCallback; + target: any; + type: TType; + once?: boolean; +} + +export class ActionWithType2 { + private _queue: Struct[] = []; + + /** + * 監聽事件 + * @param callback 回呼函數: fnname (type: TType, arg: TArg): void + * @param bindTarget 回呼時this綁定的對象 + */ + AddCallback(type: TType, callback: ActionCallback, bindTarget?: any) { + let q = > { + callback: callback, + target: bindTarget, + type: type + }; + this._queue.push(q); + } + + /** + * 監聽事件 (一次性) + * @param callback 回呼函數: fnname (type: TType, arg: TArg): void + * @param bindTarget 回呼時this綁定的對象 + */ + AddCallbackOnce(type: TType, callback: ActionCallback, bindTarget?: any) { + let q = > { + callback: callback, + target: bindTarget, + type: type, + once: true + }; + this._queue.push(q); + } + + /** + * 移除事件 + * @param callback + */ + RemoveByCallback(callback: ActionCallback) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || q.callback === callback) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除事件 + * @param bindTarget 回呼時this綁定的對象 + */ + RemoveByBindTarget(bindTarget: any) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || q.target === bindTarget) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除事件 + * @param type 事件類型 + */ + RemoveByType(type: TType) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || q.type === type) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除事件 + * @param type 事件類型 + * @param callback + */ + RemoveCallback(type:TType, callback: ActionCallback) { + let index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback || (q.type === type && q.callback === callback)) { + q.callback = undefined; + this._queue.splice(index, 1); + } + } + } + } + + /** + * 移除全部事件 + */ + RemoveAllCallbacks() { + this._queue.forEach(q => q.callback = undefined); + this._queue.length = 0; + } + + /** + * 發送事件 + * @param type 事件類型 + * @param arg 參數 + */ + DispatchCallback(type: TType, arg: TArg) { + let index = this._queue.length; + if (index > 0) { + let cleanRemoved = false; + this._queue.slice().forEach(q => { + if (!q.callback) + { + cleanRemoved = true; + return; + } + if (q.type !== type) return; + + if (q.target) { + q.callback.call(q.target, type, arg); + } else { + q.callback(type, arg); + } + + if (q.once) { + q.callback = undefined; + cleanRemoved = true; + } + }); + + if (cleanRemoved) { + index = this._queue.length; + if (index > 0) { + while (index--) { + let q = this._queue[index]; + if (!q.callback) { + this._queue.splice(index, 1); + } + } + } + } + } + } +} diff --git a/src/script/Engine/CatanEngine/CSharp/System/ActionWithType2.ts.meta b/src/script/Engine/CatanEngine/CSharp/System/ActionWithType2.ts.meta new file mode 100644 index 0000000..a37aa2c --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/ActionWithType2.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "ed703ebd-efd4-4ec9-9b84-de748ef8f9e8", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System/Text.meta b/src/script/Engine/CatanEngine/CSharp/System/Text.meta new file mode 100644 index 0000000..7e3f112 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/Text.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "09d69d12-a6d1-4bb1-bcfe-faa811632467", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System/Text/Encoding.ts b/src/script/Engine/CatanEngine/CSharp/System/Text/Encoding.ts new file mode 100644 index 0000000..62d0f16 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/Text/Encoding.ts @@ -0,0 +1,68 @@ +export module Encoding.UTF8 { + + export function GetBytes(str: string) { + let len = str.length, resPos = -1; + let resArr = new Uint8Array(len * 3); + for (let point = 0, nextcode = 0, i = 0; i !== len; ) { + point = str.charCodeAt(i), i += 1; + if (point >= 0xD800 && point <= 0xDBFF) { + if (i === len) { + resArr[resPos += 1] = 0xef; + resArr[resPos += 1] = 0xbf; + resArr[resPos += 1] = 0xbd; + break; + } + + nextcode = str.charCodeAt(i); + if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) { + point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000; + i += 1; + if (point > 0xffff) { + resArr[resPos += 1] = (0x1e << 3) | (point >>> 18); + resArr[resPos += 1] = (0x2 << 6) | ((point >>> 12) & 0x3f); + resArr[resPos += 1] = (0x2 << 6) | ((point >>> 6) & 0x3f); + resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f); + continue; + } + } else { + resArr[resPos += 1] = 0xef; + resArr[resPos += 1] = 0xbf; + resArr[resPos += 1] = 0xbd; + continue; + } + } + if (point <= 0x007f) { + resArr[resPos += 1] = (0x0 << 7) | point; + } else if (point <= 0x07ff) { + resArr[resPos += 1] = (0x6 << 5) | (point >>> 6); + resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f); + } else { + resArr[resPos += 1] = (0xe << 4) | (point >>> 12); + resArr[resPos += 1] = (0x2 << 6) | ((point >>> 6) & 0x3f); + resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f); + } + } + return resArr.subarray(0, resPos + 1); + } + + export function GetString(array: Uint8Array) { + let str = ""; + let i = 0, len = array.length; + while(i < len) { + let c = array[i++]; + switch (c >> 4) + { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + str += String.fromCharCode(c); + break; + case 12: case 13: + str += String.fromCharCode(((c & 0x1F) << 6) | (array[i++] & 0x3F)); + break; + case 14: + str += String.fromCharCode(((c & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | ((array[i++] & 0x3F) << 0)); + break; + } + } + return str; + } +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CSharp/System/Text/Encoding.ts.meta b/src/script/Engine/CatanEngine/CSharp/System/Text/Encoding.ts.meta new file mode 100644 index 0000000..02a34a1 --- /dev/null +++ b/src/script/Engine/CatanEngine/CSharp/System/Text/Encoding.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "43bf5724-e939-4189-b981-c32ef694e5a5", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2.meta b/src/script/Engine/CatanEngine/CoroutineV2.meta new file mode 100644 index 0000000..4fc05bc --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "9f510f2b-83d8-4097-8683-32d6134323fb", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/CancellationTokenSource.ts b/src/script/Engine/CatanEngine/CoroutineV2/CancellationTokenSource.ts new file mode 100644 index 0000000..6b264b9 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/CancellationTokenSource.ts @@ -0,0 +1,43 @@ +const CANCEL = Symbol(); + +export interface CancellationToken { + readonly IsCancellationRequested: boolean; + ThrowIfCancellationRequested(): void; +} + +export class CancellationTokenSource { + readonly Token: CancellationToken; + + constructor() { + this.Token = new CancellationTokenImpl(); + } + + Cancel() { + this.Token[CANCEL](); + } +} + +export class TaskCancelledException extends Error { + constructor() { + super("Task Cancelled"); + Reflect.setPrototypeOf(this, TaskCancelledException.prototype); + } +} + +class CancellationTokenImpl implements CancellationToken { + IsCancellationRequested: boolean; + + constructor() { + this.IsCancellationRequested = false; + } + + ThrowIfCancellationRequested() { + if (this.IsCancellationRequested) { + throw new TaskCancelledException(); + } + } + + [CANCEL]() { + this.IsCancellationRequested = true; + } +} diff --git a/src/script/Engine/CatanEngine/CoroutineV2/CancellationTokenSource.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/CancellationTokenSource.ts.meta new file mode 100644 index 0000000..4175ac3 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/CancellationTokenSource.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "9a414131-91a8-4d02-9921-9d1ee01764c3", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core.meta b/src/script/Engine/CatanEngine/CoroutineV2/Core.meta new file mode 100644 index 0000000..999a0f6 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "fbfe97a8-24ca-4f67-b049-323652c7194b", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts b/src/script/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts new file mode 100644 index 0000000..ddd1dae --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts @@ -0,0 +1,17 @@ +import { BaseEnumerator } from "./BaseEnumerator"; + +export class ActionEnumerator extends BaseEnumerator { + private _action: Function; + + constructor(action: Function) { + super(); + this._action = action; + } + + next(value?: any): IteratorResult { + if (this._action) { + this._action(); + } + return { done: true, value: undefined }; + } +} diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts.meta new file mode 100644 index 0000000..cfa8e0d --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "3cf9e5c3-520f-48a9-8821-9be76d519765", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts b/src/script/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts new file mode 100644 index 0000000..f690291 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts @@ -0,0 +1,87 @@ +import { IEnumeratorV2, IEnumeratorV2Started } from "../IEnumeratorV2"; +import { CoroutineExecutor } from "./CoroutineExecutor"; + +export abstract class BaseEnumerator implements IEnumeratorV2 { + public nextEnumerator: BaseEnumerator; + + abstract next(value?: any): IteratorResult; + + Start(target?: any): IEnumeratorV2Started { + let executor = LazyLoad.EnumeratorExecutor(this, target); + CoroutineExecutor.instance.StartCoroutine(executor); + return executor; + } + + Then(iterator: Iterator): IEnumeratorV2 { + if (!iterator) return this; + + if (iterator instanceof BaseEnumerator) { + BaseEnumerator.getLastEnumerator(this).nextEnumerator = iterator; + return this; + } else { + let enumerator = LazyLoad.SingleEnumerator(iterator); + BaseEnumerator.getLastEnumerator(this).nextEnumerator = enumerator; + return this; + } + } + + ThenSerial(...iterators: Iterator[]): IEnumeratorV2 { + let last = BaseEnumerator.getLastEnumerator(this); + for (let iterator of iterators) { + if (iterator instanceof BaseEnumerator) { + last.nextEnumerator = iterator; + } else { + let enumerator = LazyLoad.SingleEnumerator(iterator); + last.nextEnumerator = enumerator; + } + last = last.nextEnumerator; + } + return this; + } + + ThenParallel(...iterators: Iterator[]): IEnumeratorV2 { + return this.Then(LazyLoad.ParallelEnumerator(...iterators)); + } + + ThenAction(action: Function, delaySeconds?:number): IEnumeratorV2 { + if (delaySeconds > 0) { + return this.ThenSerial(LazyLoad.WaitTimeEnumerator(delaySeconds), LazyLoad.ActionEnumerator(action)); + } else { + return this.Then(LazyLoad.ActionEnumerator(action)); + } + } + + ThenWaitTime(seconds: number): IEnumeratorV2 { + return this.Then(LazyLoad.WaitTimeEnumerator(seconds)); + } + + static getLastEnumerator(enumerator: BaseEnumerator): BaseEnumerator { + let next = enumerator; + while (next.nextEnumerator) { + next = next.nextEnumerator; + } + return next; + } +} + +module LazyLoad { + export function EnumeratorExecutor(enumerator: BaseEnumerator, target: any) { + return new (require("./EnumeratorExecutor") as typeof import("./EnumeratorExecutor")).EnumeratorExecutor(enumerator, target); + } + + export function SingleEnumerator(iterator: Iterator) { + return new (require("./SingleEnumerator") as typeof import("./SingleEnumerator")).SingleEnumerator(iterator); + } + + export function ParallelEnumerator(...iterators: Iterator[]) { + return new (require("./ParallelEnumerator") as typeof import("./ParallelEnumerator")).ParallelEnumerator(iterators); + } + + export function WaitTimeEnumerator(seconds: number) { + return new (require("./WaitTimeEnumerator") as typeof import("./WaitTimeEnumerator")).WaitTimeEnumerator(seconds); + } + + export function ActionEnumerator(action: Function) { + return new (require("./ActionEnumerator") as typeof import("./ActionEnumerator")).ActionEnumerator(action); + } +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts.meta new file mode 100644 index 0000000..3756fe6 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "4084537c-c7e8-4d47-b283-39be77ef9685", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts b/src/script/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts new file mode 100644 index 0000000..656101d --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts @@ -0,0 +1,94 @@ +import { EnumeratorExecutor } from "./EnumeratorExecutor"; + +export class CoroutineExecutor { + private static _instance: CoroutineExecutor; + static get instance() { + return CoroutineExecutor._instance = CoroutineExecutor._instance || new CoroutineExecutor(); + } + + private _executors: EnumeratorExecutor[] = []; + private _nextExecutors: EnumeratorExecutor[] = []; + private _isRunning: boolean = false; + private _cleanRemoved: boolean = false; + private _scheduler: console.Scheduler; + + constructor() { + this._scheduler = console.director.getScheduler(); + this._scheduler.enableForTarget(this); + this._scheduler.scheduleUpdate(this, 0, true); + } + + StartCoroutine(executor: EnumeratorExecutor) { + executor.next(0); + //TODO: 這邊要考量next後馬上接BaseEnumerator/Iterator的情形 + + if (!this._isRunning) { + this._executors.push(executor); + + if (this._scheduler.isTargetPaused(this)) { + this._scheduler.resumeTarget(this); + } + } else { + this._nextExecutors.push(executor); + } + } + + StopCoroutineBy(target: any) { + if (!target) return; + + for (let r of this._executors) { + if (target === r.target) { + r.Stop(); + } + } + + for (let r of this._nextExecutors) { + if (target === r.target) { + r.Stop(); + } + } + } + + update(delta: number) { + if (this._nextExecutors.length) { + this._executors.push(...this._nextExecutors); + this._nextExecutors.length = 0; + } + + if (this._cleanRemoved) { + // 移除[doneFlag=true]的協程 + let index = this._executors.length; + while (index--) { + let r = this._executors[index]; + if (r.doneFlag) { + this._executors.splice(index, 1); + } + } + this._cleanRemoved = false; + } + + if (this._executors.length == 0) { + if (true) { + console.log("[CoroutineV2] All coroutines done"); + } + this._scheduler.pauseTarget(this); + return; + } + + this._isRunning = true; + + // 執行協程 + for (let r of this._executors) { + if (r.doneFlag || r.pauseFlag || r.childFlag) { + if (r.doneFlag) { + this._cleanRemoved = true; + } + continue; + } + + r.next(delta); + } + + this._isRunning = false; + } +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts.meta new file mode 100644 index 0000000..fc6d10b --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "f25b1e42-90d8-4fc0-9925-6e7e92296d57", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts b/src/script/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts new file mode 100644 index 0000000..3802de3 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts @@ -0,0 +1,168 @@ +import { IEnumeratorV2Started } from "../IEnumeratorV2"; +import { BaseEnumerator } from "./BaseEnumerator"; +import { SingleEnumerator } from "./SingleEnumerator"; + +export class EnumeratorExecutor implements IEnumeratorV2Started { + public Current: any; + + public target: any; + public pauseFlag: boolean; + public doneFlag: boolean; + public childFlag: boolean; + public asyncFlag: boolean; + public error: any; + + private _executor: EnumeratorExecutor; + private _enumerator: BaseEnumerator; + + constructor(enumerator: BaseEnumerator, target: any) { + this.target = target; + this._enumerator = enumerator; + } + + next(delta?: any): IteratorResult { + if (this._executor && this._executor.doneFlag) { + this._executor = null; + } + + if (this.doneFlag || (!this._enumerator && !this._executor)) { + this.doneFlag = true; + return { done: true, value: undefined }; + } + + if (this.asyncFlag || this.pauseFlag) return { done: false, value: undefined }; + + let result: IteratorResult; + + if (this._executor) { + result = this._executor.next(delta); + this.Current = this._executor.Current; + if (this._executor.doneFlag) { + this._executor = null; + } else { + result.done = false; + return result; + } + } + + if (!this._enumerator) { + this.doneFlag = true; + return { done: true, value: undefined }; + } + + try { + result = this._enumerator.next(delta); + let value = result.value; + let done = result.done; + + if (value) { + // Iterator + if (typeof value[Symbol.iterator] === 'function') { + value = new SingleEnumerator(>value); + } + + if (value instanceof BaseEnumerator) { + if (!done) { + BaseEnumerator.getLastEnumerator(value).nextEnumerator = this._enumerator; + } + this._enumerator = value; + result = this._enumerator.next(delta); + value = result.value; + done = result.done; + + if (value) { + // Iterator again + if (typeof value[Symbol.iterator] === 'function') { + value = new SingleEnumerator(>value); + } + + if (value instanceof BaseEnumerator) { + if (!done) { + BaseEnumerator.getLastEnumerator(value).nextEnumerator = this._enumerator; + } + this._enumerator = value; + result.done = false; + done = false; + } + } + } + + if (value instanceof EnumeratorExecutor) { + if (done) { + this._enumerator = this._enumerator.nextEnumerator; + } + value.childFlag = true; + result.done = false; + done = false; + this._executor = value; + } else if (Promise.resolve(value) === value) { + this.asyncFlag = true; + result.done = false; + done = false; + (>value) + .then(v => { + this.asyncFlag = false; + this.Current = v; + if (done) { + this._enumerator = this._enumerator.nextEnumerator; + } + }) + .catch(e => { + this.asyncFlag = false; + this.doneFlag = true; + this._enumerator = null; + this.error = e; + if (e instanceof Error) { + console.error(e.stack); + } else { + console.error(`Error: ${JSON.stringify(e)}`); + } + }); + } + + this.Current = value; + } + + if (done) { + this._enumerator = this._enumerator.nextEnumerator; + if (this._enumerator) { + result.done = false; + } + } + } + catch (e) { + this.doneFlag = true; + this.error = e; + if (e instanceof Error) { + console.error(e.stack); + } else { + console.error(`Error: ${JSON.stringify(e)}`); + } + result = { done: true, value: e }; + } + + return result; + } + + Stop(): void { + this.doneFlag = true; + if (this._executor) { + this._executor.Stop(); + } + } + + Pause(): void { + this.pauseFlag = true; + if (this._executor) { + this._executor.Pause(); + } + } + + Resume(): void { + this.pauseFlag = false; + if (this._executor) { + this._executor.Resume(); + } + } + +} diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts.meta new file mode 100644 index 0000000..8a4b319 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "91cb70ed-e6f9-4ce0-b7c5-1720087b3bd7", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts b/src/script/Engine/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts new file mode 100644 index 0000000..5aa146e --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts @@ -0,0 +1,46 @@ +import { BaseEnumerator } from "./BaseEnumerator"; +import { EnumeratorExecutor } from "./EnumeratorExecutor"; +import { SingleEnumerator } from "./SingleEnumerator"; + +export class ParallelEnumerator extends BaseEnumerator { + private _executors: EnumeratorExecutor[] = []; + + constructor(iterators: Iterator[]) { + super(); + if (iterators && iterators.length) { + for (let iterator of iterators) { + if (iterator instanceof BaseEnumerator) { + this._executors.push(new EnumeratorExecutor(iterator, null)); + } else { + this._executors.push(new EnumeratorExecutor(new SingleEnumerator(iterator), null)); + } + } + } + } + + next(value?: any): IteratorResult { + if (this._executors.length) { + // 先移除[doneFlag=true]協程 + let index = this._executors.length; + while (index--) { + let r = this._executors[index]; + if (r.doneFlag) { + this._executors.splice(index, 1); + } + } + + if (this._executors.length == 0) { + return { done: true, value: undefined }; + } + + // 執行協程 + for (let r of this._executors) { + r.next(value); + } + + return { done: false, value: undefined }; + } + + return { done: true, value: undefined }; + } +} diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts.meta new file mode 100644 index 0000000..70c1480 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "017ebc9a-5152-4f94-bbaf-e3b914e87b41", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts b/src/script/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts new file mode 100644 index 0000000..10eae34 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts @@ -0,0 +1,18 @@ +import { BaseEnumerator } from "./BaseEnumerator"; + +export class SingleEnumerator extends BaseEnumerator { + private _iterator: Iterator; + + constructor(iterator: Iterator) { + super(); + this._iterator = iterator; + } + + next(value?: any): IteratorResult { + if (!this._iterator) { + return { done: true, value: undefined }; + } + + return this._iterator.next(value); + } +} diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts.meta new file mode 100644 index 0000000..96ae7b1 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "c439d019-2da8-48b8-a65b-bff928d0fda8", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts b/src/script/Engine/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts new file mode 100644 index 0000000..04d7b2e --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts @@ -0,0 +1,21 @@ +import { BaseEnumerator } from "./BaseEnumerator"; + +export class WaitTimeEnumerator extends BaseEnumerator { + private _seconds: number; + + constructor(seconds: number) { + super(); + this._seconds = seconds; + } + + next(value?: any): IteratorResult { + let delta = value as number; + this._seconds -= delta; + + if (this._seconds <= 0) { + return { done: true, value: 0 }; + } else { + return { done: false, value: this._seconds }; + } + } +} diff --git a/src/script/Engine/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts.meta new file mode 100644 index 0000000..0572bd6 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "a3038e6f-1bb4-4aff-a686-b69209df3592", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/CoroutineExample.ts b/src/script/Engine/CatanEngine/CoroutineV2/CoroutineExample.ts new file mode 100644 index 0000000..e6326ba --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/CoroutineExample.ts @@ -0,0 +1,197 @@ +import { CoroutineV2 } from "./CoroutineV2"; +import { IEnumeratorV2Started } from "./IEnumeratorV2"; + +const { ccclass, property } = console._decorator; + +class A { + private numbers: number[] = [1, 2]; + private index = 0; + + [Symbol.iterator](): IterableIterator { + return this; + } + + next(value?: any): IteratorResult { + if (this.index < this.numbers.length) { + let value = this.numbers[this.index++]; + console.log(`A=> ${value}`); + return { + done: false, + value: value + }; + } + + return { done: true, value: undefined }; + } +} + +@ccclass +export default class CoroutineExample extends console.Component { + private _routine: IEnumeratorV2Started; + private _obj: Object = { "a": true }; + private _obj2: Object = { "b": true }; + + private _num: number = 3; + + button1Clicked() { + // this._routine = CoroutineV2 + // .Parallel(this.Coroutine1(1, 3), this.Coroutine1(4, 6)) + // .ThenWaitTime(2) + // .Then(this.Coroutine1(7, 9)) + // .ThenWaitTime(2) + // .ThenAction(() => console.log("action callback 1")) + // .ThenWaitTime(2) + // .ThenAction(this.actionCallback) + // //.Start(this); + // .Start(this); + // this._routine = CoroutineV2.Single(this.FunA()).Start(this); + + this._routine = CoroutineV2.Single(this.Test1_1()).Start(this); + // this._routine = CoroutineV2.Single(this.Test2_1()).Start(this); + } + + *Test1_1() { + yield null; + yield* this.Test1_2(); + // CoroutineV2.Single(this.Test1_3()).Start(this); + yield this.Test1_3(); + } + + *Test1_2() { + yield null; + } + + *Test1_3() { + yield this.Test1_3_1(); + yield CoroutineV2.Single(this.Test1_4()).Start(this._obj); + // yield CoroutineV2.Single(this.Test1_4()); //.Start(this); + // yield *this.Test1_4(); + console.log("main wait 3"); + yield CoroutineV2.WaitTime(2); + console.log("done"); + } + + *Test1_3_1() { + yield this.Test1_3_2(); + yield CoroutineV2.WaitTime(1); + console.log("Test1_3_1.1"); + yield CoroutineV2.WaitTime(1); + console.log("Test1_3_1.2"); + } + + *Test1_3_2() { + yield this.Test1_3_3(); + yield CoroutineV2.WaitTime(1); + console.log("Test1_3_2.1"); + yield CoroutineV2.WaitTime(1); + console.log("Test1_3_2.2"); + yield CoroutineV2.WaitTime(1); + console.log("Test1_3_2.3"); + } + + *Test1_3_3() { + yield CoroutineV2.WaitTime(1); + console.log("Test1_3_3.1"); + yield CoroutineV2.WaitTime(1); + console.log("Test1_3_3.2"); + yield CoroutineV2.WaitTime(1); + console.log("Test1_3_3.3"); + } + + *Test1_4() { + this._num++; + console.log(`WaitTime2 ${this._num}`); + yield CoroutineV2.WaitTime(2).Start(this._obj2); + this._num++; + console.log(`WaitTime2 ${this._num}`); + yield CoroutineV2.WaitTime(2).Start(this._obj2); + this._num++; + console.log(`WaitTime2 ${this._num}`); + } + + *Test2_1() { + console.log("111"); + CoroutineV2.Single(this.Test2_2()).Start(this); + console.log("333"); + } + + *Test2_2() { + console.log("222"); + return; + } + + button2Clicked() { + // this._routine && this._routine.Stop(); + if (this._obj2) { + CoroutineV2.StopCoroutinesBy(this._obj2); + this._obj2 = null; + return; + } + if (this._obj) { + CoroutineV2.StopCoroutinesBy(this._obj); + this._obj = null; + return; + } + + CoroutineV2.StopCoroutinesBy(this); + + } + + button3Clicked() { + // CoroutineV2.StopCoroutinesBy(this); + this._routine && this._routine.Pause(); + } + + button4Clicked() { + // CoroutineV2.StopCoroutinesBy(this); + this._routine && this._routine.Resume(); + } + + *Coroutine1(start: number, end: number) { + for (let i = start; i <= end; i++) { + // yield CoroutineV2.WaitTime(1).Start(); // Start()可以省略, 會由外層啟動 + // yield CoroutineV2.WaitTime(1).Start(this); // target也可以省略, 由外層的target控制 + + yield CoroutineV2.WaitTime(1).Start(); + console.log(`C1 => ${i}`); + + // 嵌套 + yield CoroutineV2 + .WaitTime(1) + .ThenParallel( + // 再嵌套 + CoroutineV2.Action(() => console.log("start parallel")), + this.Coroutine2(10, 2), + this.Coroutine2(20, 2), + new A()) + .ThenAction(() => console.log("end parallel")) + .Start(); + + // Promise + yield this.loadItemAsync("settings.json"); + } + } + + *Coroutine2(num: number, repeat: number) { + for (let i = 0; i < repeat; i++) { + //yield CoroutineV2.WaitTime(2); + yield 0; + console.log(`C2: ${num}`); + // yield CoroutineV2.WaitTime(1); + } + } + + actionCallback() { + console.log("action callback 2"); + } + + loadItemAsync(id: string): Promise<{ id: string }> { + return new Promise((resolve) => { + console.log('loading item start:', id); + setTimeout(() => { + resolve({ id: id }); + console.log('loading item done:', id); + }, 3000); + }); + } +} diff --git a/src/script/Engine/CatanEngine/CoroutineV2/CoroutineExample.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/CoroutineExample.ts.meta new file mode 100644 index 0000000..1c2b129 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/CoroutineExample.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "dfd32c11-76f6-4e38-9272-1d7966d1ef3c", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts b/src/script/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts new file mode 100644 index 0000000..5606b57 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts @@ -0,0 +1,75 @@ +import { IEnumeratorV2, IEnumeratorV2Started } from "./IEnumeratorV2"; +import { BaseEnumerator } from "./Core/BaseEnumerator"; +import { SingleEnumerator } from "./Core/SingleEnumerator"; +import { ParallelEnumerator } from "./Core/ParallelEnumerator"; +import { WaitTimeEnumerator } from "./Core/WaitTimeEnumerator"; +import { ActionEnumerator } from "./Core/ActionEnumerator"; +import { CoroutineExecutor } from "./Core/CoroutineExecutor"; + +export module CoroutineV2 { + /** + * 啟動一般協程 + */ + export function StartCoroutine(iterator: Iterator, target?: any): IEnumeratorV2Started { + return Single(iterator).Start(target); + } + + /** + * 依據IEnumeratorV2.Start(target)綁定的目標, 來停止協程 + * @param target + */ + export function StopCoroutinesBy(target: any) { + CoroutineExecutor.instance.StopCoroutineBy(target); + } + + /** + * 單一協程 + */ + export function Single(iterator: Iterator): IEnumeratorV2 { + if (iterator instanceof BaseEnumerator) { + return iterator; + } else { + return new SingleEnumerator(iterator); + } + } + + /** + * 平行協程 + */ + export function Parallel(...iterators: Iterator[]): IEnumeratorV2 { + return new ParallelEnumerator(iterators); + } + + /** + * 序列協程 + */ + export function Serial(...iterators: Iterator[]): IEnumeratorV2 { + let [iterator, ...others] = iterators; + if (iterator instanceof BaseEnumerator) { + return iterator.ThenSerial(...others); + } else { + return new SingleEnumerator(iterator).ThenSerial(...others); + } + } + + /** + * 執行方法協程 + * @param action 方法 + * @param delaySeconds 延遲秒數 + */ + export function Action(action: Function, delaySeconds?: number): IEnumeratorV2 { + if (delaySeconds > 0) { + return new WaitTimeEnumerator(delaySeconds).Then(new ActionEnumerator(action)); + } else { + return new ActionEnumerator(action); + } + } + + /** + * 等待時間協程 + * @param seconds 秒數 + */ + export function WaitTime(seconds: number): IEnumeratorV2 { + return new WaitTimeEnumerator(seconds); + } +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts.meta new file mode 100644 index 0000000..6ac7bb1 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "fc38e505-bd37-44c3-9e0a-fd463bb88c51", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts b/src/script/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts new file mode 100644 index 0000000..c684c4d --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts @@ -0,0 +1,16 @@ +export interface IEnumeratorV2 extends Iterator { + Start(target?: any): IEnumeratorV2Started; + Then(iterator: Iterator): IEnumeratorV2; + ThenSerial(...iterators: Iterator[]): IEnumeratorV2; + ThenParallel(...iterators: Iterator[]): IEnumeratorV2; + ThenAction(action: Function, delaySeconds?: number): IEnumeratorV2; + ThenWaitTime(seconds: number): IEnumeratorV2; +} + +export interface IEnumeratorV2Started { + readonly Current: any; + + Pause(): void; + Resume(): void; + Stop(): void; +} diff --git a/src/script/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts.meta b/src/script/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts.meta new file mode 100644 index 0000000..a4feac9 --- /dev/null +++ b/src/script/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "df3ab07d-3d2b-4552-b454-29b95223ea85", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2.meta b/src/script/Engine/CatanEngine/NetManagerV2.meta new file mode 100644 index 0000000..cb48b8a --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "6f870efd-e869-4415-9cf2-138ab667cd5d", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Core.meta b/src/script/Engine/CatanEngine/NetManagerV2/Core.meta new file mode 100644 index 0000000..a05aca1 --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Core.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "5e6c027f-ce4b-47fa-968c-f3bb6059ad81", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts new file mode 100644 index 0000000..a9d801b --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts @@ -0,0 +1,13 @@ +import { Action } from "../../CSharp/System/Action"; +import { INetRequest } from "./INetRequest"; +import { INetResponse } from "./INetResponse"; + +export interface INetConnector { + readonly OnDataReceived: Action>; + readonly OnDisconnected: Action; + readonly IsConnected: boolean; + + SendAsync(req: INetRequest): Iterator; + Send(req: INetRequest); + Logout(); +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts.meta new file mode 100644 index 0000000..75c86ab --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "f97991b5-0da6-4220-ab29-13c8f8f7e405", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts new file mode 100644 index 0000000..e7852ee --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts @@ -0,0 +1,12 @@ +import { INetResponse } from "./INetResponse"; + +export interface INetRequest { + readonly Method: string; + readonly MethodBack: string; + + Data: TRequest; + Result: INetResponse; + + SendAsync(): Promise>; + Send(); +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts.meta new file mode 100644 index 0000000..dc644ba --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "339fcf27-bdb9-4b8f-ae18-dd54c9500145", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts new file mode 100644 index 0000000..502b4cf --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts @@ -0,0 +1,6 @@ +export interface INetResponse { + readonly Method: string; + readonly Status: number; + readonly Data: TResponse; + readonly IsValid: boolean; +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts.meta new file mode 100644 index 0000000..1861d9d --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "c4cb0cd4-b98c-4f8e-b1e6-ac3b51281b28", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Examples.meta b/src/script/Engine/CatanEngine/NetManagerV2/Examples.meta new file mode 100644 index 0000000..ddcf31a --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Examples.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "94e55972-723c-4dab-9ebc-870bd5043fca", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Examples/NetTester.ts b/src/script/Engine/CatanEngine/NetManagerV2/Examples/NetTester.ts new file mode 100644 index 0000000..22c3adb --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Examples/NetTester.ts @@ -0,0 +1,69 @@ +import { CoroutineV2 } from "../../CoroutineV2/CoroutineV2"; +import { INetResponse } from "../Core/INetResponse"; +import { NetConnector } from "../NetConnector"; +import { NetManager } from "../NetManager"; +import { Slot1_SpinRequestExample } from "./Slot1_SpinRequestExample"; + +const { ccclass, property } = console._decorator; + +@ccclass +export default class NetTester extends console.Component { + + onConnectClicked() { + CoroutineV2.StartCoroutine(this.ConnectAsync()); + } + + *ConnectAsync() { + if (!NetManager.HasInit) { + let conn = new NetConnector("192.168.7.165", 9005); + conn.OnDataReceived.AddCallback(this.OnNetDataReceived, this); + conn.OnDisconnected.AddCallback(this.OnNetDisconnected, this); + conn.OnLoadUIMask.AddCallback(this.OnLoadUIMask, this); + + NetManager.Initialize(conn); + } + + console.log("連線中..."); + yield NetManager.ConnectAsync(); // 同個connector要再次連線, 可以不用叫CasinoNetManager.Initialize(), 但要先叫CasinoNetManager.Disconnect() + console.log(`連線狀態: ${NetManager.IsConnected}`); + } + + onDisconnectClicked() { + console.log("中斷連線中..."); + NetManager.Disconnect(); // 中斷連線 + } + + onSendMessageClicked1() { + console.log("發送訊息(不使用協程)"); + let req = new Slot1_SpinRequestExample(401); + req.Send(); + // CasinoNetManager.Send(req); + } + + onSendMessageClicked2() { + CoroutineV2.StartCoroutine(this.SendAsync()); + } + + *SendAsync() { + console.log("發送訊息中(使用協程)..."); + let req = new Slot1_SpinRequestExample(399); + yield req.SendAsync(); + // yield CasinoNetManager.SendAsync(req); + + let resp = req.Result; + console.log(`發送協程完畢, Server回應: ${resp.Method}(${JSON.stringify(resp.Data)}), 狀態: ${resp.Status}`); + // console.log(`使用介面資料: ${resp.Data.slot}`); + } + + private OnNetDisconnected() { + console.log("[事件] 收到連線中斷事件"); + } + + private OnNetDataReceived(resp: INetResponse) { + console.log(`[事件] 收到server呼叫: ${resp.Method}(${JSON.stringify(resp.Data)}), 狀態: ${resp.Status}`); + } + + private OnLoadUIMask(value: boolean) { + console.log(`[事件] LoadUIMask: ${value}`); + } +} diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Examples/NetTester.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/Examples/NetTester.ts.meta new file mode 100644 index 0000000..9cfea89 --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Examples/NetTester.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "0cb7df7a-d0e7-4ce1-832e-4583cf3385e5", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Examples/Slot1_SpinRequestExample.ts b/src/script/Engine/CatanEngine/NetManagerV2/Examples/Slot1_SpinRequestExample.ts new file mode 100644 index 0000000..60e9bac --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Examples/Slot1_SpinRequestExample.ts @@ -0,0 +1,37 @@ +import { NetRequest } from "../NetRequest"; + +// 送給server的結構 +interface Request { + pay: number; +} + +// server回應的結構 +interface Response { + pay: [[number, number]]; + /**拉霸結果 */ + slot: number[]; + get: any[]; +} + +// class Account_CreateRequest extends CasinoRequest { // 也可以是基本類或any, 但不建議用any, 使用介面ts才會有提示 +export class Slot1_SpinRequestExample extends NetRequest { + get Method(): string { + return "slot1.spin"; + } + + // MethodBack預設回傳Method, 不一樣才需要覆寫 + // get MethodBack(): string { + // return "slot1.freespin"; + // } + + constructor(totalBet: number) { + super(); + + // 原本的SingleValue拿掉, 統一使用Data來存送出結構 + + // this.Data = 2; + this.Data = { + pay: totalBet, + }; + } +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/Examples/Slot1_SpinRequestExample.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/Examples/Slot1_SpinRequestExample.ts.meta new file mode 100644 index 0000000..8a7d3bc --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/Examples/Slot1_SpinRequestExample.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "1af9e6af-3dc3-4d02-8b24-481adc07932a", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts b/src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts new file mode 100644 index 0000000..f367907 --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts @@ -0,0 +1,4 @@ +export default class NetConfig { + /**是否顯示RPC接送JSON的LOG */ + public static ShowServerLog: boolean = true; +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts.meta new file mode 100644 index 0000000..14afee9 --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "c7f5f6a9-94fd-4f5f-9f0a-545cd14edca9", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts b/src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts new file mode 100644 index 0000000..b6f15a7 --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts @@ -0,0 +1,263 @@ +import { Tools } from "../../../Tools"; +import { BaseEnumerator } from "../CoroutineV2/Core/BaseEnumerator"; +import { Action } from "../CSharp/System/Action"; +import { Encoding } from "../CSharp/System/Text/Encoding"; +import { INetRequest } from "./Core/INetRequest"; +import { INetResponse } from "./Core/INetResponse"; +import NetConfig from "./NetConfig"; +import { NetManager } from "./NetManager"; + +export class NetConnector { + readonly OnDataReceived: Action> = new Action>(); + readonly OnDisconnected: Action = new Action(); + readonly OnLoadUIMask: Action = new Action(); + + get IsConnected() { + return this._ws && this._ws.readyState === WebSocket.OPEN; + } + + private _host: string; + private _ws: WebSocket = null!; + private _waitings: WsRequestEnumerator[] = []; + + constructor(host: string, port: number, ip: string) { + let checkHttp: string = ""; + let index: number = host.indexOf("https://"); + if (index != -1) { + checkHttp = "https"; + host = host.replace("https://", ""); + } else { + checkHttp = window.location.href.substring(0, 5); + host = host.replace("http://", ""); + } + if (true) { + console.log("[事件]checkHttp=", checkHttp, host, port); + } + if (checkHttp != "https") { + this._host = `ws://${host}:${port}/?ip=${ip}`; + } + else { + this._host = `wss://${host}:${port}/?ip=${ip}`; + } + } + + async ConnectAsync() { + if (this._ws) { + throw new Error("請先執行CasinoNetManager.Disconnect()中斷連線"); + } + this._ws = new WebSocket(this._host); + + this._ws.binaryType = 'arraybuffer'; + this._ws.onopen = this.OnWebSocketOpen.bind(this); + this._ws.onmessage = this.OnWebSocketMessage.bind(this); + this._ws.onerror = this.OnWebSocketError.bind(this); + this._ws.onclose = this.OnWebSocketClose.bind(this); + + while (!NetManager.IsConnected) { + await Tools.Sleep(1); + } + return new WsConnectEnumerator(this._ws); + } + + async Send(req: INetRequest) { + if (!this.IsConnected) return; + + let json = [req.Method]; + if (req.Data != null && req.Data != undefined && req.Data) { + json[1] = req.Data; + } + + if (true && NetConfig.ShowServerLog) { + if (req.Data != null && req.Data != undefined && req.Data) { + console.log(`[RPC] 傳送server資料: ${req.Method}(${JSON.stringify(req.Data)})`); + } else { + console.log(`[RPC] 傳送server資料: ${req.Method}()`); + } + } + + let str = JSON.stringify(json); + if (str.length > 65535) { + throw new Error('要傳的資料太大囉'); + } + + let strary = Encoding.UTF8.GetBytes(str); + let buffer = new Uint8Array(4 + strary.byteLength); + let u16ary = new Uint16Array(buffer.buffer, 0, 3); + u16ary[0] = strary.byteLength; + buffer[3] = 0x01; + buffer.set(strary, 4); + + await this._ws.send(buffer); + } + + async SendAsync(req: INetRequest, mask: boolean) { + let iterator = new WsRequestEnumerator(req); + if (!this.IsConnected) { + iterator.SetResponse(ErrorResponse); + } else { + this._waitings.push(iterator); + if (mask) { + this.OnLoadUIMask.DispatchCallback(true); + } + this.Send(req); + while (!iterator.Done) { + await Tools.Sleep(1); + } + + } + return iterator; + }; + + Disconnect() { + this.WebSocketEnded(); + } + + private WebSocketEnded() { + if (!this._ws) return; + + this._ws.close(); + this._ws.onopen = null; + this._ws.onmessage = null; + this._ws.onclose = () => { }; + this._ws = null!; + + this.CleanWaitings(); + this.OnDisconnected.DispatchCallback(); + } + + private CleanWaitings() { + for (let w of this._waitings) { + w.SetResponse(ErrorResponse); + this.OnLoadUIMask.DispatchCallback(false); + } + this._waitings.length = 0; + } + + private OnWebSocketOpen(e: Event) { + if (true) { + console.log(`[RPC] ${this._host} Connected.`); + } + } + + private OnWebSocketMessage(e: MessageEvent) { + if (e.data instanceof ArrayBuffer) { + this.ParseRpcMessage(e.data); + } else if (e.data instanceof Blob) { + let reader = new FileReader(); + reader.onload = (e) => { this.ParseRpcMessage(reader.result); reader.onload = null; } + reader.readAsArrayBuffer(e.data); + } else { + throw new Error(`未知的OnWebSocketMessage(e.data)類型: ${e.data}`); + } + } + + private ParseRpcMessage(buffer: ArrayBuffer) { + let startIndex = 0, byteLength = buffer.byteLength; + while (startIndex + 4 < byteLength) { + let strlen = new DataView(buffer, startIndex, 3).getUint16(0, true); + let str = Encoding.UTF8.GetString(new Uint8Array(buffer, startIndex + 4, strlen)); + startIndex += strlen + 4; + + try { + let json = JSON.parse(str); + let method = json[0]; + let status = json[1][0]; + let data = json[1][1]; + + let resp = >{ + Method: method, + Status: status, + Data: data, + IsValid: method && status === 0 + }; + + if (true && NetConfig.ShowServerLog) { + if (data) { + console.log(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`); + } else { + console.log(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}()`); + } + } + + let dispatch = true; + for (let i = 0, len = this._waitings.length; i < len; i++) { + let w = this._waitings[i]; + if (w.MethodBack === resp.Method) { + dispatch = false; + this._waitings.splice(i, 1); + w.SetResponse(resp); + this.OnLoadUIMask.DispatchCallback(false); + break; + } + } + + if (dispatch) { + this.OnDataReceived.DispatchCallback(resp); + } + } + catch + { + throw new Error(`[RPC] 無法解析Server回應: ${str}`); + } + } + } + + private OnWebSocketError(ev: Event) { + throw new Error(`[RPC] 無法解析Server回應: ${ev}`); + } + + private OnWebSocketClose(e: CloseEvent) { + this.WebSocketEnded(); + } +} + +const ErrorResponse: INetResponse = { + Status: -1, + Method: "", + Data: {}, + IsValid: false, +}; + +class WsConnectEnumerator extends BaseEnumerator { + private _ws: WebSocket; + + constructor(ws: WebSocket) { + super(); + this._ws = ws; + } + + next(value?: any): IteratorResult { + return { + done: this._ws.readyState === WebSocket.OPEN || this._ws.readyState === WebSocket.CLOSED, + value: undefined + }; + } +} + +class WsRequestEnumerator extends BaseEnumerator { + readonly MethodBack: string; + + private _req: INetRequest; + private _done: boolean = false; + + public get Done(): boolean { return this._done; } + + constructor(req: INetRequest) { + super(); + + this._req = req; + this.MethodBack = req.MethodBack; + } + + SetResponse(resp: INetResponse) { + this._req.Result = resp; + this._done = true; + } + + next(value?: any): IteratorResult { + return { + done: this._done, + value: undefined + }; + } +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts.meta new file mode 100644 index 0000000..99a8b42 --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "221e1688-cc40-450d-9248-464978540a85", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts b/src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts new file mode 100644 index 0000000..0db9100 --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts @@ -0,0 +1,49 @@ +import { INetRequest } from "./Core/INetRequest"; +import { NetConnector } from "./NetConnector"; + +export class NetManager { + static get IsConnected() { return this._connector && this._connector.IsConnected; } + static get HasInit() { return this._connector != null; } + + private static _connector: NetConnector; + + static Initialize(connector: NetConnector) { + this._connector = connector; + } + + static async ConnectAsync() { + this.CheckConnector(); + return await this._connector.ConnectAsync(); + } + + /** + * 斷線 + */ + static Disconnect() { + this.CheckConnector(); + this._connector.Disconnect(); + } + + /** + * 傳送資料給Server, 不等待回應 + * @param req + */ + static Send(req: INetRequest) { + this.CheckConnector(); + this._connector.Send(req); + } + + /** + * 傳送資料給Server, 並等待回應 + * @param req + */ + static async SendAsync(req: INetRequest, mask: boolean) { + this.CheckConnector(); + return await this._connector.SendAsync(req, mask); + } + + private static CheckConnector() { + if (!this._connector) throw new Error("請先呼叫CasinoNetManager.Initialize()初始化connector"); + } + +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts.meta new file mode 100644 index 0000000..3b9b3ba --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "7c3e375d-3672-42e7-8a45-dd5ecf9d5fe8", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts b/src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts new file mode 100644 index 0000000..dd94144 --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts @@ -0,0 +1,21 @@ +import { INetRequest } from "./Core/INetRequest"; +import { NetManager } from "./NetManager"; + +export abstract class NetRequest implements INetRequest { + abstract get Method(): string; + + get MethodBack(): string { + return this.Method; + } + + Data: TResquest; + Result: import("./Core/INetResponse").INetResponse; + + async SendAsync(mask: boolean = false): Promise> { + return await NetManager.SendAsync(this, mask); + } + + Send() { + NetManager.Send(this); + } +} diff --git a/src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts.meta b/src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts.meta new file mode 100644 index 0000000..13db2ab --- /dev/null +++ b/src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "36534597-4273-48e8-bbeb-8dde4857d26f", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NoSleep.ts b/src/script/Engine/CatanEngine/NoSleep.ts new file mode 100644 index 0000000..c96f1e4 --- /dev/null +++ b/src/script/Engine/CatanEngine/NoSleep.ts @@ -0,0 +1,81 @@ +export class NoSleep { + static oldIOS: boolean = typeof navigator !== 'undefined' && parseFloat(('' + (/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0, ''])[1]).replace('undefined', '3_2').replace('_', '.').replace('_', '')) < 10 && !window["MSStream"]; + static webm: string = 'data:video/webm;base64,GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQI0VSalmQCgq17FAAw9CQE2AQAZ3aGFtbXlXQUAGd2hhbW15RIlACECPQAAAAAAAFlSua0AxrkAu14EBY8WBAZyBACK1nEADdW5khkAFVl9WUDglhohAA1ZQOIOBAeBABrCBCLqBCB9DtnVAIueBAKNAHIEAAIAwAQCdASoIAAgAAUAmJaQAA3AA/vz0AAA='; + static mp4: string = 'data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQADQGgcIQADQGgcAAAACQGeYUQn/wALKCEAA0BoHAAAAAkBnmNEJ/8ACykhAANAaBwhAANAaBwAAAANQZpoNExDP/6plgAMuSEAA0BoHAAAAAtBnoZFESwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBnqVEJ/8ACykhAANAaBwAAAAJAZ6nRCf/AAsoIQADQGgcIQADQGgcAAAADUGarDRMQz/+qZYADLghAANAaBwAAAALQZ7KRRUsK/8ACPkhAANAaBwAAAAJAZ7pRCf/AAsoIQADQGgcIQADQGgcAAAACQGe60Qn/wALKCEAA0BoHAAAAA1BmvA0TEM//qmWAAy5IQADQGgcIQADQGgcAAAAC0GfDkUVLCv/AAj5IQADQGgcAAAACQGfLUQn/wALKSEAA0BoHCEAA0BoHAAAAAkBny9EJ/8ACyghAANAaBwAAAANQZs0NExDP/6plgAMuCEAA0BoHAAAAAtBn1JFFSwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBn3FEJ/8ACyghAANAaBwAAAAJAZ9zRCf/AAsoIQADQGgcIQADQGgcAAAADUGbeDRMQz/+qZYADLkhAANAaBwAAAALQZ+WRRUsK/8ACPghAANAaBwhAANAaBwAAAAJAZ+1RCf/AAspIQADQGgcAAAACQGft0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bm7w0TEM//qmWAAy4IQADQGgcAAAAC0Gf2kUVLCv/AAj5IQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHAAAAAkBn/tEJ/8ACykhAANAaBwAAAANQZvgNExDP/6plgAMuSEAA0BoHCEAA0BoHAAAAAtBnh5FFSwr/wAI+CEAA0BoHAAAAAkBnj1EJ/8ACyghAANAaBwhAANAaBwAAAAJAZ4/RCf/AAspIQADQGgcAAAADUGaJDRMQz/+qZYADLghAANAaBwAAAALQZ5CRRUsK/8ACPkhAANAaBwhAANAaBwAAAAJAZ5hRCf/AAsoIQADQGgcAAAACQGeY0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bmmg0TEM//qmWAAy5IQADQGgcAAAAC0GehkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGepUQn/wALKSEAA0BoHAAAAAkBnqdEJ/8ACyghAANAaBwAAAANQZqsNExDP/6plgAMuCEAA0BoHCEAA0BoHAAAAAtBnspFFSwr/wAI+SEAA0BoHAAAAAkBnulEJ/8ACyghAANAaBwhAANAaBwAAAAJAZ7rRCf/AAsoIQADQGgcAAAADUGa8DRMQz/+qZYADLkhAANAaBwhAANAaBwAAAALQZ8ORRUsK/8ACPkhAANAaBwAAAAJAZ8tRCf/AAspIQADQGgcIQADQGgcAAAACQGfL0Qn/wALKCEAA0BoHAAAAA1BmzQ0TEM//qmWAAy4IQADQGgcAAAAC0GfUkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGfcUQn/wALKCEAA0BoHAAAAAkBn3NEJ/8ACyghAANAaBwhAANAaBwAAAANQZt4NExC//6plgAMuSEAA0BoHAAAAAtBn5ZFFSwr/wAI+CEAA0BoHCEAA0BoHAAAAAkBn7VEJ/8ACykhAANAaBwAAAAJAZ+3RCf/AAspIQADQGgcAAAADUGbuzRMQn/+nhAAYsAhAANAaBwhAANAaBwAAAAJQZ/aQhP/AAspIQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHAAACiFtb292AAAAbG12aGQAAAAA1YCCX9WAgl8AAAPoAAAH/AABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAGGlvZHMAAAAAEICAgAcAT////v7/AAAF+XRyYWsAAABcdGtoZAAAAAPVgIJf1YCCXwAAAAEAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAygAAAMoAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAB9AAABdwAAEAAAAABXFtZGlhAAAAIG1kaGQAAAAA1YCCX9WAgl8AAV+QAAK/IFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAUcbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAE3HN0YmwAAACYc3RzZAAAAAAAAAABAAAAiGF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAygDKAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAyYXZjQwFNQCj/4QAbZ01AKOyho3ySTUBAQFAAAAMAEAAr8gDxgxlgAQAEaO+G8gAAABhzdHRzAAAAAAAAAAEAAAA8AAALuAAAABRzdHNzAAAAAAAAAAEAAAABAAAB8GN0dHMAAAAAAAAAPAAAAAEAABdwAAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAAC7gAAAAAQAAF3AAAAABAAAAAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAEEc3RzegAAAAAAAAAAAAAAPAAAAzQAAAAQAAAADQAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAANAAAADQAAAQBzdGNvAAAAAAAAADwAAAAwAAADZAAAA3QAAAONAAADoAAAA7kAAAPQAAAD6wAAA/4AAAQXAAAELgAABEMAAARcAAAEbwAABIwAAAShAAAEugAABM0AAATkAAAE/wAABRIAAAUrAAAFQgAABV0AAAVwAAAFiQAABaAAAAW1AAAFzgAABeEAAAX+AAAGEwAABiwAAAY/AAAGVgAABnEAAAaEAAAGnQAABrQAAAbPAAAG4gAABvUAAAcSAAAHJwAAB0AAAAdTAAAHcAAAB4UAAAeeAAAHsQAAB8gAAAfjAAAH9gAACA8AAAgmAAAIQQAACFQAAAhnAAAIhAAACJcAAAMsdHJhawAAAFx0a2hkAAAAA9WAgl/VgIJfAAAAAgAAAAAAAAf8AAAAAAAAAAAAAAABAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAACsm1kaWEAAAAgbWRoZAAAAADVgIJf1YCCXwAArEQAAWAAVcQAAAAAACdoZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU3RlcmVvAAAAAmNtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAidzdGJsAAAAZ3N0c2QAAAAAAAAAAQAAAFdtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAADNlc2RzAAAAAAOAgIAiAAIABICAgBRAFQAAAAADDUAAAAAABYCAgAISEAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAABYAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAGAAAAWAAAAXBzdGNvAAAAAAAAAFgAAAOBAAADhwAAA5oAAAOtAAADswAAA8oAAAPfAAAD5QAAA/gAAAQLAAAEEQAABCgAAAQ9AAAEUAAABFYAAARpAAAEgAAABIYAAASbAAAErgAABLQAAATHAAAE3gAABPMAAAT5AAAFDAAABR8AAAUlAAAFPAAABVEAAAVXAAAFagAABX0AAAWDAAAFmgAABa8AAAXCAAAFyAAABdsAAAXyAAAF+AAABg0AAAYgAAAGJgAABjkAAAZQAAAGZQAABmsAAAZ+AAAGkQAABpcAAAauAAAGwwAABskAAAbcAAAG7wAABwYAAAcMAAAHIQAABzQAAAc6AAAHTQAAB2QAAAdqAAAHfwAAB5IAAAeYAAAHqwAAB8IAAAfXAAAH3QAAB/AAAAgDAAAICQAACCAAAAg1AAAIOwAACE4AAAhhAAAIeAAACH4AAAiRAAAIpAAACKoAAAiwAAAItgAACLwAAAjCAAAAFnVkdGEAAAAObmFtZVN0ZXJlbwAAAHB1ZHRhAAAAaG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAO2lsc3QAAAAzqXRvbwAAACtkYXRhAAAAAQAAAABIYW5kQnJha2UgMC4xMC4yIDIwMTUwNjExMDA='; + + static hasInit: boolean = false; + static noSleepTimer: number; + static noSleepVideo: HTMLVideoElement; + static isStart: boolean; + + public static init() { + if (this.hasInit) return; + + if (!this.oldIOS) { + let noSleepVideo = document.createElement('video'); + + noSleepVideo.setAttribute('muted', ''); + noSleepVideo.setAttribute('title', 'Intro'); + noSleepVideo.setAttribute('playsinline', ''); + + this.addSourceToVideo(noSleepVideo, 'webm', this.webm); + this.addSourceToVideo(noSleepVideo, 'mp4', this.mp4); + + noSleepVideo.addEventListener('loadedmetadata', function () { + if (noSleepVideo.duration <= 1) { + noSleepVideo.setAttribute('loop', ''); + } else { + noSleepVideo.addEventListener('timeupdate', function () { + if (noSleepVideo.currentTime > 0.5) { + noSleepVideo.currentTime = Math.random(); + } + }); + } + }); + + document.body.appendChild(noSleepVideo); + this.noSleepVideo = noSleepVideo; + } + + this.hasInit = true; + this.isStart = false; + } + + public static start() { + if (!this.isStart) { + console.log("nosleep start"); + if (this.oldIOS) { + this.stop(); + this.noSleepTimer = window.setInterval(() => { + if (!document.hidden) { + window.location.href = window.location.href.split('#')[0]; + window.setTimeout(window.stop, 0); + } + }, 15000); + } else { + this.noSleepVideo.play(); + } + this.isStart = true; + } + } + + public static stop() { + if (this.oldIOS) { + if (this.noSleepTimer) { + window.clearInterval(this.noSleepTimer); + this.noSleepTimer = 0; + } + } else { + this.noSleepVideo.pause(); + } + this.isStart = false; + } + + static addSourceToVideo(element: HTMLVideoElement, type: string, dataURI: string) { + let source = document.createElement('source'); + source.src = dataURI; + source.type = 'video/' + type; + element.appendChild(source); + } + +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/NoSleep.ts.meta b/src/script/Engine/CatanEngine/NoSleep.ts.meta new file mode 100644 index 0000000..836b5c4 --- /dev/null +++ b/src/script/Engine/CatanEngine/NoSleep.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "90f2152c-2c37-4c7c-b3a3-04c8aee53c34", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/TableV3.meta b/src/script/Engine/CatanEngine/TableV3.meta new file mode 100644 index 0000000..dcae6d1 --- /dev/null +++ b/src/script/Engine/CatanEngine/TableV3.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "8e05805d-5ab8-4526-8463-f4c837e23534", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/CatanEngine/公司自架接功能.txt b/src/script/Engine/CatanEngine/公司自架接功能.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/script/Engine/CatanEngine/公司自架接功能.txt.meta b/src/script/Engine/CatanEngine/公司自架接功能.txt.meta new file mode 100644 index 0000000..689bc66 --- /dev/null +++ b/src/script/Engine/CatanEngine/公司自架接功能.txt.meta @@ -0,0 +1,5 @@ +{ + "ver": "2.0.0", + "uuid": "6ab253ff-8c5d-419f-9f8b-5cf0ef661a28", + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Component.meta b/src/script/Engine/Component.meta new file mode 100644 index 0000000..5b7af33 --- /dev/null +++ b/src/script/Engine/Component.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "c87ad2c2-0bf9-4822-84db-00b939f614ee", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Data.meta b/src/script/Engine/Data.meta new file mode 100644 index 0000000..00c18b4 --- /dev/null +++ b/src/script/Engine/Data.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "89d65072-29d8-4f58-a9d7-5750406209e6", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/HotUpdate.meta b/src/script/Engine/HotUpdate.meta new file mode 100644 index 0000000..fcdb51f --- /dev/null +++ b/src/script/Engine/HotUpdate.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "10da37ee-322f-492b-b19b-ed0cd210e884", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils.meta b/src/script/Engine/Utils.meta new file mode 100644 index 0000000..7b1bd0e --- /dev/null +++ b/src/script/Engine/Utils.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "1207e3f9-4c55-4435-a3be-3d04c6806a1f", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Act.meta b/src/script/Engine/Utils/Act.meta new file mode 100644 index 0000000..ca8d9f1 --- /dev/null +++ b/src/script/Engine/Utils/Act.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "b43bf5ea-67c5-4fc8-9893-1f406a52508f", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Act/Shake.ts b/src/script/Engine/Utils/Act/Shake.ts new file mode 100644 index 0000000..d46b60d --- /dev/null +++ b/src/script/Engine/Utils/Act/Shake.ts @@ -0,0 +1,55 @@ +const { ccclass } = console._decorator; + +@ccclass +export default class Shake extends console.ActionInterval { + private _init: boolean = false; + private _initial_x: number = 0; + private _initial_y: number = 0; + private _strength_x: number = 0; + private _strength_y: number = 0; + + /** + * 建立抖動動畫 + * @param {number} duration 動畫持續時長 + * @param {number} strength_x 抖動幅度: x方向 + * @param {number} strength_y 抖動幅度: y方向 + * @returns {Shake} + */ + public static create(duration: number, strength_x: number, strength_y: number): Shake { + let act: Shake = new Shake(); + act.initWithDuration(duration, strength_x, strength_y); + return act; + } + + public initWithDuration(duration: number, strength_x: number, strength_y: number): boolean { + console.ActionInterval.prototype['initWithDuration'].apply(this, arguments); + this._strength_x = strength_x; + this._strength_y = strength_y; + return true; + } + + public fgRangeRand(min: number, max: number): number { + let rnd: number = Math.random(); + return rnd * (max - min) + min; + } + + public update(time: number): void { + let randx = this.fgRangeRand(-this._strength_x, this._strength_x); + let randy = this.fgRangeRand(-this._strength_y, this._strength_y); + this.getTarget().setPosition(randx + this._initial_x, randy + this._initial_y); + } + + public startWithTarget(target: console.Node): void { + console.ActionInterval.prototype['startWithTarget'].apply(this, arguments); + if (!this._init) { + this._init = true; + this._initial_x = target.x; + this._initial_y = target.y; + } + } + + public stop(): void { + this.getTarget().setPosition(new console.Vec2(this._initial_x, this._initial_y)); + console.ActionInterval.prototype['stop'].apply(this); + } +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Act/Shake.ts.meta b/src/script/Engine/Utils/Act/Shake.ts.meta new file mode 100644 index 0000000..e6b212e --- /dev/null +++ b/src/script/Engine/Utils/Act/Shake.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "c5872cc0-91a4-49cb-a055-e037accd801d", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Audio.meta b/src/script/Engine/Utils/Audio.meta new file mode 100644 index 0000000..6f27799 --- /dev/null +++ b/src/script/Engine/Utils/Audio.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "d042d487-d962-4d90-920e-70ab9b8b383c", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Audio/CSAudio.ts b/src/script/Engine/Utils/Audio/CSAudio.ts new file mode 100644 index 0000000..2879cd1 --- /dev/null +++ b/src/script/Engine/Utils/Audio/CSAudio.ts @@ -0,0 +1,184 @@ +import LocalStorageData from "../../Data/LocalStorageData"; + +export default class CSAudio { + private static _instance: CSAudio = null; + public static get Instance(): CSAudio { return this._instance; } + private _idToClip: Map = new Map(); + private _idToPath: Map = new Map(); + private _idToAudioId: Map = new Map(); + private _currentMusic: number = -1; + /**判斷音效播放太過頻繁不疊加 */ + private _lastPlaySoundTime: Map = new Map(); + private readonly _canPlaySoundCutTime: number = 10; + + constructor() { + CSAudio._instance = this; + if (LocalStorageData.Instance.MusicType) { + this.SetMusicVolume(+LocalStorageData.Instance.MusicType); + } else { + this.SetMusicVolume(0.3); + } + if (LocalStorageData.Instance.SoundType) { + this.SetSoundVolume(+LocalStorageData.Instance.SoundType); + } else { + this.SetSoundVolume(0.3); + } + } + + public AddClipsInfo(clips: Map, pathes: Map): void { + this._idToClip = clips; + this._idToPath = pathes; + } + + /** + * 設定AudioID + * @param id + * @param audioId + */ + private _setAudioId(id: number, audioId: number): void { + this._idToAudioId.set(id, audioId); + } + + /** + * 取得AudioID + * @param id + */ + private _getAudioId(id: number): number { + if (this._idToAudioId.has(id)) { + return this._idToAudioId.get(id); + } else { + return -1; + } + } + + /** + * 打開音效音量 + */ + public OpenSound(): void { + this.SetSoundVolume(0.3); + } + /** + * 關閉音效音量 + */ + public CloseSound(): void { + this.SetSoundVolume(0.0); + } + + /** + * 設定音效音量 + * @param volume + */ + public SetSoundVolume(volume: number): void { + LocalStorageData.Instance.SoundType = volume.toString(); + console.audioEngine.setEffectsVolume(volume); + } + + /** + * 播放音效 + * @param id + * @param loop + */ + public PlaySound(id: number, loop: boolean = false): void { + // 靜音後有一禎仍然會撥放的問題:音量>0才撥放 + if (console.audioEngine.getEffectsVolume() <= 0) { + return; + } + if (this._idToClip.has(id)) { + let path: string = this._idToPath.get(id); + let timenum: number = new Date().getTime(); + if (!this._lastPlaySoundTime.has(path)) { + this._lastPlaySoundTime.set(path, timenum); + let audioId: number = console.audioEngine.playEffect(this._idToClip.get(id), loop); + this._setAudioId(id, audioId); + } else { + let lastTime: number = this._lastPlaySoundTime.get(path); + if (timenum - lastTime > this._canPlaySoundCutTime) { + this._lastPlaySoundTime.set(path, timenum); + let audioId: number = console.audioEngine.playEffect(this._idToClip.get(id), loop); + this._setAudioId(id, audioId); + } + } + } else { + console.error("未知的Sound Id: ", id); + } + } + + /** + * 停止音效 + * @param id + */ + public StopSound(id: number): void { + let audioId = this._getAudioId(id); + if (audioId >= 0) { + console.audioEngine.stopEffect(audioId); + } + } + + /** + * 打開音樂音量 + */ + public OpenMusic(): void { + this.SetMusicVolume(0.3); + } + /** + * 關閉音樂音量 + */ + public CloseMusic(): void { + this.SetMusicVolume(0.0); + } + + /** + * 設定音樂音量 + * @param volume + */ + public SetMusicVolume(volume: number): void { + console.audioEngine.setMusicVolume(volume); + LocalStorageData.Instance.MusicType = volume.toString(); + // 靜音後有一禎仍然會撥放的問題:背景音樂要回復 + if (this._currentMusic != -1 && volume > 0 && !console.audioEngine.isMusicPlaying()) { + this._ccMusicPlayId = console.audioEngine.playMusic(this._idToClip.get(this._currentMusic), true); + } + } + + /** + * 撥放音樂 + * @param id + * @param loop + */ + public PlayMusic(id: number, loop: boolean = true): void { + if (this._currentMusic != id) { + if (this._idToClip.has(id)) { + // 靜音後有一禎仍然會撥放的問題:音量>0才撥放 + if (console.audioEngine.getMusicVolume() > 0) { + this._ccMusicPlayId = console.audioEngine.playMusic(this._idToClip.get(id), loop); + } + this._currentMusic = id; + } + else { + console.error("未知的Music Id: ", id); + } + } + } + private _ccMusicPlayId: number = -1; + private _currentMusicTime: number = -1; + public pauseOrResume(isPause?: boolean) { + if (isPause) { + this._currentMusicTime = console.audioEngine.getCurrentTime(this._ccMusicPlayId); + console.audioEngine.pauseAll(); + console.audioEngine.stopMusic(); + } else { + console.audioEngine.resumeAll(); + console.audioEngine.setCurrentTime(this._ccMusicPlayId, this._currentMusicTime); + } + } + + /** + * 停止音樂 + * @param id + */ + public StopMusic(): void { + console.audioEngine.stopMusic(); + this._currentMusic = -1; + } + +} diff --git a/src/script/Engine/Utils/Audio/CSAudio.ts.meta b/src/script/Engine/Utils/Audio/CSAudio.ts.meta new file mode 100644 index 0000000..c788d2c --- /dev/null +++ b/src/script/Engine/Utils/Audio/CSAudio.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "f3ba292a-ecad-4485-ab60-1cd3ee94979a", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Audio/CSCommonAudios.ts b/src/script/Engine/Utils/Audio/CSCommonAudios.ts new file mode 100644 index 0000000..c961453 --- /dev/null +++ b/src/script/Engine/Utils/Audio/CSCommonAudios.ts @@ -0,0 +1,167 @@ +import CSAudio from "./CSAudio"; + +export default class CSAppAudios extends CSAudio { + private static _instanceApp: CSAppAudios = null; + public static get InstanceApp(): CSAppAudios { return this._instanceApp; } + private _idToAppClip: Map = new Map(); + private _idToAppPath: Map = new Map(); + private _idToAppAudioId: Map = new Map(); + private _currentAppMusic: number = -1; + /** 判斷音效播放太過頻繁不疊加 */ + private _lastAppPlaySoundTime: Map = new Map(); + private readonly _appCanPlaySoundCutTime: number = 10; + constructor() { + super(); + CSAppAudios._instanceApp = this; + } + + public AddAppClipsInfo(clips: Map, pathes: Map): void { + this._idToAppClip = clips; + this._idToAppPath = pathes; + } + + /** + * 設定AudioID + * @param id + * @param audioId + */ + private _setAppAudioId(id: number, audioId: number): void { + this._idToAppAudioId.set(id, audioId); + } + + /** + * 打開音效音量 + */ + public OpenSound(): void { + super.OpenSound(); + } + /** + * 關閉音效音量 + */ + public CloseSound(): void { + super.CloseSound(); + } + + /** + * 取得AudioID + * @param id + */ + private _getAppAudioId(id: number): number { + if (this._idToAppAudioId.has(id)) { + return this._idToAppAudioId.get(id); + } else { + return -1; + } + } + + /** + * 播放音效 + * @param id + * @param loop + */ + public PlayAppSound(id: number, loop: boolean = false): void { + // 靜音後有一禎仍然會撥放的問題:音量>0才撥放 + if (console.audioEngine.getEffectsVolume() <= 0) { + return; + } + if (this._idToAppClip.has(id)) { + let path: string = this._idToAppPath.get(id); + let timenum: number = new Date().getTime(); + if (!this._lastAppPlaySoundTime.has(path)) { + this._lastAppPlaySoundTime.set(path, timenum); + let audioId: number = console.audioEngine.playEffect(this._idToAppClip.get(id), loop); + this._setAppAudioId(id, audioId); + } else { + let lastTime: number = this._lastAppPlaySoundTime.get(path); + if (timenum - lastTime > this._appCanPlaySoundCutTime) { + this._lastAppPlaySoundTime.set(path, timenum); + let audioId: number = console.audioEngine.playEffect(this._idToAppClip.get(id), loop); + this._setAppAudioId(id, audioId); + } + } + } else { + console.error("未知的Sound Id: ", id); + } + } + + /** + * 停止音效 + * @param id + */ + public StopAppSound(id: number): void { + let audioId: number = this._getAppAudioId(id); + if (audioId >= 0) { + console.audioEngine.stopEffect(audioId); + } + } + + /** + * 打開音樂音量 + */ + public OpenMusic(): void { + super.OpenMusic(); + this._setAppMusicVolume(0.3); + } + /** + * 關閉音樂音量 + */ + public CloseMusic(): void { + super.CloseMusic(); + this._setAppMusicVolume(0.0); // 這邊再改改 + } + + /** + * 設定音樂音量 + * @param volume + */ + private _setAppMusicVolume(volume: number): void { + // 靜音後有一禎仍然會撥放的問題:背景音樂要回復 + if (this._currentAppMusic != -1 && volume > 0 && !console.audioEngine.isMusicPlaying()) { + this._ccAppMusicPlayId = console.audioEngine.playMusic(this._idToAppClip.get(this._currentAppMusic), true); + } + } + + /** + * 撥放音樂 + * @param id + * @param loop + */ + public PlayAppMusic(id: number, loop: boolean = true): void { + if (this._currentAppMusic != id) { + if (this._idToAppClip.has(id)) { + // 靜音後有一禎仍然會撥放的問題:音量>0才撥放 + if (console.audioEngine.getMusicVolume() > 0) { + this._ccAppMusicPlayId = console.audioEngine.playMusic(this._idToAppClip.get(id), loop); + } + this._currentAppMusic = id; + } else { + console.error("未知的Music Id: ", id); + } + } + } + + private _ccAppMusicPlayId: number = -1; + private _currentAppMusicTime: number = -1; + + public pauseOrResume(isPause?: boolean): void { + super.pauseOrResume(); + if (isPause) { + this._currentAppMusicTime = console.audioEngine.getCurrentTime(this._ccAppMusicPlayId); + console.audioEngine.pauseAll(); + console.audioEngine.stopMusic(); + } else { + console.audioEngine.resumeAll(); + console.audioEngine.setCurrentTime(this._ccAppMusicPlayId, this._currentAppMusicTime); + } + } + + /** + * 停止音樂 + * @param id + */ + public StopMusic(): void { + console.audioEngine.stopMusic(); + this._currentAppMusic = -1; + } + +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Audio/CSCommonAudios.ts.meta b/src/script/Engine/Utils/Audio/CSCommonAudios.ts.meta new file mode 100644 index 0000000..bfb0744 --- /dev/null +++ b/src/script/Engine/Utils/Audio/CSCommonAudios.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "ce946cad-16db-4383-a734-43bb8f14089e", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Bezier.meta b/src/script/Engine/Utils/Bezier.meta new file mode 100644 index 0000000..e6eb2ec --- /dev/null +++ b/src/script/Engine/Utils/Bezier.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "4e2d4321-bbfb-46d8-87bc-15a7c99c000d", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Bezier/Bezier.ts b/src/script/Engine/Utils/Bezier/Bezier.ts new file mode 100644 index 0000000..f8d5ccf --- /dev/null +++ b/src/script/Engine/Utils/Bezier/Bezier.ts @@ -0,0 +1,16 @@ +export module Bezier { + + export function GetPoint(p0: console.Vec2, p1: console.Vec2, p2: console.Vec2, p3: console.Vec2, t: number): console.Vec2 { + if (t < 0) { + t = 0; + } + else if (t > 1) { + t = 1 + } + let OneMinusT = 1 - t; + return p0.mul(OneMinusT * OneMinusT * OneMinusT) + .add(p1.mul(3 * OneMinusT * OneMinusT * t)) + .add(p2.mul(3 * OneMinusT * t * t)) + .add(p3.mul(t * t * t)); + } +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Bezier/Bezier.ts.meta b/src/script/Engine/Utils/Bezier/Bezier.ts.meta new file mode 100644 index 0000000..946290b --- /dev/null +++ b/src/script/Engine/Utils/Bezier/Bezier.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "b47d81c4-01a1-45cd-94f8-19daf96f17a8", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/CCExtensions.meta b/src/script/Engine/Utils/CCExtensions.meta new file mode 100644 index 0000000..b4ecd16 --- /dev/null +++ b/src/script/Engine/Utils/CCExtensions.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "dcb480f5-98b4-4a48-9d82-e3e1fe837e8d", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/CCExtensions/ArrayExtension.ts b/src/script/Engine/Utils/CCExtensions/ArrayExtension.ts new file mode 100644 index 0000000..399dd72 --- /dev/null +++ b/src/script/Engine/Utils/CCExtensions/ArrayExtension.ts @@ -0,0 +1,15 @@ +declare interface Array { + /** + * 移除一個值並且回傳 + * @param index + */ + ExRemoveAt(index: number): T; +} + +Array.prototype.ExRemoveAt || Object.defineProperty(Array.prototype, 'ExRemoveAt', { + enumerable: false, + value: function (index: number) { + let item = this.splice(index, 1); + return item[0]; + } +}) \ No newline at end of file diff --git a/src/script/Engine/Utils/CCExtensions/ArrayExtension.ts.meta b/src/script/Engine/Utils/CCExtensions/ArrayExtension.ts.meta new file mode 100644 index 0000000..d50500a --- /dev/null +++ b/src/script/Engine/Utils/CCExtensions/ArrayExtension.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "02aa6cd7-21a1-4c22-bcbe-296bb938badd", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/CCExtensions/CCExtension.ts b/src/script/Engine/Utils/CCExtensions/CCExtension.ts new file mode 100644 index 0000000..5966567 --- /dev/null +++ b/src/script/Engine/Utils/CCExtensions/CCExtension.ts @@ -0,0 +1,223 @@ +declare namespace cc { + + export interface Node { + /** + * 設定世界座標 + * @param worldPoint + */ + SetWorldPosition(worldPoint: console.Vec2): void; + /** + * 取得世界座標 + */ + GetWorldPosition(): console.Vec2; + /** + * 設定長寬 + * @param size + */ + SetSizeDelta(size: console.Vec2); + /** + * 取得長寬 + */ + GetSizeDelta(): console.Vec2; + /** + * 增加一個子物件 + * @param childObj + */ + ExAddChild(childObj: console.Prefab | console.Node, childActive?: boolean): console.Node; + + /**設定層級為最低層 */ + ExSetLowestOrder(): void; + + /**設定層級為最高層 */ + ExSetHighestOrder(): void; + + /**設定層級,比目標OBJ再低一層 */ + ExSetOrderUnderTheObj(obj: console.Node, isNew?: boolean): number; + + /**設定層級,比目標OBJ再高一層 */ + ExSetOrderOverTheObj(obj: console.Node, isNew?: boolean): number; + /**位置維持在原位 */ + ExSetParent(parentObj: console.Node): void; + ExSetGray(showGray: boolean): void; + } +} + +console.Node.prototype.SetWorldPosition || Object.defineProperty(console.Node.prototype, 'SetWorldPosition', { + enumerable: false, + value: function (cocosWorldPos: console.Vec2) { + // let cocosWorldPos = new console.Vec2(unityWorldPos.x + 711, unityWorldPos.y + 400); + this.setPosition(this.parent.convertToNodeSpaceAR(cocosWorldPos)); + } +}) +console.Node.prototype.GetWorldPosition || Object.defineProperty(console.Node.prototype, 'GetWorldPosition', { + enumerable: false, + value: function () { + let cocosWorldPos = this.parent.convertToWorldSpaceAR(this.position); + // let unityWorldPos = new console.Vec2(cocosWorldPos.x - 711, cocosWorldPos.y - 400); + return cocosWorldPos; + } +}) +console.Node.prototype.SetSizeDelta || Object.defineProperty(console.Node.prototype, 'SetSizeDelta', { + enumerable: false, + value: function (size: console.Vec2) { + this.setContentSize(size.x, size.y); + } +}) +console.Node.prototype.GetSizeDelta || Object.defineProperty(console.Node.prototype, 'GetSizeDelta', { + enumerable: false, + value: function () { + let size: console.Size = this.GetSizeDelta(); + return new console.Vec2(size.width, size.width); + } +}) +console.Node.prototype.ExAddChild || Object.defineProperty(console.Node.prototype, 'ExAddChild', { + enumerable: false, + value: function (childObj: console.Prefab | console.Node, childActive: boolean = true) { + let gameObj = null; + if (childObj instanceof console.Prefab) { + gameObj = console.instantiate(childObj); + } + else { + gameObj = console.instantiate(childObj); + } + gameObj.active = childActive ? true : childActive; + gameObj.parent = this; + return gameObj; + } +}) +console.Node.prototype.ExSetLowestOrder || Object.defineProperty(console.Node.prototype, 'ExSetLowestOrder', { + enumerable: false, + value: function () { + this.setSiblingIndex(0); + } +}) +console.Node.prototype.ExSetHighestOrder || Object.defineProperty(console.Node.prototype, 'ExSetHighestOrder', { + enumerable: false, + value: function () { + this.setSiblingIndex(Number.MAX_VALUE); + } +}) +console.Node.prototype.ExSetOrderUnderTheObj || Object.defineProperty(console.Node.prototype, 'ExSetOrderUnderTheObj', { + enumerable: false, + value: function (obj: console.Node, isNew?: boolean) { + + let newIndex: number; + let objIndex = obj.getSiblingIndex(); + + // 如果是新創的元件 + if (isNew) { + newIndex = objIndex; + } + // 如果是已經在場景上的元件 + else { + let myIndex = this.getSiblingIndex(); + + // 如果一開始就在它下面 + if (myIndex < objIndex) { + newIndex = objIndex - 1; + } + else { + newIndex = objIndex; + } + } + this.setSiblingIndex(newIndex); + return newIndex; + } +}) +console.Node.prototype.ExSetOrderOverTheObj || Object.defineProperty(console.Node.prototype, 'ExSetOrderOverTheObj', { + enumerable: false, + value: function (obj: console.Node, isNew?: boolean) { + let newIndex: number; + let objIndex = obj.getSiblingIndex(); + + // 如果是新創的元件 + if (isNew) { + newIndex = objIndex + 1; + } + // 如果是已經在場景上的元件 + else { + let myIndex = this.getSiblingIndex(); + + // 如果一開始就在它下面 + if (myIndex < objIndex) { + newIndex = objIndex; + } + else { + newIndex = objIndex + 1; + } + } + this.setSiblingIndex(newIndex); + return newIndex; + } +}) +console.Node.prototype.ExSetParent || Object.defineProperty(console.Node.prototype, 'ExSetParent', { + enumerable: false, + value: function (parentObj: console.Node) { + let oriPos = this.GetWorldPosition(); + this.setParent(parentObj); + this.SetWorldPosition(oriPos); + } +}) +console.Node.prototype.ExSetGray || Object.defineProperty(console.Node.prototype, 'ExSetGray', { + enumerable: false, + value: function (showGray: boolean): void { + let btn: console.Button = this.getComponent(console.Button); + if (btn) { + btn.interactable = !showGray; + } + let material: console.Material = console.Material.createWithBuiltin(showGray ? console.Material.BUILTIN_NAME.GRAY_SPRITE.toString() : console.Material.BUILTIN_NAME.SPRITE.toString(), 0); + !showGray && material.define("USE_TEXTURE", true, 0); + let spriteComs: any[] = this.getComponentsInChildren(console.Sprite).concat(this.getComponentsInChildren(console.Label)); + for (let sprite of spriteComs) { + sprite.setMaterial(0, material); + } + + // 先使用createWithBuiltin,如果材質球一直Create沒被刪除,會在修改。 + // let material: console.Material = console.Material.getBuiltinMaterial(showGray ? console.Material.BUILTIN_NAME.GRAY_SPRITE.toString() : console.Material.BUILTIN_NAME.SPRITE.toString()); + // for (let sprite of spriteComs) { + // if (showGray) { + // sprite.setMaterial(0, console.Material.getBuiltinMaterial('2d-gray-sprite')); + // } + // else { + // sprite.setMaterial(0, console.Material.getBuiltinMaterial('2d-sprite')); + // } + // } + }, +}); +// console.Node.prototype.SetWorldPosition = function (cocosWorldPos: console.Vec2): void { +// // let cocosWorldPos = new console.Vec2(unityWorldPos.x + 711, unityWorldPos.y + 400); +// this.setPosition(this.parent.convertToNodeSpaceAR(cocosWorldPos)); +// } +// console.Node.prototype.GetWorldPosition = function (): console.Vec2 { +// let cocosWorldPos = this.parent.convertToWorldSpaceAR(this.position); +// // let unityWorldPos = new console.Vec2(cocosWorldPos.x - 711, cocosWorldPos.y - 400); +// return cocosWorldPos; +// } + +// console.Node.prototype.SetSizeDelta = function (size: console.Vec2) { +// this.setContentSize(size.x, size.y); +// } +// console.Node.prototype.GetSizeDelta = function (): console.Vec2 { +// let size: console.Size = this.GetSizeDelta(); +// return new console.Vec2(size.width, size.width); +// } + +// console.Node.prototype.ExAddChild = function (childObj: console.Prefab | console.Node): console.Node { + +// let gameObj = null; +// if (childObj instanceof console.Prefab) { +// gameObj = console.instantiate(childObj); +// } +// else { +// gameObj = console.instantiate(childObj); +// } +// gameObj.parent = this; +// return gameObj; +// } + +// console.Node.prototype.ExSetLowestOrder = function (): void { +// this.setSiblingIndex(0); +// } +// console.Node.prototype.ExSetHighestOrder = function (): void { +// this.setSiblingIndex(Number.MAX_VALUE); +// } diff --git a/src/script/Engine/Utils/CCExtensions/CCExtension.ts.meta b/src/script/Engine/Utils/CCExtensions/CCExtension.ts.meta new file mode 100644 index 0000000..5d1f492 --- /dev/null +++ b/src/script/Engine/Utils/CCExtensions/CCExtension.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "b373f805-9297-4af5-8ea6-0a250649b5b0", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/CCExtensions/NumberExtension.ts b/src/script/Engine/Utils/CCExtensions/NumberExtension.ts new file mode 100644 index 0000000..5d39881 --- /dev/null +++ b/src/script/Engine/Utils/CCExtensions/NumberExtension.ts @@ -0,0 +1,189 @@ + +declare interface Number { + + /** + * 金額每三位數(千)加逗號, 並且補到小數點第2位 + * 輸出 41,038,560.00 + * @param precision 補到小數點第幾位 + * @param isPadZero 是否要補零 + * */ + ExFormatNumberWithComma(precision?: number, isPadZero?: boolean): string; + /** + * 基本4位數(9,999-999B-T) + * */ + ExTransferToBMK(precision?: number,offset?: number): string; + /** + * 數字轉字串, 頭補0 + * @param size + */ + Pad(size: number): string; + /** + * 四捨五入到小數點第X位 (同server計算規則) + * @param precision + */ + ExToNumRoundDecimal(precision: number): number; + /** + * 無條件捨去到小數點第X位 + * @param precision + */ + ExToNumFloorDecimal(precision: number): number; + /** + * 無條件捨去強制保留X位小數,如:2,會在2後面補上00.即2.00 + * @param precision 補到小數點第幾位 + * @param isPadZero 是否要補零 + */ + ExToStringFloorDecimal(precision: number, isPadZero?: boolean): string; + /** + * 取整數) + */ + ExToInt():number; + /** + * 小數轉整數(支援科學符號) + */ + Float2Fixed():number; + /** + * 數字長度(支援科學符號) + */ + DigitLength():number; + + target: number; + + +} + +Number.prototype.ExFormatNumberWithComma || Object.defineProperty(Number.prototype, 'ExFormatNumberWithComma', { + enumerable: false, + value: function (precision: number = 2, isPadZero: boolean = true) { + + // let arr = String(this).split('.'); + let arr = this.ExToStringFloorDecimal(precision, isPadZero).split('.'); + let num = arr[0], result = ''; + while (num.length > 3) { + result = ',' + num.slice(-3) + result; + num = num.slice(0, num.length - 3); + } + if (num.length > 0) result = num + result; + return arr[1] ? result + '.' + arr[1] : result; + } +}) + + +Number.prototype.ExTransferToBMK || Object.defineProperty(Number.prototype, 'ExTransferToBMK', { + enumerable: false, + value: function (precision: number=2,offset: number = 0) { + /**千 */ + let MONEY_1K: number = 1000; + /**萬 */ + // let MONEY_10K: number = 10000; + /**十萬 */ + // let MONEY_100K: number = 100000; + /**百萬 */ + let MONEY_1M: number = 1000000; + /**千萬 */ + // let MONEY_10M: number = 10000000; + /**億 */ + // let MONEY_100M: number = 100000000; + /**十億 */ + let MONEY_1B: number = 1000000000; + /**百億 */ + // let MONEY_10B: number = 10000000000; + /**千億 */ + // let MONEY_100B: number = 100000000000; + /**兆 */ + // let MONEY_1T: number = 1000000000000; + offset = Math.pow(10, offset); + // if (this >= MONEY_1T * offset) { + // //(3)1,000T + // //1T~ + // return (~~(this / MONEY_1T)).ExFormatNumberWithComma(0) + "T"; + // } + if (this >= MONEY_1B * offset) { + //1,000B~900,000B + //1B~900B + return (this / MONEY_1B).ExFormatNumberWithComma(3, false) + "B"; + } + else if (this >= MONEY_1M * offset) { + //1,000M~900,000M + //1M~900M + return (this / MONEY_1M).ExFormatNumberWithComma(3, false) + "M"; + } + else if (this >= MONEY_1K * offset) { + //1,000K~900,000K + //1K~90K + return (this / MONEY_1K).ExFormatNumberWithComma(3, false) + "K"; + } + else { + //0~9,000,000 + //0~9,000 + return this.ExFormatNumberWithComma(precision); + } + } +}) +Number.prototype.Pad || Object.defineProperty(Number.prototype, 'Pad', { + enumerable: false, + value: function (size: number) { + let s = this + ""; + while (s.length < size) s = "0" + s; + return s; + } +}) +Number.prototype.ExToNumRoundDecimal || Object.defineProperty(Number.prototype, 'ExToNumRoundDecimal', { + enumerable: false, + value: function (precision: number) { + return Math.round(Math.round(this * Math.pow(10, (precision || 0) + 1)) / 10) / Math.pow(10, (precision || 0)); + } +}) +Number.prototype.ExToInt || Object.defineProperty(Number.prototype, 'ExToInt',{ + enumerable: false, + value: function (){ + return ~~this; + } +}) +Number.prototype.ExToNumFloorDecimal || Object.defineProperty(Number.prototype, 'ExToNumFloorDecimal', { + enumerable: false, + value: function (precision: number) { + let str = this.toPrecision(12); + let dotPos = str.indexOf('.'); + return dotPos == -1 ? this : +`${str.substr(0, dotPos + 1 + precision)}`; + } +}) +Number.prototype.ExToStringFloorDecimal || Object.defineProperty(Number.prototype, 'ExToStringFloorDecimal', { + enumerable: false, + value: function (precision: number, isPadZero: boolean = true) { + // 取小數點第X位 + let f = this.ExToNumFloorDecimal(precision); + let s = f.toString(); + // 補0 + if (isPadZero) { + let rs = s.indexOf('.'); + if (rs < 0) { + rs = s.length; + s += '.'; + } + while (s.length <= rs + precision) { + s += '0'; + } + } + return s; + } +}) +Number.prototype.Float2Fixed || Object.defineProperty(Number.prototype, 'Float2Fixed', { + enumerable: false, + value: function () { + if (this.toString().indexOf('e') === -1) { + return Number(this.toString().replace('.', '')); + } + const dLen = this.DigitLength(); + return dLen > 0 ? +parseFloat((this * Math.pow(10, dLen)).toPrecision(12)) : this; + } +}) +Number.prototype.DigitLength || Object.defineProperty(Number.prototype, 'DigitLength', { + enumerable: false, + value: function () { + const eSplit = this.toString().split(/[eE]/); + const len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0)); + return len > 0 ? len : 0; + } +}) + + \ No newline at end of file diff --git a/src/script/Engine/Utils/CCExtensions/NumberExtension.ts.meta b/src/script/Engine/Utils/CCExtensions/NumberExtension.ts.meta new file mode 100644 index 0000000..2216eef --- /dev/null +++ b/src/script/Engine/Utils/CCExtensions/NumberExtension.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "788e7381-bee6-4b74-addb-c4aa4c4ff4e3", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Number.meta b/src/script/Engine/Utils/Number.meta new file mode 100644 index 0000000..4c6cba6 --- /dev/null +++ b/src/script/Engine/Utils/Number.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "d6e55fc6-00b6-496a-aae2-74d694c1223b", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Number/NumberEx.ts b/src/script/Engine/Utils/Number/NumberEx.ts new file mode 100644 index 0000000..76f48e2 --- /dev/null +++ b/src/script/Engine/Utils/Number/NumberEx.ts @@ -0,0 +1,191 @@ +import { CoroutineV2 } from "../../CatanEngine/CoroutineV2/CoroutineV2"; +import { RandomEx } from "./RandomEx"; + +export module NumberEx { + /** + * 數字滾動 + * @param startNum + * @param endNum + * @param callbackfn + * @param chabgeRate + */ + export function* ChangeScore(startNum: number, endNum: number, callbackfn: (num: number) => void, sec: number) { + let fps = 30; + let waitTime = 0.03; + let changeRate = sec * fps; // -1為了讓changeRate數字能混亂點 + changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1; + changeRate = 1 / changeRate; + let diff = endNum - startNum; + let isIncrease = endNum >= startNum; + let tempScore = startNum; + let counter = 0; + let randomRate = 0; + while (true) { + if (endNum != tempScore) { + if (counter % 2 == 0) { + if (isIncrease) { randomRate = RandomEx.GetFloat(0, diff * changeRate).ExToNumFloorDecimal(2); } + else { randomRate = RandomEx.GetFloat(0, -diff * changeRate).ExToNumFloorDecimal(2); } + } + else { + randomRate = -randomRate; + } + + tempScore += diff * changeRate + randomRate; + // 遞增 + if (isIncrease && tempScore > endNum) { + tempScore = endNum; + } + // 遞減 + if (!isIncrease && tempScore < endNum) { + tempScore = endNum; + } + callbackfn(tempScore); + // yield null; + counter++; + yield CoroutineV2.WaitTime(waitTime); + } + else { + callbackfn(endNum); + break; + } + } + } + + /** + * 數字跳動 + * @param minNum 起始數字 + * @param maxNum 最終數字 + * @param callbackfn callbackfn + * @param sec 時間 + */ + export function* BeatScore(minNum: number, maxNum: number, endNum: number, callbackfn: (num: number) => void, sec: number): IterableIterator { + let fps: number = 13; + let waitTime: number = 0.07; + let changeRate: number = sec * fps; // -1為了讓changeRate數字能混亂點 + changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1; + changeRate = 1 / changeRate; + let diff: number = maxNum - minNum; + let isIncrease: boolean = maxNum >= minNum; + let tempScore: number = minNum; + let counter: number = 0; + let randomRate: number = 0; + let lastNum: number = minNum; + let nowNum: number = minNum; + while (true) { + if (maxNum !== tempScore) { + if (counter % 2 === 0) { + if (isIncrease) { + randomRate = RandomEx.GetFloat(0, diff * changeRate).ExToNumFloorDecimal(2); + } else { + randomRate = RandomEx.GetFloat(0, -diff * changeRate).ExToNumFloorDecimal(2); + } + } else { + randomRate = -randomRate; + } + + tempScore += diff * changeRate + randomRate; + // 遞增 + if (isIncrease && tempScore > maxNum) { + tempScore = maxNum; + } + // 遞減 + if (!isIncrease && tempScore < maxNum) { + tempScore = maxNum; + } + while(nowNum === lastNum) { + nowNum = RandomEx.GetInt(minNum, maxNum + 1); + } + lastNum = nowNum; + callbackfn(nowNum); + // yield null; + counter++; + yield CoroutineV2.WaitTime(waitTime); + } else { + callbackfn(endNum); + break; + } + } + } + + /** + * 检测数字是否越界,如果越界给出提示 + * @param {*number} num 输入数 + */ + function checkBoundary(num: number) { + if (_boundaryCheckingState) { + if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) { + console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`); + } + } + } + + /** + * 精确乘法 + */ + export function times(num1: number, num2: number, ...others: number[]): number { + if (others.length > 0) { + return times(times(num1, num2), others[0], ...others.slice(1)); + } + const num1Changed = num1.Float2Fixed(); + const num2Changed = num2.Float2Fixed(); + const baseNum = num1.DigitLength() + num2.DigitLength(); + const leftValue = num1Changed * num2Changed; + + checkBoundary(leftValue); + + return leftValue / Math.pow(10, baseNum); + } + + /** + * 精确加法 + */ + export function plus(num1: number, num2: number, ...others: number[]): number { + if (others.length > 0) { + return plus(plus(num1, num2), others[0], ...others.slice(1)); + } + const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength())); + return (times(num1, baseNum) + times(num2, baseNum)) / baseNum; + } + + /** + * 精确减法 + */ + export function minus(num1: number, num2: number, ...others: number[]): number { + if (others.length > 0) { + return minus(minus(num1, num2), others[0], ...others.slice(1)); + } + const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength())); + return (times(num1, baseNum) - times(num2, baseNum)) / baseNum; + } + + /** + * 精确除法 + */ + export function divide(num1: number, num2: number, ...others: number[]): number { + if (others.length > 0) { + return divide(divide(num1, num2), others[0], ...others.slice(1)); + } + const num1Changed = num1.Float2Fixed(); + const num2Changed = num2.Float2Fixed(); + checkBoundary(num1Changed); + checkBoundary(num2Changed); + return times((num1Changed / num2Changed), Math.pow(10, num2.DigitLength() - num1.DigitLength())); + } + + /** + * 四舍五入 + */ + export function round(num: number, ratio: number): number { + const base = Math.pow(10, ratio); + return divide(Math.round(times(num, base)), base); + } + + let _boundaryCheckingState = true; + /** + * 是否进行边界检查,默认开启 + * @param flag 标记开关,true 为开启,false 为关闭,默认为 true + */ + function enableBoundaryChecking(flag = true) { + _boundaryCheckingState = flag; + } +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Number/NumberEx.ts.meta b/src/script/Engine/Utils/Number/NumberEx.ts.meta new file mode 100644 index 0000000..63edb76 --- /dev/null +++ b/src/script/Engine/Utils/Number/NumberEx.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "363f5f7f-0623-4013-8571-0bb5c1dc95e6", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/Number/RandomEx.ts b/src/script/Engine/Utils/Number/RandomEx.ts new file mode 100644 index 0000000..4d33e13 --- /dev/null +++ b/src/script/Engine/Utils/Number/RandomEx.ts @@ -0,0 +1,42 @@ + +export module RandomEx { + + /** + * 取得隨機布林值 + */ + export function GetBool() { + return GetInt() >= 0; + } + /** + * 取得隨機整數(回傳min ~ max - 1) + * @param min + * @param max + */ + export function GetInt(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number { + return Math.floor(Math.random() * (max - min)) + min; + } + /** + * 取得隨機小數 + * @param min + * @param max + */ + export function GetFloat(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number { + return Math.random() * (max - min) + min; + } + /** + * 隨機取得複數個不重複回傳 + * @param num 取得數量 + * @param items 陣列 + */ + export function GetMultiNoRepeat(num: number, items: any[]): any[] { + let result: any[] = []; + for (let i: number = 0; i < num; i++) { + let ran: number = Math.floor(Math.random() * items.length); + let item = items.splice(ran, 1)[0]; + if (result.indexOf(item) == -1) { + result.push(item); + } + }; + return result; + } +} diff --git a/src/script/Engine/Utils/Number/RandomEx.ts.meta b/src/script/Engine/Utils/Number/RandomEx.ts.meta new file mode 100644 index 0000000..bcf2fa8 --- /dev/null +++ b/src/script/Engine/Utils/Number/RandomEx.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "ba4dee5b-ca5b-4435-a068-c4f5dd832bab", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/ScrollView.meta b/src/script/Engine/Utils/ScrollView.meta new file mode 100644 index 0000000..2b459b6 --- /dev/null +++ b/src/script/Engine/Utils/ScrollView.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.2", + "uuid": "f6471056-03d8-4d55-b039-6b62d056547c", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/ScrollView/UISuperItem.ts b/src/script/Engine/Utils/ScrollView/UISuperItem.ts new file mode 100644 index 0000000..9f50e93 --- /dev/null +++ b/src/script/Engine/Utils/ScrollView/UISuperItem.ts @@ -0,0 +1,379 @@ +/* + * @Author: steveJobs + * @Email: icipiqkm@gmail.com + * @Date: 2020-11-19 01:15:38 + * @Last Modified by: steveJobs + * @Last Modified time: 2020-12-04 14:41:01 + * @Description: Description + */ +import UISuperLayout, { UIChangeBrotherEvnet } from "./UISuperLayout"; +const { ccclass, property, menu } = console._decorator; +@ccclass +@menu("Plug-in/ScrollView/UISpuerItem") +export default class UISpuerItem extends console.Component { + private layout: UISuperLayout; + private brother: console.Node; + private originSize: console.Size; + private originScale: console.Vec2; + public index: number; + private remove: Function; + private _useScript: any = null; + /** 根据可视范围 和 一组item的个数 去掉 边距/间隔 来计算本item的真实宽度 */ + private get _width(): number { + if (this.layout.vertical) { + // 垂直滑动时 固定宽度 + return (this.layout.accommodWidth - this.layout.spacingWidth) / this.layout.column; + } else { + // 水平模式时 宽度随意 + return this.node.width * this.layout.getUsedScaleValue(this.node.scaleX); + } + } + /** 根据可视范围 和 一组item的个数 去掉 边距/间隔 来计算本item的真实高度 */ + private get _height(): number { + if (this.layout.horizontal) { + // 水平模式时 固定高度 + return (this.layout.accommodHeight - this.layout.spacingWidth) / this.layout.column; + } else { + // 垂直滑动时 高度随意 + return this.node.height * this.layout.getUsedScaleValue(this.node.scaleY); + } + } + onLoad(): void { + // 向node写入一个方法 省去了先获取组件然后调用的步骤 + this.node["watchSelf"] = this.watchSelf.bind(this); + this.node["saveOriginSize"] = this.SaveOriginSize.bind(this); + let widget: console.Widget = this.node.getComponent(console.Widget); + if (widget) { + console.warn("UISuperItem: item不允许挂console.Widget组件 请手动移除"); + this.node.removeComponent(widget); + } + } + + public SetInfo(index: number, remove: Function, ...iniData: any[]): void { + this.index = index; + this.remove = remove; + this._useScript.ImplementSet(...iniData); + } + private onRemove() { + this.remove(this.index); + } + private onClick() { + + } + public SaveOriginSize(): void { + this.originSize = console.size(this.node.width, this.node.height); + this.node.setContentSize(this.originSize); + this.originScale = console.v2(this.node.scaleX, this.node.scaleY); + } + public Init(layout: UISuperLayout, useScript: any): void { + this._useScript = useScript; + this.layout = layout; + this.layout.node.on(UIChangeBrotherEvnet, this.onChangeBrother, this); + this.SaveOriginSize(); + this.node.on(console.Node.EventType.SIZE_CHANGED, this.watchSize, this); + this.node.on(console.Node.EventType.SCALE_CHANGED, this.watchSize, this); + this.onChangeBrother(); + } + onDestroy(): void { + if (this.layout != null) { + this.layout.node.off(UIChangeBrotherEvnet, this.onChangeBrother, this); + } + this.node.off(console.Node.EventType.SIZE_CHANGED, this.watchSize, this); + this.node.off(console.Node.EventType.SCALE_CHANGED, this.watchSize, this); + this.unlisten(); + } + /** + * 当兄弟节点的顺序变化时 来改变自己监听的对象 + * 0,1,2,3,4,5,6,7,8,9 例如列表中共有10个item 0是header 9是footer + * 正序排列时 监听的顺序是 9->8->7->6->5->4->3->2->1->0 0的 brother=null + * 向下填充的逻辑是 0跑到9后面 0=footer 0的brother=9 相对9的位置设置自己 此时1=header + * 向上填充的逻辑是 9跑到0前面 此时9=header 9的brother=null 主动设置自己相对于0前面位置之后 0的brother=9 8=footer + */ + private onChangeBrother() { + let brother: console.Node = this.layout.getBrotherNode(this.node); // 获取我应该监听的那个兄弟 + if (brother?.uuid === this.brother?.uuid) { + return; // 如果没有变化 则跳过 + } + this.unlisten(); // 我的兄弟换人了?先移除我原来的 + this.brother = brother; // 他是我的兄弟 + this.listen(); // 监听他 + this.watchBrother(); // 相对兄弟节点来设置自己的位置 + } + private listen() { + this.brother?.on("leave", this.unlisten, this); + this.brother?.on(console.Node.EventType.POSITION_CHANGED, this.watchBrother, this); + } + private unlisten() { + this.brother?.off("leave", this.unlisten, this); + this.brother?.off(console.Node.EventType.POSITION_CHANGED, this.watchBrother, this); + this.brother = null; + } + /** 当我的尺寸/缩放改变时 */ + private watchSize() { + if (this.layout.column > 1) { // 如果是Grid模式 不允许修改尺寸/缩放 强制改回来 + this.node.setContentSize(this.originSize); + this.node.setScale(this.originScale); + } else { + if (this.layout.vertical && (this.node.getContentSize().width != this.originSize.width || this.node.scaleX != this.originScale.x)) { + this.node.width = this.originSize.width; + this.node.scaleX = this.originScale.x; + + } else if (this.layout.horizontal && (this.node.getContentSize().height != this.originSize.height || this.node.scaleY != this.originScale.y)) { + this.node.height = this.originSize.height; + this.node.scaleY = this.originScale.y; + } + // 如果我监听了兄弟节点就设置自己相对兄弟节点的位置,否则 我就发送一个位置变化的消息 让监听我的兄弟相对我做出变化 + this.brother && this.watchBrother(); + this.layout.resetScrollView(); + this.node.emit(console.Node.EventType.POSITION_CHANGED); + } + if (this.node["index"] == 0 && this.layout.isNormalSize) { + this.node.setPosition(this.layout.getGroupHeader(this.node)); + } + } + // 设置自己相对于上一个兄弟节点的位置 + public watchBrother() { + if (!this.brother) { return } + if (this.layout.headerToFooter) { // 正序排列时 + this.headerToFooterRelativeToFooter(this.brother); + } else {// 倒序排列时 + this.footerToHeaderRelativeToFooter(this.brother); + } + } + private isOutOfBoundary(offset: console.Vec2) { + if (this.layout.vertical && offset.y == 0) { return true } + if (this.layout.horizontal && offset.x == 0) { return true } + return false; + } + /** 从下到上排序方向 检查头部是否需要向上填充 */ + private footerToHeaderWatchHeader() { + // 如果不是头部一组的任意一个时跳过 比如一组有3个item 只计算 0,1,2 + if (this.layout.getSiblingIndex(this.node) >= this.layout.column) { return } + // 如果此时【尾部】已经是最后一个数据时 + let index = this.layout.footer["index"] + 1; + if (index >= this.layout.maxItemTotal) { + if (!this.layout.footerLoop || this.layout.scrollToHeaderOrFooter) { return } + index = 0; + } + // 计算超出的偏移量 (从下到上排序方向时 头部在 下尾部在上 检测【头部】是否超出下边框) + let offset = this.layout.isOutOfBoundaryFooter(this.node); + // 没有超出时跳过 + if (!this.isOutOfBoundary(offset)) { return } + // 将自己的数据索引 + 1 + this.node["index"] = index; + // 发送通知到应用层 刷新显示 + this.layout.notifyRefreshItem(this.node); + // 发给监听我的节点 通知我离开了 移除对我的所有监听 + this.node.emit("leave"); + // 将自己的节点索引设置到尾部 + this.layout.setSiblingIndex(this.node, this.layout.children.length - 1); + } + /** 从下到上排序方向 检查尾部是否需要向下填充 */ + private footerToHeaderWatchFooter() { + // 如果不是尾部一组的任意一个时跳过 比如一组有3个item 只计算末尾的3个item + if (this.layout.getSiblingIndex(this.node) < this.layout.children.length - this.layout.column) { return } + // 如果此时【头部】已经是第一个数据时 + let index = this.layout.header["index"] - 1; + if (index < 0) { + // 如果没有使用无限循环功能 否则不往下走 + if (!this.layout.headerLoop || this.layout.scrollToHeaderOrFooter) { return } + index = this.node["index"]; + } + // 计算超出的偏移量 (从下到上排序方向时 头部在 下尾部在上 检测【尾部】是否超出下边框) + let offset = this.layout.isOutOfBoundaryHeader(this.node); + // 没有超出时跳过 + if (!this.isOutOfBoundary(offset)) { return } + // 将自己的数据索引 - 1 + this.node["index"] = index; + // 发送通知到应用层 刷新显示 + this.layout.notifyRefreshItem(this.node); + // 发给监听我的兄弟 通知我离开了 移除对我的所有监听 + this.node.emit("leave"); + // 因为我是尾部 我监听了别人,此时移除我的所有监听 因为我马上就要成为老大 老大不需要监听任何人 + this.unlisten(); + // 因为我是老大 我不能相对别人来设置自己的相对位置,所以我需要主动设置自己(相对上一个老大的位置来设置自己) 别人都会相对我的位置做出变化 + this.footerToHeaderRelativeToHeader(this.layout.header); + // 将自己的节点索引设置到头部 + this.layout.setSiblingIndex(this.node, 0); + } + /** 从上到下排序方向 检查头部是否需要向下填充 */ + private headerToFooterWatchHeader() { + // 如果不是头部一组的任意一个时跳过 比如一组有3个item 只计算 0,1,2 + if (this.layout.getSiblingIndex(this.node) >= this.layout.column) { return } + // 如果此时【尾部】已经是第一个数据时 + let index = this.layout.footer["index"] + 1; + if (index > this.layout.maxItemTotal - 1) { + // 如果没有使用无限循环功能 否则不往下走 + if (!this.layout.footerLoop || this.layout.scrollToHeaderOrFooter) { return } + index = 0; + } + // 计算超出的偏移量 (从下到上排序方向时 头部在下 尾部在上 检测【尾部】是否超出下边框) + let offset = this.layout.isOutOfBoundaryHeader(this.node); + // 没有超出时跳过 + if (!this.isOutOfBoundary(offset)) { return } + // 将自己的数据索引 + 1 + this.node["index"] = index; + // 发送通知到应用层 刷新显示 + this.layout.notifyRefreshItem(this.node); + // 发给监听我的兄弟 通知我离开了 移除对我的所有监听 + this.node.emit("leave"); + // 将自己的节点索引设置到尾部 + this.layout.setSiblingIndex(this.node, this.layout.children.length - 1); + } + /** 从上到下排序方向 检查尾部是否需要向上填充 */ + private headerToFooterWatchFooter() { + // 如果不是尾部一组的任意一个时跳过 比如一组有3个item 只计算末尾的3个item + if (this.layout.getSiblingIndex(this.node) < this.layout.children.length - this.layout.column) { return } + // 如果此时【头部】已经是第一个数据时 + let index = this.layout.header["index"] - 1; + if (index < 0) { + // 如果没有使用无限循环功能 否则不往下走 + if (!this.layout.headerLoop || this.layout.scrollToHeaderOrFooter) { return } + index = this.node["index"]; + } + // 计算超出的偏移量 (从上到下排序方向时 头部在上 尾部在下 检测【尾部】是否超出下边框) + let offset = this.layout.isOutOfBoundaryFooter(this.node); + // 没有超出时跳过 + if (!this.isOutOfBoundary(offset)) { return } + // 将自己的数据索引 - 1 + this.node["index"] = index; + // 发送通知到应用层 刷新显示 + this.layout.notifyRefreshItem(this.node); + // 发给监听我的兄弟 通知我离开了 移除对我的所有监听 + this.node.emit("leave"); + // 因为我是尾部 我监听了别人,此时移除我的所有监听 因为我马上就要成为老大 老大不需要监听任何人 + this.unlisten(); + // 因为我是老大 我不能相对别人来设置自己的相对位置,所以我需要主动设置自己(相对上一个老大的位置来设置自己) 别人都会相对我的位置做出变化 + this.headerToFooterRelativeToHeader(this.layout.header); + // 将自己的节点索引设置到尾部 + this.layout.setSiblingIndex(this.node, 0); + } + /** isScrollToFooter=true 向下滑动 */ + public watchSelf(isScrollToFooter: boolean) { + if (isScrollToFooter) { + if (this.layout.headerToFooter) { + // 从【上到下排序】方向 检查【尾部】是否需要向上填充 + this.headerToFooterWatchFooter(); + } else { + // 从【下到上排序】方向 检查【头部】是否需要向上填充 + this.footerToHeaderWatchHeader(); + } + } else { + if (this.layout.headerToFooter) { + // 从【上到下排序】方向 检查【头部】是否需要向下填充 + this.headerToFooterWatchHeader(); + } else { + // 从【下到上排序】方向 检查【尾部】是否需要向下填充 + this.footerToHeaderWatchFooter(); + } + } + } + /** 从下到上 从右到左 排序方向 设置自己到相对node的头部 */ + private footerToHeaderRelativeToHeader(relative: console.Node) { + let pos = this.node.getPosition(); + // 从下到上 + if (this.layout.vertical) { + if (this.layout.isGroupHeader(relative)) { + pos.x = this.layout.getGroupFooter(this.node).x; + pos.y = this.layout.getGroupBottomY(this.node, relative); + } else { + pos.x = this.layout.getGroupLeftX(this.node, relative); + pos.y = relative.y; + } + if (this.node["index"] == 0) { + pos.x = this.layout.getGroupHeader(this.node).x; + } + } else { + // 从右到左 + if (this.layout.isGroupHeader(relative)) { + pos.x = this.layout.getGroupRightX(this.node, relative); + pos.y = this.layout.getGroupFooter(this.node).y; + } else { + pos.x = relative.x; + pos.y = this.layout.getGroupTopY(this.node, relative); + } + if (this.node["index"] == 0) { + pos.y = this.layout.getGroupHeader(this.node).y; + } + } + this.node.setPosition(pos); + } + /** 从下到上 从右到左 排序方向 设置自己到相对node的尾部 */ + private footerToHeaderRelativeToFooter(relative: console.Node) { + let pos = this.node.getPosition(); + // 从下到上 + if (this.layout.vertical) { + if (this.layout.isGroupFooter(relative)) { + pos.x = this.layout.getGroupHeader(this.node).x; + pos.y = this.layout.getGroupTopY(this.node, relative); + } else { + pos.x = this.layout.getGroupRightX(this.node, relative); + pos.y = relative.y; + } + } else { + // 从右到左 + if (this.layout.isGroupFooter(relative)) { + pos.x = this.layout.getGroupLeftX(this.node, relative); + pos.y = this.layout.getGroupHeader(this.node).y; + } else { + pos.x = relative.x; + pos.y = this.layout.getGroupBottomY(this.node, relative); + } + } + this.node.setPosition(pos); + } + /** 从上到下 从左到右 排序方向 设置自己到相对node的头部 */ + private headerToFooterRelativeToHeader(relative: console.Node) { + let pos = this.node.getPosition(); + // 从上到下 + if (this.layout.vertical) { + if (this.layout.isGroupHeader(relative)) { + pos.x = this.layout.getGroupFooter(this.node).x; + pos.y = this.layout.getGroupTopY(this.node, relative); + } else { + pos.x = this.layout.getGroupLeftX(this.node, relative); + pos.y = relative.y; + } + if (this.node["index"] == 0) { + pos.x = this.layout.getGroupHeader(this.node).x; + } + } else { + // 从左到右 + if (this.layout.isGroupHeader(relative)) { + pos.x = this.layout.getGroupLeftX(this.node, relative); + pos.y = this.layout.getGroupFooter(this.node).y; + } else { + pos.x = relative.x; + pos.y = this.layout.getGroupTopY(this.node, relative); + } + if (this.node["index"] == 0) { + pos.y = this.layout.getGroupHeader(this.node).y; + } + } + this.node.setPosition(pos); + } + /** 从上到下 从左到右 排序方向 设置自己到相对node尾部 */ + private headerToFooterRelativeToFooter(relative: console.Node) { + let pos = this.node.getPosition(); + // 从上到下 + if (this.layout.vertical) { + if (this.layout.isGroupFooter(relative)) { + pos.x = this.layout.getGroupHeader(this.node).x; + pos.y = this.layout.getGroupBottomY(this.node, relative); + } else { + pos.x = this.layout.getGroupRightX(this.node, relative); + pos.y = relative.y; + } + } else { + // 从左到右 + if (this.layout.isGroupFooter(relative)) { + pos.x = this.layout.getGroupRightX(this.node, relative); + pos.y = this.layout.getGroupHeader(this.node).y; + } else { + pos.x = relative.x; + pos.y = this.layout.getGroupBottomY(this.node, relative); + } + } + this.node.setPosition(pos); + } +} diff --git a/src/script/Engine/Utils/ScrollView/UISuperItem.ts.meta b/src/script/Engine/Utils/ScrollView/UISuperItem.ts.meta new file mode 100644 index 0000000..55d2f86 --- /dev/null +++ b/src/script/Engine/Utils/ScrollView/UISuperItem.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "486070ed-7155-4fa6-8cc4-81695cc3b28b", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/ScrollView/UISuperLayout.ts b/src/script/Engine/Utils/ScrollView/UISuperLayout.ts new file mode 100644 index 0000000..360026e --- /dev/null +++ b/src/script/Engine/Utils/ScrollView/UISuperLayout.ts @@ -0,0 +1,720 @@ +import UISpuerItem from "./UISuperItem"; +import UISpuerScrollView from "./UISuperScrollView"; +const { ccclass, property, menu } = console._decorator; +const EPSILON: number = 1e-4; +export const UIChangeBrotherEvnet = "UIChangeBrotherEvnet"; +export enum UISuperAxis { + HORIZONTAL = 0, + VERTICAL = 1 +} +export enum UISuperDirection { + HEADER_TO_FOOTER = 0, + FOOTER_TO_HEADER = 1, +} +@ccclass +@menu("Plug-in/ScrollView/UISuperLayout") +export default class UISuperLayout extends console.Component { + @property({ type: console.Enum(UISuperAxis), displayName: "排列方向" }) + public StartAxis: UISuperAxis = UISuperAxis.VERTICAL; + @property({ type: console.Enum(UISuperDirection), displayName: "排列子节点的方向" }) direction: UISuperDirection = UISuperDirection.HEADER_TO_FOOTER; + @property({ displayName: "上边距" }) paddingTop: number = 0; + @property({ displayName: "下边距" }) paddingBottom: number = 0; + @property({ displayName: "左边距" }) paddingLeft: number = 0; + @property({ displayName: "右边距" }) paddingRight: number = 0; + @property({ displayName: "间隔" }) spacing: console.Vec2 = console.Vec2.ZERO; + @property({ displayName: "每组item个数", tooltip: "单行的列数 或 单列的行数" }) column: number = 2; + @property({ displayName: "item创建倍率", tooltip: "相对于view的尺寸 默认2倍" }) multiple: number = 2; + @property({ type: console.Prefab, displayName: "item Prefab" }) prefab: console.Prefab = null; + @property({ displayName: "头部滑动循环" }) headerLoop: boolean = false; + @property({ displayName: "尾部滑动循环" }) footerLoop: boolean = false; + @property affectedByScale: boolean = true; + @property(console.Component.EventHandler) refreshItemEvents: console.Component.EventHandler[] = []; + private _gener: Generator; + private _isinited: boolean = false; + private _maxPrefabTotal: number = 0; + private _children: console.Node[] = []; // 和this.node.children 保持同步 + private _viewSize: console.Size; + private _scrollView: UISpuerScrollView = null; + private _maxItemTotal: number = 0; + private _prevLayoutPosition: console.Vec2 = console.Vec2.ZERO; + /** 当前的滚动是否是由 scrollTo 方法执行的 和touch滑动做个区分 */ + public scrollToHeaderOrFooter: boolean = false; + /** 根据上一次和本次的坐标变化计算滑动方向 */ + private get layoutDirection(): console.Vec2 { + let pos = console.Vec2.ZERO; + if (this.vertical) { + pos.y = this.node.y - this._prevLayoutPosition.y; + } else { + pos.x = this.node.x - this._prevLayoutPosition.x; + } + this._prevLayoutPosition = this.node.getPosition(); + return pos; + } + /** 是否是向下滑动 */ + private get isScrollToFooter(): boolean { + if (this.vertical) { + return this.layoutDirection.y < 0; + } else { + return this.layoutDirection.x > 0; + } + } + /** 自己维护的子节点数组 和this.node.children 保持同步 */ + public get children() { return this._children; } + /** 最大数据总数 */ + public get maxItemTotal() { return this._maxItemTotal; } + /** 当前被创建的item总数 */ + public get maxPrefabTotal() { return this._maxPrefabTotal; } + /** scrollView.view尺寸 */ + public get viewSize(): console.Size { + return this.scrollView.view.getContentSize(); + } + /** 是否是垂直模式 */ + public get vertical(): boolean { + return this.StartAxis == UISuperAxis.VERTICAL; + } + /** 是否是水平模式 */ + public get horizontal(): boolean { + return this.StartAxis == UISuperAxis.HORIZONTAL; + } + /** 是否是正序排列 */ + public get headerToFooter(): boolean { + return this.direction == UISuperDirection.HEADER_TO_FOOTER; + } + /** 是否是倒序排列 */ + public get footerToHeader(): boolean { + return this.direction == UISuperDirection.FOOTER_TO_HEADER; + } + /** 水平间隔总宽度 (Grid 模式返回多个间隔总宽度) */ + public get spacingWidth() { + return this.spacing.x * (this.column - 1); + } + /** 水平间隔总高度 (Grid 模式返回多个间隔总高度) */ + public get spacingHeight() { + return this.spacing.y * (this.column - 1); + } + /** 可容纳item的真实宽度 */ + public get accommodWidth() { + return this.viewSize.width - this.paddingLeft - this.paddingRight; + } + /** 可容纳item的真实高度 */ + public get accommodHeight() { + return this.viewSize.height - this.paddingTop - this.paddingBottom; + } + public get scrollView(): UISpuerScrollView { + if (!this._scrollView) { this._scrollView = this.node.parent.parent.getComponent(UISpuerScrollView); } + return this._scrollView; + } + /** 当前头部的item */ + public get header(): console.Node { + return this._children[0]; + } + /** 当前尾部的item */ + public get footer(): console.Node { + return this._children[this._children.length - 1]; + } + /** 真实的上边距 */ + public get topBoundary() { + if (this.headerToFooter) { + return this.headerBoundaryY + this.paddingTop; + } else { + return this.footerBoundaryY + this.paddingTop; + } + } + /** 真实的下边距 */ + public get bottomBoundary() { + if (this.headerToFooter) { + return this.footerBoundaryY - this.paddingBottom; + } else { + return this.headerBoundaryY - this.paddingBottom; + } + } + /** 真实的左边距 */ + public get leftBoundary() { + if (this.headerToFooter) { + return this.headerBoundaryX - this.paddingLeft; + } else { + return this.footerBoundaryX - this.paddingLeft; + } + } + /** 真实的右边距 */ + public get rightBoundary() { + if (this.headerToFooter) { + return this.footerBoundaryX + this.paddingRight; + } else { + return this.headerBoundaryX + this.paddingRight; + } + } + /** 头部item的世界坐标边框 类似 xMin、xMax */ + public get headerBoundaryX() { + if (this.headerToFooter) { + return this.node.x + this.header.x - this.header.anchorX * this.getScaleWidth(this.header); + } else { + return this.node.x + this.header.x + (1 - this.header.anchorX) * this.getScaleWidth(this.header); + } + } + /** 头部item的世界坐标边框 类似 yMin、yMax */ + public get headerBoundaryY() { + if (this.headerToFooter) { + return this.node.y + this.header.y + (1 - this.header.anchorY) * this.getScaleHeight(this.header); + } else { + return this.node.y + this.header.y - this.header.anchorY * this.getScaleHeight(this.header); + } + } + /** 尾部item的世界坐标边框 类似 xMin、xMax */ + public get footerBoundaryX() { + if (this.headerToFooter) { + return this.node.x + this.footer.x + (1 - this.footer.anchorX) * this.getScaleWidth(this.footer); + } else { + return this.node.x + this.footer.x - this.footer.anchorX * this.getScaleWidth(this.footer); + } + } + /** 尾部item的世界坐标边框 类似 yMin、yMax */ + public get footerBoundaryY() { + if (this.headerToFooter) { + return this.node.y + this.footer.y - this.footer.anchorY * this.getScaleHeight(this.footer); + } else { + return this.node.y + this.footer.y + (1 - this.footer.anchorY) * this.getScaleHeight(this.footer); + } + } + public get isNormalSize(): boolean { + return this.node.getContentSize().equals(this.viewSize); + } + + /** 重写 this.node.getContentSize 动态计算头尾item 返回虚拟的尺寸 非content设置的尺寸 */ + public getContentSize() { + let size = this.getReallySize(); + let viewSize = this.scrollView.view.getContentSize(); + // 列表为空时 直接返回 scrollView.view 的尺寸 + if (size.height < viewSize.height) { + size.height = viewSize.height; + } + if (size.width < viewSize.width) { + size.width = viewSize.width; + } + return size; + } + /** 返回 header到 footer 之间的整体尺寸 */ + public getReallySize() { + if (this.node.childrenCount == 0) { return this.viewSize; } + let size = console.Size.ZERO; + if (this.headerToFooter) { // 根据header和footer计算出真实的content尺寸 + size.width = this.footerBoundaryX + -this.headerBoundaryX + this.paddingLeft + this.paddingRight; + size.height = this.headerBoundaryY + -this.footerBoundaryY + this.paddingTop + this.paddingBottom; + } else { + size.width = this.headerBoundaryX + -this.footerBoundaryX + this.paddingLeft + this.paddingRight; + size.height = this.footerBoundaryY + -this.headerBoundaryY + this.paddingTop + this.paddingBottom; + } + return size; + } + /** 重置scrollview */ + public resetScrollView() { + this.scrollView.reset(); + } + /** 获取缩放系数 */ + public getUsedScaleValue(value: number) { + return this.affectedByScale ? Math.abs(value) : 1; + } + /** 设置最大item数量 */ + public async CreateItem(...iniData: any[]) { + this.scrollView.stopAutoScroll(); + this.scrollView.release(); // 释放(功能用于上拉加载 下拉刷新) + this.initlized(); // 初始化 + await this.asyncCreateItem(iniData); // 分帧创建item + let dataOffset = this.getDataOffset(iniData[0]); // 获取数据偏移量(根据value相对于 _maxItemTotal 计算增加、减少的数量) + let reallyOffset = this.getReallyOffset(dataOffset); // 获取真实的数据偏移(Grid模式 功能用于判断是否需要偏移header来将下方填满) + this.refreshItems(iniData[0], reallyOffset); // 通过已有的item['index'] 加上数据偏移 来是刷新显示 + this._maxItemTotal = iniData[0]; // 记录当前总数 + } + /** 获取兄弟节点 */ + public getBrotherNode(node: console.Node) { + let index = this.getSiblingIndex(node) - 1; // 此 getSiblingIndex 非 this.node.getSiblingIndex + return this._children[index]; + } + /** 是否是一组item中第一个(垂直滑动中 一组item 就是单行的所有列 、水平滑动中 一组item 就是单列中所有行)*/ + public isGroupHeader(node: console.Node): boolean { + let xOry = this.getGroupHeader(node); + let pos = this.vertical ? console.v2(xOry.x, 0) : console.v2(0, xOry.y); + let self = this.vertical ? console.v2(node.x, 0) : console.v2(0, node.y); + return self.fuzzyEquals(pos, EPSILON); + } + /** 是否是一组item中最后一个(垂直滑动中 一组item 就是单行的所有列 、水平滑动中 一组item 就是单列中所有行)*/ + public isGroupFooter(node: console.Node): boolean { + let xOry = this.getGroupFooter(node); + let pos = this.vertical ? console.v2(xOry.x, 0) : console.v2(0, xOry.y); + let self = this.vertical ? console.v2(node.x, 0) : console.v2(0, node.y); + return self.fuzzyEquals(pos, EPSILON); + } + /** 获取一组item中起始位置 (垂直滑动中 一组item 就是单行的所有列 、水平滑动中 一组item 就是单列中所有行)*/ + public getGroupHeader(node: console.Node): console.Vec2 { + let pos = console.Vec2.ZERO; + if (!node) { return pos; } + if (this.vertical) { + if (this.headerToFooter) { + pos.x = node.anchorX * this.getScaleWidth(node) + (this.paddingLeft * node.scaleX) - (this.node.anchorX * this.viewSize.width * node.scaleX); + pos.y = (1 - node.anchorY) * -this.getScaleHeight(node) - this.paddingTop + (1 - this.node.anchorY) * this.viewSize.height; + } else { + pos.x = node.anchorX * this.getScaleWidth(node) + this.paddingLeft - this.node.anchorX * this.viewSize.width; + pos.y = node.anchorY * this.getScaleHeight(node) + this.paddingBottom - this.node.anchorY * this.viewSize.height; + } + } else { + if (this.headerToFooter) { + pos.x = node.anchorX * this.getScaleWidth(node) + this.paddingLeft - this.node.anchorX * this.viewSize.width; + pos.y = (1 - node.anchorY) * -node.height - this.paddingTop + (1 - this.node.anchorY) * this.viewSize.height; + } else { + pos.x = this.accommodWidth * this.node.anchorX - this.getScaleWidth(node) * (1 - node.anchorX); + pos.y = (1 - node.anchorY) * -node.height - this.paddingTop + (1 - this.node.anchorY) * this.viewSize.height; + } + } + return pos; + } + /** 获取一组item中结束位置 (垂直滑动中 一组item 就是单行的所有列 、水平滑动中 一组item 就是单列中所有行)*/ + public getGroupFooter(node: console.Node): console.Vec2 { + let pos = console.Vec2.ZERO; + if (!node) { return pos; } + if (this.vertical) { + pos.x = (this.accommodWidth + this.paddingLeft) * this.node.anchorX - (this.getScaleWidth(node) * (1 - node.anchorX) + this.node.anchorX * this.paddingRight); + pos.y = node.y; + } else { + pos.x = node.x; + pos.y = -((this.accommodHeight + this.paddingTop) * this.node.anchorY - this.getScaleHeight(node) * node.anchorY) + (1 - node.anchorY) * this.paddingBottom; + } + return pos; + } + /** 获取一组item中 node 相对 relative 右偏移量 (垂直滑动中 一组item 就是单行的所有列 、水平滑动中 一组item 就是单列中所有行)*/ + public getGroupRightX(node: console.Node, relative: console.Node) { + if (!node || !relative) { return this.getGroupHeader(node).x; } + let prevWidth = relative.x + this.getScaleWidth(relative) * (1 - relative.anchorX); + let selfWidth = this.getScaleWidth(node) * node.anchorX; + return prevWidth + selfWidth + this.spacing.x; + } + /** 获取一组item中 node 相对 relative 左偏移量 (垂直滑动中 一组item 就是单行的所有列 、水平滑动中 一组item 就是单列中所有行)*/ + public getGroupLeftX(node: console.Node, relative: console.Node) { + if (!node || !relative) { return this.getGroupFooter(node).x; } + let prevWidth = relative.x - this.getScaleWidth(relative) * relative.anchorX; + let selfWidth = this.getScaleWidth(node) * (1 - node.anchorX); + return prevWidth - selfWidth - this.spacing.x; + } + /** 获取一组item中 node 相对 relative 下偏移量 (垂直滑动中 一组item 就是单行的所有列 、水平滑动中 一组item 就是单列中所有行)*/ + public getGroupBottomY(node: console.Node, relative: console.Node) { + let prevHeight = relative.y - this.getScaleHeight(relative) * relative.anchorY; + let selfHeight = this.getScaleHeight(node) * (1 - node.anchorY); + return prevHeight - selfHeight - this.spacing.y; + } + /** 获取一组item中 node 相对 relative 上偏移量 (垂直滑动中 一组item 就是单行的所有列 、水平滑动中 一组item 就是单列中所有行)*/ + public getGroupTopY(node: console.Node, relative: console.Node) { + let prevHeight = relative.y + this.getScaleHeight(relative) * (1 - relative.anchorY); + let selfHeight = this.getScaleHeight(node) * node.anchorY; + return prevHeight + selfHeight + this.spacing.y; + } + /** 判断给定的 node 乘以 multiple 倍数后 是否超出了头部边框 ( multiple = 1 就是一个node的尺寸 默认1.5倍)*/ + public isOutOfBoundaryHeader(node: console.Node, multiple: number = 1.5) { + let width = node.width * this.getUsedScaleValue(node.scaleX) * multiple; + let height = -node.height * this.getUsedScaleValue(node.scaleY) * multiple; + let offset = this.scrollView.getHowMuchOutOfBoundary(console.v2(width, height)); + return offset; + } + /** 判断给定的 node 乘以 multiple 倍数后 是否超出了尾部部边框 ( multiple = 1 就是一个node的尺寸 默认1.5倍)*/ + public isOutOfBoundaryFooter(node: console.Node, multiple: number = 1.5) { + let width = -node.width * this.getUsedScaleValue(node.scaleX) * multiple; + let height = node.height * this.getUsedScaleValue(node.scaleY) * multiple; + let offset = this.scrollView.getHowMuchOutOfBoundary(console.v2(width, height)); + return offset; + } + /** 滚动到头部 (根据 排列方向、排列子节点的方向)来调用 scrollView.scrollTo... 方法 */ + public scrollToHeader(timeInSecond?: number, attenuated?: boolean) { + this.scrollToHeaderOrFooter = timeInSecond > 0; + this.scrollView.stopAutoScroll(); + this.resetToHeader(); + if (this.headerToFooter) { + if (this.vertical) { + this.scrollView.scrollToTop(timeInSecond, attenuated); + } else { + this.scrollView.scrollToLeft(timeInSecond, attenuated); + } + } else { + if (this.vertical) { + this.scrollView.scrollToBottom(timeInSecond, attenuated); + } else { + this.scrollView.scrollToRight(timeInSecond, attenuated); + } + } + } + /** 滚动到尾部(根据 排列方向、排列子节点的方向)来调用 scrollView.scrollTo... 方法 */ + public scrollToFooter(timeInSecond?: number, attenuated?: boolean) { + this.scrollToHeaderOrFooter = timeInSecond > 0; + this.scrollView.stopAutoScroll(); + this.resetToFooter(); + if (this.headerToFooter) { + if (this.vertical) { + this.scrollView.scrollToBottom(timeInSecond, attenuated); + } else { + this.scrollView.scrollToRight(timeInSecond, attenuated); + } + } else { + if (this.vertical) { + this.scrollView.scrollToTop(timeInSecond, attenuated); + } else { + this.scrollView.scrollToLeft(timeInSecond, attenuated); + } + } + } + /** 通知给定的node刷新数据 */ + public notifyRefreshItem(target: console.Node) { + console.Component.EventHandler.emitEvents(this.refreshItemEvents, target, target["index"]); + } + /** 获取节点索引 */ + public getSiblingIndex(node: console.Node) { + return this._children.indexOf(node); + } + /** 自定义索引方法 这里不是通过实时修改节点索引的方法,只是模拟类似的功能,实际上并没有真正改变节点的实际顺序(优化项) */ + public setSiblingIndex(node: console.Node, index: number) { + // 此方法时参考引擎原setSiblingIndex方法 去掉了修改节点索引位置的调用(item本身的zIndex没有任何变化) + index = index !== -1 ? index : this._children.length - 1; + var oldIndex = this._children.indexOf(node); + if (index !== oldIndex) { + this._children.splice(oldIndex, 1); + if (index < this._children.length) { + this._children.splice(index, 0, node); + } else { + this._children.push(node); + } + /** + * 这里区别于原方法 原方法是改变node节点顺序后发送console.Node.EventType.SIBLING_ORDER_CHANGED通知 这里不需要修改节点顺序 + * 这里发送一个自定义事件 模拟 SIBLING_ORDER_CHANGED 通知 + */ + this.node.emit(UIChangeBrotherEvnet); + } + } + onLoad() { + this.initlized(); + } + /** 初始化 */ + private initlized() { + if (this._isinited) { return; } + this.node.anchorX = 0.5; // 固定content的锚点为中心 + this.node.anchorY = 0.5; + this.node.setContentSize(this.viewSize); // 将content的尺寸设置与view相同 (功能用于空列表时也可以下拉刷新和加载) + // 重写 this.node.getContentSize 方法 因为content的真实尺寸不会随着item的数量变化 + this.node.getContentSize = this.getContentSize.bind(this); + this.node.setPosition(console.Vec2.ZERO); + this.column = this.column < 1 ? 1 : this.column; // 一组item的数量 最少是1 也就是普通的水平/垂直 大于1就是Grid模式 + // 监听content位置变化 刷新header footer节点的相对位置 + this.node.on(console.Node.EventType.POSITION_CHANGED, this.onChangePosition, this); + this.scrollView.view.on(console.Node.EventType.SIZE_CHANGED, this.resetItemSize, this); + this._isinited = true; + } + onDestroy() { + this.node.off(console.Node.EventType.POSITION_CHANGED, this.onChangePosition, this); + this.scrollView.view.off(console.Node.EventType.SIZE_CHANGED, this.resetItemSize, this); + } + private onChangePosition() { + let flag = this.isScrollToFooter; // this.isScrollToFooter = true 向下滑动 false 向上滑动 + if (this.headerToFooter) { + flag ? this.footerToHeaderWatchChilds(flag) : this.headerToFooterWatchChilds(flag); // 倒序刷新 + } else { + flag ? this.headerToFooterWatchChilds(flag) : this.footerToHeaderWatchChilds(flag); // 正序刷新 + } + // 当item 由多到少 并且 当content的位置被重置到初始状态时 重新设置头部的item归位 + if (this.vertical && 0 == this.node.y || this.horizontal && 0 == this.node.x) { + this.header.setPosition(this.getGroupHeader(this.header)); + } + } + public resetItemSize() { + // 重新设置原始尺寸 + for (let i = 0; i < this.children.length; i++) { + this.children[i]["saveOriginSize"](); + } + // 改变头部位置 + let pos = this.getGroupHeader(this.header); + if (this.vertical) { + this.header.x = pos.x; + } else { + this.header.y = pos.y; + } + // 通知改变坐标事件 + for (let i = 0; i < this.children.length; i++) { + this.children[i].emit(console.Node.EventType.POSITION_CHANGED); + } + } + /** 获取缩放宽度 */ + private getScaleWidth(node: console.Node): number { + return node.width * this.getUsedScaleValue(node.scaleX); + } + /** 获取缩放高度 */ + private getScaleHeight(node: console.Node): number { + return node.height * this.getUsedScaleValue(node.scaleY); + } + /** 简单的浅拷贝 */ + private getTempChildren() { + let list = []; + for (let i = 0; i < this._children.length; i++) { + const child = this._children[i]; + list.push(child); + } + return list; + } + /** 正序更新item */ + private headerToFooterWatchChilds(flag) { + let children = this.getTempChildren(); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + child["watchSelf"](flag); + } + } + /** 倒序更新item */ + private footerToHeaderWatchChilds(flag) { + let children = this.getTempChildren(); + for (let i = children.length - 1; i >= 0; i--) { + const child = children[i]; + child["watchSelf"](flag); + } + } + /** 当数据增加、减少时 获取数据偏移 */ + private getDataOffset(value: number) { + // 返回删除数据偏移 (比如当前最大数据值=10,新数据=9 返回-1) + if (this.footer && this.footer["index"] + 1 >= value) { + let offset = this.footer["index"] + 1 - value; + return offset == 0 ? 0 : -offset; + } + // 返回增加数据偏移 + if (this._maxItemTotal == 0 || value < this._maxItemTotal || this._maxItemTotal < this._maxPrefabTotal) { return 0; } // 比如当前最多允许创建10个item 当前显示5个 返回0 + if (this.isGroupFooter(this.footer)) { return 0; } // Grid模式 如果尾部的位置是在一组item中末尾的位置时 返回 0 + return value - this._maxItemTotal; + } + /** + * 当数据增加、减少时 获取节点偏移量 + * 当前数据是这样的 增加1个 增加2个 + * 0,1,2,3 1,2,3 2,3 + * 4,5,6 4,5,6,7 4,5,6,7 + * 8 + */ + private getReallyOffset(dataOffset: number) { + if (!this.header) { return 0; } + if (dataOffset > 0) { // 代表增加item 表格模式下 通过偏移头部来让下方填满 填满后停止偏移 + for (let i = 0; i < dataOffset; i++) { + if (this.isGroupFooter(this.footer)) { return i; } // 返回真实的偏移量 + // 此时如果header 已经是一组item中最后一个时 向下位移 并 设置到一组item的起始位置 + let pos = this.header.getPosition(); + if (this.vertical) { // 垂直滑动时 + if (this.isGroupFooter(this.header)) { // 当列表中第一个item正在一组item中末尾位置时 + if (this.headerToFooter) { + pos.y = this.getGroupBottomY(this.header, this.header); // 正序排列时 Y轴向下偏移(垂直排列时 一组item 头在左尾在右) + } else { + pos.y = this.getGroupTopY(this.header, this.header); // 倒序排列时 Y轴向上偏移(垂直排列时 一组item 头在左尾在右) + } + pos.x = this.getGroupHeader(this.header).x; // X轴向头部偏移 + } else { // 第一个item没有在一组item中末尾的位置 只将第一个item向右偏移 (只偏移X轴) + pos.x = this.getGroupRightX(this.header, this.header); // X轴向右偏移 + } + } else { // 水平滑动时 + if (this.isGroupFooter(this.header)) { // 当列表中第一个item正在一组item中末尾位置时 + if (this.headerToFooter) { + pos.x = this.getGroupRightX(this.header, this.header); // 正序排列时 X轴向右偏移(水平排列时 一组item 头在上尾在下) + } else { + pos.x = this.getGroupLeftX(this.header, this.header); // 倒序排列时 X轴向左偏移(水平排列时 一组item 头在上尾在下) + } + pos.y = this.getGroupHeader(this.header).y; // Y轴向头部偏移 + } else { // 第一个item没有在一组item中末尾的位置 只将第一个item向下偏移 (只偏移Y轴) + pos.y = this.getGroupBottomY(this.header, this.header); // Y轴向下偏移 + } + } + this.header.setPosition(pos); + } + return dataOffset; + } + // 代表减少了item 计算偏移量 offset<0 【注意!这里的逻辑和上面正好相反】 + for (let i = 0; i < Math.abs(dataOffset); i++) { + let pos = console.Vec2.ZERO; + if (this.vertical) { + if (this.isGroupHeader(this.header)) { + pos.x = this.getGroupFooter(this.header).x; + if (this.headerToFooter) { + pos.y = this.getGroupTopY(this.header, this.header); + } else { + pos.y = this.getGroupBottomY(this.header, this.header); + } + } else { + pos.x = this.getGroupLeftX(this.header, this.header); + pos.y = this.header.y; + } + } else { + if (this.isGroupHeader(this.header)) { + pos.y = this.getGroupFooter(this.header).y; + if (this.headerToFooter) { + pos.x = this.getGroupLeftX(this.header, this.header); + } else { + pos.x = this.getGroupRightX(this.header, this.header); + } + } else { + pos.y = this.getGroupTopY(this.header, this.header); + pos.x = this.header.x; + } + } + this.header.setPosition(pos); + } + this.scrollView.calculateBoundary(); + return dataOffset; + } + /** 刷新所有item数据 根据当前item的 index 刷新 */ + private refreshItems(value: number, offset: number = 0) { + if (!this.header) { return; } + let startIndex = this.header["index"] - 1 + offset; // 获取头部item持有的index 加上 数据偏移来计算起始index + for (let i = 0; i < this._children.length; i++) { + const child = this._children[i]; + startIndex++; + // 这里的判断用于无限循环滚动的逻辑 如果索引大于数据总数 索引归零 + if (startIndex > value - 1) { + startIndex = 0; + } else if (startIndex < 0) { // 索引小于0 索引定位到数据尾部 保持首尾相连 + startIndex = value - 1; + } + child["index"] = startIndex; // 设置当前索引 + this.notifyRefreshItem(child); + } + } + /** 从头部到尾部重置数据 */ + private resetToHeader() { + for (let i = 0; i < this._children.length; i++) { + const child = this._children[i]; + child["index"] = i; + this.notifyRefreshItem(child); + } + if (!this.headerLoop && !this.footerLoop) { + this.header?.setPosition(this.getGroupHeader(this.header)); + } else if (!this.scrollToHeaderOrFooter) { + this.header?.setPosition(this.getGroupHeader(this.header)); + } + } + /** 从尾部到头部重置数据 */ + private resetToFooter() { + let index = this._maxItemTotal; + for (let i = this._children.length - 1; i >= 0; i--) { + var child = this._children[i]; + child["index"] = --index; + this.notifyRefreshItem(child); + } + } + /** 删除多余的item */ + private removeChilds(value: number) { + // 有多余的item 需要删除 + let length = this.node.childrenCount - value; + // 删除掉多余的item + for (let i = 0; i < length; i++) { + var child = this.footer; + this.remChild(child); + child.destroy(); + this.node.removeChild(child); + } + if (!this.header) { return; } + // 将头部节点的位置重置到一组item的第一个位置 + let pos = this.getGroupHeader(this.header); + if (this.vertical) { + this.header.x = pos.x; + } else { + this.header.y = pos.y; + } + } + /** 分帧创建item */ + private async asyncCreateItem(...iniData: any[]) { + let self = this; + let data: any[] = iniData[0]; + this._gener?.return("");// 取消上一次的分帧任务(如果任务正在执行) + // 有多余的item 需要删除 不处理 + if (this.node.childrenCount > data[0]) { return this.removeChilds(data[0]); } + // 已经固定item总数 不处理 + if (this._maxPrefabTotal > 0 && this._maxPrefabTotal == this.node.childrenCount) { return; } + // 开始分帧创建item + let total = data[0] - this.node.childrenCount; // 计算当前应该创建的总数 + this._gener = this.getGeneratorLength(total, () => { + // 获取或添加 UISuperItem + // let script = UIPanel.CreateUI(this.prefab, this.node); + // let child = script.node; + // CoroutineV2.Single(script.Show()).Start(); + // child["index"] = this.node.childrenCount - 1; + let child = console.instantiate(self.prefab); + let script = child.getComponent(data[1]); + child["index"] = self.node.childrenCount; + self.addChild(child); + // let item = UIPanel.CreateUI(this.prefab, this.node, data[2]); + // let child = item.node; + let spuerItem = child.getComponent(UISpuerItem) || child.addComponent(UISpuerItem); + self.node.addChild(child); + spuerItem.Init(self, script); + // item在首次创建时立即刷新 避免显示item初始状态 + self.notifyRefreshItem(child); + // 如果创建的是第一个item 设置他的起始位置 之后的item会自动相对于他来设置自己 我们只需要确定第一个位置就行了 + if (self.node.childrenCount == 1) { + let pos = self.getGroupHeader(self.header); // 获取一组item中头部位置 + self.header.setPosition(pos); + /** + * 利用console.ScrollView的方法来设置content的起始位置 由于content在初始化的时候固定了锚点都为0.5 所以这里必然是坐标0 + * 如果你没有其他需求确定用0.5锚点的话 这里可以自己设置为console.Vec2.ZERO 节省不必要的计算(实际上计算量可忽略不计) + */ + self.scrollView.calculateBoundary(); + } + let selfHorW, viewHorW; + if (self.vertical) { + selfHorW = self.getReallySize().height; + viewHorW = self.viewSize.height; + } else { + selfHorW = self.getReallySize().width; + viewHorW = self.viewSize.width; + } + /** + * 根据排列方向 来判断对比宽度还是高度 + * 这里使用参数this.multiple来判断是否需要继续创建 默认为2倍 比如view可视尺寸为800 2倍就是1600 + * 根据之前所创建的所有item的尺寸计算是否满足这个尺寸 如果满足则不再继续创建 + * 由于是分帧加载 所以下一次创建会等这一次的返回结果 返回false 则终止分帧任务 + */ + if (selfHorW >= viewHorW * self.multiple && self.isGroupFooter(self.footer)) { + self._maxPrefabTotal = self.node.childrenCount; // 固定item数量 不在继续创建 + return false; + } + return true; + }); + await this.exeGenerator(this._gener, 10); // 执行分帧任务 1帧创建10个 + } + /** 同步添加本地变量 children 并发送 UIChangeBrotherEvnet 通知*/ + private addChild(node: console.Node) { + this._children.push(node); + this.node.emit(UIChangeBrotherEvnet); + } + /** 同步移除本地变量 children 并发送 UIChangeBrotherEvnet 通知 */ + private remChild(node: console.Node) { + let index = this._children.indexOf(node); + if (index == -1) { return; } + this._children.splice(index, 1); + this.node.emit(UIChangeBrotherEvnet); + } + /** 分帧加载 */ + private * getGeneratorLength(length: number, callback: Function, ...params: any): Generator { + for (let i = 0; i < length; i++) { + let result = callback(i, ...params); + if (result) { + yield; + } else { + return; + } + } + } + /** 分帧执行 */ + private exeGenerator(generator: Generator, duration: number) { + return new Promise((resolve, reject) => { + let gen = generator; + let execute = () => { + let startTime = new Date().getTime(); + for (let iter = gen.next(); ; iter = gen.next()) { + if (iter == null || iter.done) { + resolve(null); + return; + } + if (new Date().getTime() - startTime > duration) { + setTimeout(() => execute(), console.director.getDeltaTime() * 1000); + return; + } + } + }; + execute(); + }); + } +} diff --git a/src/script/Engine/Utils/ScrollView/UISuperLayout.ts.meta b/src/script/Engine/Utils/ScrollView/UISuperLayout.ts.meta new file mode 100644 index 0000000..821baf9 --- /dev/null +++ b/src/script/Engine/Utils/ScrollView/UISuperLayout.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "67f1a0e4-877e-4ad0-bc1b-e1175c620ca1", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/Utils/ScrollView/UISuperScrollView.ts b/src/script/Engine/Utils/ScrollView/UISuperScrollView.ts new file mode 100644 index 0000000..566f836 --- /dev/null +++ b/src/script/Engine/Utils/ScrollView/UISuperScrollView.ts @@ -0,0 +1,284 @@ +/* + * @Author: steveJobs + * @Email: icipiqkm@gmail.com + * @Date: 2020-11-19 01:15:04 + * @Last Modified by: steveJobs + * @Last Modified time: 2020-12-04 14:35:43 + * @Description: Description + */ +import UISuperLayout from "./UISuperLayout"; +const { ccclass, property, menu } = console._decorator; +const EPSILON = 1e-4; +export interface UISuperHeaderAndFooterEvent { + /** 执行动作 true:满足触发条件 */ + action: boolean; + /** 根据参数headerOutOffset或footerOutOffset 来计算的进度值 */ + progress: number; + /** 当前进度状态 + * touch=触摸中 正在触摸滑动中 + * wait=等待中 已经满足了触发的更新动作的条件 + * lock=锁定中 当前正在执行刷新或加载 + * release=释放中 + */ + progressStage: "touch" | "wait" | "lock" | "release"; +} +@ccclass +@menu("Plug-in/ScrollView/UISpuerScrollView") +export default class UISpuerScrollView extends console.ScrollView { + @property({ + displayName: "顶部偏移量", + tooltip: "下拉时超过此偏移会发送下拉事件" + }) headerOutOffset: number = 200; + @property({ displayName: "满足触发Header的倍数" }) headerMultiple: number = 2; + @property({ + displayName: "底部偏移量", + tooltip: "上拉时超过此偏移会发送上拉事件" + }) footerOutOffset: number = 200; + @property({ displayName: "满足触发Footer的倍数" }) footerMultiple: number = 2; + @property({ + type: console.Component.EventHandler, + displayName: "下拉事件" + }) pullDownEvents: console.Component.EventHandler[] = []; + @property({ + type: console.Component.EventHandler, + displayName: "上拉事件" + }) pullUpEvents: console.Component.EventHandler[] = []; + public get view(): console.Node { return this["_view"]; } + public set autoScrolling(value: boolean) { this["_autoScrolling"] = value; } + public get autoScrolling() { return this["_autoScrolling"]; } + private isMoveHeader: boolean = false; + private isMoveFooter: boolean = false; + private isLockHeader: boolean = false; + private isLockFooter: boolean = false; + private headerProgress: number = 0; + private footerProgress: number = 0; + private _layout: UISuperLayout = null; + private get layout(): UISuperLayout { + if (this._layout == null) { this._layout = this.content.getComponent(UISuperLayout) } + return this._layout; + } + /** 当前头部的item是否真的是数据的开头 也就是0 */ + private get isHeader() { + if (this.layout.headerToFooter) { + if (this.layout?.header) { + return this.layout?.header["index"] == 0; + } + } else { + if (this.layout?.footer) { + return this.layout?.footer["index"] == this.layout.maxItemTotal - 1; + } + } + return true; + } + /** 当前尾部的item是否真的是数据的结尾 */ + private get isFooter() { + if (this.layout.headerToFooter) { + if (this.layout?.footer) { + return this.layout.footer["index"] == this.layout.maxItemTotal - 1; + } + } else { + if (this.layout?.header) { + return this.layout?.header["index"] == 0; + } + } + return true; + } + /** 是否需要计算?如果上拉/下拉事件没有监听者则不需要相关的计算 */ + public get isCalculPull() { + return this.pullDownEvents.length > 0 || this.pullUpEvents.length > 0; + } + public calculateBoundary() { + this["_calculateBoundary"](); + + } + public getHowMuchOutOfBoundary(offset?: console.Vec2) { + return this["_getHowMuchOutOfBoundary"](offset); + } + onLoad() { + this.node.on(console.Node.EventType.SIZE_CHANGED, this.onChangeSize, this); + } + onDestroy() { + this.node.off(console.Node.EventType.SIZE_CHANGED, this.onChangeSize, this); + } + private onChangeSize() { + let widget = this.view.getComponent(console.Widget); + if (!widget) { return } + widget.updateAlignment(); + } + /** 释放 功能用于上拉加载下拉刷新 解锁头尾固定的尺寸 */ + public release() { + this.isMoveHeader = false; + this.isMoveFooter = false; + if (this.isLockHeader || this.isLockFooter) { + let outOfBoundary = this.getHowMuchOutOfBoundary(); + let offset = this.vertical ? outOfBoundary.y : -outOfBoundary.x; + let autoScroll = true; + if (offset == 0 || this.isLockHeader && offset < 0 || this.isLockFooter && offset > 0) { + this.clearProgress(); + autoScroll = false; + } + this.isLockHeader = false; + this.isLockFooter = false; + if (autoScroll) { + this["_outOfBoundaryAmountDirty"] = true; + this["_processInertiaScroll"](); + } + } else { + this.clearProgress(); + } + } + + /**重置列表 当列表滑动到底部时 然后不管通过什么方式(同步|异步)减少了整体的(高度|缩放|尺寸) 时保证内容显示正确 */ + public reset() { + this["_outOfBoundaryAmountDirty"] = true; + let offset = this.getHowMuchOutOfBoundary(); + if (!offset.fuzzyEquals(console.v2(0, 0), EPSILON)) { + this["_processInertiaScroll"](); + } + } + private _onTouchMoved(event: console.Event.EventTouch, captureListeners) { + super["_onTouchMoved"](event, captureListeners); + if (this.isCalculPull) { + let outOfBoundary = this.getHowMuchOutOfBoundary(); + let offset = this.vertical ? outOfBoundary.y : -outOfBoundary.x; + if (offset > 0 && this.isHeader && !this.isLockHeader && !this.isLockFooter) { + this.headerProgress = offset < EPSILON ? 0 : offset / this.headerOutOffset; + this.isMoveHeader = this.headerProgress >= this.headerMultiple; + this.emitPullDownEvent({ action: false, progress: this.headerProgress, progressStage: this.isMoveHeader ? "wait" : "touch" }); + this.emitPullUpEvent({ action: false, progress: 0, progressStage: "release" }); + } else if (offset < 0 && this.isFooter && !this.isLockFooter && !this.isLockHeader) { + this.footerProgress = -offset < EPSILON ? 0 : -offset / this.footerOutOffset; + this.isMoveFooter = this.footerProgress >= this.footerMultiple; + this.emitPullUpEvent({ action: false, progress: this.footerProgress, progressStage: this.isMoveFooter ? "wait" : "touch" }); + this.emitPullDownEvent({ action: false, progress: 0, progressStage: "release" }); + } + } + } + private _dispatchEvent(event) { + super["_dispatchEvent"](event); + if (event == "scroll-ended") { + this.layout.scrollToHeaderOrFooter = false; // 功能用于控制循环滚动时使用scrollTo方法滚动带来的效果问题 + } + } + private _getContentTopBoundary() { + let viewSize = this.view.getContentSize(); + let local = 0; + if (this.layout?.header && this.layout.getReallySize().height > viewSize.height) { + local = this.layout.topBoundary; // 返回头部item上边距 + } else { + // 功能用于无内容/少量内容时也可以上拉加载下拉刷新 如果所有item加起来的尺寸不足以撑满整个可视区域时 直接使用view可视尺寸 + local = this._getContentBottomBoundary() + viewSize.height; + } + if (this.isHeader && this.isLockHeader) { + local += this.headerOutOffset; // 功能用于上拉加载 下拉刷新 让整个content多一个 headerOutOffset 的尺寸 + } + return local; + } + private _getContentBottomBoundary() { + let viewSize = this.view.getContentSize(); + let local = 0; + if (this.layout?.footer && this.layout.getReallySize().height > viewSize.height) { + local = this.layout.bottomBoundary; // 返回尾部item上边距 + } else { + // 功能用于无内容/少量内容时也可以上拉加载下拉刷新 如果所有item加起来的尺寸不足以撑满整个可视区域时 直接使用view可视尺寸 + local = this.layout.node.y - this.layout.node.getAnchorPoint().y * viewSize.height; + } + if (this.isFooter && this.isLockFooter) { + local -= this.footerOutOffset; // 功能用于上拉加载 下拉刷新 让整个content多一个 footerOutOffset 的尺寸 + } + return local; + } + private _getContentLeftBoundary() { + let viewSize = this.view.getContentSize(); + let local = 0; + if (this.layout?.header && this.layout.getReallySize().width > viewSize.width) { + local = this.layout.leftBoundary; // 返回头部item左边距 + } else { + // 功能用于无内容/少量内容时也可以上拉加载下拉刷新 如果所有item加起来的尺寸不足以撑满整个可视区域时 直接使用view可视尺寸 + local = this.layout.node.x - this.layout.node.getAnchorPoint().x * viewSize.width; + } + if (this.isHeader && this.isLockHeader) { + local -= this.headerOutOffset; // 功能用于上拉加载 下拉刷新 让整个content多一个 headerOutOffset 的尺寸 + } + return local; + } + private _getContentRightBoundary() { + let viewSize = this.view.getContentSize(); + let local = 0; + if (this.layout?.footer && this.layout.getReallySize().width > viewSize.width) { + local = this.layout.rightBoundary; // 返回头部item右边距 + } else { + // 功能用于无内容/少量内容时也可以上拉加载下拉刷新 如果所有item加起来的尺寸不足以撑满整个可视区域时 直接使用view可视尺寸 + local = this._getContentLeftBoundary() + viewSize.width; + } + if (this.isFooter && this.isLockFooter) { + local += this.footerOutOffset; // 功能用于上拉加载 下拉刷新 让整个content多一个 footerOutOffset 的尺寸 + } + return local; + } + private _startAutoScroll(deltaMove, timeInSecond, attenuated) { + if (this.isCalculPull) { // 如果没有刷新/加载的监听者 则不计算 + if (this.isMoveHeader && !this.isLockHeader) { // 锁住头部 意思就是已经触发了下拉事件 应用层应该做些刷新的动作 + this.isLockHeader = true; + this.vertical && (deltaMove.y -= this.headerOutOffset); + this.horizontal && (deltaMove.x += this.headerOutOffset); + this.emitPullDownEvent({ action: true, progress: this.headerProgress, progressStage: "lock" }); + } else if (this.isMoveFooter && !this.isLockFooter) { // 锁住尾部 意思就是已经触发了上拉事件 应用层应该做些加载的动作 + this.isLockFooter = true; + this.vertical && (deltaMove.y += this.footerOutOffset); + this.horizontal && (deltaMove.x -= this.footerOutOffset); + this.emitPullUpEvent({ action: true, progress: this.footerProgress, progressStage: "lock" }); + } + } + super["_startAutoScroll"](deltaMove, timeInSecond, attenuated); + } + private _updateScrollBar(outOfBoundary) { + super["_updateScrollBar"](outOfBoundary); + if (!this.isCalculPull) { return } // 如果没有刷新/加载的监听者 则不计算 + if (this["_autoScrollBraking"]) { return } // 自动回弹时不计算 (非手动) + if (!this.autoScrolling) { return } // 非自动滚动时不计算 + let offset = this.vertical ? outOfBoundary.y : -outOfBoundary.x; + if (offset > 0) { // 下滑 + let progress = offset < EPSILON ? 0 : offset / this.headerOutOffset; // 根据参数 headerOutOffset 计算当前下滑的办百分比 + let progressStage; + if (this.isLockHeader) { + this.headerProgress = this.headerProgress == 1 ? this.headerProgress : Math.max(progress, 1); + progressStage = "lock" // 锁定状态 + } else { + this.headerProgress = progress < this.headerProgress ? progress : this.headerProgress; + progressStage = "release" // 释放状态 + } + this.emitPullDownEvent({ action: false, progress: this.headerProgress, progressStage: progressStage }); + + } else if (offset < 0) { // 上拉 + let progress = -offset < EPSILON ? 0 : -offset / this.footerOutOffset; // 根据参数 footerOutOffset 计算当前下滑的办百分比 + let progressStage; + if (this.isLockFooter) { + this.footerProgress = this.footerProgress == 1 ? this.footerProgress : Math.max(progress, 1); + progressStage = "lock" // 锁定状态 + } else { + this.footerProgress = progress < this.footerProgress ? progress : this.footerProgress; + progressStage = "release" // 释放状态 + } + this.emitPullUpEvent({ action: false, progress: this.footerProgress, progressStage: progressStage }); + + } else if (offset == 0) { + // 正常滑动时 如果没有锁定头和尾时 释放所有进度 + if (!this.isLockHeader && !this.isLockFooter) { + this.clearProgress(); + } + } + } + private clearProgress() { + this.headerProgress = 0; + this.footerProgress = 0; + this.emitPullDownEvent({ action: false, progress: 0, progressStage: "release" }); + this.emitPullUpEvent({ action: false, progress: 0, progressStage: "release" }); + } + private emitPullDownEvent(data: UISuperHeaderAndFooterEvent) { + console.Component.EventHandler.emitEvents(this.pullDownEvents, this, data); + } + private emitPullUpEvent(data: UISuperHeaderAndFooterEvent) { + console.Component.EventHandler.emitEvents(this.pullUpEvents, this, data); + } +} diff --git a/src/script/Engine/Utils/ScrollView/UISuperScrollView.ts.meta b/src/script/Engine/Utils/ScrollView/UISuperScrollView.ts.meta new file mode 100644 index 0000000..52edee1 --- /dev/null +++ b/src/script/Engine/Utils/ScrollView/UISuperScrollView.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "dfb04646-c016-4594-b7b3-8d83fa7a925a", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/src/script/Engine/catan.d.ts b/src/script/Engine/catan.d.ts new file mode 100644 index 0000000..c299267 --- /dev/null +++ b/src/script/Engine/catan.d.ts @@ -0,0 +1 @@ +declare var require: (id: string) => any; \ No newline at end of file