hzengine-core 0.1.2-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/async/index.js +162 -0
- package/dist/async/zeppos_timer.js +58 -0
- package/dist/audio/index.js +260 -0
- package/dist/config/index.js +57 -0
- package/dist/debug/index.js +8 -0
- package/dist/index.js +103 -0
- package/dist/platform/index.js +1 -0
- package/dist/plugins/basic_command/$.js +8 -0
- package/dist/plugins/basic_command/audio.js +40 -0
- package/dist/plugins/basic_command/basic.js +124 -0
- package/dist/plugins/basic_command/character.js +112 -0
- package/dist/plugins/basic_command/conditional.js +260 -0
- package/dist/plugins/basic_command/config.js +22 -0
- package/dist/plugins/basic_command/decorator.js +24 -0
- package/dist/plugins/basic_command/eval.js +67 -0
- package/dist/plugins/basic_command/img.js +249 -0
- package/dist/plugins/basic_command/index.js +22 -0
- package/dist/plugins/basic_command/menu.js +140 -0
- package/dist/plugins/global_gesture/index.js +25 -0
- package/dist/plugins/transform/animation.js +440 -0
- package/dist/plugins/transform/commands.js +38 -0
- package/dist/plugins/transform/example_profiles.js +32 -0
- package/dist/plugins/transform/hz_anime.js +211 -0
- package/dist/plugins/transform/index.js +93 -0
- package/dist/script/index.js +537 -0
- package/dist/script/readscript.js +15 -0
- package/dist/script/strtools.js +157 -0
- package/dist/storage/decorator.js +260 -0
- package/dist/storage/fs.js +96 -0
- package/dist/storage/index.js +442 -0
- package/dist/system/index.js +144 -0
- package/dist/ui/index.js +535 -0
- package/dist/utils/path.js +289 -0
- package/license.txt +202 -0
- package/package.json +26 -0
- package/src/async/index.ts +124 -0
- package/src/async/zeppos_timer.js +65 -0
- package/src/audio/index.ts +224 -0
- package/src/config/index.ts +80 -0
- package/src/debug/index.ts +11 -0
- package/src/index.ts +122 -0
- package/src/platform/index.ts +158 -0
- package/src/plugins/basic_command/$.ts +11 -0
- package/src/plugins/basic_command/audio.ts +53 -0
- package/src/plugins/basic_command/basic.ts +145 -0
- package/src/plugins/basic_command/character.ts +144 -0
- package/src/plugins/basic_command/conditional.ts +349 -0
- package/src/plugins/basic_command/config.ts +29 -0
- package/src/plugins/basic_command/decorator.ts +29 -0
- package/src/plugins/basic_command/eval.ts +88 -0
- package/src/plugins/basic_command/img.ts +317 -0
- package/src/plugins/basic_command/index.ts +24 -0
- package/src/plugins/basic_command/menu.ts +178 -0
- package/src/plugins/global_gesture/index.ts +29 -0
- package/src/plugins/transform/animation.ts +542 -0
- package/src/plugins/transform/commands.ts +53 -0
- package/src/plugins/transform/example_profiles.ts +36 -0
- package/src/plugins/transform/hz_anime.ts +214 -0
- package/src/plugins/transform/index.ts +141 -0
- package/src/plugins/transform/readme.md +1 -0
- package/src/script/index.ts +623 -0
- package/src/script/readscript.ts +17 -0
- package/src/script/strtools.ts +159 -0
- package/src/storage/decorator.ts +473 -0
- package/src/storage/fs.ts +104 -0
- package/src/storage/index.ts +541 -0
- package/src/system/index.ts +95 -0
- package/src/ui/index.ts +699 -0
- package/src/utils/path.js +338 -0
- package/tsconfig.json +111 -0
- package/types/async/index.d.ts +24 -0
- package/types/async/zeppos_timer.d.ts +14 -0
- package/types/audio/index.d.ts +64 -0
- package/types/config/index.d.ts +9 -0
- package/types/debug/index.d.ts +6 -0
- package/types/index.d.ts +41 -0
- package/types/platform/index.d.ts +134 -0
- package/types/plugins/basic_command/$.d.ts +2 -0
- package/types/plugins/basic_command/audio.d.ts +2 -0
- package/types/plugins/basic_command/basic.d.ts +3 -0
- package/types/plugins/basic_command/character.d.ts +2 -0
- package/types/plugins/basic_command/conditional.d.ts +2 -0
- package/types/plugins/basic_command/config.d.ts +2 -0
- package/types/plugins/basic_command/decorator.d.ts +2 -0
- package/types/plugins/basic_command/eval.d.ts +2 -0
- package/types/plugins/basic_command/img.d.ts +2 -0
- package/types/plugins/basic_command/index.d.ts +2 -0
- package/types/plugins/basic_command/menu.d.ts +2 -0
- package/types/plugins/global_gesture/index.d.ts +2 -0
- package/types/plugins/transform/animation.d.ts +131 -0
- package/types/plugins/transform/commands.d.ts +7 -0
- package/types/plugins/transform/example_profiles.d.ts +2 -0
- package/types/plugins/transform/hz_anime.d.ts +51 -0
- package/types/plugins/transform/index.d.ts +13 -0
- package/types/script/index.d.ts +123 -0
- package/types/script/readscript.d.ts +2 -0
- package/types/script/strtools.d.ts +31 -0
- package/types/storage/decorator.d.ts +41 -0
- package/types/storage/fs.d.ts +1 -0
- package/types/storage/index.d.ts +86 -0
- package/types/system/index.d.ts +35 -0
- package/types/ui/index.d.ts +167 -0
- package/types/utils/path.d.ts +84 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zeppos-timer.js
|
|
3
|
+
* @description An accurate timer for ZeppOS. 一个适用于ZeppOS的准确的计时器
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
* @date 2023/04/07
|
|
6
|
+
* @author XiaomaiTX
|
|
7
|
+
* @license MIT
|
|
8
|
+
* https://github.com/XiaomaiTX/zeppos-timer
|
|
9
|
+
*
|
|
10
|
+
* */
|
|
11
|
+
import { Time } from "@zos/sensor";
|
|
12
|
+
|
|
13
|
+
export class ZeppTimer {
|
|
14
|
+
constructor(callback, interval) {
|
|
15
|
+
this.callback = callback;
|
|
16
|
+
this.interval = interval;
|
|
17
|
+
this.timerId = null;
|
|
18
|
+
this.startTime = null;
|
|
19
|
+
this.nextTick = null;
|
|
20
|
+
this.time = new Time();
|
|
21
|
+
this.stopped = false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
start(delay = 0) {
|
|
25
|
+
this.startTime = this.time.getTime() + delay;
|
|
26
|
+
this.nextTick = this.startTime + this.interval;
|
|
27
|
+
this.scheduleTick();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
stop() {
|
|
31
|
+
this.timerId && clearTimeout(this.timerId);
|
|
32
|
+
this.stopped = true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
scheduleTick() {
|
|
36
|
+
if(this.stopped) return
|
|
37
|
+
const currentTime = this.time.getTime();
|
|
38
|
+
const delay = Math.max(0, this.nextTick - currentTime);
|
|
39
|
+
this.timerId = setTimeout(() => {
|
|
40
|
+
this.timerId = null
|
|
41
|
+
this.tick();
|
|
42
|
+
}, delay);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
tick() {
|
|
46
|
+
const currentTime = this.time.getTime();
|
|
47
|
+
|
|
48
|
+
// 计算误差,确保计时器的准确性
|
|
49
|
+
const error = currentTime - this.nextTick;
|
|
50
|
+
|
|
51
|
+
if (error > this.interval) {
|
|
52
|
+
// 如果误差大于一个间隔时间,则将 nextTick 更新为当前时间
|
|
53
|
+
this.nextTick = currentTime;
|
|
54
|
+
} else {
|
|
55
|
+
// 否则将 nextTick 加上一个间隔时间
|
|
56
|
+
this.nextTick += this.interval;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 调用回调函数
|
|
60
|
+
this.callback();
|
|
61
|
+
|
|
62
|
+
// 继续调度下一个 tick
|
|
63
|
+
this.scheduleTick();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// / <reference path="./zos_media.d.ts" />
|
|
2
|
+
// import { create, id } from "@zos/media";
|
|
3
|
+
import { CustomSave, Save } from "../storage/decorator.js";
|
|
4
|
+
import { HZEngineCore } from "../index.js";
|
|
5
|
+
|
|
6
|
+
import { Platform } from "../platform/index.js";
|
|
7
|
+
|
|
8
|
+
export class Audio {
|
|
9
|
+
// static _hmPlayer = create(id.PLAYER);
|
|
10
|
+
|
|
11
|
+
constructor(public _core: HZEngineCore) {
|
|
12
|
+
this._channels["audio"] = new Audio.Channel(this);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@CustomSave(
|
|
16
|
+
"audio.channels",
|
|
17
|
+
function (channels) {
|
|
18
|
+
let res = <Record<string, Audio.Channel.Serialized>>{};
|
|
19
|
+
for (let key in channels) {
|
|
20
|
+
res[key] = channels[key].serialize();
|
|
21
|
+
}
|
|
22
|
+
return res;
|
|
23
|
+
},
|
|
24
|
+
function (serialized) {
|
|
25
|
+
// clear exist channels
|
|
26
|
+
for (let key in this._channels) {
|
|
27
|
+
this._channels[key].stop();
|
|
28
|
+
this._channels[key].onRelease();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let res = <Record<string, Audio.Channel>>{};
|
|
32
|
+
for (let key in serialized) {
|
|
33
|
+
res[key] = Audio.Channel.deserialize(this, serialized[key]);
|
|
34
|
+
}
|
|
35
|
+
return res;
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
private accessor _channels: Record<string, Audio.Channel> = {};
|
|
39
|
+
get channels() {
|
|
40
|
+
return this._channels;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
createChannel(name: string): Audio.Channel {
|
|
44
|
+
// TODO support more audio channels
|
|
45
|
+
if (Object.getOwnPropertyNames(this._channels).length > 0)
|
|
46
|
+
throw "Can't create more than one audio channel on ZeppOS";
|
|
47
|
+
|
|
48
|
+
if (this._channels[name]) throw "Channel Already Exist";
|
|
49
|
+
return (this._channels[name] = new Audio.Channel(this));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export namespace Audio {
|
|
54
|
+
export class Channel {
|
|
55
|
+
// platform specific
|
|
56
|
+
|
|
57
|
+
// universal
|
|
58
|
+
_audioPlayer: Platform.AudioPlayer;
|
|
59
|
+
constructor(public _audio: Audio) {
|
|
60
|
+
this._audioPlayer = _audio._core.platform.createAudioPlayer();
|
|
61
|
+
|
|
62
|
+
this._audioPlayer.onPrepared = (success: boolean) => {
|
|
63
|
+
this._onPrepared(success);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
this._audioPlayer.onCompleted = () => {
|
|
67
|
+
this._onCompleted();
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Audio._hmPlayer.addEventListener(Audio._hmPlayer.event.PREPARE, (res) =>
|
|
71
|
+
// this._onPrepared(res)
|
|
72
|
+
// );
|
|
73
|
+
// Audio._hmPlayer.addEventListener(Audio._hmPlayer.event.COMPLETE, () => {
|
|
74
|
+
// this._onCompleted();
|
|
75
|
+
// });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
onRelease() {
|
|
79
|
+
this._audioPlayer.release() // TODO
|
|
80
|
+
this._audio._core.platform.releaseAudioPlayer(this._audioPlayer);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
mode: Channel.Mode = Channel.Mode.PlayInOrder;
|
|
84
|
+
status: Channel.Status = Channel.Status.Stopped;
|
|
85
|
+
_playbackList: PlaybackItem[] = [];
|
|
86
|
+
_nowIndex: number | null = null;
|
|
87
|
+
|
|
88
|
+
currentInfo: AudioInfo | null = null;
|
|
89
|
+
|
|
90
|
+
push(item: PlaybackItem) {
|
|
91
|
+
this._playbackList.push(item);
|
|
92
|
+
}
|
|
93
|
+
play() {
|
|
94
|
+
if (this.status !== Channel.Status.Stopped) return;
|
|
95
|
+
this._playNext();
|
|
96
|
+
}
|
|
97
|
+
pause() {
|
|
98
|
+
if (this.status !== Channel.Status.Playing) return;
|
|
99
|
+
this._audioPlayer.stop();
|
|
100
|
+
this.status = Channel.Status.Stopped;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 停止并清空队列
|
|
104
|
+
*/
|
|
105
|
+
stop() {
|
|
106
|
+
if (this.status !== Channel.Status.Playing) return;
|
|
107
|
+
this._audio._core.debug.log("audio channel stop");
|
|
108
|
+
this._audioPlayer.stop();
|
|
109
|
+
this._playbackList = [];
|
|
110
|
+
this.currentInfo = null;
|
|
111
|
+
this._nowIndex = null;
|
|
112
|
+
this.status = Channel.Status.Stopped;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
serialize(): Channel.Serialized {
|
|
116
|
+
return {
|
|
117
|
+
mode: this.mode,
|
|
118
|
+
status: this.status,
|
|
119
|
+
playbackList: this._playbackList,
|
|
120
|
+
nowIndex: this._nowIndex,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
static deserialize(audio: Audio, data: Channel.Serialized): Channel {
|
|
124
|
+
let channel = new Channel(audio);
|
|
125
|
+
channel.mode = data.mode;
|
|
126
|
+
channel.status = data.status;
|
|
127
|
+
channel._playbackList = data.playbackList;
|
|
128
|
+
channel._nowIndex = data.nowIndex;
|
|
129
|
+
if (channel.status === Channel.Status.Playing) channel._playNext();
|
|
130
|
+
return channel;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
_onPrepared(result: boolean) {
|
|
134
|
+
if (result) {
|
|
135
|
+
let mediaInfo = this._audioPlayer.getMediaInfo();
|
|
136
|
+
this.currentInfo = {
|
|
137
|
+
// placeholder
|
|
138
|
+
artist: mediaInfo.artist ?? "未知",
|
|
139
|
+
title: mediaInfo.title ?? "未知",
|
|
140
|
+
duration: mediaInfo.duration,
|
|
141
|
+
};
|
|
142
|
+
console.log("=== prepare succeed ===");
|
|
143
|
+
this._audioPlayer.start();
|
|
144
|
+
} else {
|
|
145
|
+
console.log("=== prepare fail ===");
|
|
146
|
+
this.status = Channel.Status.Error;
|
|
147
|
+
this._audioPlayer.release();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
_onCompleted() {
|
|
152
|
+
// update playback list
|
|
153
|
+
if (this.mode === Channel.Mode.PlayInOrder) {
|
|
154
|
+
this._playbackList.shift();
|
|
155
|
+
if (this._playbackList.length > 0) this._nowIndex = 0;
|
|
156
|
+
else this._nowIndex = null;
|
|
157
|
+
} else if (this.mode === Channel.Mode.PlayInOrderRepeat) {
|
|
158
|
+
if (this._playbackList.length > 0) {
|
|
159
|
+
if (this._nowIndex === null) this._nowIndex = 0;
|
|
160
|
+
this._nowIndex = (this._nowIndex + 1) % this._playbackList.length;
|
|
161
|
+
} else this._nowIndex = null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this._playNext();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Play the next item in the playback list. If there are no items in the
|
|
169
|
+
* playback list, set the channel status to Stopped and return.
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
_playNext() {
|
|
173
|
+
if (this._playbackList.length === 0) {
|
|
174
|
+
this._nowIndex = null;
|
|
175
|
+
this.status = Channel.Status.Stopped;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (this.mode === Channel.Mode.PlayInOrder) {
|
|
180
|
+
if (this._nowIndex === null) this._nowIndex = 0;
|
|
181
|
+
// otherwise onCompleted will set _nowIndex to next index
|
|
182
|
+
} else if (this.mode === Channel.Mode.PlayInOrderRepeat) {
|
|
183
|
+
if (this._nowIndex === null) this._nowIndex = 0;
|
|
184
|
+
// otherwise onCompleted will set _nowIndex to next index
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Audio._hmPlayer.setSource(Audio._hmPlayer.source.FILE, {
|
|
188
|
+
// file: this._playbackList[this._nowIndex!].path,
|
|
189
|
+
// });
|
|
190
|
+
this._audioPlayer.setSource({
|
|
191
|
+
path: this._playbackList[this._nowIndex!].path,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
this._audioPlayer.prepare();
|
|
195
|
+
|
|
196
|
+
this.status = Channel.Status.Playing;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export namespace Channel {
|
|
200
|
+
export enum Mode {
|
|
201
|
+
PlayInOrder,
|
|
202
|
+
PlayInOrderRepeat,
|
|
203
|
+
}
|
|
204
|
+
export enum Status {
|
|
205
|
+
Stopped,
|
|
206
|
+
Playing,
|
|
207
|
+
Error,
|
|
208
|
+
}
|
|
209
|
+
export type Serialized = {
|
|
210
|
+
mode: Channel.Mode;
|
|
211
|
+
status: Channel.Status;
|
|
212
|
+
playbackList: PlaybackItem[];
|
|
213
|
+
nowIndex: number | null;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
export interface PlaybackItem {
|
|
217
|
+
path: string;
|
|
218
|
+
}
|
|
219
|
+
export interface AudioInfo {
|
|
220
|
+
duration: number;
|
|
221
|
+
title: string;
|
|
222
|
+
artist: string;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { HZEngineCore } from "../index.js";
|
|
2
|
+
|
|
3
|
+
export class Config {
|
|
4
|
+
constructor(private _core: HZEngineCore) {
|
|
5
|
+
_core.on("initGlobalData", () => {
|
|
6
|
+
_core.storage.setSaveableData(
|
|
7
|
+
_core.storage.globalData,
|
|
8
|
+
true,
|
|
9
|
+
{ ...defaultConfig },
|
|
10
|
+
"config"
|
|
11
|
+
);
|
|
12
|
+
});
|
|
13
|
+
_core.on("afterLoadGlobalData", () => {
|
|
14
|
+
if (!(_core.storage.globalData as any)?.config) {
|
|
15
|
+
_core.storage.setSaveableData(
|
|
16
|
+
_core.storage.globalData,
|
|
17
|
+
true,
|
|
18
|
+
{ ...defaultConfig },
|
|
19
|
+
"config"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
_core.storage.saveGlobalData();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getConfig(key: string) {
|
|
27
|
+
let keys = this.normalizeKey(key);
|
|
28
|
+
// this._core.debug.log(`getConfig keys = ${JSON.stringify(keys)}`);
|
|
29
|
+
|
|
30
|
+
if (keys == null) return;
|
|
31
|
+
try {
|
|
32
|
+
let res = this._core.storage.getSaveableData(
|
|
33
|
+
this._core.storage.globalData,
|
|
34
|
+
false,
|
|
35
|
+
"config",
|
|
36
|
+
...keys
|
|
37
|
+
);
|
|
38
|
+
// this._core.debug.log(`getConfig res = ${JSON.stringify(res)}`);
|
|
39
|
+
return res;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
// this._core.debug.log(`getConfig error`,e);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setConfig(key: string, value: any) {
|
|
47
|
+
let keys = this.normalizeKey(key);
|
|
48
|
+
if (keys == null) return;
|
|
49
|
+
this._core.storage.setSaveableData(
|
|
50
|
+
this._core.storage.globalData,
|
|
51
|
+
true,
|
|
52
|
+
value,
|
|
53
|
+
"config",
|
|
54
|
+
...keys
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
this._core.storage.saveGlobalData();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private normalizeKey(key: string): string[] | null {
|
|
61
|
+
let keys = <string[]>[];
|
|
62
|
+
key.split(".").forEach((key) => {
|
|
63
|
+
if (key.trim().length > 0) keys.push(key);
|
|
64
|
+
else return null;
|
|
65
|
+
});
|
|
66
|
+
return keys;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
resetConfig() {}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const defaultConfig = {
|
|
73
|
+
game: {
|
|
74
|
+
autoplay: {
|
|
75
|
+
enable: false,
|
|
76
|
+
ms_per_char: 150,
|
|
77
|
+
extra_delay: 1300,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HZEngineCore
|
|
3
|
+
* @copyright Copyright (c) 2024 CuberQAQ. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Async } from "./async/index.js";
|
|
7
|
+
import { Audio } from "./audio/index.js";
|
|
8
|
+
import { Config } from "./config/index.js";
|
|
9
|
+
import { Debug } from "./debug/index.js";
|
|
10
|
+
import Platform from "./platform/index.js";
|
|
11
|
+
import { basic_command } from "./plugins/basic_command/index.js";
|
|
12
|
+
import { global_gesture } from "./plugins/global_gesture/index.js";
|
|
13
|
+
import { registerPlugin } from "./plugins/transform/index.js";
|
|
14
|
+
import { Script } from "./script/index.js";
|
|
15
|
+
import { Storage } from "./storage/index.js";
|
|
16
|
+
import { System } from "./system/index.js";
|
|
17
|
+
import { UI } from "./ui/index.js";
|
|
18
|
+
|
|
19
|
+
class HZEngineCore<PlatformType extends Platform = any> {
|
|
20
|
+
private _eventCallbacks: Map<string, Set<Function>> = new Map();
|
|
21
|
+
public storage;
|
|
22
|
+
public async;
|
|
23
|
+
public ui;
|
|
24
|
+
public script;
|
|
25
|
+
public system;
|
|
26
|
+
public config;
|
|
27
|
+
public audio;
|
|
28
|
+
public debug;
|
|
29
|
+
constructor(public platform: PlatformType) {
|
|
30
|
+
// 請不要調整這裡的初始化順序,不然會有問題(裝飾器裡有時候要用到前面初始化的東西)
|
|
31
|
+
this.storage = new Storage(this);
|
|
32
|
+
this.async = new Async(this);
|
|
33
|
+
this.ui = new UI(this);
|
|
34
|
+
this.script = new Script(this);
|
|
35
|
+
this.system = new System(this);
|
|
36
|
+
this.config = new Config(this);
|
|
37
|
+
this.audio = new Audio(this);
|
|
38
|
+
this.debug = new Debug(this);
|
|
39
|
+
|
|
40
|
+
// internal plugin
|
|
41
|
+
this.loadPlugin("global_gesture", global_gesture);
|
|
42
|
+
this.loadPlugin("transform", registerPlugin);
|
|
43
|
+
this.loadPlugin("basic_command", basic_command);
|
|
44
|
+
}
|
|
45
|
+
loadProject(options: {
|
|
46
|
+
projectPath: string;
|
|
47
|
+
cachePath: string;
|
|
48
|
+
savePath: string;
|
|
49
|
+
}) {
|
|
50
|
+
this.storage.loadProject(options);
|
|
51
|
+
}
|
|
52
|
+
start(callback?: () => unknown) {
|
|
53
|
+
// this.system.start()
|
|
54
|
+
Async.nextTick(() => {
|
|
55
|
+
this.debug.log("[HZEngine] Game Start");
|
|
56
|
+
let title = this.storage.packageData?.name;
|
|
57
|
+
if (title == null) {
|
|
58
|
+
throw `[HZEngine] project name is null, please loadProject first or check your project.json format`;
|
|
59
|
+
}
|
|
60
|
+
this.ui.getRouter("page")!.push("title", {
|
|
61
|
+
title,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// this.on("gameEnd", () => {
|
|
65
|
+
// let router = this.ui.getRouter("page")!;
|
|
66
|
+
// if (router.length > 0) return;
|
|
67
|
+
// router.push("title", {
|
|
68
|
+
// title,
|
|
69
|
+
// });
|
|
70
|
+
// });
|
|
71
|
+
callback?.();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
end() {
|
|
76
|
+
this.debug.log("[HZEngine] Game End, return to title");
|
|
77
|
+
|
|
78
|
+
let title = this.storage.packageData?.name;
|
|
79
|
+
if (title == null) {
|
|
80
|
+
throw `[HZEngine] project name is null, please loadProject first or check your project.json format`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.system.condition = System.Condition.Free;
|
|
84
|
+
|
|
85
|
+
this.ui.resetUI();
|
|
86
|
+
|
|
87
|
+
if (this.ui.getRouter("page")!.length > 0) return;
|
|
88
|
+
this.ui.getRouter("page")!.push("title", {
|
|
89
|
+
title,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public plugins: Map<string, unknown> = new Map();
|
|
94
|
+
// Load Plugin
|
|
95
|
+
loadPlugin(name: string, plugin: Plugin<PlatformType>) {
|
|
96
|
+
this.debug.log(`[HZEngine] load plugin [${name}]`);
|
|
97
|
+
let slot = plugin(this);
|
|
98
|
+
if (slot != undefined) this.plugins.set(name, slot);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Event Bus
|
|
102
|
+
on(event: string, cb: Function) {
|
|
103
|
+
if (this._eventCallbacks.has(event)) {
|
|
104
|
+
this._eventCallbacks.get(event)!.add(cb);
|
|
105
|
+
} else {
|
|
106
|
+
this._eventCallbacks.set(event, new Set<Function>().add(cb));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
off(event: string, cb: Function): boolean {
|
|
110
|
+
return !!this._eventCallbacks.get(event)?.delete(cb);
|
|
111
|
+
}
|
|
112
|
+
emit(event: string, ...args: any[]) {
|
|
113
|
+
this._eventCallbacks.get(event)?.forEach((cb) => {
|
|
114
|
+
cb(...args);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
type Plugin<PlatformType extends Platform> = (core: HZEngineCore<PlatformType>) => any;
|
|
120
|
+
|
|
121
|
+
export { HZEngineCore, UI, Storage, Script, System, Async, type Platform };
|
|
122
|
+
export * as TransformPlugin from "./plugins/transform/index.js";
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
export interface Platform<WidgetFactory = any> {
|
|
2
|
+
name: string;
|
|
3
|
+
|
|
4
|
+
// screen
|
|
5
|
+
getScreenSize(): [width: number, height: number];
|
|
6
|
+
|
|
7
|
+
// ui
|
|
8
|
+
createUILayer({ z_index }: { z_index: number }): WidgetFactory;
|
|
9
|
+
deleteUILayer(widgetFactory: WidgetFactory): void;
|
|
10
|
+
|
|
11
|
+
// fs
|
|
12
|
+
isFileSync({ path }: { path: string }): boolean;
|
|
13
|
+
readdirSync(option: Platform.readdirSync.Option): Platform.readdirSync.Result;
|
|
14
|
+
statSync(option: Platform.statSync.Option): Platform.statSync.Result;
|
|
15
|
+
readFileSync(option: Platform.readFileSync.Option): Platform.readFileSync.Result;
|
|
16
|
+
writeFileSync(option: Platform.writeFileSync.Option): void;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
getImageInfo(img_path: string): {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// timer & async
|
|
26
|
+
getTime(): number; // in Utc
|
|
27
|
+
|
|
28
|
+
// audio
|
|
29
|
+
createAudioPlayer(): Platform.AudioPlayer;
|
|
30
|
+
releaseAudioPlayer(audio_player: Platform.AudioPlayer): void;
|
|
31
|
+
|
|
32
|
+
setFrameInterval(callback: () => void): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export declare namespace Platform {
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
export interface AudioPlayer {
|
|
39
|
+
onPrepared?: (success: boolean) => void | undefined;
|
|
40
|
+
onCompleted?: () => void | undefined;
|
|
41
|
+
prepare(): void;
|
|
42
|
+
start(): void;
|
|
43
|
+
stop(): void;
|
|
44
|
+
release(): void;
|
|
45
|
+
getMediaInfo(): MediaInfo;
|
|
46
|
+
setSource({ path }: { path: string }): void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface MediaInfo {
|
|
50
|
+
title: string | undefined;
|
|
51
|
+
artist: string | undefined;
|
|
52
|
+
duration: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export namespace readdirSync {
|
|
56
|
+
interface Option {
|
|
57
|
+
/**
|
|
58
|
+
* @zh 目录路径
|
|
59
|
+
* @en Directory path
|
|
60
|
+
*/
|
|
61
|
+
path: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @zh 如果返回 `undefined` 则目录不存在,否则返回文件名数组
|
|
66
|
+
* @en If `undefined` is returned, the directory does not exist, otherwise an array of filenames is returned
|
|
67
|
+
*/
|
|
68
|
+
type Result = Array<string> | undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export namespace statSync {
|
|
72
|
+
interface Option {
|
|
73
|
+
/**
|
|
74
|
+
* @zh 路径
|
|
75
|
+
* @en path
|
|
76
|
+
*/
|
|
77
|
+
path: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @zh 如果返回 `undefined` 则目标文件不存在,否则返回文件信息对象
|
|
82
|
+
* @en If `undefined` is returned, the target file does not exist, otherwise the file information object is returned
|
|
83
|
+
*/
|
|
84
|
+
type Result = FSStat | undefined;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @output
|
|
88
|
+
*/
|
|
89
|
+
interface FSStat {
|
|
90
|
+
/**
|
|
91
|
+
* @zh 文件大小(单位为字节)
|
|
92
|
+
* @en The size of the file in bytes
|
|
93
|
+
*/
|
|
94
|
+
size: number;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export namespace readFileSync {
|
|
99
|
+
interface Option {
|
|
100
|
+
/**
|
|
101
|
+
* @zh 文件路径
|
|
102
|
+
* @en path
|
|
103
|
+
*/
|
|
104
|
+
path: string;
|
|
105
|
+
/**
|
|
106
|
+
* @zh 其他选项
|
|
107
|
+
* @en Other Options
|
|
108
|
+
*/
|
|
109
|
+
options?: Options;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface Options {
|
|
113
|
+
/**
|
|
114
|
+
* @zh 当指定了编码方式之后,API 返回结果为 `string`
|
|
115
|
+
* @en When the encoding method is specified, the API returns `string` as the result
|
|
116
|
+
*/
|
|
117
|
+
encoding?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @zh 文件内容。如果返回 `undefined`,则表明读取文件失败
|
|
122
|
+
* @en File content. If `undefined` is returned, the file failed to be read
|
|
123
|
+
*/
|
|
124
|
+
type Result = ArrayBuffer | string | undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export namespace writeFileSync {
|
|
128
|
+
interface Option {
|
|
129
|
+
/**
|
|
130
|
+
* @zh 文件路径或者文件句柄
|
|
131
|
+
* @en File path or file descriptor
|
|
132
|
+
*/
|
|
133
|
+
path: string | number;
|
|
134
|
+
/**
|
|
135
|
+
* @zh 写入目标文件的数据
|
|
136
|
+
* @en Data to be written to the target file
|
|
137
|
+
*/
|
|
138
|
+
data: ArrayBuffer | string | DataView;
|
|
139
|
+
/**
|
|
140
|
+
* @zh 其他选项
|
|
141
|
+
* @en Other Options
|
|
142
|
+
*/
|
|
143
|
+
options?: Options;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface Options {
|
|
147
|
+
/**
|
|
148
|
+
* @zh 如果数据格式为 `string`,需要指定编码方式
|
|
149
|
+
* @en If the `data` format is `string`, you need to specify the encoding method
|
|
150
|
+
* @defaultValue utf8
|
|
151
|
+
*/
|
|
152
|
+
encoding?: string;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
export default Platform;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HZEngineCore } from "../../index.js";
|
|
2
|
+
|
|
3
|
+
export function $_command(core: HZEngineCore) {
|
|
4
|
+
|
|
5
|
+
core.script.use((ctx, next) => {
|
|
6
|
+
if(ctx.rawtext.trim().startsWith("$")) {
|
|
7
|
+
ctx.rawtext = "eval " + ctx.rawtext.trim().slice(1).trim()
|
|
8
|
+
}
|
|
9
|
+
return next()
|
|
10
|
+
})
|
|
11
|
+
}
|