[add] first
This commit is contained in:
commit
bd457c7f10
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
src/FormTable
|
||||||
|
src/FormTableExt
|
366
.eslintrc.json
Normal file
366
.eslintrc.json
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true,
|
||||||
|
"jest": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
|
"overrides": [],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react",
|
||||||
|
"@typescript-eslint",
|
||||||
|
"react-hooks"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-alert": 0, //禁止使用alert confirm prompt
|
||||||
|
"no-bitwise": 0, //禁止使用按位运算符
|
||||||
|
"no-console": "off", //禁止使用console
|
||||||
|
"no-continue": 0, //禁止使用continue
|
||||||
|
"no-debugger": 2, //禁止使用debugger
|
||||||
|
"no-delete-var": 2, //不能对var声明的变量使用delete操作符
|
||||||
|
"no-div-regex": 1, //不能使用看起来像除法的正则表达式/=foo/
|
||||||
|
"no-dupe-args": 2, //函数参数不能重复
|
||||||
|
"no-duplicate-case": 2, //switch中的case标签不能重复
|
||||||
|
"no-else-return": "off", //如果if语句里面有return,后面不能跟else语句
|
||||||
|
"no-empty-label": "off", //禁止使用空label
|
||||||
|
"no-eq-null": "off", //禁止对null使用==或!=运算符
|
||||||
|
"no-extend-native": "off", //禁止扩展native对象
|
||||||
|
"no-extra-parens": "off", //禁止非必要的括号
|
||||||
|
"no-extra-semi": 2, //禁止多余的冒号
|
||||||
|
"no-floating-decimal": 2, //禁止省略浮点数中的0 .5 3.
|
||||||
|
"no-implicit-coercion": "off", //禁止隐式转换
|
||||||
|
"no-inline-comments": 0, //禁止行内备注
|
||||||
|
"no-invalid-this": 2, //禁止无效的this,只能用在构造器,类,对象字面量
|
||||||
|
"no-iterator": 2, //禁止使用__iterator__ 属性
|
||||||
|
"no-lonely-if": "off", //禁止else语句内只有if语句
|
||||||
|
"no-mixed-requires": [
|
||||||
|
0,
|
||||||
|
false
|
||||||
|
], //声明时不能混用声明类型
|
||||||
|
"no-mixed-spaces-and-tabs": "off", //禁止混用tab和空格
|
||||||
|
"no-multiple-empty-lines": [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"max": 2
|
||||||
|
}
|
||||||
|
], //空行最多不能超过2行
|
||||||
|
"no-nested-ternary": 0, //禁止使用嵌套的三目运算
|
||||||
|
"no-new": "off", //禁止在使用new构造一个实例后不赋值
|
||||||
|
"no-new-require": 2, //禁止使用new require
|
||||||
|
"no-param-reassign": "off", //禁止给参数重新赋值
|
||||||
|
"no-path-concat": 0, //node中不能使用__dirname或__filename做路径拼接
|
||||||
|
"no-plusplus": 0, //禁止使用++,--
|
||||||
|
"no-process-env": 0, //禁止使用process.env
|
||||||
|
"no-process-exit": 0, //禁止使用process.exit()
|
||||||
|
"no-redeclare": "off", //禁止重复声明变量
|
||||||
|
"no-restricted-modules": 0, //如果禁用了指定模块,使用就会报错
|
||||||
|
"no-return-assign": "off", //return 语句中不能有赋值表达式
|
||||||
|
"no-self-compare": 2, //不能比较自身
|
||||||
|
"no-sequences": 0, //禁止使用逗号运算符
|
||||||
|
"no-shadow": "off", //外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
|
||||||
|
"no-sync": 0, //nodejs 禁止同步方法
|
||||||
|
"no-ternary": 0, //禁止使用三目运算符
|
||||||
|
"no-this-before-super": 0, //在调用super()之前不能使用this或super
|
||||||
|
"no-throw-literal": 2, //禁止抛出字面量错误 throw "error";
|
||||||
|
"no-undef": "off", //不能有未定义的变量
|
||||||
|
"no-undef-init": "off", //变量初始化时不能直接给它赋值为undefined
|
||||||
|
"no-undefined": "off", //不能使用undefined
|
||||||
|
"no-unexpected-multiline": 2, //避免多行表达式
|
||||||
|
"no-underscore-dangle": "off", //标识符不能以_开头或结尾
|
||||||
|
"no-unneeded-ternary": 2, //禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
|
||||||
|
"no-unused-expressions": "off", //禁止无用的表达式
|
||||||
|
"no-unused-vars": "off", //不能有声明后未被使用的变量或参数
|
||||||
|
"no-use-before-define": "off", //未定义前不能使用
|
||||||
|
"no-useless-call": "off", //禁止不必要的call和apply
|
||||||
|
"no-void": "off", //禁用void操作符
|
||||||
|
"no-var": 0, //禁用var,用let和const代替
|
||||||
|
"no-warning-comments": "off", //不能有警告备注
|
||||||
|
"no-array-constructor": "error", // 禁止使用数组构造器
|
||||||
|
"no-caller": "error", // 禁止使用arguments.caller或arguments.callee
|
||||||
|
"no-catch-shadow": "error", // 禁止catch子句参数与外部作用域变量同名
|
||||||
|
"no-class-assign": "error", // 禁止给类赋值
|
||||||
|
"no-cond-assign": [
|
||||||
|
"error",
|
||||||
|
"except-parens"
|
||||||
|
], // 禁止在条件表达式中使用赋值语句
|
||||||
|
"no-constant-condition": "error", // 禁止在条件中使用常量表达式 if(true) if(1)
|
||||||
|
"no-control-regex": "error", // 禁止在正则表达式中使用控制字符
|
||||||
|
"no-dupe-keys": "error", // 在创建对象字面量时不允许键重复 {a: 1, a: 1}
|
||||||
|
"no-empty": "error", // 块语句中的内容不能为空
|
||||||
|
"no-empty-character-class": "error", // 正则表达式中的[]内容不能为空
|
||||||
|
"no-eval": "error", // 禁止使用eval
|
||||||
|
"no-ex-assign": "error", // 禁止给catch语句中的异常参数赋值
|
||||||
|
"no-extra-bind": "error", // 禁止不必要的函数绑定
|
||||||
|
"no-extra-boolean-cast": "off", // 禁止不必要的bool转换
|
||||||
|
"no-fallthrough": "error", // 禁止switch穿透
|
||||||
|
"no-func-assign": "error", // 禁止重复的函数声明
|
||||||
|
"no-implied-eval": "error", // 禁止使用隐式eval
|
||||||
|
"no-inner-declarations": "off", // 禁止在块语句中使用声明函数
|
||||||
|
"no-invalid-regexp": "error", // 禁止无效的正则表达式
|
||||||
|
"no-irregular-whitespace": "error", // 不能有不规则的空格
|
||||||
|
"no-label-var": "error", // label名不能与var声明的变量名相同
|
||||||
|
"no-labels": "error", // 禁止标签声明
|
||||||
|
"no-lone-blocks": "error", // 禁止不必要的嵌套块
|
||||||
|
"no-loop-func": "error", // 禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
|
||||||
|
"no-multi-spaces": "error", // 不能用多余的空格
|
||||||
|
"no-multi-str": "error", // 字符串不能用\换行
|
||||||
|
"no-native-reassign": "error", // 不能重写native对象
|
||||||
|
"no-negated-in-lhs": "error", // in 操作符的左边不能有!
|
||||||
|
"no-new-func": "error", // 禁止使用new Function
|
||||||
|
"no-new-object": "error", // 禁止使用new Object()
|
||||||
|
"no-new-wrappers": "error", // 禁止使用new创建包装实例,new String new Boolean new Number
|
||||||
|
"no-obj-calls": "error", // 不能调用内置的全局对象,比如Math() JSON()
|
||||||
|
"no-octal": "error", // 禁止使用八进制数字(因为八进制数字以0开头)
|
||||||
|
"no-octal-escape": "error", // 禁止使用八进制转义序列
|
||||||
|
"no-proto": "error", // 禁止使用__proto__属性(按照标准,__proto__为私有属性,不应公开)
|
||||||
|
"no-regex-spaces": "error", // 禁止在正则表达式字面量中使用多个空格 /foo bar/
|
||||||
|
"no-script-url": "off", // 禁止使用javascript:void(0)
|
||||||
|
"no-shadow-restricted-names": "error", // 严格模式中规定的限制标识符不能作为声明时的变量名使用
|
||||||
|
"no-spaced-func": "error", // 函数调用时 函数名与()之间不能有空格
|
||||||
|
"no-sparse-arrays": "error", // 禁止稀疏数组, [1,,2]
|
||||||
|
"no-trailing-spaces": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"skipBlankLines": true
|
||||||
|
}
|
||||||
|
], // 一行结束后面不要有空格( 空白行忽略 )
|
||||||
|
"no-unreachable": "error", // 不能有无法执行的代码
|
||||||
|
"no-const-assign": "error", // 禁止修改const声明的变量
|
||||||
|
"no-with": "error", // 禁用with
|
||||||
|
"comma-dangle": "off", // 数组或对象最后不允许出现多余的逗号
|
||||||
|
"comma-spacing": "error", // 逗号前面不允许有空格,后面还有东西的时候必须有一个空格
|
||||||
|
"curly": [
|
||||||
|
"error",
|
||||||
|
"multi-line"
|
||||||
|
], // 块级代码需要换行的时候必须使用 {}将代码裹起来
|
||||||
|
"eqeqeq": "off", // 必须使用全等
|
||||||
|
"indent": [
|
||||||
|
"off",
|
||||||
|
"tab",
|
||||||
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
|
], // 缩进用tab
|
||||||
|
"key-spacing": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"beforeColon": false,
|
||||||
|
"afterColon": true
|
||||||
|
}
|
||||||
|
], // 对象字面量中冒号的后面必须有空格,前面不允许有空格
|
||||||
|
"keyword-spacing": "off", // 关键字前后必须存在空格
|
||||||
|
"new-parens": "error", // new时必须加小括号 const person = new Person();
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"double",
|
||||||
|
{
|
||||||
|
"allowTemplateLiterals": true
|
||||||
|
}
|
||||||
|
], // 引号类型 ''
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
], // 语句必须分号结尾
|
||||||
|
"semi-spacing": [
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"before": false,
|
||||||
|
"after": true
|
||||||
|
}
|
||||||
|
], // 分号前面不允许有空格,后面有其他东西的时候必须空一空格
|
||||||
|
"space-before-blocks": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
], // 不以新行开始的块 { 前面要有空格
|
||||||
|
// "space-before-function-paren": ["error", "never"], // 函数定义时括号前面不允许有空格
|
||||||
|
"space-infix-ops": "error", // 中缀操作符周围必须有空格 a + b
|
||||||
|
"space-unary-ops": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"words": true,
|
||||||
|
"nonwords": false
|
||||||
|
}
|
||||||
|
], // 一元运算符的前/后如果是单词则空一空格,如果是运算符则不需要空空格 new Foo √ 1++ √
|
||||||
|
// "spaced-comment": ["error", "always", { "markers": ["*!"] }], // 注释风格, 双斜杠后面空一格空格再写注释
|
||||||
|
"strict": [
|
||||||
|
"error",
|
||||||
|
"global"
|
||||||
|
], // 使用全局严格模式
|
||||||
|
"use-isnan": "error", // 禁止比较时使用NaN,只能用isNaN()
|
||||||
|
"arrow-parens": 0, //箭头函数用小括号括起来
|
||||||
|
"arrow-spacing": 0, //=>的前/后括号
|
||||||
|
"accessor-pairs": 0, //在对象中使用getter/setter
|
||||||
|
"block-scoped-var": 0, //块语句中使用var
|
||||||
|
"brace-style": "off", //大括号风格
|
||||||
|
"callback-return": "off", //避免多次调用回调什么的
|
||||||
|
"comma-style": [
|
||||||
|
"error",
|
||||||
|
"last"
|
||||||
|
], //逗号风格,换行时在行首还是行尾
|
||||||
|
"complexity": [
|
||||||
|
0,
|
||||||
|
11
|
||||||
|
], //循环复杂度
|
||||||
|
"computed-property-spacing": [
|
||||||
|
0,
|
||||||
|
"never"
|
||||||
|
], //是否允许计算后的键名什么的
|
||||||
|
"consistent-return": 0, //return 后面是否允许省略
|
||||||
|
"consistent-this": "off", //this别名
|
||||||
|
"constructor-super": 0, //非派生类不能调用super,派生类必须调用super
|
||||||
|
"default-case": "off", //switch语句最后必须有default
|
||||||
|
"dot-location": 0, //对象访问符的位置,换行的时候在行首还是行尾
|
||||||
|
"dot-notation": [
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"allowKeywords": true
|
||||||
|
}
|
||||||
|
], //避免不必要的方括号
|
||||||
|
"eol-last": 0, //文件以单一的换行符结束
|
||||||
|
"func-names": 0, //函数表达式必须有名字
|
||||||
|
"func-style": [
|
||||||
|
0,
|
||||||
|
"declaration"
|
||||||
|
], //函数风格,规定只能使用函数声明/函数表达式
|
||||||
|
"generator-star-spacing": 0, //生成器函数*的前后空格
|
||||||
|
"guard-for-in": 0, //for in循环要用if语句过滤
|
||||||
|
"handle-callback-err": 0, //nodejs 处理错误
|
||||||
|
"id-length": 0, //变量名长度
|
||||||
|
"init-declarations": 0, //声明时必须赋初值
|
||||||
|
"lines-around-comment": 0, //行前/行后备注
|
||||||
|
"max-depth": [
|
||||||
|
0,
|
||||||
|
4
|
||||||
|
], //嵌套块深度
|
||||||
|
"max-len": [
|
||||||
|
0,
|
||||||
|
80,
|
||||||
|
4
|
||||||
|
], //字符串最大长度
|
||||||
|
"max-nested-callbacks": [
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
], //回调嵌套深度
|
||||||
|
"max-params": [
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
], //函数最多只能有3个参数
|
||||||
|
"max-statements": [
|
||||||
|
0,
|
||||||
|
10
|
||||||
|
], //函数内最多有几个声明
|
||||||
|
"new-cap": "off", //函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用
|
||||||
|
"newline-after-var": "off", //变量声明后是否需要空一行
|
||||||
|
"object-shorthand": 0, //强制对象字面量缩写语法
|
||||||
|
"one-var": "off", //连续声明
|
||||||
|
"operator-assignment": [
|
||||||
|
0,
|
||||||
|
"always"
|
||||||
|
], //赋值运算符 += -=什么的
|
||||||
|
"operator-linebreak": "off", //换行时运算符在行尾还是行首
|
||||||
|
"padded-blocks": 0, //块语句内行首行尾是否要空行
|
||||||
|
"prefer-spread": 0, //首选展开运算
|
||||||
|
"prefer-reflect": 0, //首选Reflect的方法
|
||||||
|
"quote-props": "off", //对象字面量中的属性名是否强制双引号
|
||||||
|
"radix": "off", //parseInt必须指定第二个参数
|
||||||
|
"id-match": 0, //命名检测
|
||||||
|
"sort-vars": 0, //变量声明时排序
|
||||||
|
"space-after-keywords": [
|
||||||
|
0,
|
||||||
|
"always"
|
||||||
|
], //关键字后面是否要空一格
|
||||||
|
"space-before-function-paren": [
|
||||||
|
0,
|
||||||
|
"always"
|
||||||
|
], //函数定义时括号前面要不要有空格
|
||||||
|
"space-in-parens": [
|
||||||
|
0,
|
||||||
|
"never"
|
||||||
|
], //小括号里面要不要有空格
|
||||||
|
"space-return-throw-case": "off", //return throw case后面要不要加空格
|
||||||
|
"spaced-comment": 0, //注释风格不要有空格什么的
|
||||||
|
"valid-jsdoc": 0, //jsdoc规则
|
||||||
|
"valid-typeof": "error", //必须使用合法的typeof的值
|
||||||
|
"vars-on-top": "error", //var必须放在作用域顶部
|
||||||
|
"wrap-iife": [
|
||||||
|
"error",
|
||||||
|
"inside"
|
||||||
|
], //立即执行函数表达式的小括号风格
|
||||||
|
"wrap-regex": 0, //正则表达式字面量用小括号包起来
|
||||||
|
"yoda": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
], //禁止尤达条件
|
||||||
|
"linebreak-style": [
|
||||||
|
0,
|
||||||
|
"windows"
|
||||||
|
], //换行风格
|
||||||
|
"array-bracket-spacing": [
|
||||||
|
2,
|
||||||
|
"never"
|
||||||
|
], //是否允许非空数组里面有多余的空格
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"camelcase": "off",
|
||||||
|
"block-spacing": "error",
|
||||||
|
"no-duplicate-imports": "error",
|
||||||
|
"require-yield": "off",
|
||||||
|
"prefer-const": "off",
|
||||||
|
"object-curly-spacing": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"react/jsx-curly-spacing": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-namespace": "off",
|
||||||
|
"@typescript-eslint/no-inferrable-types": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"@typescript-eslint/prefer-namespace-keyword": "off",
|
||||||
|
"@typescript-eslint/ban-types": "off",
|
||||||
|
"@typescript-eslint/no-this-alias": "off",
|
||||||
|
"@typescript-eslint/no-empty-interface": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": [
|
||||||
|
"off",
|
||||||
|
{
|
||||||
|
"allowExpressions": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/typedef": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"arrayDestructuring": false,
|
||||||
|
"arrowParameter": false,
|
||||||
|
"objectDestructuring": false,
|
||||||
|
"memberVariableDeclaration": true,
|
||||||
|
"parameter": true,
|
||||||
|
"propertyDeclaration": true,
|
||||||
|
"variableDeclaration": false,
|
||||||
|
"variableDeclarationIgnoreFunction": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"typescript": {}
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
web-mobile
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
/.vscode
|
||||||
|
/src/FormTable
|
||||||
|
/src/FormTable/*
|
||||||
|
/src/FormTableExt
|
||||||
|
/src/FormTableExt/*
|
5
auto-imports.d.ts
vendored
Normal file
5
auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// Generated by 'unplugin-auto-import'
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
|
||||||
|
}
|
BIN
build-templates/favicon.ico
Normal file
BIN
build-templates/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 134 KiB |
124
build.cjs
Normal file
124
build.cjs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
//build.js文件
|
||||||
|
// let exec = require('child_process').exec // 异步子进程
|
||||||
|
let fs = require('fs')
|
||||||
|
const path = "./build-templates/version.json"
|
||||||
|
let packageJSON = require(path)
|
||||||
|
/** package.json文件的version参数 */
|
||||||
|
let version = packageJSON.version
|
||||||
|
// /** 命令行的所有参数 */
|
||||||
|
// let options = process.argv
|
||||||
|
// /** 命令行的type参数 */
|
||||||
|
// let type = null
|
||||||
|
// /** 新的version参数 */
|
||||||
|
// let newVersion = null
|
||||||
|
|
||||||
|
const dt = new Date();
|
||||||
|
const Year = +((dt.getFullYear() + "")[2] + (dt.getFullYear() + "")[3]);
|
||||||
|
const Month = +(dt.getMonth() + 1);
|
||||||
|
const Day = +(dt.getDate());
|
||||||
|
let MajorVersion = +(version.MajorVersion);
|
||||||
|
let MinorVersion = +(version.MinorVersion);
|
||||||
|
let BuildVersion = +(version.BuildVersion);
|
||||||
|
let Revision = +(version.Revision);
|
||||||
|
|
||||||
|
if (!MajorVersion || !MinorVersion || !BuildVersion || !Revision || Year != MajorVersion) {
|
||||||
|
MajorVersion = Year;
|
||||||
|
MinorVersion = Month;
|
||||||
|
BuildVersion = Day;
|
||||||
|
Revision = 1;
|
||||||
|
} else if (Month != MinorVersion) {
|
||||||
|
MinorVersion = Month;
|
||||||
|
BuildVersion = Day;
|
||||||
|
Revision = 1;
|
||||||
|
} else if (Day != BuildVersion) {
|
||||||
|
BuildVersion = Day;
|
||||||
|
Revision = 1;
|
||||||
|
} else {
|
||||||
|
Revision++;
|
||||||
|
}
|
||||||
|
var data_new = {
|
||||||
|
version: {
|
||||||
|
MajorVersion,
|
||||||
|
MinorVersion,
|
||||||
|
BuildVersion,
|
||||||
|
Revision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let VersionNew = JSON.stringify(data_new);
|
||||||
|
console.log(`version: ${MajorVersion}.${MinorVersion}.${BuildVersion}.${Revision}`)
|
||||||
|
//同步寫入package.json文件
|
||||||
|
fs.writeFileSync(path, VersionNew)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// //判断命令行是否存在type参数或version参数进行逻辑处理
|
||||||
|
// for (let i = 0; i < options.length; i++) {
|
||||||
|
// if (options[i].indexOf('type') > -1) {
|
||||||
|
// //存在type参数
|
||||||
|
// type = options[i].split('=')[1]
|
||||||
|
// } else if (options[i].indexOf('version') > -1) {
|
||||||
|
// //存在version参数
|
||||||
|
// newVersion = options[i].split('=')[1]
|
||||||
|
// } else {
|
||||||
|
// //code
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (newVersion) {
|
||||||
|
// //存在设置version参数则改变原来的version
|
||||||
|
// version = newVersion
|
||||||
|
// } else if (type) {
|
||||||
|
// //不设置version则根据type来进行修改version
|
||||||
|
// version = handleType(version, type)
|
||||||
|
// } else {
|
||||||
|
// version = null
|
||||||
|
// console.log('-----------没有改变version-----------')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //修改了version则写入
|
||||||
|
// if (version) {
|
||||||
|
// packageJSON.version = version
|
||||||
|
// //同步写入package.json文件
|
||||||
|
// fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2))
|
||||||
|
// console.log('-----------更新package的version为:%s参数成功-----------', version)
|
||||||
|
// // handleGitAdd('./package.json')
|
||||||
|
// // pullRemote()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 根据分支类型处理版本号version
|
||||||
|
// * @param {string} oldVersion 旧的版本号
|
||||||
|
// * @param {string} type 分支类型
|
||||||
|
// * @private
|
||||||
|
// */
|
||||||
|
// function handleType(oldVersion, type) {
|
||||||
|
// let oldVersionArr = oldVersion.split('.')
|
||||||
|
// //版本号第一位 如:1.2.3 则为 1
|
||||||
|
// let firstNum = +oldVersionArr[0]
|
||||||
|
// //版本号第二位 如:1.2.3 则为 2
|
||||||
|
// let secondNum = +oldVersionArr[1]
|
||||||
|
// //版本号第三位 如:1.2.3 则为 3
|
||||||
|
// let thirdNum = +oldVersionArr[2]
|
||||||
|
// switch (type) {
|
||||||
|
// case 'release':
|
||||||
|
// //release分支的处理逻辑
|
||||||
|
// ++secondNum
|
||||||
|
// thirdNum = 0
|
||||||
|
// break
|
||||||
|
|
||||||
|
// case 'hotfix':
|
||||||
|
// //hotfix分支的处理逻辑
|
||||||
|
// ++thirdNum
|
||||||
|
// break
|
||||||
|
|
||||||
|
// default:
|
||||||
|
// // 默认按照最小版本处理
|
||||||
|
// ++thirdNum
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return firstNum + '.' + secondNum + '.' + thirdNum
|
||||||
|
// }
|
24
index.html
Normal file
24
index.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover">
|
||||||
|
<script>
|
||||||
|
history.pushState(null, null, location.href);
|
||||||
|
window.onpopstate = function () {
|
||||||
|
history.go(1);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<title>來博娛樂城</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body oncontextmenu="return false">
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
8946
package-lock.json
generated
Normal file
8946
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
99
package.json
Normal file
99
package.json
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
{
|
||||||
|
"name": "lp-react",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"homepage": "./",
|
||||||
|
"scripts": {
|
||||||
|
"start": "npm run dev",
|
||||||
|
"dev": "vite --host --port 8000",
|
||||||
|
"build": "tsc && vite build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.15",
|
||||||
|
"@line/liff": "^2.22.3",
|
||||||
|
"@types/lodash": "^4.14.197",
|
||||||
|
"@types/node": "^20.2.5",
|
||||||
|
"antd": "^5.11.3",
|
||||||
|
"axios": "^1.5.0",
|
||||||
|
"bootstrap": "^5.3.0",
|
||||||
|
"breakpoint-sass": "^3.0.0",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
|
"date-fns": "^2.30.0",
|
||||||
|
"dayjs": "^1.11.9",
|
||||||
|
"eslint-import-resolver-typescript": "^3.6.0",
|
||||||
|
"eslint-plugin-react": "^7.33.2",
|
||||||
|
"eslint-plugin-unused-imports": "^3.0.0",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"firebase": "^10.5.2",
|
||||||
|
"gsap": "^3.12.2",
|
||||||
|
"immer": "^9.0.21",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"lz-string": "^1.5.0",
|
||||||
|
"miragejs": "^0.1.47",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"nosleep.js": "^0.12.0",
|
||||||
|
"numeral": "^2.0.6",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"pure-react-carousel": "^1.30.1",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-countdown": "^2.3.5",
|
||||||
|
"react-device-detect": "^2.2.3",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-draggable": "^4.4.5",
|
||||||
|
"react-fast-marquee": "^1.6.0",
|
||||||
|
"react-fitty": "^1.0.1",
|
||||||
|
"react-iframe": "^1.8.5",
|
||||||
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
|
"react-query": "^3.39.3",
|
||||||
|
"react-router-dom": "^6.15.0",
|
||||||
|
"react-scroll": "^1.8.9",
|
||||||
|
"react-spinners": "^0.13.8",
|
||||||
|
"react-timer-hook": "^3.0.7",
|
||||||
|
"react-use": "^17.4.0",
|
||||||
|
"rxjs": "^7.8.1",
|
||||||
|
"sass": "^1.66.1",
|
||||||
|
"string-width": "^6.1.0",
|
||||||
|
"styled-components": "^5.3.6",
|
||||||
|
"swiper": "^8.4.5",
|
||||||
|
"ts-key-enum": "^2.0.12",
|
||||||
|
"use-immer": "^0.9.0",
|
||||||
|
"use-onclickoutside": "^0.4.1",
|
||||||
|
"use-reducer-async": "^2.1.1",
|
||||||
|
"uuid": "^9.0.0",
|
||||||
|
"vconsole": "^3.15.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.15",
|
||||||
|
"@types/react-dom": "^18.2.7",
|
||||||
|
"@types/styled-components": "^5.1.26",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
|
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||||
|
"eslint": "^8.45.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.3",
|
||||||
|
"typescript": "^5.0.2",
|
||||||
|
"vite": "^4.4.5"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
242
src/Common/DataReceived/MainControlData.ts
Normal file
242
src/Common/DataReceived/MainControlData.ts
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
|
||||||
|
import CSSettingsV3 from "@/FormTable/CSSettingsV3";
|
||||||
|
import { confirmModalObj } from "@/UI/MessageBox/ConfirmModalContext";
|
||||||
|
import { modalObj } from "@/UIControl/ModalContext";
|
||||||
|
import { Cocos } from "@/assets/VueScript/Cocos";
|
||||||
|
import { CommonEventCallBack, JackpotPoolCallBack } from "@/assets/VueScript/CocosVueScript";
|
||||||
|
import GameData_Cocos from "@/assets/VueScript/share/GameData_Cocos";
|
||||||
|
import { chatLog, chatMessage, setChatBanTime } from "@/components/ModalContent/ChatRoomModal/chatUtils";
|
||||||
|
import Config from "@/define/Config";
|
||||||
|
import Player from "@/modules/player";
|
||||||
|
import UserData from "@/modules/player/UserData";
|
||||||
|
import { State } from "@/modules/player/define/data";
|
||||||
|
import { ILineShareData, ModalContentType } from "@/types";
|
||||||
|
import { CommonEventType } from "@/utils";
|
||||||
|
import { NumberEx } from "@/utils/Number/NumberEx";
|
||||||
|
import { activityComSync, backpackInfo, friendAllowList, friendDenyList, profileInfo, txnCenter, txnNew, txnTrade, txnUserAdd, vipInfo } from "@/utils/setRPCData";
|
||||||
|
import MainControl from "../MainControl/MainControl";
|
||||||
|
import CSMessage from "../Message/CSMessage";
|
||||||
|
import CSResource from "../ResourceItem/CSResource";
|
||||||
|
import IResourceItem from "../ResourceItem/IResourceItem";
|
||||||
|
|
||||||
|
export default class MainControlData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Normal = 0, //一般斷線or登出
|
||||||
|
RpcError = 1, //RPC錯誤
|
||||||
|
SystemError = 2, //系統錯誤
|
||||||
|
UserError = 3, //使用者造成的錯誤
|
||||||
|
DbaError = 4, //query時回傳錯誤
|
||||||
|
SendBufferError = 5, //儲存傳送資料的buff錯誤
|
||||||
|
Kick = 6, //指定踢除
|
||||||
|
ConnectLimit = 7, //連線限制
|
||||||
|
RequireHardLimit = 8, //機台請求逾時
|
||||||
|
SignalWallet = 9,
|
||||||
|
RepeatLogin = 101, //重覆登入
|
||||||
|
LoginFailed = 102, //登入失敗
|
||||||
|
AppVerError = 103, //Client版本過舊
|
||||||
|
Maintain = 104, //維護中
|
||||||
|
Ban = 105, //帳號鎖定
|
||||||
|
SlotClose = 106, //機台關閉
|
||||||
|
ResourceWait = 107, //使用者資料尚未寫入
|
||||||
|
IsDelete = 108, // 被刪除的帳號
|
||||||
|
Delete = 109, // 刪除主動斷線
|
||||||
|
SafeDisconnect = 201, // 安全離線
|
||||||
|
*/
|
||||||
|
private _disconnectErrorType: number = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
Player.DataReceivedEvent.AddCallback(this._dataReceivedEvent, this);
|
||||||
|
}
|
||||||
|
private _dataReceivedEvent(param: any[] = null): void {
|
||||||
|
let type: MainControl.DataType = param[0];
|
||||||
|
let data: any = param[1];
|
||||||
|
switch (type) {
|
||||||
|
case MainControl.DataType.ServerData:
|
||||||
|
this._serverData(data);
|
||||||
|
break;
|
||||||
|
case MainControl.DataType.NetDisconnected:
|
||||||
|
this._netDisconnected();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// =======================================================================================
|
||||||
|
/** SERVER主動通知 */
|
||||||
|
private _serverData(resp: INetResponse<any>): void {
|
||||||
|
if (resp.IsValid) {
|
||||||
|
switch (resp.Method) {
|
||||||
|
case "activity_com.sync": {
|
||||||
|
activityComSync(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "backpack.info": {
|
||||||
|
backpackInfo(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "chat.ban": {
|
||||||
|
setChatBanTime(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "chat.message": {
|
||||||
|
chatMessage(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "chat.log": {
|
||||||
|
chatLog(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "friend.allow_list": {
|
||||||
|
friendAllowList(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "friend.deny_list": {
|
||||||
|
friendDenyList(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "game.share": {
|
||||||
|
const { openLineShareGame } = confirmModalObj;
|
||||||
|
const data: ILineShareData = {
|
||||||
|
slotID: resp.Data[0],
|
||||||
|
winMilt: resp.Data[1]
|
||||||
|
};
|
||||||
|
openLineShareGame(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "game.sync": {
|
||||||
|
Cocos.VicKing_Bridge.InGameGetUUID = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "jackpot.get": {
|
||||||
|
const { handleOpen, setModalType } = modalObj;
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
playerData.account.jackpotGet.push(...resp.Data);
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
setModalType(ModalContentType.jackpot);
|
||||||
|
handleOpen();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "jackpot.pool": {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
if (!Config.IsVite || MainControl.Instance.IsInGame) {
|
||||||
|
for (let i = 0; i < playerData.game.jackpotPool.length; i++) {
|
||||||
|
const oldJpData: [gameId: number, maxJPId: number, jp: number] = playerData.game.jackpotPool[i];
|
||||||
|
let newJpData: [gameId: number, maxJPId: number, jp: number] = null;
|
||||||
|
for (let j = 0; j < resp.Data.length; j++) {
|
||||||
|
const [gameId, maxJPId, jp] = resp.Data[j];
|
||||||
|
if (gameId === oldJpData[0]) {
|
||||||
|
newJpData = resp.Data[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!newJpData) {
|
||||||
|
JackpotPoolCallBack.DispatchCallback(oldJpData[0], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playerData.game.jackpotPool = resp.Data;
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
if (!Config.IsVite || MainControl.Instance.IsInGame) {
|
||||||
|
for (let i = 0; i < playerData.game.jackpotPool.length; i++) {
|
||||||
|
const jpData: [gameId: number, maxJPId: number, jp: number] = playerData.game.jackpotPool[i];
|
||||||
|
JackpotPoolCallBack.DispatchCallback(jpData[0], jpData[2]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.UpdateJPPool, null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "maintain.info": {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
if (resp.Data) {
|
||||||
|
resp.Data[3] = NumberEx.plus(MainControl.Instance.NowTime, resp.Data[3]);
|
||||||
|
playerData.maintain = resp.Data;
|
||||||
|
CommonEventCallBack.DispatchCallback(CommonEventType.Maintenance, null);
|
||||||
|
} else {
|
||||||
|
playerData.maintain = undefined;
|
||||||
|
}
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "profile.info": {
|
||||||
|
profileInfo(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "resource.update": {
|
||||||
|
const resourceItems: IResourceItem[] = CSResource.GetResourceItemsFromServer(resp.Data);
|
||||||
|
UserData.DoResUpdate(resourceItems);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "resource.bankruptcy": {
|
||||||
|
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.Bankruptcy, null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "system.disconnect": {
|
||||||
|
this._disconnectErrorType = +resp.Data["c"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "txn.new": {
|
||||||
|
txnNew(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "txn.center": {
|
||||||
|
txnCenter(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "txn.trade": {
|
||||||
|
txnTrade(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "txn.user_add": {
|
||||||
|
txnUserAdd(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "vip.info": {
|
||||||
|
vipInfo(resp.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "vip.level": {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
playerData.vip.level = resp.Data;
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (resp.Method) {
|
||||||
|
case "chat.log": {
|
||||||
|
chatLog([null, null]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// =======================================================================================
|
||||||
|
/** SOCKET斷線 */
|
||||||
|
private _netDisconnected(): void {
|
||||||
|
console.debug("Disconnected Error Type : " + this._disconnectErrorType);
|
||||||
|
let str: string = null;
|
||||||
|
if (this._disconnectErrorType < 10 && this._disconnectErrorType >= 0) {
|
||||||
|
str = CSSettingsV3.prototype.CommonString(55 + this._disconnectErrorType);
|
||||||
|
} else if (this._disconnectErrorType > 100 && this._disconnectErrorType < 110) {
|
||||||
|
str = CSSettingsV3.prototype.CommonString(65 + this._disconnectErrorType - 101);
|
||||||
|
} else {
|
||||||
|
str = "Server Disconnected";
|
||||||
|
}
|
||||||
|
CSMessage.CreateYesMsg(
|
||||||
|
str,
|
||||||
|
this._disconnectedReload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _disconnectedReload(): void {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================================
|
||||||
|
}
|
29
src/Common/MainControl/MainControl.ts
Normal file
29
src/Common/MainControl/MainControl.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import BaseSingleton from "@/Engine/Utils/Singleton/BaseSingleton";
|
||||||
|
|
||||||
|
export class MainControl extends BaseSingleton<MainControl>() {
|
||||||
|
/** 每次啟動APP */
|
||||||
|
public IsFirstEnteringLobby: boolean = true;
|
||||||
|
// /** 登入成功判斷 */
|
||||||
|
// public IsLogin: boolean = false;
|
||||||
|
// /** 選桌頁內的機台號碼 */
|
||||||
|
// public TableID: number = 0;
|
||||||
|
/** 最後遊玩的一個廠商 */
|
||||||
|
public LastPlayComponyID: number = 0;
|
||||||
|
/** 最後遊玩的一個遊戲 */
|
||||||
|
public LastPlayGameID: number = 0;
|
||||||
|
// /** 主動斷線=字串.被動斷線=null(因為SERVER有時候會先主動斷線所以有的要預先設定字串)*/
|
||||||
|
// public IsLogoutStr: string = null;
|
||||||
|
public IsInGame: boolean = false;
|
||||||
|
/** 現在時間 */
|
||||||
|
public get NowTime(): number { return Date.now(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export module MainControl {
|
||||||
|
export enum DataType {
|
||||||
|
ServerData,
|
||||||
|
ChangeDire,
|
||||||
|
NetDisconnected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MainControl;
|
53
src/Common/Mask/CSMask.ts
Normal file
53
src/Common/Mask/CSMask.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import LoadMaskBase from "./Group/LoadMaskBase";
|
||||||
|
|
||||||
|
export class CSMask {
|
||||||
|
/** 遮罩Dic */
|
||||||
|
private static _dicMask: Map<CSMask.MaskType, LoadMaskBase> = new Map();
|
||||||
|
public static Initialize(masks: LoadMaskBase[]): void {
|
||||||
|
this._dicMask.clear();
|
||||||
|
for (let i = 0; i < masks.length; i++) {
|
||||||
|
const mask: LoadMaskBase = masks[i];
|
||||||
|
this._dicMask.set(mask.MaskType, mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetMask(type: CSMask.MaskType): LoadMaskBase {
|
||||||
|
if (this._dicMask.has(type)) {
|
||||||
|
return this._dicMask.get(type);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShowMask(type: CSMask.MaskType, ...param: any[]): void {
|
||||||
|
let mask = this.GetMask(type);
|
||||||
|
if (mask != null) {
|
||||||
|
mask.Show(...param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HideMask(type: CSMask.MaskType, ...param: any[]): void {
|
||||||
|
let mask = this.GetMask(type);
|
||||||
|
if (mask != null) {
|
||||||
|
mask.Hide(...param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HideAllMask(): void {
|
||||||
|
this._dicMask.forEach((k, type) => {
|
||||||
|
let mask: LoadMaskBase = this.GetMask(type);
|
||||||
|
if (mask != null) {
|
||||||
|
mask.Reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export module CSMask {
|
||||||
|
export enum MaskType {
|
||||||
|
/** Loading */
|
||||||
|
LoadingMask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default CSMask;
|
35
src/Common/Mask/Group/LoadMaskBase.ts
Normal file
35
src/Common/Mask/Group/LoadMaskBase.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { loadingModalObj } from "@/context/LoadingModalContext";
|
||||||
|
import CSMask from "../CSMask";
|
||||||
|
|
||||||
|
export default abstract class LoadMaskBase {
|
||||||
|
/** 一般遮罩記數 */
|
||||||
|
private _count: number = 0;
|
||||||
|
public get MaskType(): CSMask.MaskType { throw new Error(`請初始化MaskType`); }
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
this._count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Show(...param: any[]): void {
|
||||||
|
if (this._count == 0) {
|
||||||
|
const { handleOpen } = loadingModalObj;
|
||||||
|
handleOpen();
|
||||||
|
}
|
||||||
|
this._count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hide(...param: any[]): void {
|
||||||
|
this._count--;
|
||||||
|
if (this._count <= 0) {
|
||||||
|
const { handleClose } = loadingModalObj;
|
||||||
|
handleClose();
|
||||||
|
this._count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reset(): void {
|
||||||
|
this._count = 0;
|
||||||
|
const { handleClose } = loadingModalObj;
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
}
|
6
src/Common/Mask/Group/LoadingMask.ts
Normal file
6
src/Common/Mask/Group/LoadingMask.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import CSMask from "../CSMask";
|
||||||
|
import LoadMaskBase from "./LoadMaskBase";
|
||||||
|
|
||||||
|
export default class LoadingMask extends LoadMaskBase {
|
||||||
|
public get MaskType(): CSMask.MaskType { return CSMask.MaskType.LoadingMask; }
|
||||||
|
}
|
49
src/Common/Message/CSMessage.ts
Normal file
49
src/Common/Message/CSMessage.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import CSSettingsV3 from "@/FormTable/CSSettingsV3";
|
||||||
|
import { IConfirmMessageData } from "@/UI/MessageBox/ConfirmMessage";
|
||||||
|
import { confirmModalObj } from "@/UI/MessageBox/ConfirmModalContext";
|
||||||
|
import { StringFormKey } from "@/define/formkey";
|
||||||
|
|
||||||
|
|
||||||
|
/** 訊息框相關 */
|
||||||
|
export default class CSMessage {
|
||||||
|
public static Record: IConfirmMessageData[] = [];
|
||||||
|
|
||||||
|
/** 一個按鈕的訊息框 */
|
||||||
|
public static CreateYesMsg(content: string, yesCallback: () => void = null, enterStr: string = null, title: string = null, textAlign: "center" | "left" | "right" = null) {
|
||||||
|
enterStr = enterStr ? enterStr : CSSettingsV3.prototype.CommonString(StringFormKey.String.Confirm);
|
||||||
|
let data: IConfirmMessageData = {
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
isShowCancel: false,
|
||||||
|
handleConfirm: yesCallback,
|
||||||
|
enterStr: enterStr,
|
||||||
|
textAlign: textAlign
|
||||||
|
};
|
||||||
|
const { openOtherConfirm } = confirmModalObj;
|
||||||
|
openOtherConfirm(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 兩個按鈕的訊息框 */
|
||||||
|
public static CreateYesNoMsg(content: string, yesCallback: () => void = null, noCallback: () => void = null, enterStr: string = null, title: string = null, cancelStr: string = null, textAlign: "center" | "left" | "right" = null) {
|
||||||
|
enterStr = enterStr ? enterStr : CSSettingsV3.prototype.CommonString(StringFormKey.String.Confirm);
|
||||||
|
cancelStr = cancelStr ? cancelStr : CSSettingsV3.prototype.CommonString(StringFormKey.String.Cancel);
|
||||||
|
let data: IConfirmMessageData = {
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
isShowCancel: true,
|
||||||
|
handleConfirm: yesCallback,
|
||||||
|
handleCancel: noCallback,
|
||||||
|
enterStr: enterStr,
|
||||||
|
cancelStr: cancelStr,
|
||||||
|
textAlign: textAlign
|
||||||
|
};
|
||||||
|
const { openOtherConfirm } = confirmModalObj;
|
||||||
|
openOtherConfirm(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 網路錯誤訊息 */
|
||||||
|
public static NetError(method: string, state: number, str: string = ""): void {
|
||||||
|
let error = String.Format("[{0}] state:{1} {2}", method, state, str);
|
||||||
|
console.debug("網路錯誤訊息: ", error);
|
||||||
|
}
|
||||||
|
}
|
92
src/Common/ResourceItem/CSResource.ts
Normal file
92
src/Common/ResourceItem/CSResource.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { ResourceInfo } from "@/define/resource";
|
||||||
|
import { ActionWithType } from "../../Engine/CatanEngine/CSharp/System/ActionWithType";
|
||||||
|
import IResourceItem from "./IResourceItem";
|
||||||
|
import CardCouponItem from "./Items/CardCouponItem";
|
||||||
|
import LpPointItem from "./Items/LpPointItem";
|
||||||
|
import MoneyItem from "./Items/MoneyItem";
|
||||||
|
import { ResourceItemType } from "./ResourceItemType";
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateResourceEvent extends ActionWithType<ResourceItemType, any[]> {
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CSResource {
|
||||||
|
private static _event: UpdateResourceEvent;
|
||||||
|
|
||||||
|
/** 更新資源事件 */
|
||||||
|
public static get Event() {
|
||||||
|
return CSResource._event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Initialize(): void {
|
||||||
|
CSResource._event = new UpdateResourceEvent();
|
||||||
|
// Cocos.VicKing_Bridge.GetCSResourceEvent = () => { return CSResource._event; };
|
||||||
|
// Cocos.VicKing_Bridge.AddResourceItemFromServer = (data: JSON) => { return CSResource.AddResourceItemFromServer(data); };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增一筆資源(資料必須是從Server來的)
|
||||||
|
* @param data ex [1, 15] --> 新增元寶資源
|
||||||
|
*/
|
||||||
|
public static AddResourceItemFromServer(data: ResourceInfo | JSON): IResourceItem {
|
||||||
|
let item = this.GetResourceItemFromServer(data);
|
||||||
|
item.AddToResource();
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增多筆資源(資料必須是從Server來的)
|
||||||
|
* @param data ex: [[1, 15], [2, 30], [28, ["0000123", [124, 3, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0, 1, 1, 0.000, 0.00, 1, 0, 0, 0, 0, 0, 3]]]] --> 新增[元寶, 銀兩, 卡片]等資源
|
||||||
|
*/
|
||||||
|
public static AddResourceItemsFromServer(data: ResourceInfo[] | JSON): IResourceItem[] {
|
||||||
|
let itemList: IResourceItem[] = [];
|
||||||
|
for (let i = 0, count = Object.keys(data).length; i < count; i++) {
|
||||||
|
let itemData: JSON = data[i];
|
||||||
|
itemList.push(this.AddResourceItemFromServer(itemData));
|
||||||
|
}
|
||||||
|
return itemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 從Server資料還原對應的資源物件
|
||||||
|
* @param data ex: [1, 15] --> 元寶, [2, 30] --> 銀兩
|
||||||
|
*/
|
||||||
|
public static GetResourceItemFromServer(data: ResourceInfo | JSON): IResourceItem {
|
||||||
|
let type = data[0];
|
||||||
|
switch (type) {
|
||||||
|
case ResourceItemType.Money:
|
||||||
|
return new MoneyItem(data);
|
||||||
|
case ResourceItemType.Card_Coupon:
|
||||||
|
return new CardCouponItem(data);
|
||||||
|
case ResourceItemType.LpPoint:
|
||||||
|
return new LpPointItem(data);
|
||||||
|
default:
|
||||||
|
throw new Error("沒有對應[" + type + "]類型的Server資源類別");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 從Server資料還原對應的資源物件
|
||||||
|
* @param data ex: [[1, 15], [2, 30], [45, [1, 10], ...] --> [元寶, 銀兩, 節慶貨幣, ...]
|
||||||
|
*/
|
||||||
|
public static GetResourceItemsFromServer(data: JSON): IResourceItem[] {
|
||||||
|
let itemList: IResourceItem[] = [];
|
||||||
|
for (let i = 0, count = Object.keys(data).length; i < count; i++) {
|
||||||
|
let itemData: JSON = data[i];
|
||||||
|
itemList.push(this.GetResourceItemFromServer(itemData));
|
||||||
|
}
|
||||||
|
return itemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增多筆資源(直接拿資料本身)
|
||||||
|
* @param items
|
||||||
|
*/
|
||||||
|
public static AddResourceItems(items: IResourceItem[]): void {
|
||||||
|
for (let item of items) {
|
||||||
|
item.AddToResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
}
|
16
src/Common/ResourceItem/IResourceItem.ts
Normal file
16
src/Common/ResourceItem/IResourceItem.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { ResourceItemType } from "./ResourceItemType";
|
||||||
|
|
||||||
|
interface IResourceItem {
|
||||||
|
/** 取得資源名稱 */
|
||||||
|
readonly Name: string;
|
||||||
|
/** 取得資源類型 */
|
||||||
|
readonly ResourceType: ResourceItemType;
|
||||||
|
/** 資源編號 */
|
||||||
|
readonly ID: number;
|
||||||
|
/** 數量 */
|
||||||
|
Count: number;
|
||||||
|
|
||||||
|
/** 加入到玩家資源中 */
|
||||||
|
AddToResource(): void;
|
||||||
|
}
|
||||||
|
export default IResourceItem;
|
28
src/Common/ResourceItem/Items/CardCouponItem.ts
Normal file
28
src/Common/ResourceItem/Items/CardCouponItem.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import CSSettingsV3 from "@/FormTable/CSSettingsV3";
|
||||||
|
import { LanguageManager } from "@/FormTableExt/Manage/Language/LanguageManager";
|
||||||
|
import { ResourceInfo } from "@/define/resource";
|
||||||
|
import { backpackInfo } from "@/utils/setRPCData";
|
||||||
|
import CSResource from "../CSResource";
|
||||||
|
import IResourceItem from "../IResourceItem";
|
||||||
|
import { ResourceItemType } from "../ResourceItemType";
|
||||||
|
|
||||||
|
export default class CardCouponItem implements IResourceItem {
|
||||||
|
public get Name(): string { return CSSettingsV3.ItemSetting.StringDetail[+CSSettingsV3.ItemSetting.CouponSetting[this.ID].CardName][LanguageManager.GetMsgId()]; }
|
||||||
|
public get ResourceType(): ResourceItemType { return ResourceItemType.Card_Coupon; }
|
||||||
|
public ID: number;
|
||||||
|
public Count: number;
|
||||||
|
|
||||||
|
public AddToResource(): void {
|
||||||
|
backpackInfo([[this.ResourceType, [this.ID, this.Count]]], true);
|
||||||
|
CSResource.Event.DispatchCallback(ResourceItemType.Card_Coupon, [this.ResourceType, [this.ID, this.Count]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 從db來的格式建立可數資源物件 */
|
||||||
|
public constructor(db: ResourceInfo | JSON) {
|
||||||
|
if (!db[1]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.ID = db[1][0];
|
||||||
|
this.Count = <number>db[1][1];
|
||||||
|
}
|
||||||
|
}
|
10
src/Common/ResourceItem/Items/CollectTokenItem.ts.meta
Normal file
10
src/Common/ResourceItem/Items/CollectTokenItem.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "0cab7ce7-c8b2-4c65-a80b-502bb8eddc53",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
10
src/Common/ResourceItem/Items/LevelItem.ts.meta
Normal file
10
src/Common/ResourceItem/Items/LevelItem.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "5a6fedda-63d3-4fb7-b8f7-7ca1889cf7db",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
28
src/Common/ResourceItem/Items/LpPointItem.ts
Normal file
28
src/Common/ResourceItem/Items/LpPointItem.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import CSSettingsV3 from "@/FormTable/CSSettingsV3";
|
||||||
|
import { Shop } from "@/define/formkey";
|
||||||
|
import { ResourceInfo } from "@/define/resource";
|
||||||
|
import Player from "@/modules/player";
|
||||||
|
import { State } from "@/modules/player/define/data";
|
||||||
|
import { NumberEx } from "@/utils/Number/NumberEx";
|
||||||
|
import CSResource from "../CSResource";
|
||||||
|
import IResourceItem from "../IResourceItem";
|
||||||
|
import { ResourceItemType } from "../ResourceItemType";
|
||||||
|
|
||||||
|
export default class LpPointItem implements IResourceItem {
|
||||||
|
public get Name(): string { return CSSettingsV3.prototype.ShopString(Shop.String.LpPoint); }
|
||||||
|
public get ResourceType(): ResourceItemType { return ResourceItemType.LpPoint; }
|
||||||
|
public ID: number;
|
||||||
|
public Count: number;
|
||||||
|
|
||||||
|
public AddToResource(): void {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
playerData.account.lpPoint = NumberEx.plus(playerData.account.lpPoint, this.Count);
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
CSResource.Event.DispatchCallback(ResourceItemType.LpPoint, [playerData.account.lpPoint, this.Count]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 從db來的格式建立可數資源物件 */
|
||||||
|
public constructor(db: ResourceInfo | JSON) {
|
||||||
|
this.Count = <number>db[1];
|
||||||
|
}
|
||||||
|
}
|
10
src/Common/ResourceItem/Items/LpPointItem.ts.meta
Normal file
10
src/Common/ResourceItem/Items/LpPointItem.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "db699d15-9603-4466-8ffd-d34fde6abb5f",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
31
src/Common/ResourceItem/Items/MoneyItem.ts
Normal file
31
src/Common/ResourceItem/Items/MoneyItem.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import CSSettingsV3 from "@/FormTable/CSSettingsV3";
|
||||||
|
import Config from "@/define/Config";
|
||||||
|
import { Shop } from "@/define/formkey";
|
||||||
|
import { ResourceInfo } from "@/define/resource";
|
||||||
|
import Player from "@/modules/player";
|
||||||
|
import { NumberEx } from "@/utils/Number/NumberEx";
|
||||||
|
import CSResource from "../CSResource";
|
||||||
|
import IResourceItem from "../IResourceItem";
|
||||||
|
import { ResourceItemType } from "../ResourceItemType";
|
||||||
|
|
||||||
|
export default class MoneyItem implements IResourceItem {
|
||||||
|
public get Name(): string { return CSSettingsV3.prototype.ShopString(Shop.String.Money); }
|
||||||
|
public get ResourceType(): ResourceItemType { return ResourceItemType.Money; }
|
||||||
|
public ID: number;
|
||||||
|
public Count: number;
|
||||||
|
|
||||||
|
public AddToResource(): void {
|
||||||
|
const playerData = Player.data.getState();
|
||||||
|
playerData.account.money = NumberEx.plus(playerData.account.money, this.Count);
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
if (Config.ShowMoneyLog) {
|
||||||
|
console.debug(String.Format("[{0}], {1}, {2}", "Money_" + ResourceItemType.Money, playerData.account.money, this.Count));
|
||||||
|
}
|
||||||
|
CSResource.Event.DispatchCallback(ResourceItemType.Money, [playerData.account.money, this.Count]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 從db來的格式建立可數資源物件 */
|
||||||
|
public constructor(db: ResourceInfo | JSON) {
|
||||||
|
this.Count = <number>db[1];
|
||||||
|
}
|
||||||
|
}
|
10
src/Common/ResourceItem/Items/MoneyItem.ts.meta
Normal file
10
src/Common/ResourceItem/Items/MoneyItem.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "e03637c9-24be-42ff-960f-b3377e20efdc",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
12
src/Common/ResourceItem/ResourceItemType.ts
Normal file
12
src/Common/ResourceItem/ResourceItemType.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export enum ResourceItemType {
|
||||||
|
/** 錢 */
|
||||||
|
Money = 1,
|
||||||
|
/** 等級 */
|
||||||
|
Level = 2,
|
||||||
|
/** 卡片 */
|
||||||
|
Card = 5,
|
||||||
|
/** 卡片優惠券 */
|
||||||
|
Card_Coupon = 101,
|
||||||
|
/** 來亨卷 */
|
||||||
|
LpPoint = 102,
|
||||||
|
}
|
22
src/Common/System/SystemEventBase.ts
Normal file
22
src/Common/System/SystemEventBase.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import SystemEventManager from "./SystemEventManager";
|
||||||
|
|
||||||
|
/** 系統事件底層 */
|
||||||
|
export default class SystemEventBase {
|
||||||
|
constructor() {
|
||||||
|
SystemEventManager.AddSystem(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): string { return this.constructor.name; }
|
||||||
|
|
||||||
|
/** 首次進入大廳 */
|
||||||
|
public ImplementFirstEnteringLobby(): void { }
|
||||||
|
|
||||||
|
/** 進入大廳 */
|
||||||
|
public ImplementEnteringLobby(): void { }
|
||||||
|
|
||||||
|
/** 關閉商城頁 */
|
||||||
|
public ImplementCloseMall(): void { }
|
||||||
|
|
||||||
|
/** 離開機台 */
|
||||||
|
public ImplementLeaveSlot(slotID: number): void { }
|
||||||
|
}
|
34
src/Common/System/SystemEventManager.ts
Normal file
34
src/Common/System/SystemEventManager.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import SystemEventBase from "./SystemEventBase";
|
||||||
|
|
||||||
|
/** 系統事件管理 */
|
||||||
|
export default class SystemEventManager {
|
||||||
|
private static systems: Map<string, SystemEventBase> = new Map();
|
||||||
|
|
||||||
|
public static AddSystem(system: SystemEventBase): void {
|
||||||
|
this.systems.set(system.name, system);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DestroySystem(system: SystemEventBase): void {
|
||||||
|
this.systems.delete(system.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 首次進入大廳 */
|
||||||
|
public static FirstEnteringLobby(): void {
|
||||||
|
SystemEventManager.systems.forEach(system => system.ImplementFirstEnteringLobby());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 進入大廳 */
|
||||||
|
public static EnteringLobby(): void {
|
||||||
|
SystemEventManager.systems.forEach(system => system.ImplementEnteringLobby());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 關閉商城頁 */
|
||||||
|
public static CloseMall(): void {
|
||||||
|
SystemEventManager.systems.forEach(system => system.ImplementCloseMall());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 離開機台 */
|
||||||
|
public static LeaveSlot(slotID: number): void {
|
||||||
|
SystemEventManager.systems.forEach(system => system.ImplementLeaveSlot(slotID));
|
||||||
|
}
|
||||||
|
}
|
125
src/Engine/CatanEngine/CSharp/System/Action.ts
Normal file
125
src/Engine/CatanEngine/CSharp/System/Action.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TArg> {
|
||||||
|
(arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TArg> {
|
||||||
|
callback: ActionCallback<TArg>;
|
||||||
|
target: any;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Action<TArg> {
|
||||||
|
private _queue: Struct<TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件 (一次性)
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallbackOnce(callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TArg>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
src/Engine/CatanEngine/CSharp/System/ActionWithType.ts
Normal file
165
src/Engine/CatanEngine/CSharp/System/ActionWithType.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TArg> {
|
||||||
|
(arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TType, TArg> {
|
||||||
|
callback: ActionCallback<TArg>;
|
||||||
|
target: any;
|
||||||
|
type: TType;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActionWithType<TType, TArg> {
|
||||||
|
private _queue: Struct<TType, TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(type: TType, callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件 (一次性)
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallbackOnce(type: TType, callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TArg>) {
|
||||||
|
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<TArg>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
src/Engine/CatanEngine/CSharp/System/ActionWithType2.ts
Normal file
165
src/Engine/CatanEngine/CSharp/System/ActionWithType2.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (type: TType, arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TType, TArg> {
|
||||||
|
(type: TType, arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TType, TArg> {
|
||||||
|
callback: ActionCallback<TType, TArg>;
|
||||||
|
target: any;
|
||||||
|
type: TType;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActionWithType2<TType, TArg> {
|
||||||
|
private _queue: Struct<TType, TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (type: TType, arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(type: TType, callback: ActionCallback<TType, TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>>{
|
||||||
|
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<TType, TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TType, TArg>) {
|
||||||
|
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<TType, TArg>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
src/Engine/CatanEngine/CSharp/System/Text/Encoding.ts
Normal file
75
src/Engine/CatanEngine/CSharp/System/Text/Encoding.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
10
src/Engine/CatanEngine/CSharp/System/Text/Encoding.ts.meta
Normal file
10
src/Engine/CatanEngine/CSharp/System/Text/Encoding.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "43bf5724-e939-4189-b981-c32ef694e5a5",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
17
src/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts
Normal file
17
src/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts
Normal file
@ -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<any> {
|
||||||
|
if (this._action) {
|
||||||
|
this._action();
|
||||||
|
}
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
}
|
137
src/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts
Normal file
137
src/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { IEnumeratorV2, IEnumeratorV2Started } from "../IEnumeratorV2";
|
||||||
|
import { CoroutineExecutor } from "./CoroutineExecutor";
|
||||||
|
|
||||||
|
let EnumeratorExecutorClass: typeof import("./EnumeratorExecutor").EnumeratorExecutor = null;
|
||||||
|
let SingleEnumeratorClass: typeof import("./SingleEnumerator").SingleEnumerator = null;
|
||||||
|
let ParallelEnumeratorClass: typeof import("./ParallelEnumerator").ParallelEnumerator = null;
|
||||||
|
let WaitTimeEnumeratorClass: typeof import("./WaitTimeEnumerator").WaitTimeEnumerator = null;
|
||||||
|
let ActionEnumeratorClass: typeof import("./ActionEnumerator").ActionEnumerator = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用前初始場景第一個事件一定要先Init過
|
||||||
|
* @example
|
||||||
|
* 而且不能同時間有其他onLoad在跑,放start可以
|
||||||
|
* @example
|
||||||
|
* protected async onLoad(): Promise<void> {
|
||||||
|
* await BaseEnumerator.Init();
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export abstract class BaseEnumerator implements IEnumeratorV2 {
|
||||||
|
public nextEnumerator: BaseEnumerator;
|
||||||
|
|
||||||
|
abstract next(value?: any): IteratorResult<any>;
|
||||||
|
|
||||||
|
public static isInit: boolean = false;
|
||||||
|
|
||||||
|
public static async Init(): Promise<any> {
|
||||||
|
await Promise.all([
|
||||||
|
(async () => {
|
||||||
|
EnumeratorExecutorClass = (await import("./EnumeratorExecutor")).EnumeratorExecutor;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
SingleEnumeratorClass = (await import("./SingleEnumerator")).SingleEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
ParallelEnumeratorClass = (await import("./ParallelEnumerator")).ParallelEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
WaitTimeEnumeratorClass = (await import("./WaitTimeEnumerator")).WaitTimeEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
ActionEnumeratorClass = (await import("./ActionEnumerator")).ActionEnumerator;
|
||||||
|
})(),
|
||||||
|
]);
|
||||||
|
BaseEnumerator.isInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Start(target?: any): IEnumeratorV2Started {
|
||||||
|
let executor = LazyLoad.EnumeratorExecutor(this, target);
|
||||||
|
CoroutineExecutor.instance.StartCoroutine(executor);
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
Then(iterator: Iterator<any>): 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<any>[]): 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<any>[]): 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) {
|
||||||
|
let newclass = new EnumeratorExecutorClass(enumerator, target);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./EnumeratorExecutor") as typeof import("./EnumeratorExecutor")).EnumeratorExecutor(enumerator, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SingleEnumerator(iterator: Iterator<any>) {
|
||||||
|
let newclass: any = new SingleEnumeratorClass(iterator);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./SingleEnumerator") as typeof import("./SingleEnumerator")).SingleEnumerator(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ParallelEnumerator(...iterators: Iterator<any>[]) {
|
||||||
|
let newclass: any = new ParallelEnumeratorClass(iterators);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./ParallelEnumerator") as typeof import("./ParallelEnumerator")).ParallelEnumerator(iterators);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WaitTimeEnumerator(seconds: number) {
|
||||||
|
let newclass: any = new WaitTimeEnumeratorClass(seconds);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./WaitTimeEnumerator") as typeof import("./WaitTimeEnumerator")).WaitTimeEnumerator(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ActionEnumerator(action: Function) {
|
||||||
|
let newclass: any = new ActionEnumeratorClass(action);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./ActionEnumerator") as typeof import("./ActionEnumerator")).ActionEnumerator(action);
|
||||||
|
}
|
||||||
|
}
|
103
src/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts
Normal file
103
src/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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: NodeJS.Timeout = null;
|
||||||
|
private _time: number = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._time = new Date().getTime();
|
||||||
|
console.debug("[CoroutineV2] Coroutines Start");
|
||||||
|
this._scheduler = setInterval(this.update.bind(this), 1 / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
StartCoroutine(executor: EnumeratorExecutor) {
|
||||||
|
executor.next(0);
|
||||||
|
// TODO: 這邊要考量next後馬上接BaseEnumerator/Iterator的情形
|
||||||
|
|
||||||
|
if (!this._isRunning) {
|
||||||
|
this._executors.push(executor);
|
||||||
|
|
||||||
|
if (!this._scheduler) {
|
||||||
|
console.debug("[CoroutineV2] Coroutines Start");
|
||||||
|
this._time = new Date().getTime();
|
||||||
|
this._scheduler = setInterval(this.update.bind(this), 1 / 60);
|
||||||
|
} else {
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines add now: ${this._executors.length}`);
|
||||||
|
}
|
||||||
|
} 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() {
|
||||||
|
const time: number = new Date().getTime();
|
||||||
|
const delta: number = (time - this._time) / 1000;
|
||||||
|
this._time = time;
|
||||||
|
if (this._nextExecutors.length) {
|
||||||
|
this._executors.push(...this._nextExecutors);
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines addNext now: ${this._executors.length}, next: ${this._nextExecutors.length}`);
|
||||||
|
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);
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines sub now: ${this._executors.length}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._cleanRemoved = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._executors.length == 0) {
|
||||||
|
console.debug("[CoroutineV2] All Coroutines Done");
|
||||||
|
clearInterval(this._scheduler);
|
||||||
|
this._scheduler = null;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
167
src/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts
Normal file
167
src/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
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<any> {
|
||||||
|
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<any>;
|
||||||
|
|
||||||
|
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(<Iterator<any>>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(<Iterator<any>>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;
|
||||||
|
(<Promise<any>>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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<any>[]) {
|
||||||
|
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<any> {
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
}
|
18
src/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts
Normal file
18
src/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
|
||||||
|
export class SingleEnumerator extends BaseEnumerator {
|
||||||
|
private _iterator: Iterator<any>;
|
||||||
|
|
||||||
|
constructor(iterator: Iterator<any>) {
|
||||||
|
super();
|
||||||
|
this._iterator = iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
if (!this._iterator) {
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._iterator.next(value);
|
||||||
|
}
|
||||||
|
}
|
@ -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<any> {
|
||||||
|
let delta = value as number;
|
||||||
|
this._seconds -= delta;
|
||||||
|
|
||||||
|
if (this._seconds <= 0) {
|
||||||
|
return { done: true, value: 0 };
|
||||||
|
} else {
|
||||||
|
return { done: false, value: this._seconds };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
133
src/Engine/CatanEngine/CoroutineV2/CoroutineExample.ts
Normal file
133
src/Engine/CatanEngine/CoroutineV2/CoroutineExample.ts
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import { CoroutineV2 } from "./CoroutineV2";
|
||||||
|
|
||||||
|
export default class CoroutineExample {
|
||||||
|
private _obj: Object = { "a": true };
|
||||||
|
private _obj2: Object = { "b": true };
|
||||||
|
|
||||||
|
private _num: number = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
CoroutineV2.Single(this.Test1_1()).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
*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;
|
||||||
|
}
|
||||||
|
|
||||||
|
*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),
|
||||||
|
)
|
||||||
|
.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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
75
src/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts
Normal file
75
src/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { ActionEnumerator } from "./Core/ActionEnumerator";
|
||||||
|
import { BaseEnumerator } from "./Core/BaseEnumerator";
|
||||||
|
import { CoroutineExecutor } from "./Core/CoroutineExecutor";
|
||||||
|
import { ParallelEnumerator } from "./Core/ParallelEnumerator";
|
||||||
|
import { SingleEnumerator } from "./Core/SingleEnumerator";
|
||||||
|
import { WaitTimeEnumerator } from "./Core/WaitTimeEnumerator";
|
||||||
|
import { IEnumeratorV2, IEnumeratorV2Started } from "./IEnumeratorV2";
|
||||||
|
|
||||||
|
export module CoroutineV2 {
|
||||||
|
/**
|
||||||
|
* 啟動一般協程
|
||||||
|
*/
|
||||||
|
export function StartCoroutine(iterator: Iterator<any>, 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<any>): IEnumeratorV2 {
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
return iterator;
|
||||||
|
} else {
|
||||||
|
return new SingleEnumerator(iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平行協程
|
||||||
|
*/
|
||||||
|
export function Parallel(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
return new ParallelEnumerator(iterators);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列協程
|
||||||
|
*/
|
||||||
|
export function Serial(...iterators: Iterator<any>[]): 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);
|
||||||
|
}
|
||||||
|
}
|
23
src/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts
Normal file
23
src/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export interface IEnumeratorV2 extends Iterator<any> {
|
||||||
|
Start(target?: any): IEnumeratorV2Started;
|
||||||
|
|
||||||
|
Then(iterator: Iterator<any>): IEnumeratorV2;
|
||||||
|
|
||||||
|
ThenSerial(...iterators: Iterator<any>[]): IEnumeratorV2;
|
||||||
|
|
||||||
|
ThenParallel(...iterators: Iterator<any>[]): IEnumeratorV2;
|
||||||
|
|
||||||
|
ThenAction(action: Function, delaySeconds?: number): IEnumeratorV2;
|
||||||
|
|
||||||
|
ThenWaitTime(seconds: number): IEnumeratorV2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEnumeratorV2Started {
|
||||||
|
readonly Current: any;
|
||||||
|
|
||||||
|
Pause(): void;
|
||||||
|
|
||||||
|
Resume(): void;
|
||||||
|
|
||||||
|
Stop(): void;
|
||||||
|
}
|
15
src/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts
Normal file
15
src/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Action } from "../../CSharp/System/Action";
|
||||||
|
import { INetRequest } from "./INetRequest";
|
||||||
|
import { INetResponse } from "./INetResponse";
|
||||||
|
|
||||||
|
export interface INetConnector {
|
||||||
|
readonly OnDataReceived: Action<INetResponse<any>>;
|
||||||
|
readonly OnDisconnected: Action<void>;
|
||||||
|
readonly IsConnected: boolean;
|
||||||
|
|
||||||
|
SendAsync<TRequest, TResponse>(req: INetRequest<TRequest, TResponse>): Iterator<any>;
|
||||||
|
|
||||||
|
Send<TRequest, TResponse>(req: INetRequest<TRequest, TResponse>);
|
||||||
|
|
||||||
|
Logout();
|
||||||
|
}
|
13
src/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts
Normal file
13
src/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { INetResponse } from "./INetResponse";
|
||||||
|
|
||||||
|
export interface INetRequest<TRequest, TResponse> {
|
||||||
|
readonly Method: string;
|
||||||
|
readonly MethodBack: string;
|
||||||
|
|
||||||
|
Data: TRequest;
|
||||||
|
Result: INetResponse<TResponse>;
|
||||||
|
|
||||||
|
SendAsync(): Iterator<any>;
|
||||||
|
|
||||||
|
Send();
|
||||||
|
}
|
6
src/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts
Normal file
6
src/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export interface INetResponse<TResponse> {
|
||||||
|
readonly Method: string;
|
||||||
|
readonly Status: number;
|
||||||
|
readonly Data: TResponse;
|
||||||
|
readonly IsValid: boolean;
|
||||||
|
}
|
4
src/Engine/CatanEngine/NetManagerV2/NetConfig.ts
Normal file
4
src/Engine/CatanEngine/NetManagerV2/NetConfig.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default class NetConfig {
|
||||||
|
/** 是否顯示RPC接送JSON的LOG */
|
||||||
|
public static ShowServerLog: boolean = true;
|
||||||
|
}
|
279
src/Engine/CatanEngine/NetManagerV2/NetConnector.ts
Normal file
279
src/Engine/CatanEngine/NetManagerV2/NetConnector.ts
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
import Event from "@/modules/event";
|
||||||
|
import { Action } from "../CSharp/System/Action";
|
||||||
|
import { Encoding } from "../CSharp/System/Text/Encoding";
|
||||||
|
import { BaseEnumerator } from "../CoroutineV2/Core/BaseEnumerator";
|
||||||
|
import { INetRequest } from "./Core/INetRequest";
|
||||||
|
import { INetResponse } from "./Core/INetResponse";
|
||||||
|
import NetConfig from "./NetConfig";
|
||||||
|
|
||||||
|
export namespace Socket {
|
||||||
|
export const Connect = Symbol("socket.connect");
|
||||||
|
export const Message = Symbol("socket.message");
|
||||||
|
export const Disconnect = Symbol("socket.disconnect");
|
||||||
|
export const Error = Symbol("socket.error");
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Func {
|
||||||
|
[Socket.Connect]: () => void,
|
||||||
|
[Socket.Message]: (e) => void,
|
||||||
|
[Socket.Disconnect]: () => void,
|
||||||
|
[Socket.Error]: () => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NetConnector {
|
||||||
|
/** Event */
|
||||||
|
public readonly event: Event<Func> = new Event<Func>();
|
||||||
|
readonly OnDataReceived: Action<INetResponse<any>> = new Action<INetResponse<any>>();
|
||||||
|
readonly OnDisconnected: Action<void> = new Action<void>();
|
||||||
|
readonly OnLoadUIMask: Action<boolean> = new Action<boolean>();
|
||||||
|
|
||||||
|
get IsConnected() {
|
||||||
|
return this._ws && this._ws.readyState === WebSocket.OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get ws(): WebSocket {
|
||||||
|
return this._ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _host: string;
|
||||||
|
private _ws: WebSocket;
|
||||||
|
private _waitings: WsRequestEnumerator[] = [];
|
||||||
|
|
||||||
|
constructor(host: string, port: number) {
|
||||||
|
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 (CC_DEBUG) {
|
||||||
|
console.debug("[事件]checkHttp=", checkHttp, host, port);
|
||||||
|
// }
|
||||||
|
if (checkHttp != "https") {
|
||||||
|
this._host = `ws://${host}:${port}`;
|
||||||
|
} else {
|
||||||
|
this._host = `wss://${host}:${port}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.onclose = this.OnWebSocketClose.bind(this);
|
||||||
|
this._ws.onerror = this.OnWebSocketError.bind(this);
|
||||||
|
|
||||||
|
return new WsConnectEnumerator(this._ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
Send(req: INetRequest<any, any>) {
|
||||||
|
if (!this.IsConnected) return;
|
||||||
|
|
||||||
|
let json = [req.Method];
|
||||||
|
if (req.Data != null && req.Data != undefined && !Number.isNaN(req.Data)) {
|
||||||
|
json[1] = req.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (CC_DEBUG && NetConfig.ShowServerLog) {
|
||||||
|
if (NetConfig.ShowServerLog) {
|
||||||
|
if (req.Data != null && req.Data != undefined && !Number.isNaN(req.Data)) {
|
||||||
|
console.debug(`[RPC] 傳送server資料: ${req.Method}(${JSON.stringify(req.Data)})`);
|
||||||
|
} else {
|
||||||
|
console.debug(`[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);
|
||||||
|
|
||||||
|
this._ws.send(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
SendAsync(req: INetRequest<any, any>, 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);
|
||||||
|
}
|
||||||
|
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 (CC_DEBUG) {
|
||||||
|
console.debug(`[RPC] ${this._host} Connected.`);
|
||||||
|
// }
|
||||||
|
this.event.emit(Socket.Connect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnWebSocketMessage(e: MessageEvent) {
|
||||||
|
if (e.data instanceof ArrayBuffer) {
|
||||||
|
this.ParseRpcMessage(e.data, e);
|
||||||
|
} else if (e.data instanceof Blob) {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
this.ParseRpcMessage(<ArrayBuffer>reader.result, e);
|
||||||
|
reader.onload = null;
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(e.data);
|
||||||
|
} else {
|
||||||
|
throw new Error(`未知的OnWebSocketMessage(e.data)類型: ${e.data}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParseRpcMessage(buffer: ArrayBuffer, e: any) {
|
||||||
|
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 = <string>json[0];
|
||||||
|
let status = <number>json[1][0];
|
||||||
|
let data = json[1][1];
|
||||||
|
|
||||||
|
let resp = <INetResponse<any>>{
|
||||||
|
Method: method,
|
||||||
|
Status: status,
|
||||||
|
Data: data,
|
||||||
|
IsValid: method && status === 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// if (CC_DEBUG && NetConfig.ShowServerLog) {
|
||||||
|
if (NetConfig.ShowServerLog) {
|
||||||
|
if (data) {
|
||||||
|
console.debug(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`);
|
||||||
|
} else {
|
||||||
|
console.debug(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}()`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dispatch = true;
|
||||||
|
let isCocos = false;
|
||||||
|
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 OnWebSocketClose(e: CloseEvent) {
|
||||||
|
this.WebSocketEnded();
|
||||||
|
this.event.emit(Socket.Disconnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnWebSocketError(e: CloseEvent) {
|
||||||
|
this.event.emit(Socket.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorResponse: INetResponse<any> = {
|
||||||
|
Status: -1,
|
||||||
|
Method: "",
|
||||||
|
Data: {},
|
||||||
|
IsValid: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WsConnectEnumerator extends BaseEnumerator {
|
||||||
|
private _ws: WebSocket;
|
||||||
|
|
||||||
|
constructor(ws: WebSocket) {
|
||||||
|
super();
|
||||||
|
this._ws = ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
return {
|
||||||
|
done: this._ws.readyState === WebSocket.OPEN || this._ws.readyState === WebSocket.CLOSED,
|
||||||
|
value: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WsRequestEnumerator extends BaseEnumerator {
|
||||||
|
readonly MethodBack: string;
|
||||||
|
|
||||||
|
private _req: INetRequest<any, any>;
|
||||||
|
private _done: boolean = false;
|
||||||
|
|
||||||
|
constructor(req: INetRequest<any, any>) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._req = req;
|
||||||
|
this.MethodBack = req.MethodBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetResponse(resp: INetResponse<any>) {
|
||||||
|
this._req.Result = resp;
|
||||||
|
this._done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
return {
|
||||||
|
done: this._done,
|
||||||
|
value: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
54
src/Engine/CatanEngine/NetManagerV2/NetManager.ts
Normal file
54
src/Engine/CatanEngine/NetManagerV2/NetManager.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
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 ConnectAsync() {
|
||||||
|
this.CheckConnector();
|
||||||
|
return this._connector.ConnectAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 斷線
|
||||||
|
*/
|
||||||
|
static Disconnect() {
|
||||||
|
this.CheckConnector();
|
||||||
|
this._connector.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 傳送資料給Server, 不等待回應
|
||||||
|
* @param req
|
||||||
|
*/
|
||||||
|
static Send(req: INetRequest<any, any>) {
|
||||||
|
this.CheckConnector();
|
||||||
|
this._connector.Send(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 傳送資料給Server, 並等待回應
|
||||||
|
* @param req
|
||||||
|
*/
|
||||||
|
static SendAsync(req: INetRequest<any, any>, mask: boolean) {
|
||||||
|
this.CheckConnector();
|
||||||
|
return this._connector.SendAsync(req, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CheckConnector() {
|
||||||
|
if (!this._connector) throw new Error("請先呼叫CasinoNetManager.Initialize()初始化connector");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
src/Engine/CatanEngine/NetManagerV2/NetRequest.ts
Normal file
28
src/Engine/CatanEngine/NetManagerV2/NetRequest.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { VueNetConnector } from "@/assets/VueScript/Net/VueNetConnector";
|
||||||
|
import { INetRequest } from "./Core/INetRequest";
|
||||||
|
|
||||||
|
export abstract class NetRequest<TResquest, TResponse> implements INetRequest<TResquest, TResponse> {
|
||||||
|
abstract get Method(): string;
|
||||||
|
|
||||||
|
get MethodBack(): string {
|
||||||
|
return this.Method;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data: TResquest;
|
||||||
|
Result: import("./Core/INetResponse").INetResponse<TResponse>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在大廳呼叫Cocos會收到SERVER主動通知
|
||||||
|
* 在大廳呼叫Cocos會收到SERVER主動通知
|
||||||
|
* 在大廳呼叫Cocos會收到SERVER主動通知
|
||||||
|
*/
|
||||||
|
SendAsync(mask: boolean = false): Iterator<any> {
|
||||||
|
// return NetManager.SendAsync(this, mask);
|
||||||
|
return VueNetConnector.SendAsync(this, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
Send() {
|
||||||
|
// NetManager.Send(this);
|
||||||
|
VueNetConnector.Send(this);
|
||||||
|
}
|
||||||
|
}
|
4
src/Engine/CatanEngine/TableV3/Core/ITableJson.ts
Normal file
4
src/Engine/CatanEngine/TableV3/Core/ITableJson.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface ITableJson {
|
||||||
|
cols: string[],
|
||||||
|
rows: any[],
|
||||||
|
}
|
10
src/Engine/CatanEngine/TableV3/Core/ITableRow.ts
Normal file
10
src/Engine/CatanEngine/TableV3/Core/ITableRow.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface ITableRow {
|
||||||
|
Id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表沒有欄位
|
||||||
|
*/
|
||||||
|
export class WithoutRow implements ITableRow {
|
||||||
|
Id: number;
|
||||||
|
}
|
30
src/Engine/CatanEngine/TableV3/Core/TableBase.ts
Normal file
30
src/Engine/CatanEngine/TableV3/Core/TableBase.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { ITableRow } from "./ITableRow";
|
||||||
|
|
||||||
|
export abstract class TableBase<TRow extends ITableRow> extends Array<TRow> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
Object.setPrototypeOf(this, new.target.prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 欄位數量 */
|
||||||
|
public get Count(): number {
|
||||||
|
return this.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取得全部鍵值 */
|
||||||
|
public get Keys(): string[] {
|
||||||
|
return Object.keys(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取得全部欄位值 */
|
||||||
|
public get Rows(): Array<TRow> {
|
||||||
|
return Object["values"](this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public get Rows(): Array<TRow> { return this; }
|
||||||
|
|
||||||
|
/** 是否包含該Id值的欄位 */
|
||||||
|
public ContainsRow(id: number): boolean {
|
||||||
|
return id in this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { TableManager } from "../TableManager";
|
||||||
|
import { StringExampleTableRow, StringTableExample } from "./Tables/StringTableExample";
|
||||||
|
|
||||||
|
export default class CSSettingsV3Example {
|
||||||
|
|
||||||
|
private static _stringExample: StringTableExample;
|
||||||
|
|
||||||
|
/** 共用_字串表#string.xlsx */
|
||||||
|
public static get StringExample(): StringTableExample {
|
||||||
|
return this._stringExample = this._stringExample || TableManager.InitTable("#string", StringTableExample, StringExampleTableRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
78
src/Engine/CatanEngine/TableV3/Examples/TableUseExample.ts
Normal file
78
src/Engine/CatanEngine/TableV3/Examples/TableUseExample.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import CSSettingsV3Example from "./CSSettingsV3Example";
|
||||||
|
import { StringExampleTable } from "./Tables/StringTableExample";
|
||||||
|
|
||||||
|
export default class TableUseExample {
|
||||||
|
|
||||||
|
start() {
|
||||||
|
|
||||||
|
// #region StringExample表
|
||||||
|
console.log("----------------#stringExample");
|
||||||
|
console.log(CSSettingsV3Example.StringExample instanceof StringExampleTable); // true
|
||||||
|
console.log(Array.isArray(CSSettingsV3Example.StringExample)); // true, 所以Array相關的方法都可以拿來操作
|
||||||
|
|
||||||
|
console.log(CSSettingsV3Example.StringExample.length);
|
||||||
|
console.log(CSSettingsV3Example.StringExample.Count); // 跟length一樣
|
||||||
|
|
||||||
|
console.log(CSSettingsV3Example.StringExample.ContainsRow(11)); // 是否包含id=11的Row
|
||||||
|
console.log(11 in CSSettingsV3Example.StringExample); // 同上
|
||||||
|
|
||||||
|
console.log(CSSettingsV3Example.StringExample[1].MsgZnCh);
|
||||||
|
console.log(CSSettingsV3Example.StringExample[1]["MsgZnCh"]); // 同上
|
||||||
|
console.log(CSSettingsV3Example["StringExample"][1]["MsgZnCh"]); // 同上
|
||||||
|
|
||||||
|
console.log("----------------");
|
||||||
|
for (let row of CSSettingsV3Example.StringExample) {
|
||||||
|
if (row) { // 如果Row沒有連號, 那有可能取到undefined值, 要先判斷, 不想判斷就用 CSSettings.StringExample.Rows
|
||||||
|
console.log(row.Id, row.MsgZnCh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("----------------");
|
||||||
|
for (let id of CSSettingsV3Example.StringExample.Keys) {
|
||||||
|
console.log(id); // 只會列出有值的id, undefined會跳過
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("----------------");
|
||||||
|
for (let row of CSSettingsV3Example.StringExample.Rows) {
|
||||||
|
console.log(row.Id, row.MsgZnCh); // 只會列出有值的Row, undefined會跳過
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region StringExample表 #StringFilter表
|
||||||
|
console.log("----------------#stringExample#string_filter");
|
||||||
|
// console.log(CSSettings.StringExample.StringFilter instanceof StringFilterTable); // true
|
||||||
|
console.log(Array.isArray(CSSettingsV3Example.StringExample.StringFilter)); // true, 所以Array相關的方法都可以拿來操作
|
||||||
|
|
||||||
|
console.log(CSSettingsV3Example.StringExample.StringFilter.length);
|
||||||
|
console.log(CSSettingsV3Example.StringExample.StringFilter.Count); // 跟length一樣
|
||||||
|
|
||||||
|
console.log(CSSettingsV3Example.StringExample.StringFilter.ContainsRow(11)); // 是否包含id=11的Row
|
||||||
|
console.log(11 in CSSettingsV3Example.StringExample.StringFilter); // 同上
|
||||||
|
|
||||||
|
console.log(CSSettingsV3Example.StringExample.StringFilter[1].FilterWord);
|
||||||
|
console.log(CSSettingsV3Example.StringExample.StringFilter[1]["FilterWord"]); // 同上
|
||||||
|
console.log(CSSettingsV3Example["StringExample"]["StringFilter"][1]["FilterWord"]); // 同上
|
||||||
|
|
||||||
|
console.log("----------------");
|
||||||
|
for (let row of CSSettingsV3Example.StringExample.StringFilter) {
|
||||||
|
if (row) { // 如果Row沒有連號, 那有可能取到undefined值, 要先判斷, 不想判斷就用 CSSettings.StringExample.StringFilter.Rows
|
||||||
|
console.log(row.Id, row.FilterWord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("----------------");
|
||||||
|
for (let id of CSSettingsV3Example.StringExample.StringFilter.Keys) {
|
||||||
|
console.log(id); // 只會列出有值的id, undefined會跳過
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("----------------");
|
||||||
|
for (let row of CSSettingsV3Example.StringExample.StringFilter.Rows) {
|
||||||
|
console.log(row.Id, row.FilterWord); // 只會列出有值的Row, undefined會跳過
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
console.log("----------------");
|
||||||
|
// CSSettingsV3.ResetTables(); // 重置表
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import { ITableRow } from "../../Core/ITableRow";
|
||||||
|
import { TableBase } from "../../Core/TableBase";
|
||||||
|
import { TableManager } from "../../TableManager";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 共用_字串表#string.xlsx
|
||||||
|
* ##程式碼由工具產生, 在此做的修改都將被覆蓋##
|
||||||
|
*/
|
||||||
|
export class StringTableExample extends TableBase<StringExampleTableRow> {
|
||||||
|
private _stringFilter: StringFilterTable;
|
||||||
|
|
||||||
|
/** 共用_字串表#string.xlsx > #string_filter */
|
||||||
|
public get StringFilter(): StringFilterTable {
|
||||||
|
return this._stringFilter = this._stringFilter || TableManager.InitTable("#string#string_filter", StringFilterTable, StringFilterTableRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #string
|
||||||
|
*/
|
||||||
|
export class StringExampleTable extends TableBase<StringExampleTableRow> {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StringExampleTableRow implements ITableRow {
|
||||||
|
/** 編號 */
|
||||||
|
Id: number;
|
||||||
|
/** 英文訊息 */
|
||||||
|
MsgEn: string;
|
||||||
|
/** 繁體中文訊息 */
|
||||||
|
MsgZnTw: string;
|
||||||
|
/** 簡體中文讯息 */
|
||||||
|
MsgZnCh: string;
|
||||||
|
/** 越南文讯息 */
|
||||||
|
MsgVi: string;
|
||||||
|
/** 泰文讯息 */
|
||||||
|
MsgTh: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #string_filter
|
||||||
|
*/
|
||||||
|
export class StringFilterTable extends TableBase<StringFilterTableRow> {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StringFilterTableRow implements ITableRow {
|
||||||
|
/** 編號 */
|
||||||
|
Id: number;
|
||||||
|
/** 過濾字串 */
|
||||||
|
FilterWord: string;
|
||||||
|
}
|
53
src/Engine/CatanEngine/TableV3/TableManager.ts
Normal file
53
src/Engine/CatanEngine/TableV3/TableManager.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { ITableJson } from "./Core/ITableJson";
|
||||||
|
import { ITableRow } from "./Core/ITableRow";
|
||||||
|
|
||||||
|
export class TableManager {
|
||||||
|
private static _tableJsons: { [key: string]: ITableJson } = {};
|
||||||
|
|
||||||
|
public static AddJsonAssets(jsonAssets: JSON[]) {
|
||||||
|
if (!jsonAssets) return;
|
||||||
|
const newAssets: JSON[] = jsonAssets.concat();
|
||||||
|
for (const jsonAsset of newAssets) {
|
||||||
|
this.AddJsonAsset(jsonAsset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AddJsonAsset(jsonAsset: JSON) {
|
||||||
|
if (!jsonAsset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const tableName in jsonAsset) {
|
||||||
|
console.debug(`TableV3 [${ tableName }] json loaded`);
|
||||||
|
this._tableJsons[tableName] = jsonAsset[tableName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetTable(name: string): ITableJson {
|
||||||
|
return this._tableJsons[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InitTable<T extends Array<ITableRow>>(
|
||||||
|
name: string,
|
||||||
|
tableType: { new(): T },
|
||||||
|
rowType: { new(): ITableRow },
|
||||||
|
): T {
|
||||||
|
const json = this._tableJsons[name];
|
||||||
|
if (!json) {
|
||||||
|
return null;
|
||||||
|
// throw new Error(`TableV3 [${name}] 尚未載入json檔`);
|
||||||
|
}
|
||||||
|
const table = new tableType();
|
||||||
|
const cols = json.cols;
|
||||||
|
const colLength = cols.length;
|
||||||
|
const rows = json.rows;
|
||||||
|
for (const r of rows) {
|
||||||
|
const trow = new rowType();
|
||||||
|
for (let i = 0; i < colLength; i++) {
|
||||||
|
trow[cols[i]] = r[i];
|
||||||
|
}
|
||||||
|
table[trow.Id] = trow;
|
||||||
|
}
|
||||||
|
// console.log(`TableV3 [${name}] init done`);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
}
|
57
src/Engine/Data/LocalStorageData.ts
Normal file
57
src/Engine/Data/LocalStorageData.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* 本機系統記錄(切換帳號也不可刪除EX記錄音效開關)
|
||||||
|
*/
|
||||||
|
export default class LocalStorageData {
|
||||||
|
private static _instance: LocalStorageData = null;
|
||||||
|
public static get Instance(): LocalStorageData {
|
||||||
|
return LocalStorageData._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
LocalStorageData._instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================================
|
||||||
|
//
|
||||||
|
public get CompileVersion(): string { return cc.sys.localStorage.getItem("CompileVersion"); }
|
||||||
|
public set CompileVersion(value: string) { cc.sys.localStorage.setItem("CompileVersion", value.toString()); }
|
||||||
|
//
|
||||||
|
public get RemoteVerList(): string { return cc.sys.localStorage.getItem("RemoteVerList"); }
|
||||||
|
public set RemoteVerList(value: string) { cc.sys.localStorage.setItem("RemoteVerList", value); }
|
||||||
|
//
|
||||||
|
public get LocalVerList(): string { return cc.sys.localStorage.getItem("LocalVerList"); }
|
||||||
|
public set LocalVerList(value: string) { cc.sys.localStorage.setItem("LocalVerList", value); }
|
||||||
|
//
|
||||||
|
public get ComboDeviceID(): string { return cc.sys.localStorage.getItem("ComboDeviceID") || ""; }
|
||||||
|
public set ComboDeviceID(value: string) { cc.sys.localStorage.setItem("ComboDeviceID", value); }
|
||||||
|
//
|
||||||
|
public get BundleUrl(): string { return cc.sys.localStorage.getItem("BundleUrl"); }
|
||||||
|
public set BundleUrl(value: string) { cc.sys.localStorage.setItem("BundleUrl", value); }
|
||||||
|
//
|
||||||
|
public get Language(): string { return cc.sys.localStorage.getItem("language"); }
|
||||||
|
public set Language(value: string) { cc.sys.localStorage.setItem("language", value); }
|
||||||
|
//
|
||||||
|
public get MusicType(): string { return cc.sys.localStorage.getItem("MusicType"); }
|
||||||
|
public set MusicType(value: string) { cc.sys.localStorage.setItem("MusicType", value); }
|
||||||
|
//
|
||||||
|
public get SoundType(): string { return cc.sys.localStorage.getItem("SoundType"); }
|
||||||
|
public set SoundType(value: string) { cc.sys.localStorage.setItem("SoundType", value); }
|
||||||
|
//
|
||||||
|
public get LvUpNotifyType(): boolean { return JSON.parse(cc.sys.localStorage.getItem("LvUpNotifyType")); }
|
||||||
|
public set LvUpNotifyType(value: boolean) { cc.sys.localStorage.setItem("LvUpNotifyType", JSON.stringify(value)); }
|
||||||
|
//
|
||||||
|
public get WinNotifyType(): boolean { return JSON.parse(cc.sys.localStorage.getItem("WinNotifyType")); }
|
||||||
|
public set WinNotifyType(value: boolean) { cc.sys.localStorage.setItem("WinNotifyType", JSON.stringify(value)); }
|
||||||
|
//
|
||||||
|
public get DownloadList_Preview(): string { return cc.sys.localStorage.getItem("DownloadList_Preview"); }
|
||||||
|
public set DownloadList_Preview(value: string) { cc.sys.localStorage.setItem("DownloadList_Preview", value); }
|
||||||
|
//
|
||||||
|
public get AutoLogin(): number { return Number.parseInt(cc.sys.localStorage.getItem("AutoLogin")); }
|
||||||
|
public set AutoLogin(value: number) { cc.sys.localStorage.setItem("AutoLogin", value); }
|
||||||
|
//
|
||||||
|
public get GameInfoData(): string[] { return JSON.parse(cc.sys.localStorage.getItem("GameInfoData")); }
|
||||||
|
public set GameInfoData(value: string[]) { cc.sys.localStorage.setItem("GameInfoData", JSON.stringify(value)); }
|
||||||
|
//
|
||||||
|
public get LoginDays(): string[] { return JSON.parse(cc.sys.localStorage.getItem("LoginDays")); }
|
||||||
|
public set LoginDays(value: string[]) { cc.sys.localStorage.setItem("LoginDays", JSON.stringify(value)); }
|
||||||
|
}
|
192
src/Engine/Timer/Timer.ts
Normal file
192
src/Engine/Timer/Timer.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { NumberEx } from "@/utils/Number/NumberEx";
|
||||||
|
import { CoroutineV2 } from "../CatanEngine/CoroutineV2/CoroutineV2";
|
||||||
|
import { ActionWithType } from "../CatanEngine/CSharp/System/ActionWithType";
|
||||||
|
|
||||||
|
class TimerEvent extends ActionWithType<number, any> { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 計時器(使用CoroutineV2)
|
||||||
|
*/
|
||||||
|
export class Timer {
|
||||||
|
|
||||||
|
//#region private
|
||||||
|
|
||||||
|
/** 訊息資料 */
|
||||||
|
private static Group: Map<any, TimerDataClass> = new Map<any, TimerDataClass>();
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region static
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 啟動計時
|
||||||
|
* @param {number} time 計時(seconds)
|
||||||
|
* @param {Function} callback Function
|
||||||
|
* @param {any} type (選填) 可以識別的東西
|
||||||
|
* @param {any} bindTarget (選填) 回呼時this綁定的對象
|
||||||
|
* @example
|
||||||
|
* Timer.Start(1, () => { console.log(`example`); });
|
||||||
|
* Timer.Start(1, () => { console.log(`example`); }, "example");
|
||||||
|
* Timer.Start(1, () => { console.log(`example`); }, "example", this);
|
||||||
|
*/
|
||||||
|
public static Start(time: number, callback: Function, bindTarget?: any, type?: any): void {
|
||||||
|
let self: typeof Timer = this;
|
||||||
|
let thisType: any = type;
|
||||||
|
if (!type) {
|
||||||
|
thisType = callback;
|
||||||
|
}
|
||||||
|
if (Timer.Group.has(thisType)) {
|
||||||
|
console.error(`Timer Start Error Timer.Group.has(${thisType})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let timerData: TimerDataClass = new TimerDataClass(thisType, time, callback, bindTarget);
|
||||||
|
Timer.Group.set(thisType, timerData);
|
||||||
|
let CoroutineFN: () => IterableIterator<any> = function* (): IterableIterator<any> {
|
||||||
|
yield CoroutineV2.WaitTime(time).Start(bindTarget);
|
||||||
|
if (Timer.Group.has(thisType)) {
|
||||||
|
self._callback(timerData.Type, timerData.Callback, timerData.BindTarget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CoroutineV2.Single(CoroutineFN()).Start(bindTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刪除計時 By Target
|
||||||
|
* @param {any} target target
|
||||||
|
* @example
|
||||||
|
* Timer.ClearByTarget(this);
|
||||||
|
*/
|
||||||
|
public static ClearByTarget(target: any): void {
|
||||||
|
let timerDataGroup: TimerDataClass[] = [];
|
||||||
|
Timer.Group.forEach(timerData => {
|
||||||
|
if (timerData.BindTarget === target) {
|
||||||
|
timerDataGroup.push(timerData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (timerDataGroup.length === 0) {
|
||||||
|
console.warn(`Timer Clear Error Timer.Group.has not target`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i: number = 0; i < timerDataGroup.length; i++) {
|
||||||
|
let timerData: TimerDataClass = timerDataGroup[i];
|
||||||
|
let type: any = timerData.Type;
|
||||||
|
Timer.Group.delete(type);
|
||||||
|
timerData = null;
|
||||||
|
}
|
||||||
|
CoroutineV2.StopCoroutinesBy(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刪除計時 By Type
|
||||||
|
* @param PS 還是會吃效能在倒數 只是時間到不會執行
|
||||||
|
* @param {any} type type
|
||||||
|
* @example
|
||||||
|
* Timer.ClearByType("example");
|
||||||
|
*/
|
||||||
|
public static ClearByType(type: any): void {
|
||||||
|
let timerDataGroup: TimerDataClass[] = [];
|
||||||
|
Timer.Group.forEach(timerData => {
|
||||||
|
if (timerData.Type === type) {
|
||||||
|
timerDataGroup.push(timerData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (timerDataGroup.length === 0) {
|
||||||
|
console.warn(`Timer Clear Error Timer.Group.has not type`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i: number = 0; i < timerDataGroup.length; i++) {
|
||||||
|
let timerData: TimerDataClass = timerDataGroup[i];
|
||||||
|
let type: any = timerData.Type;
|
||||||
|
Timer.Group.delete(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 結束計時時callback
|
||||||
|
* @param {Function} callback Function
|
||||||
|
*/
|
||||||
|
private static _callback(type: any, callback: Function, bindTarget: any): void {
|
||||||
|
if (Timer.Group.has(type)) {
|
||||||
|
Timer.Group.delete(type);
|
||||||
|
}
|
||||||
|
if (bindTarget) {
|
||||||
|
callback.bind(bindTarget)();
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定時事件(時間用 updateTime秒跑一次fn)
|
||||||
|
* @param startNum 起始index
|
||||||
|
* @param endNum 結束index
|
||||||
|
* @param updateTime 事件刷新間隔
|
||||||
|
* @param callbackfn 事件
|
||||||
|
* @example
|
||||||
|
* let startNum: number = 10;
|
||||||
|
* let endNum: number = 0;
|
||||||
|
* let updateTime: number = 1;
|
||||||
|
* yield CoroutineV2.Single(Timer.Timing(
|
||||||
|
* startNum,
|
||||||
|
* endNum,
|
||||||
|
* updateTime,
|
||||||
|
* (x: number) => {
|
||||||
|
* console.log(`x: ${x}`);
|
||||||
|
* }
|
||||||
|
* )).Start(this);
|
||||||
|
*/
|
||||||
|
public static *Timing(startNum: number, endNum: number, updateTime: number, callbackfn: Function): IterableIterator<any> {
|
||||||
|
let isIncrease: boolean = endNum >= startNum;
|
||||||
|
let totalCount: number = Math.abs(endNum - startNum) + 1;
|
||||||
|
let nowCount: number = NumberEx.divide(totalCount, updateTime);
|
||||||
|
let diff: number = NumberEx.divide(totalCount, nowCount) * (isIncrease ? 1 : -1);
|
||||||
|
let tempScore: number = startNum;
|
||||||
|
callbackfn(startNum);
|
||||||
|
while (true) {
|
||||||
|
if (endNum !== tempScore) {
|
||||||
|
yield CoroutineV2.WaitTime(updateTime);
|
||||||
|
tempScore += diff;
|
||||||
|
// 遞增
|
||||||
|
if (isIncrease && tempScore > endNum) {
|
||||||
|
tempScore = endNum;
|
||||||
|
}
|
||||||
|
// 遞減
|
||||||
|
if (!isIncrease && tempScore < endNum) {
|
||||||
|
tempScore = endNum;
|
||||||
|
}
|
||||||
|
callbackfn(Math.floor(tempScore));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//#region Class
|
||||||
|
|
||||||
|
/** Timer資料 */
|
||||||
|
export class TimerDataClass {
|
||||||
|
/** Type */
|
||||||
|
public Type: any = null;
|
||||||
|
|
||||||
|
/** Time */
|
||||||
|
public Time: number = null;
|
||||||
|
|
||||||
|
/** Callback */
|
||||||
|
public Callback: Function = null;
|
||||||
|
|
||||||
|
/** BindTarget */
|
||||||
|
public BindTarget?: any = null;
|
||||||
|
|
||||||
|
constructor(type: any, time: number, callback: Function, bindTarget?: any) {
|
||||||
|
this.Type = type;
|
||||||
|
this.Time = time;
|
||||||
|
this.Callback = callback;
|
||||||
|
this.BindTarget = bindTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// //#endregion
|
5
src/Engine/Utils/Audio/CSAudio.ts
Normal file
5
src/Engine/Utils/Audio/CSAudio.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default class CSAudio {
|
||||||
|
private static _instance: CSAudio = null;
|
||||||
|
public static get Instance(): CSAudio { return this._instance; }
|
||||||
|
public AddClipsInfo(clips: Map<number, cc.AudioClip>, pathes: Map<number, string>): void { }
|
||||||
|
}
|
120
src/Engine/Utils/PSPS.ts
Normal file
120
src/Engine/Utils/PSPS.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
//#region Class
|
||||||
|
|
||||||
|
import { CoroutineV2 } from "../../Engine/CatanEngine/CoroutineV2/CoroutineV2";
|
||||||
|
|
||||||
|
/** 表演節目序列處理系統(playShow Sequence Processing System) */
|
||||||
|
export default class PSPS {
|
||||||
|
//#region public
|
||||||
|
|
||||||
|
public ShowData: ShowDataClass[] = [];
|
||||||
|
|
||||||
|
public IsRun: boolean = false;
|
||||||
|
|
||||||
|
// /** 可以插隊時間 */
|
||||||
|
// public CanCutInLineTime: number = null;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region private
|
||||||
|
|
||||||
|
private _playShowFunction: (data: any) => IterableIterator<any> = null;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Lifecycle
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表演節目序列處理系統(PlayShow Sequence Processing System)
|
||||||
|
* @param playShowFunction 要表演的函式
|
||||||
|
* @example
|
||||||
|
* let CoroutineFunction: (data: any) => IterableIterator<any> = function* (data: any): IterableIterator<any> {}
|
||||||
|
* new PSPS(this.CoroutineFunction.bind(this));
|
||||||
|
*/
|
||||||
|
constructor(playShowFunction: (data: any) => IterableIterator<any>) {
|
||||||
|
this.SetPlayShowFunction(playShowFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SetPlayShowFunction(playShowFunction: (data: any) => IterableIterator<any>): void {
|
||||||
|
this._playShowFunction = playShowFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region playShow
|
||||||
|
|
||||||
|
/** 增加表演資料 */
|
||||||
|
public PushPlayShowData(data: any, priority: number = 0): void {
|
||||||
|
const playShowData: ShowDataClass = new ShowDataClass(data, priority);
|
||||||
|
this.ShowData.push(playShowData);
|
||||||
|
this.ShowData.ObjectSort([true], ["Priority"]);
|
||||||
|
if (!this.IsRun) {
|
||||||
|
CoroutineV2.Single(this._performanceShowData()).Start(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表演 */
|
||||||
|
private *_performanceShowData(): IterableIterator<any> {
|
||||||
|
this.IsRun = true;
|
||||||
|
if (this._playShowFunction) {
|
||||||
|
const showData: ShowDataClass = this.ShowData.shift();
|
||||||
|
const data: any = showData.Data;
|
||||||
|
yield* this._playShowFunction(data);
|
||||||
|
}
|
||||||
|
if (this.ShowData.length > 0) {
|
||||||
|
CoroutineV2.Single(this._performanceShowData()).Start(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.StopPerformance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 停止表演 */
|
||||||
|
public StopPerformance(): void {
|
||||||
|
this.IsRun = false;
|
||||||
|
CoroutineV2.StopCoroutinesBy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClearFromPriority(priority: any): void {
|
||||||
|
let deleteDatas: ShowDataClass[] = [];
|
||||||
|
for (let i: number = 0; i < this.ShowData.length; i++) {
|
||||||
|
const showData: ShowDataClass = this.ShowData[i];
|
||||||
|
if (showData.Priority === priority) {
|
||||||
|
deleteDatas.push(showData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i: number = 0; i < deleteDatas.length; i++) {
|
||||||
|
const deleteData: ShowDataClass = deleteDatas[i];
|
||||||
|
for (let j: number = 0; j < this.ShowData.length; j++) {
|
||||||
|
const showData: ShowDataClass = this.ShowData[j];
|
||||||
|
if (showData.Priority === deleteData.Priority) {
|
||||||
|
this.ShowData.splice(j, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清除所有表演 */
|
||||||
|
public ClearPerformance(): void {
|
||||||
|
this.IsRun = false;
|
||||||
|
this.ShowData = [];
|
||||||
|
CoroutineV2.StopCoroutinesBy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ShowDataClass */
|
||||||
|
export class ShowDataClass {
|
||||||
|
/** 優先度(越低越前面) */
|
||||||
|
public Priority: number = 0;
|
||||||
|
|
||||||
|
/** Data */
|
||||||
|
public Data: any = null;
|
||||||
|
|
||||||
|
constructor(data: any, priority: number) {
|
||||||
|
this.Data = data;
|
||||||
|
this.Priority = priority;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
27
src/Engine/Utils/Singleton/BaseSingleton.ts
Normal file
27
src/Engine/Utils/Singleton/BaseSingleton.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 單例基類(要先new在使用)
|
||||||
|
* @example
|
||||||
|
* export default class Test extends BaseSingleton<Test>() { ...... }
|
||||||
|
* new Test();
|
||||||
|
* Test.Instance.Init();
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
|
export default function BaseSingleton<T>() {
|
||||||
|
class BaseSingleton {
|
||||||
|
public constructor() {
|
||||||
|
if ((<any>this)._instance == null) {
|
||||||
|
BaseSingleton._instance = <any>this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static _instance: BaseSingleton = null;
|
||||||
|
public static get Instance(): T {
|
||||||
|
return (<any>this)._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 銷毀 */
|
||||||
|
public Destroy(): void {
|
||||||
|
(<any>this)._instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BaseSingleton;
|
||||||
|
}
|
7
src/UI/Game.tsx
Normal file
7
src/UI/Game.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const Game = () => {
|
||||||
|
return (
|
||||||
|
<>Game</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Game;
|
7
src/UI/Lobby.tsx
Normal file
7
src/UI/Lobby.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const Lobby = () => {
|
||||||
|
return (
|
||||||
|
<>Lobby</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Lobby;
|
53
src/UI/Login.tsx
Normal file
53
src/UI/Login.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
|
||||||
|
import { useGameItems } from "@/context/GameItemsContext";
|
||||||
|
import { Button, Cascader } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
children?: Option[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Login = () => {
|
||||||
|
const { onLoad } = useGameItems();
|
||||||
|
const serverType: typeof BusinessEnum.ServerType = BusinessEnum.ServerType;
|
||||||
|
const [type, setType] = useState<number>(BusinessEnum.ServerType.Internal_Dev);
|
||||||
|
const [isLogin, setIsLogin] = useState<boolean>(false);
|
||||||
|
const options: Option[] = [];
|
||||||
|
for (let i = 0, names: string[] = Object.keys(serverType); i < names.length; i++) {
|
||||||
|
const key: string = names[i];
|
||||||
|
if (!Number.isNaN(+key)) {
|
||||||
|
options.push({
|
||||||
|
value: key,
|
||||||
|
label: serverType[key],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
setIsLogin(true);
|
||||||
|
await onLoad(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>{!isLogin &&
|
||||||
|
<div style={boxStyle}>
|
||||||
|
<Cascader defaultValue={[BusinessEnum.ServerType[BusinessEnum.ServerType.Internal_Dev]]} options={options} onChange={(v: string[]) => setType(+v[0])} />
|
||||||
|
<Button type="primary" onClick={login}>登入</Button>
|
||||||
|
</div>
|
||||||
|
}</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Login;
|
||||||
|
|
||||||
|
const boxStyle: React.CSSProperties = {
|
||||||
|
width: "100%",
|
||||||
|
height: "100vh",
|
||||||
|
borderRadius: 6,
|
||||||
|
border: "1px solid #40a9ff",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
};
|
255
src/_BusinessTypeSetting/BusinessTypeSetting.ts
Normal file
255
src/_BusinessTypeSetting/BusinessTypeSetting.ts
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
export module BusinessEnum {
|
||||||
|
export enum BusinessType {
|
||||||
|
Type1 = "LP1",
|
||||||
|
Type2 = "LP2"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ServerType {
|
||||||
|
/** 外版 */
|
||||||
|
Out = 2,
|
||||||
|
/** 內版開發(內網&4G) */
|
||||||
|
Internal_Dev = 5,
|
||||||
|
/** 外部商業DEMO(B2B) */
|
||||||
|
Out_B2B = 6,
|
||||||
|
/** QA */
|
||||||
|
QA = 7,
|
||||||
|
/** Test */
|
||||||
|
Test = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LogoType {
|
||||||
|
/** 完美(目前只有WEB) */
|
||||||
|
// WM = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
產品商業類別設定檔
|
||||||
|
@explain 讀不同表就代表不同商業類別.要多開GIT並設定新測試環境
|
||||||
|
@explain 遊戲一定都一樣不能改動介面
|
||||||
|
*/
|
||||||
|
export default class BusinessTypeSetting {
|
||||||
|
/** 產品商業類別字串(組合判斷用) */
|
||||||
|
public static readonly TYPE_BUSINESS: string = BusinessEnum.BusinessType.Type1;
|
||||||
|
|
||||||
|
/** 必要JSON載入結束 */
|
||||||
|
public static GetLoadInitEnd(): boolean {
|
||||||
|
return this.SetLoadInitEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SetLoadInitEnd: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 執行環境ProductEnum.ServerType
|
||||||
|
* @description 請統一從Cocos掛載Loading的面板去設定ServerType
|
||||||
|
*/
|
||||||
|
public static UseServerType: BusinessEnum.ServerType = BusinessEnum.ServerType[import.meta.env.VITE_Deploy] as unknown as BusinessEnum.ServerType;
|
||||||
|
|
||||||
|
/** 連線IP(網頁版會接網址參數所以要多開變數直接指定) */
|
||||||
|
public static UseHost: string = BusinessTypeSetting.GetHostUrl(BusinessTypeSetting.UseServerType);
|
||||||
|
|
||||||
|
/** 連接阜(網頁版會接網址參數所以要多開變數直接指定) */
|
||||||
|
public static UsePort: number = BusinessTypeSetting.GetPortNum(BusinessTypeSetting.UseServerType);
|
||||||
|
|
||||||
|
/** 資源伺服器網址 */
|
||||||
|
public static UsePatch: string = BusinessTypeSetting.GetPatchUrl(BusinessTypeSetting.UseServerType);
|
||||||
|
|
||||||
|
/** 靜態伺服器網址 */
|
||||||
|
public static UseDownloadUrl: string = BusinessTypeSetting.GetDownloadUrl(BusinessTypeSetting.UseServerType);
|
||||||
|
|
||||||
|
/** Line Liff */
|
||||||
|
public static UseLiffID: string = BusinessTypeSetting.GetLiffID(BusinessTypeSetting.UseServerType);
|
||||||
|
|
||||||
|
/** Line Liff */
|
||||||
|
public static UseLineID: string = BusinessTypeSetting.GetLineID(BusinessTypeSetting.UseServerType);
|
||||||
|
|
||||||
|
/** 編譯版本 */
|
||||||
|
public static get COMPILE_VERSION(): string {
|
||||||
|
return BusinessTypeSetting.SET_COMPILE_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SET_COMPILE_VERSION: string = null;
|
||||||
|
|
||||||
|
// =======================================================================================
|
||||||
|
/** 網頁測試讀取對應資源的位置 */
|
||||||
|
public static readonly FolderUrl: string = "shared/";
|
||||||
|
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/";
|
||||||
|
|
||||||
|
/** Line */
|
||||||
|
public static readonly LineFriendUrl: string = "https://line.me/R/ti/p/@";
|
||||||
|
/** 原始Liff開啟方式(電腦也支援) */
|
||||||
|
public static readonly LiffUrl: string = "https://liff.line.me/";
|
||||||
|
/** 電腦開會導向Line官網 */
|
||||||
|
public static readonly LiffUrlTypeA: string = "https://line.me/R/app/";
|
||||||
|
/** 電腦無法打開 */
|
||||||
|
public static readonly LiffUrlTypeB: string = "line://app/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取得PATH資原路徑
|
||||||
|
* @param type 執行環境()
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public static GetPatchUrl(type: BusinessEnum.ServerType): string {
|
||||||
|
switch (type) {
|
||||||
|
case BusinessEnum.ServerType.Out:
|
||||||
|
return "https://patch.lybobet.com/";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Internal_Dev:
|
||||||
|
return "https://patch-dev.lybobet.com/";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Out_B2B:
|
||||||
|
return "https://patch-b2b.lybobet.com/";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.QA:
|
||||||
|
return "https://patch-qa.lybobet.com/";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Test:
|
||||||
|
return "https://patch-testing.lybobet.com/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取得連線伺服器IP
|
||||||
|
* @param type 執行環境
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public static GetHostUrl(type: BusinessEnum.ServerType): string {
|
||||||
|
switch (type) {
|
||||||
|
case BusinessEnum.ServerType.Out:
|
||||||
|
return "https://game.lybobet.com";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Internal_Dev:
|
||||||
|
return "https://dev.lybobet.com";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.QA:
|
||||||
|
return "https://qa.lybobet.com";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Test:
|
||||||
|
return "https://testing.lybobet.com";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Out_B2B:
|
||||||
|
return "https://b2b.lybobet.com";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取得伺服器連接端口
|
||||||
|
* @param type 執行環境
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public static GetPortNum(type: BusinessEnum.ServerType): number {
|
||||||
|
switch (type) {
|
||||||
|
case BusinessEnum.ServerType.Out:
|
||||||
|
return 9005;
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Internal_Dev:
|
||||||
|
return 9005;
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.QA:
|
||||||
|
return 9005;
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Test:
|
||||||
|
return 9005;
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Out_B2B:
|
||||||
|
return 9005;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetDownloadUrl(type: BusinessEnum.ServerType): string {
|
||||||
|
switch (type) {
|
||||||
|
case BusinessEnum.ServerType.Out:
|
||||||
|
return "https://static.lybobet.com/";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Internal_Dev:
|
||||||
|
return "https://static-dev.lybobet.com/";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.QA:
|
||||||
|
return "https://static-qa.lybobet.com/";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Test:
|
||||||
|
return "https://static-testing.lybobet.com/";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Out_B2B:
|
||||||
|
return "https://static-b2b.lybobet.com/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetUploadUrl(type: BusinessEnum.ServerType): string {
|
||||||
|
let port: string = ":9080";
|
||||||
|
switch (type) {
|
||||||
|
case BusinessEnum.ServerType.Internal_Dev: {
|
||||||
|
let url: string = this.GetHostUrl(type);
|
||||||
|
url = url.replace("http://", "");
|
||||||
|
url = url.replace("https://", "");
|
||||||
|
return "https://static-" + url + port;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return this.GetHostUrl(type) + port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetLiffID(type: BusinessEnum.ServerType): string {
|
||||||
|
switch (type) {
|
||||||
|
case BusinessEnum.ServerType.Out:
|
||||||
|
return "1657864491-kA7gnVMp";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Internal_Dev:
|
||||||
|
return "1657713613-we8Gk929";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.QA:
|
||||||
|
return "1657864462-xM7dgPGK";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Test:
|
||||||
|
return "1657864500-N3YEgz6p";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Out_B2B:
|
||||||
|
return "1657864484-YeqWEV9O";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetLineID(type: BusinessEnum.ServerType): string {
|
||||||
|
switch (type) {
|
||||||
|
case BusinessEnum.ServerType.QA:
|
||||||
|
case BusinessEnum.ServerType.Test:
|
||||||
|
case BusinessEnum.ServerType.Out:
|
||||||
|
return "070hdlum";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Internal_Dev:
|
||||||
|
return "349pbusa";
|
||||||
|
|
||||||
|
case BusinessEnum.ServerType.Out_B2B:
|
||||||
|
return "114pcwux";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================================
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FileType {
|
||||||
|
PNG = ".png",
|
||||||
|
JPG = ".jpg",
|
||||||
|
MP3 = ".mp3",
|
||||||
|
TXT = ".txt",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FolderName {
|
||||||
|
Logo = "Logo/",
|
||||||
|
SlotImg = "SlotImg/",
|
||||||
|
Avatar = "Avatar/",
|
||||||
|
Report = "report/",
|
||||||
|
PA = "pa/",
|
||||||
|
LobbyAd = "lobby_ad/",
|
||||||
|
CoverAd = "inter_ad/",
|
||||||
|
Activity = "Activity/",
|
||||||
|
Game = "game/",
|
||||||
|
Dlygo = "dlygo/",
|
||||||
|
Event = "event/",
|
||||||
|
}
|
45
src/context/GameItemsContext.tsx
Normal file
45
src/context/GameItemsContext.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
|
||||||
|
import { IGameItems } from "@/types";
|
||||||
|
import { LineTools } from "@/utils/LineTools";
|
||||||
|
import { ReactNode, createContext, useContext, useState } from "react";
|
||||||
|
|
||||||
|
type GameItemsProviderProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
const GameItemsContext = createContext<IGameItems>(undefined);
|
||||||
|
|
||||||
|
export function useGameItems() {
|
||||||
|
return useContext(GameItemsContext);
|
||||||
|
}
|
||||||
|
export let gameObj: IGameItems = null;
|
||||||
|
|
||||||
|
export function GameItemsProvider({ children }: GameItemsProviderProps) {
|
||||||
|
const [gameId, setGameId] = useState<number>(null);
|
||||||
|
|
||||||
|
const game: IGameItems = gameObj = {
|
||||||
|
onLoad,
|
||||||
|
gameId,
|
||||||
|
setGameId
|
||||||
|
};
|
||||||
|
|
||||||
|
async function onLoad(serverType: BusinessEnum.ServerType) {
|
||||||
|
await Promise.all([
|
||||||
|
// // 設定執行環境
|
||||||
|
// setBusinessType(),
|
||||||
|
|
||||||
|
// // 設定LineTools環境
|
||||||
|
// await setLineTools(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 設定LineTools環境 */
|
||||||
|
async function setLineTools() {
|
||||||
|
await LineTools.onLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GameItemsContext.Provider value={game}>
|
||||||
|
{children}
|
||||||
|
</GameItemsContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
5
src/index.css
Normal file
5
src/index.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
38
src/index.tsx
Normal file
38
src/index.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import "@/FormTableExt/TableExt/CSSettingsV3Ext";
|
||||||
|
import { GameItemsProvider } from "@/context/GameItemsContext";
|
||||||
|
import "@/utils/ArrayExtension";
|
||||||
|
import "@/utils/NumberExtension";
|
||||||
|
import "@/utils/String";
|
||||||
|
import type { Router } from "@remix-run/router";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import "dayjs/locale/zh-tw";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { RouterProvider, createHashRouter } from "react-router-dom";
|
||||||
|
import { BaseEnumerator } from "./Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator";
|
||||||
|
import Game from "./UI/Game";
|
||||||
|
import Lobby from "./UI/Lobby";
|
||||||
|
import Login from "./UI/Login";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
|
BaseEnumerator.Init();
|
||||||
|
dayjs.locale("zh-tw");
|
||||||
|
const hashRouter: Router = createHashRouter([
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <Login />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/lobby",
|
||||||
|
element: <Lobby />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/game/:id",
|
||||||
|
element: <Game />,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<GameItemsProvider>
|
||||||
|
<RouterProvider router={hashRouter} />
|
||||||
|
</GameItemsProvider>
|
||||||
|
);
|
7
src/types/index.ts
Normal file
7
src/types/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
|
||||||
|
|
||||||
|
export interface IGameItems {
|
||||||
|
onLoad: (serverType: BusinessEnum.ServerType) => Promise<void>;
|
||||||
|
gameId: number;
|
||||||
|
setGameId: (gameId: number) => void;
|
||||||
|
}
|
140
src/utils/ArrayExtension.ts
Normal file
140
src/utils/ArrayExtension.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
declare interface Array<T> {
|
||||||
|
/**
|
||||||
|
* 移除一個值並且回傳
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
ExRemoveAt(index: number): T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全部值(注意. 參考的也會被清空)
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* let bar: number[] = [1, 2, 3];
|
||||||
|
* let bar2: number[] = bar;
|
||||||
|
* bar.Clear();
|
||||||
|
* console.log(bar, bar2);
|
||||||
|
*
|
||||||
|
* // {
|
||||||
|
* // "bar": [],
|
||||||
|
* // "bar2": []
|
||||||
|
* // }
|
||||||
|
*/
|
||||||
|
Clear(): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拷貝
|
||||||
|
* PS. pass by value 多維陣列也OK
|
||||||
|
*/
|
||||||
|
Copy(): T[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物件陣列排序,asc&key陣列長度請一樣
|
||||||
|
* PS. boolean 帶false是先true在false
|
||||||
|
* @link JavaScript Object 排序 http://www.eion.com.tw/Blogger/?Pid=1170#:~:text=JavaScript%20Object%20排序
|
||||||
|
* @param asc 是否升序排列(小到大)
|
||||||
|
* @param key 需排序的key(優先順序左到右)(沒有就放空)
|
||||||
|
*/
|
||||||
|
ObjectSort(asc?: boolean[], key?: string[]): T[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 設計給Array<cc.Component.EventHandler>forHoldButton使用
|
||||||
|
* Add a none persistent listener to the UnityEvent.
|
||||||
|
* @param call Callback function.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
AddListener(call: Function): void
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.ExRemoveAt ||
|
||||||
|
Object.defineProperty(Array.prototype, "ExRemoveAt", {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (index: number): any {
|
||||||
|
const item: any = this.splice(index, 1);
|
||||||
|
return item[0];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.prototype.Clear ||
|
||||||
|
Object.defineProperty(Array.prototype, "Clear", {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (): void {
|
||||||
|
this.length = 0;
|
||||||
|
|
||||||
|
// let foo: number[] = [1, 2, 3];
|
||||||
|
// let bar: number[] = [1, 2, 3];
|
||||||
|
// let foo2: number[] = foo;
|
||||||
|
// let bar2: number[] = bar;
|
||||||
|
// foo = [];
|
||||||
|
// bar.length = 0;
|
||||||
|
// console.log(foo, bar, foo2, bar2);
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "foo": [],
|
||||||
|
// "bar": [],
|
||||||
|
// "foo2": [
|
||||||
|
// 1,
|
||||||
|
// 2,
|
||||||
|
// 3
|
||||||
|
// ],
|
||||||
|
// "bar2": []
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.prototype.Copy ||
|
||||||
|
Object.defineProperty(Array.prototype, "Copy", {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (): any[] {
|
||||||
|
return Array.from(this);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.prototype.ObjectSort ||
|
||||||
|
Object.defineProperty(Array.prototype, "ObjectSort", {
|
||||||
|
enumerable: false,
|
||||||
|
/**
|
||||||
|
* @param asc 是否升序排列(小到大)
|
||||||
|
* @param key 需排序的key(優先順序左到右)(沒有就放空)
|
||||||
|
*/
|
||||||
|
value: function (asc: boolean[] = [true], key?: string[]): any[] {
|
||||||
|
if (this.length === 0) {
|
||||||
|
return this;
|
||||||
|
} else if (!key || key.length === 0) {
|
||||||
|
console.error("ObjectSort key error");
|
||||||
|
return this;
|
||||||
|
} else if (asc.length !== key.length) {
|
||||||
|
console.error(
|
||||||
|
`ObjectSort key asc error asc.length: ${asc.length}, key.length: ${key.length}`,
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (let i: number = 0; i < key.length; i++) {
|
||||||
|
const keyname: string = key[i];
|
||||||
|
if (this[0][keyname] === undefined) {
|
||||||
|
console.error(`ObjectSort has not key[${i}]: ${keyname}`);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const count: number = key ? key.length : 1;
|
||||||
|
let arr: any[];
|
||||||
|
for (let i: number = count - 1; i >= 0; i--) {
|
||||||
|
arr = this.sort(function (a: any, b: any): 1 | -1 {
|
||||||
|
let mya: any = a;
|
||||||
|
let myb: any = b;
|
||||||
|
if (key) {
|
||||||
|
mya = a[key[i]];
|
||||||
|
myb = b[key[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加個等於數字相同不要再去排序到
|
||||||
|
if (asc[i]) {
|
||||||
|
return mya >= myb ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
return mya <= myb ? 1 : -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
},
|
||||||
|
});
|
565
src/utils/LineTools.ts
Normal file
565
src/utils/LineTools.ts
Normal file
@ -0,0 +1,565 @@
|
|||||||
|
import BusinessTypeSetting from "@/_BusinessTypeSetting/BusinessTypeSetting";
|
||||||
|
import liff from "@line/liff";
|
||||||
|
/**
|
||||||
|
* Line工具
|
||||||
|
* @doc https://developers.line.biz/en/docs/messaging-api/message-types
|
||||||
|
*/
|
||||||
|
export class LineTools {
|
||||||
|
//#region Lifecycle
|
||||||
|
|
||||||
|
public static async onLoad(): Promise<void> {
|
||||||
|
const hasToken: boolean = location.search.includes("token=") || location.hash.includes("token=");
|
||||||
|
if (hasToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await LineTools.init();
|
||||||
|
const isLoggedIn: boolean = await LineTools.checkLogin();
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
await LineTools.login();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.debug(`[Line] Line is Login`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Custom
|
||||||
|
|
||||||
|
public static GetAccessToken(): string {
|
||||||
|
const accessToken: string = liff.getAccessToken();
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region FriendShip
|
||||||
|
/** 確認是否加官方賬號好友 */
|
||||||
|
public static async CheckAddFriend(): Promise<boolean> {
|
||||||
|
const ans: boolean = (await liff.getFriendship()).friendFlag;
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Custom Message
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text message
|
||||||
|
* @param {string} text Message text. Max character limit: 5000
|
||||||
|
* @doc https://developers.line.biz/en/reference/messaging-api/#text-message
|
||||||
|
* @example LineTools.MakeText("Hello, world");
|
||||||
|
*/
|
||||||
|
public static async MakeText(text: string): Promise<boolean> {
|
||||||
|
if (!text) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const data: any[] = [{
|
||||||
|
"type": "text",
|
||||||
|
"text": text
|
||||||
|
}];
|
||||||
|
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text message
|
||||||
|
* @deprecated 有問題用不了
|
||||||
|
* @param {string} text Message text. Max character limit: 5000
|
||||||
|
* @doc https://developers.line.biz/en/reference/messaging-api/#text-message
|
||||||
|
* @LINE_Emoji https://developers.line.biz/en/docs/messaging-api/emoji-list/#line-emoji-definitions
|
||||||
|
* @example LineTools.MakeText("Hello, world");
|
||||||
|
*/
|
||||||
|
public static async MakeTextEmoji(text: string): Promise<boolean> {
|
||||||
|
if (!text) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const data: any[] = [{
|
||||||
|
"type": "text",
|
||||||
|
"text": "$$$ LINE emoji",
|
||||||
|
"emojis": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"productId": "5ac21a8c040ab15980c9b43f",
|
||||||
|
"emojiId": "004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"productId": "5ac21a8c040ab15980c9b43f",
|
||||||
|
"emojiId": "001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"productId": "5ac21a8c040ab15980c9b43f",
|
||||||
|
"emojiId": "025"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}];
|
||||||
|
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sticker message
|
||||||
|
* @param packageId Package ID for a set of stickers. For information on package IDs
|
||||||
|
* @param stickerId Sticker ID. For a list of sticker IDs for stickers that can be sent with the Messaging API
|
||||||
|
* @param isAnim isAnim
|
||||||
|
* @doc https://developers.line.biz/en/reference/messaging-api/#audio-message
|
||||||
|
* @example LineTools.MakeSticker(26162, 505588336, true);
|
||||||
|
*/
|
||||||
|
public static async MakeSticker(packageId: string, stickerId: string, isAnim: boolean = false): Promise<boolean> {
|
||||||
|
if (!packageId || !stickerId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let pngtype: string = "";
|
||||||
|
if (isAnim) {
|
||||||
|
pngtype = "/IOS/sticker@2x.png";
|
||||||
|
} else {
|
||||||
|
pngtype = "/IOS/sticker_animation@2x.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: any[] = [{
|
||||||
|
"type": "template",
|
||||||
|
"altText": "Sticker",
|
||||||
|
"template": {
|
||||||
|
"type": "image_carousel",
|
||||||
|
"columns": [{
|
||||||
|
"imageUrl": "https://stickershop.line-scdn.net/stickershop/v1/sticker/" + stickerId + pngtype,
|
||||||
|
"action": {
|
||||||
|
"type": "uri",
|
||||||
|
"uri": "line://shop/sticker/detail/" + packageId
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image message
|
||||||
|
* @param {string} originalContentUrl Image URL (Max character limit: 2000) HTTPS over TLS 1.2 or later JPEG or PNG Max image size: No limits Max file size: 10 MB
|
||||||
|
* @param {string} previewImageUrl Preview image URL (Max character limit: 2000) HTTPS over TLS 1.2 or later JPEG or PNG Max image size: No limits Max file size: 1 MB
|
||||||
|
* @doc https://developers.line.biz/en/reference/messaging-api/#image-message
|
||||||
|
* @example LineTools.MakeImage("https://example.com/original.jpg", "https://example.com/preview.jpg");
|
||||||
|
*/
|
||||||
|
public static async MakeImage(originalContentUrl: string, previewImageUrl: string = originalContentUrl): Promise<boolean> {
|
||||||
|
if (!originalContentUrl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const data: any[] = [{
|
||||||
|
"type": "image",
|
||||||
|
"originalContentUrl": originalContentUrl,
|
||||||
|
"previewImageUrl": previewImageUrl
|
||||||
|
}];
|
||||||
|
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Video message
|
||||||
|
* If the video isn't playing properly, make sure the video is a supported file type and the HTTP server hosting the video supports HTTP range requests.
|
||||||
|
* @param {string} originalContentUrl URL of video file (Max character limit: 2000) HTTPS over TLS 1.2 or later mp4 Max file size: 200 MB
|
||||||
|
* @param {string} previewImageUrl URL of preview image (Max character limit: 2000) HTTPS over TLS 1.2 or later JPEG or PNG Max file size: 1 MB
|
||||||
|
* @doc https://developers.line.biz/en/reference/messaging-api/#video-message
|
||||||
|
*/
|
||||||
|
public static async MakeVideo(originalContentUrl: string, previewImageUrl: string): Promise<boolean> {
|
||||||
|
if (!originalContentUrl || !previewImageUrl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const data: any[] = [{
|
||||||
|
"type": "video",
|
||||||
|
"originalContentUrl": originalContentUrl,
|
||||||
|
"previewImageUrl": previewImageUrl
|
||||||
|
}];
|
||||||
|
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Audio message
|
||||||
|
* @param {string} originalContentUrl URL of audio file (Max character limit: 2000) HTTPS over TLS 1.2 or later m4a Max file size: 200 MB
|
||||||
|
* @param {number} duration Length of audio file (milliseconds)
|
||||||
|
* @doc https://developers.line.biz/en/reference/messaging-api/#audio-message
|
||||||
|
*/
|
||||||
|
public static async MakeAudio(originalContentUrl: string, duration: number = 60000): Promise<boolean> {
|
||||||
|
if (!originalContentUrl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const data: any[] = [{
|
||||||
|
"type": "audio",
|
||||||
|
"originalContentUrl": originalContentUrl,
|
||||||
|
"duration": duration
|
||||||
|
}];
|
||||||
|
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buttons template
|
||||||
|
* @param altText Alternative text. When a user receives a message, it will appear as an alternative to the template message in the notification or chat list of their device. Max character limit: 400
|
||||||
|
* @param thumbnailImageUrl Image URL (Max character limit: 2,000) HTTPS over TLS 1.2 or later JPEG or PNG Max width: 1024px Max file size: 10 MB
|
||||||
|
* @param title Title Max character limit: 40
|
||||||
|
* @param text Message text Max character limit: 160 (no image or title) Max character limit: 60 (message with an image or title)
|
||||||
|
* @param defaultAction Action when image, title or text area is tapped.
|
||||||
|
* @param actions Action when tapped Max objects: 4
|
||||||
|
* @param imageAspectRatio rectangle: 1.51:1
|
||||||
|
* @param imageAspectRatio square: 1:1
|
||||||
|
* @param imageSize cover: The image fills the entire image area. Parts of the image that do not fit in the area are not displayed.
|
||||||
|
* @param imageSize contain: The entire image is displayed in the image area. A background is displayed in the unused areas to the left and right of vertical images and in the areas above and below horizontal images.
|
||||||
|
* @doc https://developers.line.biz/en/reference/messaging-api/#buttons
|
||||||
|
*/
|
||||||
|
public static async MakeTemplate(altText: string, thumbnailImageUrl: string, title: string, text: string, defaultAction: any = null, actions: any[] = [], imageAspectRatio: string = "square", imageSize: string = "cover"): Promise<boolean> {
|
||||||
|
if (actions.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: any[] = [{
|
||||||
|
"type": "template",
|
||||||
|
"altText": altText,
|
||||||
|
"template": {
|
||||||
|
"type": "buttons",
|
||||||
|
"thumbnailImageUrl": thumbnailImageUrl,
|
||||||
|
"imageAspectRatio": imageAspectRatio,
|
||||||
|
"imageSize": imageSize,
|
||||||
|
"title": title,
|
||||||
|
"text": text
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (defaultAction) {
|
||||||
|
data["defaultAction"] = defaultAction;
|
||||||
|
// data["defaultAction"] = {
|
||||||
|
// "type": "uri",
|
||||||
|
// "label": "View detail",
|
||||||
|
// "uri": "http://example.com/page/123"
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actions) {
|
||||||
|
data["actions"] = actions;
|
||||||
|
// data["actions"] = [{
|
||||||
|
// "type": "uri",
|
||||||
|
// "label": "立即玩",
|
||||||
|
// "uri": "https://liff.line.me/1657713613-we8Gk929"
|
||||||
|
// }];
|
||||||
|
}
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
//#region SendImageTemplate
|
||||||
|
/**
|
||||||
|
* MeProfile
|
||||||
|
* @param altText Alternative text. When a user receives a message, it will appear as an alternative to the template message in the notification or chat list of their device. Max character limit: 400
|
||||||
|
* @param text Message text Max character limit: 160 (no image or title) Max character limit: 60 (message with an image or title)
|
||||||
|
* @simulator https://developers.line.biz/flex-simulator/
|
||||||
|
*/
|
||||||
|
public static async MakeImageTemplate(altText: string, text?: string): Promise<boolean> {
|
||||||
|
let data: any[] = [{
|
||||||
|
"type": "flex",
|
||||||
|
"altText": altText,
|
||||||
|
"contents":
|
||||||
|
{
|
||||||
|
"type": "bubble",
|
||||||
|
"size": "giga",
|
||||||
|
"body": {
|
||||||
|
"type": "box",
|
||||||
|
"layout": "vertical",
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"url": "https://scontent.ftpe8-3.fna.fbcdn.net/v/t39.30808-6/313961998_137927952344738_4107082514038089088_n.jpg?stp=dst-jpg_p526x296&_nc_cat=111&ccb=1-7&_nc_sid=730e14&_nc_ohc=3jmqu3srEAYAX9drH1H&_nc_ht=scontent.ftpe8-3.fna&oh=00_AfDe36ZgvY6aqmN3nge4Fmw9ZGuOwxdS5fj9eAMLe6wtBg&oe=63A4C088",
|
||||||
|
"size": "full",
|
||||||
|
"aspectMode": "cover"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paddingAll": "none"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "uri",
|
||||||
|
"label": "action",
|
||||||
|
"uri": `https://liff.line.me/${BusinessTypeSetting.UseLiffID}}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
if (text) {
|
||||||
|
data[0].contents.body.contents.push({
|
||||||
|
"type": "text",
|
||||||
|
"text": text,
|
||||||
|
"size": "md",
|
||||||
|
"align": "center",
|
||||||
|
"color": "#0000FF",
|
||||||
|
"margin": "50px",
|
||||||
|
"gravity": "center",
|
||||||
|
"offsetBottom": "20px"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region MakeShareBigWinGame
|
||||||
|
/**
|
||||||
|
* 分享遊戲
|
||||||
|
* @param {number} slotID 遊戲編號
|
||||||
|
* @param {number} ratio 倍率
|
||||||
|
* @param {string} altText 列表顯示的訊息
|
||||||
|
* @param {string} text 弟一行文字
|
||||||
|
* @param {string} btnText 按鈕文字
|
||||||
|
* @param {string} btnUrl 按鈕連結
|
||||||
|
*/
|
||||||
|
public static async MakeShareBigWinGame(slotID: number, ratio: number, altText: string, text: string, btnText: string, ...param: any[]): Promise<boolean> {
|
||||||
|
text = String.Format(text, ratio);
|
||||||
|
let data: any[] = [{
|
||||||
|
"type": "flex",
|
||||||
|
"altText": altText,
|
||||||
|
"contents":
|
||||||
|
{
|
||||||
|
"type": "bubble",
|
||||||
|
"size": "mega",
|
||||||
|
"body": {
|
||||||
|
"type": "box",
|
||||||
|
"layout": "vertical",
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"type": "box",
|
||||||
|
"layout": "vertical",
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"url": `${BusinessTypeSetting.UseDownloadUrl}game/${slotID}/s`,
|
||||||
|
"position": "absolute",
|
||||||
|
"offsetTop": "11%",
|
||||||
|
"offsetStart": "71%",
|
||||||
|
"size": "75px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"url": `${BusinessTypeSetting.UsePatch}shared/img/LineShareUI/ShareAward01.png`,
|
||||||
|
"size": "full",
|
||||||
|
"aspectRatio": "20:13",
|
||||||
|
"aspectMode": "cover"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paddingAll": "none"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "box",
|
||||||
|
"layout": "vertical",
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": text,
|
||||||
|
"weight": "bold",
|
||||||
|
"size": "md",
|
||||||
|
"align": "center",
|
||||||
|
"wrap": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"margin": "xl"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paddingAll": "none"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"type": "box",
|
||||||
|
"layout": "vertical",
|
||||||
|
"spacing": "sm",
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"type": "button",
|
||||||
|
"style": "link",
|
||||||
|
"height": "sm",
|
||||||
|
"action": {
|
||||||
|
"type": "uri",
|
||||||
|
"label": btnText,
|
||||||
|
"uri": `https://line.me/R/app/${BusinessTypeSetting.UseLiffID}/?gamein=${slotID}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flex": 0
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "uri",
|
||||||
|
"label": btnText,
|
||||||
|
"uri": `https://line.me/R/app/${BusinessTypeSetting.UseLiffID}/?gamein=${slotID}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
return await LineTools._sendMessages(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region SelfProfile
|
||||||
|
/**
|
||||||
|
* MeProfile
|
||||||
|
* @simulator https://developers.line.biz/flex-simulator/
|
||||||
|
*/
|
||||||
|
public static async MeProfile(): Promise<void> {
|
||||||
|
const altText: string = "立即玩爆機娛樂城";
|
||||||
|
// eslint-disable-next-line @typescript-eslint/typedef
|
||||||
|
liff.getProfile().then(function (profileData): void {
|
||||||
|
let statusMessage: string = profileData.statusMessage ?? "";
|
||||||
|
if (statusMessage.length > 60) {
|
||||||
|
statusMessage = "Status Message is to long! Max 60 words";
|
||||||
|
}
|
||||||
|
const data: any[] = [
|
||||||
|
{
|
||||||
|
"type": "flex",
|
||||||
|
"altText": altText,
|
||||||
|
"contents":
|
||||||
|
{
|
||||||
|
"type": "bubble",
|
||||||
|
// "size": "giga",
|
||||||
|
"size": "kilo",
|
||||||
|
"body": {
|
||||||
|
"type": "box",
|
||||||
|
"layout": "vertical",
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"url": profileData.pictureUrl,
|
||||||
|
"size": "full",
|
||||||
|
"aspectMode": "cover"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "box",
|
||||||
|
"layout": "vertical",
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": profileData.displayName,
|
||||||
|
"weight": "bold",
|
||||||
|
"size": "lg",
|
||||||
|
"margin": "lg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": statusMessage,
|
||||||
|
"size": "sm",
|
||||||
|
"margin": "md",
|
||||||
|
"wrap": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paddingEnd": "5%",
|
||||||
|
"paddingStart": "5%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": "立即玩",
|
||||||
|
"size": "md",
|
||||||
|
"align": "center",
|
||||||
|
"color": "#0000FF",
|
||||||
|
"margin": "50px",
|
||||||
|
"gravity": "center",
|
||||||
|
"offsetBottom": "20px"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paddingAll": "none"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "uri",
|
||||||
|
"label": "action",
|
||||||
|
"uri": `https://liff.line.me/${BusinessTypeSetting.UseLiffID}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
LineTools._sendMessages(data);
|
||||||
|
}).catch(function (error: any): void {
|
||||||
|
alert(`[Line] Failed to getProfile: \n${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async GetLineProfile(): Promise<any> {
|
||||||
|
return await liff.getProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 傳送
|
||||||
|
* @param {SendMessagesParams} messages
|
||||||
|
* @param {boolean} isMultiple If you set the isMultiple property to true, the user can select multiple message recipients in the target picker. If you set it to false, the user can select only one friend as the message recipient. The default value is true.
|
||||||
|
*/
|
||||||
|
private static async _sendMessages(messages: any[], isMultiple: boolean = true): Promise<boolean> {
|
||||||
|
// 這邊是為了防止token過期
|
||||||
|
const isLoggedIn: boolean = await LineTools.checkLogin();
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
await LineTools.login();
|
||||||
|
}
|
||||||
|
let isSuccess: boolean = false;
|
||||||
|
if (liff.isApiAvailable("shareTargetPicker")) {
|
||||||
|
try {
|
||||||
|
const res = await liff.shareTargetPicker(messages, { isMultiple: isMultiple });
|
||||||
|
if (res) {
|
||||||
|
if (res.status === "success") {
|
||||||
|
isSuccess = true;
|
||||||
|
console.log(`[Line] 分享成功`);
|
||||||
|
} else {
|
||||||
|
console.error(`[Line] 分享失敗: \n${JSON.stringify(res)}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`[Line] 分享取消`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[Line] Failed to launch ShareTargetPicker: \n${error}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("[Line] 你的 LINE App 暫時不支援 Share Target Picker");
|
||||||
|
}
|
||||||
|
return isSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static sleep(ms: any): Promise<any> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async init(): Promise<void> {
|
||||||
|
const liffId: string = BusinessTypeSetting.UseLiffID;
|
||||||
|
const myLIFF_STORE: Object = LineTools.getLIFF_STORE();
|
||||||
|
await liff.init({ liffId: liffId });
|
||||||
|
LineTools.setLIFF_STORE(myLIFF_STORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async checkLogin(): Promise<boolean> {
|
||||||
|
let isLoggedIn: boolean = liff.isLoggedIn();
|
||||||
|
try {
|
||||||
|
const a = await liff.getProfile();
|
||||||
|
} catch (error) {
|
||||||
|
isLoggedIn = false;
|
||||||
|
}
|
||||||
|
return isLoggedIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async login(): Promise<void> {
|
||||||
|
if (liff.isInClient()) {
|
||||||
|
liff.login({ redirectUri: parent.location.href });
|
||||||
|
} else {
|
||||||
|
const search: string = location.search ? location.search : location.hash;
|
||||||
|
const callbackURL: string = BusinessTypeSetting.UsePatch + "/" + search;
|
||||||
|
liff.login({ redirectUri: callbackURL });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getLIFF_STORE(): Object {
|
||||||
|
const LIFF_STORE: Object = {};
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
if (key.includes("LIFF_STORE")) {
|
||||||
|
LIFF_STORE[key] = localStorage.getItem(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LIFF_STORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static setLIFF_STORE(LIFF_STORE: Object) {
|
||||||
|
for (let i = 0, keys = Object.keys(LIFF_STORE); i < keys.length; i++) {
|
||||||
|
const key = keys[i];
|
||||||
|
const item = LIFF_STORE[key];
|
||||||
|
localStorage.setItem(key, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
181
src/utils/Number/NumberEx.ts
Normal file
181
src/utils/Number/NumberEx.ts
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
|
||||||
|
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
|
||||||
|
import { RandomEx } from "./RandomEx";
|
||||||
|
|
||||||
|
export module NumberEx {
|
||||||
|
/**
|
||||||
|
* 數字滾動
|
||||||
|
* @param startNum 開始
|
||||||
|
* @param endNum 結束
|
||||||
|
* @param callbackfn 結束回呼
|
||||||
|
* @param toInt 是否只顯示整數(預設FALSE)
|
||||||
|
*/
|
||||||
|
export function* ChangeScore(startNum: number, endNum: number, callbackfn: (num: number) => void, sec: number, toInt: boolean = true) {
|
||||||
|
let fps = 30;
|
||||||
|
let waitTime = 0.03;
|
||||||
|
let changeRate = sec * fps;
|
||||||
|
changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
|
||||||
|
changeRate = 1 / changeRate;
|
||||||
|
let diff = endNum - startNum;
|
||||||
|
let isIncrease = endNum >= startNum;
|
||||||
|
let tempScore = startNum;
|
||||||
|
let counter = 0;
|
||||||
|
while (true) {
|
||||||
|
if (endNum != tempScore) {
|
||||||
|
tempScore += diff * changeRate;
|
||||||
|
// 遞增
|
||||||
|
if (isIncrease && tempScore > endNum) {
|
||||||
|
tempScore = endNum;
|
||||||
|
}
|
||||||
|
// 遞減
|
||||||
|
if (!isIncrease && tempScore < endNum) {
|
||||||
|
tempScore = endNum;
|
||||||
|
}
|
||||||
|
if (toInt) {
|
||||||
|
callbackfn(tempScore.ExToInt());
|
||||||
|
} else {
|
||||||
|
callbackfn(tempScore);
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
yield CoroutineV2.WaitTime(waitTime);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callbackfn(endNum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 數字跳動(時間內循環EX:1~4=>1.2.3.4.1.2.3.4.1.2.3.4...)
|
||||||
|
* @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<any> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**是否进行边界检查 */
|
||||||
|
let _boundaryCheckingState = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测数字是否越界,如果越界给出提示
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
src/utils/Number/NumberEx.ts.meta
Normal file
10
src/utils/Number/NumberEx.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "363f5f7f-0623-4013-8571-0bb5c1dc95e6",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
92
src/utils/Number/RandomEx.ts
Normal file
92
src/utils/Number/RandomEx.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據權重取得複數個不重複回傳
|
||||||
|
* @param prize 獎項
|
||||||
|
* @param weights 機率
|
||||||
|
* @param count 數量
|
||||||
|
*/
|
||||||
|
export function GetMultiNoRepeatByWeight(prize: any[], weights: number[] = null, count: number = 1): any[] {
|
||||||
|
if (weights === null) {
|
||||||
|
weights = [];
|
||||||
|
for (let i: number = 0; i < prize.length; i++) {
|
||||||
|
weights.push(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let target: any[] = [];
|
||||||
|
for (let i: number = 0; i < count; i++) {
|
||||||
|
let results: number[] = RandomEx.GetPrizeByWeight(prize, weights);
|
||||||
|
prize.splice(results[0], 1);
|
||||||
|
weights.splice(results[0], 1);
|
||||||
|
target.push(results[1]);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據權重隨機取值
|
||||||
|
* @param prize 獎項
|
||||||
|
* @param weights 機率
|
||||||
|
*/
|
||||||
|
export function GetPrizeByWeight(prize: any[], weights: number[]): any[] {
|
||||||
|
if (prize.length !== weights.length) {
|
||||||
|
console.error(`GetWeight error -> prize.length:${ prize.length } !== weights.length:${ weights.length }`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let totalWeight: number = 0;
|
||||||
|
for (let i: number = 0; i < weights.length; i++) {
|
||||||
|
totalWeight += weights[i];
|
||||||
|
}
|
||||||
|
let random: number = RandomEx.GetInt(0, totalWeight) + 1;
|
||||||
|
let nowWeight: number = weights[0];
|
||||||
|
for (let i: number = 0; i < weights.length; i++) {
|
||||||
|
if (nowWeight >= random) {
|
||||||
|
return [i, prize[i]];
|
||||||
|
}
|
||||||
|
nowWeight += weights[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/utils/Number/RandomEx.ts.meta
Normal file
10
src/utils/Number/RandomEx.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "ba4dee5b-ca5b-4435-a068-c4f5dd832bab",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
192
src/utils/NumberExtension.ts
Normal file
192
src/utils/NumberExtension.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
declare interface Number {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金額每三位數(千)加逗號, 並且補到小數點第2位
|
||||||
|
* 輸出 41,038,560.00
|
||||||
|
* @param precision 補到小數點第幾位
|
||||||
|
* @param isPadZero 是否要補零(預設FALSE)
|
||||||
|
* */
|
||||||
|
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 是否要補零(預設FALSE)
|
||||||
|
*/
|
||||||
|
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 = false) {
|
||||||
|
|
||||||
|
// let arr = String(this).split('.');
|
||||||
|
const 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) {
|
||||||
|
/** 千 */
|
||||||
|
const MONEY_1K: number = 1000;
|
||||||
|
/** 萬 */
|
||||||
|
// let MONEY_10K: number = 10000;
|
||||||
|
/** 十萬 */
|
||||||
|
// let MONEY_100K: number = 100000;
|
||||||
|
/** 百萬 */
|
||||||
|
const MONEY_1M: number = 1000000;
|
||||||
|
/** 千萬 */
|
||||||
|
// let MONEY_10M: number = 10000000;
|
||||||
|
/** 億 */
|
||||||
|
// let MONEY_100M: number = 100000000;
|
||||||
|
/** 十億 */
|
||||||
|
const 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) {
|
||||||
|
const str = this.toPrecision(12);
|
||||||
|
const 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 = false) {
|
||||||
|
// 取小數點第X位
|
||||||
|
const 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;
|
||||||
|
}
|
||||||
|
});
|
99
src/utils/PWANotification.ts
Normal file
99
src/utils/PWANotification.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import BusinessTypeSetting, { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
|
||||||
|
import { initializeApp } from "firebase/app";
|
||||||
|
import { getMessaging, getToken } from "firebase/messaging";
|
||||||
|
|
||||||
|
|
||||||
|
// Public Key:
|
||||||
|
// BPxB0rLAHuETo-CFw6xe2_ZlQ8qm6WAg-i45UTStiYYU0pPYR1wuO0jwt8S_gRl_hYFNoI1l0l4vFksncHC5yUs
|
||||||
|
|
||||||
|
// Private Key:
|
||||||
|
// 2-dDtLj8ibIOj51RD9ASV3DtTjkmFQyn_zId3V4OtwA
|
||||||
|
const applicationServerPublicKey: string = `BPxB0rLAHuETo-CFw6xe2_ZlQ8qm6WAg-i45UTStiYYU0pPYR1wuO0jwt8S_gRl_hYFNoI1l0l4vFksncHC5yUs`;
|
||||||
|
const swjsPath: string = "./assets/sw.js";
|
||||||
|
|
||||||
|
export function PWAOnLoad() {
|
||||||
|
if (BusinessTypeSetting.UseServerType !== BusinessEnum.ServerType.Internal_Dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("serviceWorker" in navigator) {
|
||||||
|
navigator.serviceWorker
|
||||||
|
.register(swjsPath)
|
||||||
|
.then((reg: ServiceWorkerRegistration) => {
|
||||||
|
console.debug("Service Worker Registered");
|
||||||
|
askForNotificationPermission();
|
||||||
|
setFCM(reg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @url https://stackoverflow.com/questions/50399170/what-bearer-token-should-i-be-using-for-firebase-cloud-messaging-testing
|
||||||
|
*/
|
||||||
|
function setFCM(reg: ServiceWorkerRegistration) {
|
||||||
|
// Your web app's Firebase configuration
|
||||||
|
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyAZ1Hkr-tmF5xqnNWy0jprXDy1xEZUyYy8",
|
||||||
|
authDomain: "jm-webpush.firebaseapp.com",
|
||||||
|
projectId: "jm-webpush",
|
||||||
|
storageBucket: "jm-webpush.appspot.com",
|
||||||
|
messagingSenderId: "903093229309",
|
||||||
|
appId: "1:903093229309:web:54a3006e1e1afdeaefd094",
|
||||||
|
measurementId: "G-SBKR7HBFBB"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize Firebase
|
||||||
|
const app = initializeApp(firebaseConfig);
|
||||||
|
// const analytics = getAnalytics(app);
|
||||||
|
|
||||||
|
// Initialize Firebase Cloud Messaging and get a reference to the service
|
||||||
|
const messaging = getMessaging(app);
|
||||||
|
|
||||||
|
// Add the public key generated from the console here.
|
||||||
|
getToken(messaging, { vapidKey: applicationServerPublicKey, serviceWorkerRegistration: reg }).then((currentToken) => {
|
||||||
|
if (currentToken) {
|
||||||
|
// Send the token to your server and update the UI if necessary
|
||||||
|
console.debug(`[Test] Firebase Token: ${currentToken}`);
|
||||||
|
} else {
|
||||||
|
// Show permission request UI
|
||||||
|
console.debug("[Test] No registration token available. Request permission to generate one.");
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.debug("[Test] An error occurred while retrieving token. ", err);
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function askForNotificationPermission() {
|
||||||
|
if ("Notification" in window) {
|
||||||
|
// console.log("Notification permission default status:", Notification.permission);
|
||||||
|
Notification.requestPermission(function (result: NotificationPermission) {
|
||||||
|
// 這裡result只會有兩種結果:一個是用戶允許(granted),另一個是用戶封鎖(denied)
|
||||||
|
// console.log("Notification permission status:", status);
|
||||||
|
if (result !== "granted") {
|
||||||
|
console.log("No notification permission granted!");
|
||||||
|
} else {
|
||||||
|
displayConfirmNotification();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayConfirmNotification() {
|
||||||
|
// pushNotification("成功訂閱!! (from Service Worker)", "您已成功訂閱我們的推播服務!");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pushNotification(title: string, content: string) {
|
||||||
|
if ("serviceWorker" in navigator && Notification.permission == "granted") {
|
||||||
|
const options = {
|
||||||
|
body: content,
|
||||||
|
icon: "../img/jpg/logo512.jpg"
|
||||||
|
};
|
||||||
|
// navigator.serviceWorker.ready.then(function (swreg: ServiceWorkerRegistration) {
|
||||||
|
navigator.serviceWorker.getRegistration(swjsPath).then(function (swreg: ServiceWorkerRegistration) {
|
||||||
|
swreg.showNotification(title, options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
20
src/utils/String.ts
Normal file
20
src/utils/String.ts
Normal file
@ -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) => {
|
||||||
|
const value = args[index];
|
||||||
|
if (value === null || value === undefined) return "";
|
||||||
|
return "" + value;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {};
|
48
src/utils/catan.ts
Normal file
48
src/utils/catan.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
declare namespace cc { }
|
||||||
|
declare namespace cc._decorator {
|
||||||
|
// export function ccclass(name?: string): Function;
|
||||||
|
// export function ccclass(_class?: Function): void;
|
||||||
|
}
|
||||||
|
declare let CC_PREVIEW: boolean;
|
||||||
|
namespace cc._decorator {
|
||||||
|
// export function ccclass(name?: string): Function { return () => { }; }
|
||||||
|
export const ccclass: any = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace cc {
|
||||||
|
export class path {
|
||||||
|
static basename(pathStr: string, extname?: string): any { }
|
||||||
|
}
|
||||||
|
export class sys {
|
||||||
|
static localStorage: any;
|
||||||
|
}
|
||||||
|
export class AudioClip {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export class Prefab {
|
||||||
|
|
||||||
|
}
|
||||||
|
export class Asset {
|
||||||
|
constructor(name?: string) { }
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export class assetManager {
|
||||||
|
static loadRemote<T extends cc.Asset>(url: string, onComplete: (err: Error, asset: T) => void): void { }
|
||||||
|
}
|
||||||
|
export class Node extends Asset {
|
||||||
|
getChildByName(name: string): Node { return undefined; }
|
||||||
|
ExAddChild(childObj: cc.Prefab | cc.Node, childActive?: boolean): cc.Node { return undefined; }
|
||||||
|
setParent(value: Node): void { }
|
||||||
|
active: boolean;
|
||||||
|
children: Node[];
|
||||||
|
}
|
||||||
|
export function log(msg: string | any, ...subst: any[]): void {
|
||||||
|
console.log(msg, ...subst);
|
||||||
|
}
|
||||||
|
export function warn(msg: string | any, ...subst: any[]): void {
|
||||||
|
console.warn(msg, ...subst);
|
||||||
|
}
|
||||||
|
export function error(msg: string | any, ...subst: any[]): void {
|
||||||
|
console.error(msg, ...subst);
|
||||||
|
}
|
||||||
|
}
|
47
src/utils/iframeUtils.ts
Normal file
47
src/utils/iframeUtils.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { CSSProperties } from "react";
|
||||||
|
import { sleep } from ".";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 動態創建iframe
|
||||||
|
* @param parent 容器節點
|
||||||
|
* @param url 遊戲網址
|
||||||
|
* @param style CSSProperties
|
||||||
|
*/
|
||||||
|
export function createIframe(parent: string, url: string, style?: CSSProperties): HTMLIFrameElement {
|
||||||
|
const iframe: HTMLIFrameElement = document.createElement("iframe");
|
||||||
|
iframe.src = url;
|
||||||
|
iframe.frameBorder = "0";
|
||||||
|
iframe.scrolling = "0";
|
||||||
|
for (let i: number = 0, keys: string[] = Object.keys(style); i < keys.length; i++) {
|
||||||
|
const key: string = keys[i];
|
||||||
|
iframe.style[key] = style[key];
|
||||||
|
}
|
||||||
|
document.getElementById(parent).appendChild(iframe);
|
||||||
|
return iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 銷毀 iframe
|
||||||
|
* @param parent 容器節點
|
||||||
|
*/
|
||||||
|
export async function destroyIframe(parent: string) {
|
||||||
|
const iframeContent: HTMLElement = document.getElementById(parent);
|
||||||
|
for (let i = 0; i < iframeContent.children.length; i++) {
|
||||||
|
const iframeNode: HTMLIFrameElement = iframeContent.children[i] as HTMLIFrameElement;
|
||||||
|
try {
|
||||||
|
iframeNode.src = "about:blank";
|
||||||
|
await sleep(10);
|
||||||
|
const iframeWindow = iframeNode.contentWindow;
|
||||||
|
iframeWindow.document.open();
|
||||||
|
iframeWindow.document.write("");
|
||||||
|
iframeWindow.document.clear && iframeWindow.document.clear();
|
||||||
|
iframeWindow.document.close();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error);
|
||||||
|
}
|
||||||
|
iframeNode.remove();
|
||||||
|
iframeNode.parentNode && iframeContent.removeChild(iframeNode);
|
||||||
|
// Cocos.CocosEventListener.RemoveAllCallbacks();
|
||||||
|
// await sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
652
src/utils/index.ts
Normal file
652
src/utils/index.ts
Normal file
@ -0,0 +1,652 @@
|
|||||||
|
import { ResourceItemType } from "@/Common/ResourceItem/ResourceItemType";
|
||||||
|
import { BaseEnumerator } from "@/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator";
|
||||||
|
import { TableManager } from "@/Engine/CatanEngine/TableV3/TableManager";
|
||||||
|
import CSSettingsV3 from "@/FormTable/CSSettingsV3";
|
||||||
|
import { ShopMycardTableRow, ShopShow2TableRow } from "@/FormTable/Tables/ShopTable";
|
||||||
|
import { Cocos } from "@/assets/VueScript/Cocos";
|
||||||
|
import { CocosVueScript } from "@/assets/VueScript/CocosVueScript";
|
||||||
|
import GameData_Cocos from "@/assets/VueScript/share/GameData_Cocos";
|
||||||
|
import { FriendRequest } from "@/define/Request/FriendRequest";
|
||||||
|
import { TxnRequest } from "@/define/Request/TxnRequest";
|
||||||
|
import { VIPLevelMapForChat } from "@/map";
|
||||||
|
import Player from "@/modules/player";
|
||||||
|
import { UserBindFlag } from "@/modules/player/define/userbind_flag";
|
||||||
|
import { ChatRoomRole, Games, ItemCodeList, ItemPropsType, ItemSize, PriceList, SelectorItemProps, StringContentType, TagTypes, TxnCenterData } from "@/types";
|
||||||
|
import liff from "@line/liff";
|
||||||
|
import axios, { AxiosResponse } from "axios";
|
||||||
|
import { isMobile } from "react-device-detect";
|
||||||
|
import * as Scroll from "react-scroll";
|
||||||
|
import stringWidth from "string-width";
|
||||||
|
import BusinessTypeSetting, { FolderName } from "../_BusinessTypeSetting/BusinessTypeSetting";
|
||||||
|
|
||||||
|
export const transArray = <T>(array: T[], split: number): T[][] => {
|
||||||
|
const newArr = [];
|
||||||
|
const copiedArr = array ? array?.slice() : [];
|
||||||
|
const length = copiedArr?.length;
|
||||||
|
for (let i = 0, j = 0; i < length; i += split, j++) {
|
||||||
|
newArr[j] = copiedArr.splice(0, split);
|
||||||
|
}
|
||||||
|
return newArr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 去掉英文+百分比% */
|
||||||
|
export function onlyNumber(stringText: string): number {
|
||||||
|
const str = +stringText.replace(/[A-Za-z%]/g, "");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wordsLimit(limit: number, string: string) {
|
||||||
|
const length = string?.length;
|
||||||
|
const isOverLimit = length > limit;
|
||||||
|
const result = isOverLimit ? "..." : "";
|
||||||
|
return string?.substring(0, limit) + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const capitalize = (str: string) =>
|
||||||
|
`${str.charAt(0).toUpperCase()}${str.slice(1)}`;
|
||||||
|
|
||||||
|
export function checkForUnique(str: string): boolean {
|
||||||
|
const chineseCharacterMatch =
|
||||||
|
/[\p{Unified_Ideograph}\u3006\u3007][\ufe00-\ufe0f\u{e0100}-\u{e01ef}]?/gmu;
|
||||||
|
const arr = str.match(chineseCharacterMatch);
|
||||||
|
if (arr === null) return true;
|
||||||
|
return !hasDuplicates(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onlyChinese(str: string) {
|
||||||
|
const chineseCharacterMatch =
|
||||||
|
/[\p{Unified_Ideograph}\u3006\u3007][\ufe00-\ufe0f\u{e0100}-\u{e01ef}]?/gmu;
|
||||||
|
const arr = str.match(chineseCharacterMatch);
|
||||||
|
return arr.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasDuplicates(array: string[]): boolean {
|
||||||
|
return new Set(array).size !== array.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generatePriceList(
|
||||||
|
arr: [ID: number, ProductId: number, ShowMoney: number][],
|
||||||
|
items: { [id: string]: [ID: number, price: number] },
|
||||||
|
priceRef: ShopMycardTableRow[],
|
||||||
|
itemCodeList?: Map<number, ItemCodeList>,
|
||||||
|
): PriceList[] {
|
||||||
|
if (itemCodeList) {
|
||||||
|
arr = arr.filter((item) => itemCodeList.has(item[0]));
|
||||||
|
}
|
||||||
|
const MyCardPriceRefMap = new Map<number, number>(
|
||||||
|
priceRef.map((item) => [item.Id, item.Price]),
|
||||||
|
);
|
||||||
|
const newArr: [ID: number, ProductId: number, ShowMoney: number][] = [];
|
||||||
|
for (const item of Object.values(items)) {
|
||||||
|
arr.forEach((v: [ID: number, ProductId: number, ShowMoney: number]) => {
|
||||||
|
if (v[1] === item[0]) {
|
||||||
|
newArr.push(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return newArr
|
||||||
|
.map((item) => ({
|
||||||
|
ID: item[0],
|
||||||
|
points: item[2],
|
||||||
|
price: MyCardPriceRefMap.get(item[1]),
|
||||||
|
}))
|
||||||
|
.sort((a, b) => b.price - a.price);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 支付列表 */
|
||||||
|
export function generatePaymentList(
|
||||||
|
arr: string[],
|
||||||
|
priceRef: ShopShow2TableRow[],
|
||||||
|
): ShopShow2TableRow[] {
|
||||||
|
const newArr: ShopShow2TableRow[] = [];
|
||||||
|
for (const item of Object.values(priceRef)) {
|
||||||
|
arr.forEach((x) => {
|
||||||
|
if (item.Show && x === item.Key) newArr.push(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// priceRef.forEach((value: ShopShow2TableRow) => {
|
||||||
|
// arr.includes(value.Key);
|
||||||
|
// })
|
||||||
|
return newArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** getQueryParameters */
|
||||||
|
export function getQueryParameters(v: string): string {
|
||||||
|
const queryParameters = new URLSearchParams(location.search);
|
||||||
|
return queryParameters.get(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMap(obj: unknown) {
|
||||||
|
return new Map(Object.entries(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isArray(itemCode: string | string[]): boolean {
|
||||||
|
return Array.isArray(itemCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTime(date: Date): string {
|
||||||
|
const hours = date.getHours();
|
||||||
|
const minutes = date.getMinutes();
|
||||||
|
const amPm = hours >= 12 ? "pm" : "am";
|
||||||
|
const formattedHours = hours % 12 === 0 ? 12 : hours % 12;
|
||||||
|
const formattedMinutes = minutes < 10 ? "0" + minutes : minutes;
|
||||||
|
return `${formattedHours}:${formattedMinutes} ${amPm}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentLocalTime() {
|
||||||
|
const date = new Date();
|
||||||
|
const utcTime = date.getTime();
|
||||||
|
return new Date(utcTime).toLocaleTimeString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transferColorText(str: string): string {
|
||||||
|
const regex = /color=/g;
|
||||||
|
const replacement = "span style=color:";
|
||||||
|
const result = str.replace(regex, replacement);
|
||||||
|
const regex2 = /color>/g;
|
||||||
|
const replacement2 = "span>";
|
||||||
|
return result.replace(regex2, replacement2);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateItemsData(
|
||||||
|
games: Map<string, number[]>,
|
||||||
|
favoriteGames: number[],
|
||||||
|
): ItemPropsType[] {
|
||||||
|
let itemsData: boolean | ItemPropsType[] = [];
|
||||||
|
const s = new Set(favoriteGames);
|
||||||
|
if (games.size) {
|
||||||
|
// @ts-ignore
|
||||||
|
for (const [gameID, [vendorID, id, VIPLimit, status, tag]] of games) {
|
||||||
|
const dataObj: ItemPropsType = {
|
||||||
|
id: gameID,
|
||||||
|
vendorID: vendorID.toString(),
|
||||||
|
img: {
|
||||||
|
url: `${BusinessTypeSetting.UseDownloadUrl}${FolderName.Game}${id}/b`,
|
||||||
|
},
|
||||||
|
size: ItemSize.small,
|
||||||
|
tag: tag as unknown as TagTypes,
|
||||||
|
lockBtn: VIPLimit,
|
||||||
|
like: s.has(id),
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
|
||||||
|
itemsData.push(dataObj);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itemsData = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemsData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateItemsDataMap(games: Games): Map<string, number[]> {
|
||||||
|
const map = new Map();
|
||||||
|
for (const [gameID, value] of Object.entries(games)) {
|
||||||
|
map.set(gameID, value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortedGames(sorts: number[], map: Map<string, number[]>) {
|
||||||
|
const obj = new Map();
|
||||||
|
sorts?.forEach((gameID) => {
|
||||||
|
obj.set("" + gameID, map?.get("" + gameID));
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addPropertiesToDefaultItem(
|
||||||
|
games: any[],
|
||||||
|
selectedID: number,
|
||||||
|
): SelectorItemProps[] {
|
||||||
|
const index = games.findIndex((item) => parseInt(item.id) === selectedID);
|
||||||
|
const newGames = games.slice();
|
||||||
|
|
||||||
|
return newGames.map((g, i) =>
|
||||||
|
i === index
|
||||||
|
? { ...g, selected: true, defaultItem: true }
|
||||||
|
: {
|
||||||
|
...g,
|
||||||
|
selected: false,
|
||||||
|
defaultItem: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function switchObjToMap(games: Games) {
|
||||||
|
// for (const [gameID, value] of Object.entries(games)) {
|
||||||
|
// map.set(gameID, value)
|
||||||
|
// }
|
||||||
|
return new Map(Object.entries(games));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hexToRgb(
|
||||||
|
hex: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/typedef
|
||||||
|
result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex),
|
||||||
|
) {
|
||||||
|
const x = result ? result.map((i) => parseInt(i, 16)).slice(1) : null;
|
||||||
|
return {
|
||||||
|
r: x[0],
|
||||||
|
g: x[1],
|
||||||
|
b: x[2],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceBorderColor(string: string, color: string) {
|
||||||
|
const hasComma = string.includes(",");
|
||||||
|
if (hasComma) {
|
||||||
|
const result = string.split(",").map((s, i) => {
|
||||||
|
const index = s.indexOf("#");
|
||||||
|
const replaceStr = s.substring(index);
|
||||||
|
return s.replace(replaceStr, i === 0 ? "black" : color);
|
||||||
|
});
|
||||||
|
return result.join(",");
|
||||||
|
} else {
|
||||||
|
const index = string.indexOf("#");
|
||||||
|
const replaceStr = string.substring(index);
|
||||||
|
return string.replace(replaceStr, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateMessage(message: string) {
|
||||||
|
return {
|
||||||
|
AID: "10000000063",
|
||||||
|
nickName: "masterkai",
|
||||||
|
profileIMG: "./img/png/avatar.png",
|
||||||
|
role: ChatRoomRole.player,
|
||||||
|
message: message,
|
||||||
|
created: getCurrentLocalTime(),
|
||||||
|
vip: VIPLevelMapForChat.get(4),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getVIPLevelFromStr(vipStr: string) {
|
||||||
|
const arr = vipStr.split(".");
|
||||||
|
const str = arr[0];
|
||||||
|
return str.at(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Accumulator {
|
||||||
|
[AID: number]: {
|
||||||
|
AID: number;
|
||||||
|
nickName: string;
|
||||||
|
avatar: number;
|
||||||
|
role: number;
|
||||||
|
vip: number;
|
||||||
|
messages: {
|
||||||
|
message: string;
|
||||||
|
created: number;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sleep(ms: any): Promise<any> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downloadJSON(formname: string) {
|
||||||
|
const patchUrl: string =
|
||||||
|
BusinessTypeSetting.UsePatch + BusinessTypeSetting.FolderUrlJson;
|
||||||
|
let fileUrl: string = `${patchUrl}${formname}.json`;
|
||||||
|
fileUrl = fileUrl + "?v=" + Date.now();
|
||||||
|
let resp: AxiosResponse<any, any> = null;
|
||||||
|
axios.get(fileUrl).then((res: AxiosResponse<any, any>) => {
|
||||||
|
loadJsonProcess(null, res, formname);
|
||||||
|
resp = res;
|
||||||
|
});
|
||||||
|
while (!resp) {
|
||||||
|
await sleep(0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadJsonProcess(
|
||||||
|
err: any,
|
||||||
|
res: AxiosResponse<any, any>,
|
||||||
|
formname: string,
|
||||||
|
) {
|
||||||
|
res["name"] = formname;
|
||||||
|
TableManager.AddJsonAsset(res.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProfileImgUrl(avatar: number, aId: number) {
|
||||||
|
return avatar === 1
|
||||||
|
? `${BusinessTypeSetting.UseDownloadUrl}avatar/${aId}`
|
||||||
|
: "./img/common/DefaultAvatar.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CssStringContent = (
|
||||||
|
StringKey: number,
|
||||||
|
StringID: number | string,
|
||||||
|
): string => {
|
||||||
|
switch (StringKey) {
|
||||||
|
case StringContentType.String:
|
||||||
|
return StringID?.toString();
|
||||||
|
case StringContentType.CSSString:
|
||||||
|
return CSSettingsV3.prototype.CommonString(+StringID);
|
||||||
|
case StringContentType.CSSMailString:
|
||||||
|
return CSSettingsV3.prototype.CSSMailString(+StringID);
|
||||||
|
case StringContentType.CSSNetworkString:
|
||||||
|
return ""; // CSSettingsV3.Network.Priority[StringID][LanguageManager.GetMsgId()];
|
||||||
|
case StringContentType.HallString:
|
||||||
|
/* 廳管1~4 但表格是從2號位開始 */
|
||||||
|
return CSSettingsV3.prototype.LobbyString(1 + +StringID);
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function txnDataTransformer(
|
||||||
|
arr: TxnRequest.TxnInfo[],
|
||||||
|
playerAid: number,
|
||||||
|
): TxnCenterData[] {
|
||||||
|
return arr.map(
|
||||||
|
([
|
||||||
|
sn,
|
||||||
|
time,
|
||||||
|
giver,
|
||||||
|
receiver,
|
||||||
|
status,
|
||||||
|
fee,
|
||||||
|
[[category, categoryId, quantity]],
|
||||||
|
]) => ({
|
||||||
|
serialNum: sn,
|
||||||
|
createdAt: time,
|
||||||
|
giverAid: giver[0],
|
||||||
|
giverName: giver[1],
|
||||||
|
isGiver: giver[0] === playerAid,
|
||||||
|
receiverAid: receiver[0],
|
||||||
|
receiverName: receiver[1],
|
||||||
|
status: +status,
|
||||||
|
quantity,
|
||||||
|
fee,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculatedReward(rewards: [number, number][]) {
|
||||||
|
const maximum = 6;
|
||||||
|
const result = [];
|
||||||
|
const couponType = ResourceItemType.Card_Coupon;
|
||||||
|
rewards.forEach(([type, quantity]) => {
|
||||||
|
if (type === couponType && quantity > maximum) {
|
||||||
|
const packNum = Math.floor(quantity / maximum);
|
||||||
|
const returnPack = Array.from({ length: packNum }, () => [
|
||||||
|
couponType,
|
||||||
|
maximum,
|
||||||
|
]);
|
||||||
|
const remainder = quantity % maximum;
|
||||||
|
const returnRemainder = remainder ? [couponType, remainder] : null;
|
||||||
|
if (returnRemainder) {
|
||||||
|
returnPack.push(returnRemainder);
|
||||||
|
}
|
||||||
|
returnPack.forEach((item) => result.push(item));
|
||||||
|
} else {
|
||||||
|
result.push([type, quantity]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const random = (min: number, max: number) =>
|
||||||
|
Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
|
||||||
|
// Default color is a bright yellow
|
||||||
|
const DEFAULT_COLOR = "hsl(50deg, 100%, 50%)";
|
||||||
|
export const generateSparkle = (color = DEFAULT_COLOR) => {
|
||||||
|
return {
|
||||||
|
id: "" + random(10000, 99999),
|
||||||
|
createdAt: Date.now(),
|
||||||
|
// Bright yellow color:
|
||||||
|
color,
|
||||||
|
size: random(20, 60),
|
||||||
|
style: {
|
||||||
|
// Pick a random spot in the available space
|
||||||
|
top: random(0, 100) + "%",
|
||||||
|
left: random(0, 100) + "%",
|
||||||
|
// Float sparkles above sibling content
|
||||||
|
zIndex: 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const range = (start, end, step = 1) => {
|
||||||
|
const output = [];
|
||||||
|
if (typeof end === "undefined") {
|
||||||
|
end = start;
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
for (let i = start; i < end; i += step) {
|
||||||
|
output.push(i);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function waitSetBusinessType() {
|
||||||
|
while (!BusinessTypeSetting.UseHost) {
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否為好友
|
||||||
|
* @param {number} aId AID
|
||||||
|
*/
|
||||||
|
export function isMyFriend(aId: number): boolean {
|
||||||
|
let isTrue: boolean = false;
|
||||||
|
const playerData = Player.data.getState();
|
||||||
|
const lists: FriendRequest.ListFriendData = playerData.account.allowList;
|
||||||
|
for (let i = 0; i < lists.length; i++) {
|
||||||
|
const list: FriendRequest.SingleFriendData = lists[i];
|
||||||
|
if (list[0] === aId) {
|
||||||
|
isTrue = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否為黑單
|
||||||
|
* @param {number} aId AID
|
||||||
|
*/
|
||||||
|
export function isMyDeny(aId: number): boolean {
|
||||||
|
let isTrue: boolean = false;
|
||||||
|
const playerData = Player.data.getState();
|
||||||
|
const lists: FriendRequest.ListFriendData = playerData.account.denyList;
|
||||||
|
for (let i = 0; i < lists.length; i++) {
|
||||||
|
const list: FriendRequest.SingleFriendData = lists[i];
|
||||||
|
if (list[0] === aId) {
|
||||||
|
isTrue = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sheetNameResourceTypeSwitcher = (
|
||||||
|
resourceType: ResourceItemType,
|
||||||
|
) => {
|
||||||
|
switch (resourceType) {
|
||||||
|
case ResourceItemType.Card_Coupon:
|
||||||
|
return "CouponSetting";
|
||||||
|
case ResourceItemType.Card:
|
||||||
|
return "Card1Setting";
|
||||||
|
default:
|
||||||
|
return "Card1Setting";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function escapeCodesNToBr(v: string): string {
|
||||||
|
return v.replace(/\n/g, "<br>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切割顯示字串長度
|
||||||
|
* @param str
|
||||||
|
* @param showBytes 字元數(1中文2BYTES)
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function trimString(
|
||||||
|
str: string,
|
||||||
|
showBytes: number = 12,
|
||||||
|
ellipses: boolean = true,
|
||||||
|
): string {
|
||||||
|
if (!str) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
let bytes: number = stringWidth(str);
|
||||||
|
if (bytes <= showBytes) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
let byteAmount: number = 0;
|
||||||
|
let strLength: number = str.length;
|
||||||
|
for (let i: number = 0; i < strLength; i++) {
|
||||||
|
let word: string = str[i];
|
||||||
|
bytes = stringWidth(word);
|
||||||
|
byteAmount += bytes;
|
||||||
|
if (byteAmount > showBytes) {
|
||||||
|
let checkStr: string = str.substring(0, i + 1);
|
||||||
|
let checkByte: number = stringWidth(checkStr);
|
||||||
|
if (checkByte < showBytes) {
|
||||||
|
byteAmount = checkByte;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let result: string = str.substring(0, i);
|
||||||
|
if (ellipses) {
|
||||||
|
return result + "...";
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.error("Trim Nickname Error.");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** CommonEventType */
|
||||||
|
export enum CommonEventType {
|
||||||
|
/** Maintenance */
|
||||||
|
Maintenance,
|
||||||
|
/**ActivityReRender */
|
||||||
|
ActivityReRender,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function responsiveText(characters: number): number {
|
||||||
|
if (characters <= 10) return 1.125;
|
||||||
|
if (characters > 10 && characters <= 20) return 0.9;
|
||||||
|
if (characters > 20) return 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function discount(numberOff: number): number {
|
||||||
|
return (100 - numberOff) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 預載字體 */
|
||||||
|
export function PreloadFont(fonts: string[]): void {
|
||||||
|
// Check if API exists
|
||||||
|
if (document && document.fonts) {
|
||||||
|
// Do not block page loading
|
||||||
|
setTimeout(function (): void {
|
||||||
|
let successCount: number = 0;
|
||||||
|
for (let i: number = 0; i < fonts.length; i++) {
|
||||||
|
const font: string = fonts[i];
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
document.fonts.load(`16px ${font}`).then(() => {
|
||||||
|
// Make font using elements visible
|
||||||
|
successCount++;
|
||||||
|
if (successCount === fonts.length) {
|
||||||
|
document.documentElement.classList.add("font-loaded");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
} else {
|
||||||
|
// Fallback if API does not exist
|
||||||
|
document.documentElement.classList.add("font-loaded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PWA
|
||||||
|
/** BeforeInstallPromptEvent */
|
||||||
|
export let deferredPrompt: any;
|
||||||
|
window.addEventListener("beforeinstallprompt", (e) => {
|
||||||
|
// Prevent Chrome 67 and earlier from automatically showing the prompt
|
||||||
|
e.preventDefault();
|
||||||
|
// Stash the event so it can be triggered later.
|
||||||
|
deferredPrompt = e;
|
||||||
|
// Update UI to notify the user they can add to home screen
|
||||||
|
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.PWAInitOK, e);
|
||||||
|
});
|
||||||
|
export function addToHomeScreen(): void {
|
||||||
|
if (isMobile) {
|
||||||
|
let url: string =
|
||||||
|
BusinessTypeSetting.UsePatch +
|
||||||
|
"addtohomescreen/index.html" +
|
||||||
|
"?v=" +
|
||||||
|
Date.now();
|
||||||
|
liff.openWindow({
|
||||||
|
url: url,
|
||||||
|
external: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (deferredPrompt) {
|
||||||
|
// Show the prompt
|
||||||
|
deferredPrompt.prompt();
|
||||||
|
// Wait for the user to respond to the prompt
|
||||||
|
deferredPrompt.userChoice.then((choiceResult) => {
|
||||||
|
if (choiceResult.outcome === "accepted") {
|
||||||
|
// console.log("User accepted the A2HS prompt");
|
||||||
|
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.PWAInitOK, false);
|
||||||
|
} else {
|
||||||
|
// console.log("User dismissed the A2HS prompt");
|
||||||
|
}
|
||||||
|
deferredPrompt = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getArray(count: number): number[] {
|
||||||
|
const array: number[] = [];
|
||||||
|
for (let i: number = 0; i < count; i++) {
|
||||||
|
array.push(i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 判斷登入並且Line綁定完 */
|
||||||
|
export function checkWait(): boolean {
|
||||||
|
const playerData = Player.data.getState();
|
||||||
|
const isLineBind: boolean = Player.hasUserBindFlag(UserBindFlag.LineBind);
|
||||||
|
if (!BaseEnumerator.isInit) {
|
||||||
|
return true;
|
||||||
|
} else if (!CocosVueScript.Instance || !CocosVueScript.Instance?.GetLoginData()) {
|
||||||
|
return true;
|
||||||
|
} else if (!playerData.account.role && !isLineBind) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scrollToBottom(dynamicListHeight: number, duration: number = 200) {
|
||||||
|
Scroll.animateScroll.scrollTo(dynamicListHeight, {
|
||||||
|
duration: duration,
|
||||||
|
smooth: "easeInQuad",
|
||||||
|
containerId: "scrollableDiv",
|
||||||
|
offset: 50
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Copy(serialNum: string) {
|
||||||
|
try {
|
||||||
|
navigator.clipboard.writeText(serialNum);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
235
src/utils/setRPCData.ts
Normal file
235
src/utils/setRPCData.ts
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
import MainControl from "@/Common/MainControl/MainControl";
|
||||||
|
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
|
||||||
|
import { TTeamBattleData } from "@/UI/RouterPage/GameContent/GameContentUtils";
|
||||||
|
import { Cocos } from "@/assets/VueScript/Cocos";
|
||||||
|
import { CommonEventCallBack } from "@/assets/VueScript/CocosVueScript";
|
||||||
|
import GameData_Cocos from "@/assets/VueScript/share/GameData_Cocos";
|
||||||
|
import { GiftCallBack, GiftEventEnum, PanelType, UpdateOneListInfo } from "@/components/ModalContent/TxnModal/txnUtils";
|
||||||
|
import { gameObj } from "@/context/GameItemsContext";
|
||||||
|
import { LocalStorage } from "@/define";
|
||||||
|
import { EActivitySyncType, RpcActivityComSyncResponse, TActivityComSyncData, TActivitySyncData } from "@/define/Request/ActivityRequest";
|
||||||
|
import { ResponseBackpackInfo } from "@/define/Request/BackpackRequest";
|
||||||
|
import { FriendRequest } from "@/define/Request/FriendRequest";
|
||||||
|
import { ProfileRequest } from "@/define/Request/ProfileRequest";
|
||||||
|
import { TxnRequest } from "@/define/Request/TxnRequest";
|
||||||
|
import { VipRequest } from "@/define/Request/VIPRequest";
|
||||||
|
import Player from "@/modules/player";
|
||||||
|
import { State } from "@/modules/player/define/data";
|
||||||
|
import { BackpackItemData } from "@/modules/player/define/data/backpack";
|
||||||
|
import { CommonEventType } from ".";
|
||||||
|
|
||||||
|
let SN: number = 0;
|
||||||
|
|
||||||
|
export function profileInfo(data: ProfileRequest.InfoResponse): void {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
if (data.aId !== playerData.account.aId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
playerData.account.name = data.name;
|
||||||
|
playerData.account.message = data.msg;
|
||||||
|
playerData.account.phone = data.phone;
|
||||||
|
playerData.account.money = data.money;
|
||||||
|
playerData.vip.level = data.vip;
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function vipInfo(data: VipRequest.InfoResponse): void {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
playerData.vip.level = data.level;
|
||||||
|
playerData.vip.totalBet = data.bet;
|
||||||
|
playerData.vip.totalCharge = data.sv;
|
||||||
|
playerData.vip.rich = data.rich;
|
||||||
|
playerData.vip.et = data.et;
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function friendAllowList(data: FriendRequest.ListFriendData): void {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
playerData.account.allowList = data;
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function friendDenyList(data: FriendRequest.ListFriendData): void {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
playerData.account.denyList = data;
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function activityComSync(data: RpcActivityComSyncResponse): void {
|
||||||
|
const { teamBattleData, setTeamBattleData } = gameObj;
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const activityComSyncData: TActivityComSyncData = data[i];
|
||||||
|
const [id, activitySyncDatas] = activityComSyncData;
|
||||||
|
// for (let j = 0; j < teamBattleData.length; j++) {
|
||||||
|
// const teamBattle: TTeamBattleData = teamBattleData[j];
|
||||||
|
// const [teamBattleId, , ,] = teamBattle;
|
||||||
|
// if (id === teamBattleId) {
|
||||||
|
// for (let k = 0; k < activitySyncDatas.length; k++) {
|
||||||
|
// const activitySyncData: TActivitySyncData = activitySyncDatas[k];
|
||||||
|
// const [type, value] = activitySyncData;
|
||||||
|
// switch (type) {
|
||||||
|
// case EActivitySyncType.IsOpen: {
|
||||||
|
// teamBattleData[j][1] = -1;
|
||||||
|
// setTeamBattleData(teamBattleData);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// case EActivitySyncType.Sync: {
|
||||||
|
// teamBattleData[j][1] = value;
|
||||||
|
// setTeamBattleData(teamBattleData);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// case EActivitySyncType.Task: {
|
||||||
|
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// default:
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
for (let j = 0; j < activitySyncDatas.length; j++) {
|
||||||
|
const activitySyncData: TActivitySyncData = activitySyncDatas[j];
|
||||||
|
const [type, value] = activitySyncData;
|
||||||
|
switch (type) {
|
||||||
|
case EActivitySyncType.IsOpen: {
|
||||||
|
// for (let k = 0; k < teamBattleData.length; k++) {
|
||||||
|
// const teamBattle: TTeamBattleData = teamBattleData[k];
|
||||||
|
// const [teamBattleId, , ,] = teamBattle;
|
||||||
|
// if (id === teamBattleId) {
|
||||||
|
// teamBattleData[j][1] = value;
|
||||||
|
// setTeamBattleData(teamBattleData);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EActivitySyncType.Sync: {
|
||||||
|
for (let k = 0; k < teamBattleData.length; k++) {
|
||||||
|
const teamBattle: TTeamBattleData = teamBattleData[k];
|
||||||
|
const [teamBattleId, , ,] = teamBattle;
|
||||||
|
if (id === teamBattleId) {
|
||||||
|
teamBattleData[j][1] = value;
|
||||||
|
setTeamBattleData(teamBattleData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EActivitySyncType.Task: {
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommonEventCallBack.DispatchCallback(CommonEventType.ActivityReRender, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function backpackInfo(data: ResponseBackpackInfo[], isAdd: boolean = false): void {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
const backpackList: BackpackItemData[] = isAdd ? playerData.backpack.Copy() : [];
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const backpackServerData = data[i];
|
||||||
|
const id: number = backpackServerData[1][0];
|
||||||
|
const count: number = backpackServerData[1][1];
|
||||||
|
for (let j = 0; j < count; j++) {
|
||||||
|
const backpackClientData: BackpackItemData = {
|
||||||
|
SN: SN,
|
||||||
|
ResourceType: backpackServerData[0],
|
||||||
|
ID: id,
|
||||||
|
Viewed: false,
|
||||||
|
};
|
||||||
|
backpackList.push(backpackClientData);
|
||||||
|
SN++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isAdd) {
|
||||||
|
const oldBackpackListStr: string = localStorage.getItem(LocalStorage.Key.Backpack);
|
||||||
|
if (oldBackpackListStr) {
|
||||||
|
const oldBackpackList: BackpackItemData[] = JSON.parse(oldBackpackListStr);
|
||||||
|
for (let i = 0; i < backpackList.length; i++) {
|
||||||
|
const backpack: BackpackItemData = backpackList[i];
|
||||||
|
for (let j = 0; j < oldBackpackList.length; j++) {
|
||||||
|
const oldBackpack: BackpackItemData = oldBackpackList[j];
|
||||||
|
if (backpack.ID === oldBackpack.ID && backpack.ResourceType === oldBackpack.ResourceType) {
|
||||||
|
backpackList[i].Viewed = oldBackpack.Viewed;
|
||||||
|
oldBackpackList.splice(j, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playerData.backpack = backpackList;
|
||||||
|
localStorage.setItem(LocalStorage.Key.Backpack, JSON.stringify(playerData.backpack));
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
|
||||||
|
const totalUnreadCount: number = playerData.backpack.filter((item) => !item.Viewed).length;
|
||||||
|
if (!MainControl.Instance.IsInGame) {
|
||||||
|
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.SetBackpackUnreadCount, totalUnreadCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function txnNew(data: TxnRequest.TxnInfo): void {
|
||||||
|
const playerData = Player.data.getState();
|
||||||
|
const centerList = playerData.txn.centerList.slice();
|
||||||
|
centerList.push(data);
|
||||||
|
playerData.txn.centerList = centerList;
|
||||||
|
GiftCallBack.DispatchCallback(GiftEventEnum.ReFlash, null);
|
||||||
|
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.SetTxnUnreadCount, playerData.txn.centerList.length);
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function txnCenter(data: TxnRequest.TxnInfo[]): void {
|
||||||
|
const playerData = Player.data.getState();
|
||||||
|
playerData.txn.centerList = data;
|
||||||
|
GiftCallBack.DispatchCallback(GiftEventEnum.ReFlash, null);
|
||||||
|
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.SetTxnUnreadCount, playerData.txn.centerList.length);
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function txnTrade(data: TxnRequest.TradeResponse): void {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
const centerList: TxnRequest.TxnInfo[] = playerData.txn.centerList;
|
||||||
|
let type: number = PanelType.RecordPanel;
|
||||||
|
let status: number = +data.s;
|
||||||
|
if (status < 20) {
|
||||||
|
type = PanelType.CenterPanel;
|
||||||
|
switch (status) {
|
||||||
|
case 11:
|
||||||
|
case 12:
|
||||||
|
case 13: {
|
||||||
|
for (let i: number = 0; i < centerList.length; i++) {
|
||||||
|
const centerData: TxnRequest.TxnInfo = centerList[i];
|
||||||
|
if (centerData[0] == +data.id && centerData[3][0] == playerData.account.aId) {
|
||||||
|
type = PanelType.RecordPanel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoroutineV2.Single(UpdateOneListInfo(data.id, type)).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function txnUserAdd(data: TxnRequest.UserAddResponse): void {
|
||||||
|
const playerData: State = Player.data.getState();
|
||||||
|
playerData.txn.receiverList.push(data.u);
|
||||||
|
Player.data.setState(playerData);
|
||||||
|
}
|
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
38
src/zSbmoduleUpdate.bat
Normal file
38
src/zSbmoduleUpdate.bat
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
@echo off
|
||||||
|
title Git Working
|
||||||
|
goto selectAll
|
||||||
|
|
||||||
|
:selectAll
|
||||||
|
echo ---------------------------------------------------
|
||||||
|
set n=0
|
||||||
|
echo 1.第一次取專案設定所需子儲存庫
|
||||||
|
echo 2.所有子儲存庫呼叫小烏龜PULL視窗
|
||||||
|
echo *注意此儲存庫也會跟著呼叫.沒有錯誤會自動關閉
|
||||||
|
|
||||||
|
set/p n= 請選擇:
|
||||||
|
|
||||||
|
if "%n%"=="1" ( goto change )
|
||||||
|
|
||||||
|
if "%n%"=="2" ( goto childupdate )
|
||||||
|
goto exit
|
||||||
|
|
||||||
|
:change
|
||||||
|
echo 第一次取專案設定所需子儲存庫
|
||||||
|
echo.
|
||||||
|
git.exe clone --progress -v "git@git.catan.com.tw:Line_Project_1/Plan-Form_Table.git" FormTable
|
||||||
|
git.exe clone --progress -v "git@git.catan.com.tw:Frontend/FormTableExt.git" FormTableExt
|
||||||
|
echo 結束
|
||||||
|
goto selectAll
|
||||||
|
|
||||||
|
:childupdate
|
||||||
|
echo 所有子儲存庫呼叫小烏龜PULL視窗
|
||||||
|
cd FormTable
|
||||||
|
TortoiseGitProc -command:pull /closeonend:2
|
||||||
|
echo path:FormTable done.
|
||||||
|
cd ..
|
||||||
|
cd FormTableExt
|
||||||
|
TortoiseGitProc -command:pull /closeonend:2
|
||||||
|
echo path:FormTableExt done.
|
||||||
|
cd ..
|
||||||
|
echo 結束
|
||||||
|
goto selectAll
|
41
tsconfig.json
Normal file
41
tsconfig.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2015",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": false,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": false,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"types": [
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src",
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
tsconfig.node.json
Normal file
11
tsconfig.node.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"vite.config.ts"
|
||||||
|
]
|
||||||
|
}
|
2
vite.config.d.ts
vendored
Normal file
2
vite.config.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
declare const _default: import("vite").UserConfig;
|
||||||
|
export default _default;
|
28
vite.config.ts
Normal file
28
vite.config.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react-swc";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
define: {
|
||||||
|
"AppVersion": JSON.stringify(process.env.npm_package_version),
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: [
|
||||||
|
{ find: "@", replacement: path.resolve(__dirname, "src") },
|
||||||
|
{ find: "@views", replacement: path.resolve(__dirname, "src/views") },
|
||||||
|
{ find: "@assets", replacement: path.resolve(__dirname, "src/assets") },
|
||||||
|
{ find: "@css", replacement: path.resolve(__dirname, "src/assets/css") },
|
||||||
|
{ find: "@less", replacement: path.resolve(__dirname, "src/assets/less") },
|
||||||
|
{ find: "@images", replacement: path.resolve(__dirname, "src/assets/images") },
|
||||||
|
{ find: "@components", replacement: path.resolve(__dirname, "src/components") },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
publicDir: "build-templates",
|
||||||
|
build: {
|
||||||
|
outDir: "./build"
|
||||||
|
},
|
||||||
|
base: "./",
|
||||||
|
envDir: "./viteEnv",
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user