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