webtalekit-alpha 0.2.10
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/README.md +193 -0
- package/engineConfig.json +10 -0
- package/package.json +49 -0
- package/parser/cli.js +61 -0
- package/parser/parser.js +53 -0
- package/src/core/drawer.js +399 -0
- package/src/core/drawer.js.map +1 -0
- package/src/core/index.js +654 -0
- package/src/core/resourceManager.js +19 -0
- package/src/core/resourceManager.js.map +1 -0
- package/src/core/scenarioManager.js +107 -0
- package/src/core/scenarioManager.js.map +1 -0
- package/src/resource/ImageObject.js +152 -0
- package/src/resource/ImageObject.js.map +1 -0
- package/src/resource/soundObject.js +146 -0
- package/src/resource/soundObject.js.map +1 -0
- package/src/utils/logger.js +75 -0
- package/src/utils/logger.js.map +1 -0
- package/src/utils/store.js +28 -0
- package/src/utils/store.js.map +1 -0
- package/src/utils/waitUtil.js +48 -0
- package/src/utils/waitUtil.js.map +1 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
import { Drawer } from './drawer'
|
|
2
|
+
import { ScenarioManager } from './scenarioManager'
|
|
3
|
+
import { ImageObject } from '../resource/ImageObject'
|
|
4
|
+
import { ResourceManager } from './resourceManager'
|
|
5
|
+
import { SoundObject } from '../resource/soundObject'
|
|
6
|
+
import engineConfig from '../../engineConfig.json'
|
|
7
|
+
import { outputLog } from '../utils/logger'
|
|
8
|
+
import { sleep } from '../utils/waitUtil'
|
|
9
|
+
|
|
10
|
+
export class Core {
|
|
11
|
+
bgm = null
|
|
12
|
+
isAuto = false
|
|
13
|
+
isNext = false
|
|
14
|
+
isSkip = false
|
|
15
|
+
onNextHandler = null
|
|
16
|
+
sceneFile = {}
|
|
17
|
+
sceneConfig = {}
|
|
18
|
+
commandList = {
|
|
19
|
+
text: this.textHandler,
|
|
20
|
+
choice: this.choiceHandler,
|
|
21
|
+
show: this.showHandler,
|
|
22
|
+
newpage: this.newpageHandler,
|
|
23
|
+
hide: this.hideHandler,
|
|
24
|
+
jump: this.jumpHandler,
|
|
25
|
+
sound: this.soundHandler,
|
|
26
|
+
say: this.sayHandler,
|
|
27
|
+
if: this.ifHandler,
|
|
28
|
+
call: this.callHandler,
|
|
29
|
+
moveto: this.moveToHandler,
|
|
30
|
+
route: this.routeHandler,
|
|
31
|
+
wait: this.waitHandler,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
constructor() {
|
|
35
|
+
this.gameContainer = document.getElementById('gameContainer')
|
|
36
|
+
// Drawerの初期化(canvasタグのサイズを設定する)
|
|
37
|
+
this.drawer = new Drawer(this.gameContainer)
|
|
38
|
+
// ScenarioManagerの初期化(変数の初期値設定)
|
|
39
|
+
this.scenarioManager = new ScenarioManager()
|
|
40
|
+
// ResourceManagerの初期化(引数にconfigを渡して、リソース管理配列を作る)
|
|
41
|
+
this.resourceManager = new ResourceManager(import(/* webpackIgnore: true */ '/src/resource/config.js')) // webpackIgnoreでバンドルを無視する
|
|
42
|
+
this.displayedImages = {}
|
|
43
|
+
this.usedSounds = {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async start(initScene) {
|
|
47
|
+
outputLog('call', 'debug', initScene)
|
|
48
|
+
// TODO: ブラウザ用のビルドの場合は、最初にクリックしてもらう
|
|
49
|
+
// titleタグの内容を書き換える
|
|
50
|
+
document.title = engineConfig.title
|
|
51
|
+
// sceneファイルを読み込む
|
|
52
|
+
await this.loadScene(initScene || 'title')
|
|
53
|
+
// 画面を表示する
|
|
54
|
+
await this.loadScreen(this.sceneConfig)
|
|
55
|
+
// 入力イベントを設定する
|
|
56
|
+
document.querySelector('#gameContainer').addEventListener('keydown', (e) => {
|
|
57
|
+
if (e.key === 'Enter') {
|
|
58
|
+
this.onNextHandler()
|
|
59
|
+
} else if (e.key === 'Control') {
|
|
60
|
+
this.drawer.isSkip = true
|
|
61
|
+
this.isNext = true
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
document.querySelector('#gameContainer').addEventListener('keyup', (e) => {
|
|
65
|
+
if (e.key === 'Control') {
|
|
66
|
+
this.drawer.isSkip = true
|
|
67
|
+
this.isNext = false
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
document.querySelector('#gameContainer').addEventListener('click', (e) => {
|
|
71
|
+
this.onNextHandler()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
await this.textHandler('タップでスタート')
|
|
75
|
+
// BGMを再生する
|
|
76
|
+
this.bgm.play(true)
|
|
77
|
+
// シナリオを実行する
|
|
78
|
+
while (this.scenarioManager.hasNext()) {
|
|
79
|
+
await this.runScenario()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async loadScene(sceneFileName) {
|
|
84
|
+
outputLog('call', 'debug', sceneFileName)
|
|
85
|
+
// sceneファイルを読み込む
|
|
86
|
+
this.sceneFile = await import(/* webpackChunkName: "[request]" */ `/src/js/${sceneFileName}.js`)
|
|
87
|
+
// sceneファイルの初期化処理を実行
|
|
88
|
+
if (this.sceneFile.init) {
|
|
89
|
+
this.sceneFile.init(this.getAPIForScript())
|
|
90
|
+
}
|
|
91
|
+
// シナリオの進行状況を初期化
|
|
92
|
+
this.scenarioManager.setScenario(this.sceneFile.scenario, sceneFileName)
|
|
93
|
+
this.sceneConfig = { ...this.sceneConfig, ...this.sceneFile.sceneConfig }
|
|
94
|
+
outputLog('sceneFile', 'debug', this.sceneFile)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async loadScreen(sceneConfig) {
|
|
98
|
+
outputLog('call', 'debug', sceneConfig)
|
|
99
|
+
// sceneConfig.templateを読み込んで、HTMLを表示する
|
|
100
|
+
const htmlString = await (await fetch(sceneConfig.template)).text()
|
|
101
|
+
// 読み込んだhtmlからIDにmainを持つdivタグとStyleタグ以下を取り出して、gameContainerに表示する
|
|
102
|
+
var parser = new DOMParser()
|
|
103
|
+
var doc = parser.parseFromString(htmlString, 'text/html')
|
|
104
|
+
this.gameContainer.innerHTML = doc.getElementById('main').innerHTML
|
|
105
|
+
// 既に読み込んだスタイルシートがあったら削除する
|
|
106
|
+
const styleTags = document.head.getElementsByTagName('style')
|
|
107
|
+
for (const styleTag of styleTags) {
|
|
108
|
+
document.head.removeChild(styleTag)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Styleタグを取り出して、headタグに追加する
|
|
112
|
+
const styleElement = doc.head.getElementsByTagName('style')[0]
|
|
113
|
+
document.head.appendChild(styleElement)
|
|
114
|
+
// ゲーム進行用に必要な情報をセットする
|
|
115
|
+
this.drawer.setScreen(this.gameContainer, engineConfig.resolution)
|
|
116
|
+
// シナリオの進行状況を保存
|
|
117
|
+
this.scenarioManager.progress.currentScene = sceneConfig.name
|
|
118
|
+
// 背景画像を表示する
|
|
119
|
+
const background = await new ImageObject().setImageAsync(sceneConfig.background)
|
|
120
|
+
this.displayedImages['background'] = {
|
|
121
|
+
image: background,
|
|
122
|
+
size: {
|
|
123
|
+
width: this.gameContainer.clientWidth,
|
|
124
|
+
height: this.gameContainer.clientHeight,
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
this.drawer.show(this.displayedImages)
|
|
128
|
+
this.bgm = await new SoundObject().setAudioAsync(sceneConfig.bgm)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async runScenario() {
|
|
132
|
+
outputLog('call index:', 'debug', this.scenarioManager.getIndex())
|
|
133
|
+
let scenarioObject = this.scenarioManager.next()
|
|
134
|
+
if (!scenarioObject) {
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
outputLog('scenarioObject', 'debug', scenarioObject)
|
|
138
|
+
// シナリオオブジェクトのtypeプロパティに応じて、対応する関数を実行する
|
|
139
|
+
const boundFunction = this.commandList[scenarioObject.type || 'text'].bind(this)
|
|
140
|
+
outputLog(`boundFunction:${boundFunction.name.split(' ')[1]}`, 'debug', scenarioObject)
|
|
141
|
+
scenarioObject = await this.httpHandler(scenarioObject)
|
|
142
|
+
await boundFunction(scenarioObject)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async textHandler(scenarioObject) {
|
|
146
|
+
outputLog('textHandler:line', 'debug', scenarioObject)
|
|
147
|
+
// 文章だけの場合は、contentプロパティに配列として設定する
|
|
148
|
+
if (typeof scenarioObject === 'string') scenarioObject = { content: [scenarioObject] }
|
|
149
|
+
// httpレスポンスがある場合は、list.contentに追加して、表示対象に加える
|
|
150
|
+
if (scenarioObject.then || scenarioObject.error) {
|
|
151
|
+
scenarioObject.content = scenarioObject.content.concat(scenarioObject.then || scenarioObject.error)
|
|
152
|
+
}
|
|
153
|
+
outputLog('call', 'debug', scenarioObject)
|
|
154
|
+
|
|
155
|
+
// 名前が設定されている場合は、名前を表示する
|
|
156
|
+
if (scenarioObject.name) {
|
|
157
|
+
this.drawer.drawName(scenarioObject.name)
|
|
158
|
+
} else {
|
|
159
|
+
this.drawer.drawName('')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//prettier-ignore
|
|
163
|
+
this.onNextHandler = () => { this.drawer.isSkip = true }
|
|
164
|
+
this.drawer.clearText() // テキスト表示領域をクリア
|
|
165
|
+
// 表示する文章を1行ずつ表示する
|
|
166
|
+
for (const text of scenarioObject.content) {
|
|
167
|
+
outputLog('textSpeed', 'debug', text)
|
|
168
|
+
if (typeof text === 'string') {
|
|
169
|
+
await this.drawer.drawText(this.expandVariable(text), scenarioObject.speed || 25)
|
|
170
|
+
} else {
|
|
171
|
+
if (text.type === 'br' || text.type === 'wait') {
|
|
172
|
+
outputLog('text', 'debug', text)
|
|
173
|
+
if (text.type === 'br') this.drawer.drawLineBreak()
|
|
174
|
+
if (!text.nw) {
|
|
175
|
+
await this.waitHandler({ wait: text.time })
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
const container = this.drawer.createDecoratedElement(text)
|
|
179
|
+
await this.drawer.drawText(this.expandVariable(text.content[0]), text.speed || 25, container)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
await this.waitHandler({ wait: scenarioObject.time })
|
|
184
|
+
this.drawer.isSkip = false
|
|
185
|
+
this.scenarioManager.setHistory(scenarioObject.content)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
expandVariable(text) {
|
|
189
|
+
outputLog('call', 'debug', text)
|
|
190
|
+
if (typeof text !== 'string') return text
|
|
191
|
+
return text.replace(/{{([^{}]+)}}/g, (match) => {
|
|
192
|
+
const expr = match.slice(2, -2)
|
|
193
|
+
const returnValue = this.executeCode(`return ${expr}`)
|
|
194
|
+
return typeof returnValue == 'object' ? JSON.stringify(returnValue) : returnValue
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async waitHandler(line) {
|
|
199
|
+
// line.timeがある場合、line.waitに代入する
|
|
200
|
+
if (line.time) line.wait = line.time
|
|
201
|
+
outputLog('call', 'debug', line)
|
|
202
|
+
//prettier-ignore
|
|
203
|
+
this.onNextHandler = () => { this.isNext = true }
|
|
204
|
+
outputLog('wait type', 'debug', typeof line.wait)
|
|
205
|
+
|
|
206
|
+
// line.waitが数値に変換可能な文字列の場合、数値に変換
|
|
207
|
+
if (typeof line.wait === 'string' && !isNaN(Number(line.wait))) {
|
|
208
|
+
line.wait = Number(line.wait)
|
|
209
|
+
}
|
|
210
|
+
if (typeof line.wait === 'number') {
|
|
211
|
+
outputLog('wait number', 'debug', line.wait)
|
|
212
|
+
if (line.wait > 0 || this.isAuto) {
|
|
213
|
+
const waitTime = line.wait || 1500
|
|
214
|
+
// 指定された時間だけ待機
|
|
215
|
+
await sleep(waitTime)
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
// 改行ごとに入力待ち
|
|
219
|
+
await this.clickWait()
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// クリック待ち処理
|
|
224
|
+
async clickWait() {
|
|
225
|
+
outputLog('call', 'debug')
|
|
226
|
+
this.drawer.setVisibility('#waitCircle', true)
|
|
227
|
+
return new Promise((resolve) => {
|
|
228
|
+
const intervalId = setInterval(() => {
|
|
229
|
+
if (this.isNext) {
|
|
230
|
+
this.drawer.setVisibility('#waitCircle', false)
|
|
231
|
+
clearInterval(intervalId)
|
|
232
|
+
this.isNext = false
|
|
233
|
+
resolve(null)
|
|
234
|
+
}
|
|
235
|
+
}, 500)
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async sayHandler(line) {
|
|
240
|
+
outputLog('call', 'debug', line)
|
|
241
|
+
// say(name:string, pattern: string, voice: {playの引数}, ...text)
|
|
242
|
+
if (line.voice) await this.soundHandler({ path: line.voice, play: undefined })
|
|
243
|
+
await this.textHandler({ content: line.content, name: line.name, speed: line.speed || 25 })
|
|
244
|
+
this.scenarioManager.setHistory(line)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async choiceHandler(line) {
|
|
248
|
+
outputLog('call', 'debug', line)
|
|
249
|
+
document.querySelector('#interactiveView').style.visibility = 'visible'
|
|
250
|
+
if (line.prompt) this.textHandler(line.prompt)
|
|
251
|
+
// ムスタッシュ構文があるときは、変数の展開
|
|
252
|
+
line.content.forEach((choice) => {
|
|
253
|
+
choice.label = this.expandVariable(choice.label)
|
|
254
|
+
})
|
|
255
|
+
const { selectId, onSelect: selectHandler } = await this.drawer.drawChoices(line)
|
|
256
|
+
if (selectHandler !== undefined) {
|
|
257
|
+
this.scenarioManager.addScenario(selectHandler)
|
|
258
|
+
}
|
|
259
|
+
this.scenarioManager.setHistory({ line, ...selectId })
|
|
260
|
+
document.querySelector('#interactiveView').style.visibility = 'hidden'
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
jumpHandler(line) {
|
|
264
|
+
outputLog('call:', 'debug', line.index)
|
|
265
|
+
// ジャンプ先が現在の行より小さいときは、今の行とジャンプ先の行の間で、sub=falseの行を抽出して、scenarioManagerに追加する
|
|
266
|
+
if (line.index < this.scenarioManager.getIndex()) {
|
|
267
|
+
// scenarioManagerからシナリオを取得
|
|
268
|
+
const scenario = this.scenarioManager.getScenario()
|
|
269
|
+
// 結合用に、ジャンプ先までのインデックスを取得
|
|
270
|
+
const noEditScenarioList = {
|
|
271
|
+
before: scenario.slice(0, line.index),
|
|
272
|
+
after: scenario.slice(this.scenarioManager.getIndex()),
|
|
273
|
+
}
|
|
274
|
+
outputLog('noEditScenarioList', 'debug', noEditScenarioList)
|
|
275
|
+
// ジャンプ先のインデックスまでのシナリオを取得
|
|
276
|
+
const scenarioList = scenario.slice(line.index, this.scenarioManager.getIndex())
|
|
277
|
+
outputLog('scenarioList', 'debug', scenarioList)
|
|
278
|
+
// sub=falseの行だけを取得
|
|
279
|
+
const subFalseScenario = scenarioList.filter((line) => !line.sub)
|
|
280
|
+
outputLog('subFalseScenario', 'debug', subFalseScenario)
|
|
281
|
+
// scenarioManagerに追加
|
|
282
|
+
this.scenarioManager.setScenario([...noEditScenarioList.before, ...subFalseScenario, ...noEditScenarioList.after])
|
|
283
|
+
outputLog('scenarioManager', 'debug', this.scenarioManager.getScenario())
|
|
284
|
+
}
|
|
285
|
+
this.newpageHandler()
|
|
286
|
+
this.scenarioManager.setIndex(Number(line.index))
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async showHandler(line) {
|
|
290
|
+
outputLog('line', 'debug', line)
|
|
291
|
+
// ムスタッシュ構文があるときは、変数の展開
|
|
292
|
+
Object.keys(line).forEach((item) => {
|
|
293
|
+
line[item] = this.expandVariable(line[item])
|
|
294
|
+
})
|
|
295
|
+
// 表示する画像の情報を管理オブジェクトに追加
|
|
296
|
+
const modeList = { bg: 'background', cutin: '', chara: '', cg: 'background', effect: 'effect' }
|
|
297
|
+
const key = Object.keys(modeList).includes(line.mode) ? modeList[line.mode] : line.name || line.src.split('/').pop()
|
|
298
|
+
const baseLine = engineConfig.resolution.height / 2
|
|
299
|
+
const centerPoint = {
|
|
300
|
+
left: { x: engineConfig.resolution.width * 0.25, y: baseLine },
|
|
301
|
+
center: { x: engineConfig.resolution.width * 0.5, y: baseLine },
|
|
302
|
+
right: { x: engineConfig.resolution.width * 0.75, y: baseLine },
|
|
303
|
+
}
|
|
304
|
+
line.src = this.expandVariable(line.src) || line.name
|
|
305
|
+
|
|
306
|
+
const image = await this.getImageObject(line)
|
|
307
|
+
// 画像の表示位置を設定
|
|
308
|
+
let position = { x: line.x || 0, y: line.y || 0 }
|
|
309
|
+
// prettier-ignore
|
|
310
|
+
let size = line.width && line.height ? { width: line.width, height: line.height } : { width: image.getSize().width, height: image.getSize().height }
|
|
311
|
+
|
|
312
|
+
// line.modeが'cutin'の場合、center:middleのエイリアスを強制する
|
|
313
|
+
if (line.mode === 'cutin') {
|
|
314
|
+
line.pos = 'center:middle'
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (line.mode === 'cg') {
|
|
318
|
+
this.tempImages = { ...this.displayedImages }
|
|
319
|
+
this.displayedImages = { background: line.src }
|
|
320
|
+
size = { width: engineConfig.resolution.width, height: engineConfig.resolution.height }
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (line.pos) {
|
|
324
|
+
const pos = line.pos.split(':')
|
|
325
|
+
const baseLines = {
|
|
326
|
+
top: 0 + size.height,
|
|
327
|
+
middle: engineConfig.resolution.height / 2,
|
|
328
|
+
bottom: engineConfig.resolution.height - size.height,
|
|
329
|
+
}
|
|
330
|
+
// エイリアスが設定されている場合、画像の中心点を求めて、画像の表示位置を設定する
|
|
331
|
+
position.x = centerPoint[pos[0]].x - size.width / 2
|
|
332
|
+
if (pos[1] === 'middle') {
|
|
333
|
+
position.y = baseLines[pos[1]] - size.width / 2
|
|
334
|
+
} else if (pos[1]) {
|
|
335
|
+
position.y = baseLines[pos[1]]
|
|
336
|
+
} else {
|
|
337
|
+
position.y = baseLine / 2
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
this.displayedImages[key] = {
|
|
341
|
+
image,
|
|
342
|
+
pos: position,
|
|
343
|
+
size: size,
|
|
344
|
+
look: line.look,
|
|
345
|
+
entry: line.entry,
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
outputLog('displayedImages', 'debug', this.displayedImages[key])
|
|
349
|
+
if (line.sepia) this.displayedImages[key].image.setSepia(line.sepia)
|
|
350
|
+
if (line.mono) this.displayedImages[key].image.setMonochrome(line.mono)
|
|
351
|
+
if (line.blur) this.displayedImages[key].image.setBlur(line.blur)
|
|
352
|
+
if (line.opacity) this.displayedImages[key].image.setOpacity(line.opacity)
|
|
353
|
+
|
|
354
|
+
if (line.transition === 'fade') {
|
|
355
|
+
// フェードイン効果で表示
|
|
356
|
+
await this.drawer.fadeIn(line.duration || 2000, await this.getImageObject(line), {
|
|
357
|
+
pos: position,
|
|
358
|
+
size,
|
|
359
|
+
look: line.look,
|
|
360
|
+
entry: line.entry,
|
|
361
|
+
})
|
|
362
|
+
this.drawer.show(this.displayedImages)
|
|
363
|
+
} else {
|
|
364
|
+
// 通常の表示処理
|
|
365
|
+
this.drawer.show(this.displayedImages)
|
|
366
|
+
}
|
|
367
|
+
outputLog('this.displayedImages', 'debug', this.displayedImages)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async hideHandler(line) {
|
|
371
|
+
outputLog('call', 'debug', line)
|
|
372
|
+
const targetImage = this.displayedImages[line.name]
|
|
373
|
+
if (line.mode === 'cg') {
|
|
374
|
+
this.displayedImages = { ...this.tempImages }
|
|
375
|
+
this.tempImages = {}
|
|
376
|
+
} else {
|
|
377
|
+
delete this.displayedImages[line.name]
|
|
378
|
+
}
|
|
379
|
+
this.drawer.show(this.displayedImages)
|
|
380
|
+
if (line.transition === 'fade') {
|
|
381
|
+
// フェードアウト効果で非表示
|
|
382
|
+
await this.drawer.fadeOut(line.duration || 1000, targetImage.image, {
|
|
383
|
+
pos: targetImage.pos,
|
|
384
|
+
size: targetImage.size,
|
|
385
|
+
look: targetImage.look,
|
|
386
|
+
})
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async moveToHandler(line) {
|
|
391
|
+
outputLog('moveToHandler:line', 'debug', line)
|
|
392
|
+
const key = line.name
|
|
393
|
+
outputLog('moveToHandler:displayedImages', 'debug', this.displayedImages)
|
|
394
|
+
await this.drawer.moveTo(key, this.displayedImages, { x: line.x, y: line.y }, line.duration | 1)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async getImageObject(line) {
|
|
398
|
+
outputLog('call', 'debug', line)
|
|
399
|
+
const name = line.name || line.src.split('/').pop()
|
|
400
|
+
let image
|
|
401
|
+
// 既にインスタンスがある場合は、それを使う
|
|
402
|
+
if (Object.hasOwn(this.displayedImages, name)) {
|
|
403
|
+
const targetImage = this.displayedImages[name]
|
|
404
|
+
const imageObject = targetImage ? targetImage.image : new ImageObject()
|
|
405
|
+
image = await imageObject.setImageAsync(line.src)
|
|
406
|
+
} else {
|
|
407
|
+
outputLog('new ImageObject', 'debug')
|
|
408
|
+
image = await new ImageObject().setImageAsync(line.src)
|
|
409
|
+
}
|
|
410
|
+
return image
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async soundHandler(line) {
|
|
414
|
+
outputLog('call', 'debug', line)
|
|
415
|
+
let soundObject = null
|
|
416
|
+
if (line.mode === 'bgm') {
|
|
417
|
+
if (this.bgm.isPlaying) {
|
|
418
|
+
this.bgm.stop()
|
|
419
|
+
}
|
|
420
|
+
soundObject = await this.getSoundObject(line)
|
|
421
|
+
this.bgm = soundObject
|
|
422
|
+
} else {
|
|
423
|
+
// soundObjectを作成
|
|
424
|
+
soundObject = await this.getSoundObject(line)
|
|
425
|
+
// playプロパティが存在する場合は、再生する
|
|
426
|
+
}
|
|
427
|
+
if ('play' in line) {
|
|
428
|
+
'loop' in line ? soundObject.play(true) : soundObject.play()
|
|
429
|
+
} else if ('stop' in line) {
|
|
430
|
+
soundObject.stop()
|
|
431
|
+
} else if ('pause' in line) {
|
|
432
|
+
soundObject.pause()
|
|
433
|
+
}
|
|
434
|
+
// soundObjectを管理オブジェクトに追加
|
|
435
|
+
const key = line.name || line.src.split('/').pop()
|
|
436
|
+
this.usedSounds[key] = {
|
|
437
|
+
audio: soundObject,
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async getSoundObject(line) {
|
|
442
|
+
outputLog('call', 'debug', line)
|
|
443
|
+
const name = line.name || line.src.split('/').pop()
|
|
444
|
+
let resource
|
|
445
|
+
if (Object.hasOwn(this.usedSounds, name)) {
|
|
446
|
+
const targetResource = this.usedSounds[name]
|
|
447
|
+
const soundObject = targetResource ? targetResource.audio : new SoundObject()
|
|
448
|
+
resource = await soundObject.setAudioAsync(line.src)
|
|
449
|
+
} else {
|
|
450
|
+
resource = await new SoundObject().setAudioAsync(line.src)
|
|
451
|
+
}
|
|
452
|
+
return resource
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
newpageHandler() {
|
|
456
|
+
outputLog('call', 'debug')
|
|
457
|
+
this.displayedImages = {
|
|
458
|
+
background: {
|
|
459
|
+
image: this.getBackground(),
|
|
460
|
+
size: {
|
|
461
|
+
width: this.gameContainer.clientWidth,
|
|
462
|
+
height: this.gameContainer.clientHeight,
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
}
|
|
466
|
+
this.drawer.clear()
|
|
467
|
+
this.drawer.show(this.displayedImages)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async ifHandler(line) {
|
|
471
|
+
outputLog('call', 'debug', line)
|
|
472
|
+
const isTrue = this.executeCode(`return ${line.condition}`)
|
|
473
|
+
outputLog(`${isTrue}`, 'debug')
|
|
474
|
+
const appendScenario = isTrue ? line.content[0].content : line.content[1].content
|
|
475
|
+
outputLog('', 'debug', appendScenario)
|
|
476
|
+
this.scenarioManager.addScenario(appendScenario)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async routeHandler(line) {
|
|
480
|
+
outputLog('call', 'debug', line)
|
|
481
|
+
if (this.bgm.isPlaying) {
|
|
482
|
+
this.bgm.stop()
|
|
483
|
+
this.bgm = null
|
|
484
|
+
}
|
|
485
|
+
this.newpageHandler()
|
|
486
|
+
if (this.sceneFile.cleanUp) {
|
|
487
|
+
// 終了処理を実行する
|
|
488
|
+
this.sceneFile.cleanUp()
|
|
489
|
+
}
|
|
490
|
+
// sceneファイルを読み込む
|
|
491
|
+
await this.loadScene(line.to)
|
|
492
|
+
// 画面を表示する
|
|
493
|
+
await this.loadScreen(this.sceneConfig)
|
|
494
|
+
// BGMを再生する
|
|
495
|
+
this.bgm.play(true)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Sceneファイルに、ビルド時に実行処理を追加して、そこに処理をお願いしたほうがいいかも?
|
|
499
|
+
callHandler(line) {
|
|
500
|
+
outputLog('call', 'debug', line)
|
|
501
|
+
this.executeCode(line.method)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
async httpHandler(line) {
|
|
505
|
+
if (!(line.get || line.post || line.put || line.delete)) {
|
|
506
|
+
return line
|
|
507
|
+
}
|
|
508
|
+
outputLog('call', 'debug', line)
|
|
509
|
+
// progress属性を処理する
|
|
510
|
+
// prettier-ignore
|
|
511
|
+
const progressText = line.content.filter((content) => content.type === 'progress')[0]
|
|
512
|
+
if (progressText) {
|
|
513
|
+
await this.textHandler({ content: [progressText.content][0], wait: 0 })
|
|
514
|
+
}
|
|
515
|
+
// get,post,put,delete属性を処理する
|
|
516
|
+
const headers = line.content
|
|
517
|
+
.filter((content) => content.type === 'header')[0]
|
|
518
|
+
.content.reduce(
|
|
519
|
+
(acc, header) => ({
|
|
520
|
+
...acc,
|
|
521
|
+
[header.type]: header.content,
|
|
522
|
+
}),
|
|
523
|
+
{},
|
|
524
|
+
)
|
|
525
|
+
const body = line.content
|
|
526
|
+
.filter((content) => content.type === 'data')[0]
|
|
527
|
+
.content.reduce(
|
|
528
|
+
(acc, header) => ({
|
|
529
|
+
...acc,
|
|
530
|
+
[header.type]: header.content,
|
|
531
|
+
}),
|
|
532
|
+
{},
|
|
533
|
+
)
|
|
534
|
+
outputLog('headers', 'debug', headers)
|
|
535
|
+
outputLog('body', 'debug', body)
|
|
536
|
+
const response = await fetch(line.get || line.post || line.put || line.delete, {
|
|
537
|
+
method: line.get ? 'GET' : line.post ? 'POST' : line.put ? 'PUT' : 'DELETE',
|
|
538
|
+
headers: headers,
|
|
539
|
+
body: JSON.stringify(body),
|
|
540
|
+
})
|
|
541
|
+
if (response.ok) {
|
|
542
|
+
const json = await response.json()
|
|
543
|
+
this.sceneFile.res = json
|
|
544
|
+
outputLog('res', 'debug', json)
|
|
545
|
+
line.then = line.content.filter((content) => content.type === 'then')[0].content
|
|
546
|
+
} else {
|
|
547
|
+
this.sceneFile.res = json
|
|
548
|
+
line.error = line.content.filter((content) => content.type === 'error')[0].content
|
|
549
|
+
}
|
|
550
|
+
if (line.content) {
|
|
551
|
+
line.content = line.content.filter(
|
|
552
|
+
(content) =>
|
|
553
|
+
!(
|
|
554
|
+
content.type &&
|
|
555
|
+
(content.type === 'header' ||
|
|
556
|
+
content.type === 'data' ||
|
|
557
|
+
content.type === 'then' ||
|
|
558
|
+
content.type === 'error' ||
|
|
559
|
+
content.type === 'progress')
|
|
560
|
+
),
|
|
561
|
+
)
|
|
562
|
+
}
|
|
563
|
+
return line
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
setBackground(image) {
|
|
567
|
+
this.displayedImages['background'] = image
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
getBackground() {
|
|
571
|
+
return this.displayedImages['background'].image
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
executeCode(code) {
|
|
575
|
+
outputLog('call', 'debug', code)
|
|
576
|
+
try {
|
|
577
|
+
const context = { ...this.sceneFile }
|
|
578
|
+
const func = new Function(...Object.keys(context), code)
|
|
579
|
+
return func.apply(null, Object.values(context))
|
|
580
|
+
} catch (error) {
|
|
581
|
+
console.error('Error executing code:', error)
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Scriptから安全にアクセスできるメソッドを定義
|
|
586
|
+
getAPIForScript() {
|
|
587
|
+
return {
|
|
588
|
+
drawer: {
|
|
589
|
+
drawName: this.drawer.drawName.bind(this.drawer),
|
|
590
|
+
drawText: this.drawer.drawText.bind(this.drawer),
|
|
591
|
+
drawChoices: this.drawer.drawChoices.bind(this.drawer),
|
|
592
|
+
clearText: this.drawer.clearText.bind(this.drawer),
|
|
593
|
+
show: this.drawer.show.bind(this.drawer),
|
|
594
|
+
moveTo: this.drawer.moveTo.bind(this.drawer),
|
|
595
|
+
fadeIn: this.drawer.fadeIn.bind(this.drawer),
|
|
596
|
+
fadeOut: this.drawer.fadeOut.bind(this.drawer),
|
|
597
|
+
},
|
|
598
|
+
sound: {
|
|
599
|
+
play: this.soundHandler.bind(this),
|
|
600
|
+
stop: (name) => this.soundHandler({ name, stop: true }),
|
|
601
|
+
pause: (name) => this.soundHandler({ name, pause: true }),
|
|
602
|
+
},
|
|
603
|
+
scenario: {
|
|
604
|
+
jump: this.jumpHandler.bind(this),
|
|
605
|
+
addScene: this.scenarioManager.addScenario.bind(this.scenarioManager),
|
|
606
|
+
getProgress: () => this.scenarioManager.progress,
|
|
607
|
+
setProgress: (progress) => (this.scenarioManager.progress = progress),
|
|
608
|
+
getIndex: () => this.scenarioManager.getIndex(),
|
|
609
|
+
setIndex: (index) => this.scenarioManager.setIndex(index),
|
|
610
|
+
hasNext: () => this.scenarioManager.hasNext(),
|
|
611
|
+
next: () => this.scenarioManager.next(),
|
|
612
|
+
getHistory: () => this.scenarioManager.getHistory(),
|
|
613
|
+
setHistory: (history) => this.scenarioManager.setHistory(history),
|
|
614
|
+
setScenario: (scenario) => this.scenarioManager.setScenario(scenario),
|
|
615
|
+
getScenario: () => this.scenarioManager.getScenario(),
|
|
616
|
+
getSceneName: () => this.scenarioManager.progress.currentScene,
|
|
617
|
+
setScreenName: (name) => (this.sceneConfig.name = name),
|
|
618
|
+
},
|
|
619
|
+
images: {
|
|
620
|
+
get: this.getImageObject.bind(this),
|
|
621
|
+
getAll: () => this.displayedImages,
|
|
622
|
+
set: (name, image) => (this.displayedImages[name] = image),
|
|
623
|
+
delete: (name) => delete this.displayedImages[name],
|
|
624
|
+
},
|
|
625
|
+
sounds: {
|
|
626
|
+
get: (name) => this.usedSounds[name],
|
|
627
|
+
getAll: () => this.usedSounds,
|
|
628
|
+
set: (name, sound) => (this.usedSounds[name] = sound),
|
|
629
|
+
delete: (name) => delete this.usedSounds[name],
|
|
630
|
+
load: this.getSoundObject.bind(this),
|
|
631
|
+
},
|
|
632
|
+
background: {
|
|
633
|
+
set: this.setBackground.bind(this),
|
|
634
|
+
get: this.getBackground.bind(this),
|
|
635
|
+
},
|
|
636
|
+
wait: this.waitHandler.bind(this),
|
|
637
|
+
clickWait: this.clickWait.bind(this),
|
|
638
|
+
core: {
|
|
639
|
+
text: this.textHandler.bind(this),
|
|
640
|
+
choice: this.choiceHandler.bind(this),
|
|
641
|
+
show: this.showHandler.bind(this),
|
|
642
|
+
newpage: this.newpageHandler.bind(this),
|
|
643
|
+
hide: this.hideHandler.bind(this),
|
|
644
|
+
jump: this.jumpHandler.bind(this),
|
|
645
|
+
sound: this.soundHandler.bind(this),
|
|
646
|
+
say: this.sayHandler.bind(this),
|
|
647
|
+
if: this.ifHandler.bind(this),
|
|
648
|
+
moveto: this.moveToHandler.bind(this),
|
|
649
|
+
route: this.routeHandler.bind(this),
|
|
650
|
+
wait: this.waitHandler.bind(this),
|
|
651
|
+
},
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResourceManager = void 0;
|
|
4
|
+
var ResourceManager = /** @class */ (function () {
|
|
5
|
+
function ResourceManager() {
|
|
6
|
+
this.resourceMap = {}; // リソースを管理するオブジェクト
|
|
7
|
+
}
|
|
8
|
+
// リソースを追加または更新
|
|
9
|
+
ResourceManager.prototype.addResource = function (name, path) {
|
|
10
|
+
this.resourceMap[name] = path;
|
|
11
|
+
};
|
|
12
|
+
// リソースのパスを取得
|
|
13
|
+
ResourceManager.prototype.getResourcePath = function (name) {
|
|
14
|
+
return this.resourceMap[name];
|
|
15
|
+
};
|
|
16
|
+
return ResourceManager;
|
|
17
|
+
}());
|
|
18
|
+
exports.ResourceManager = ResourceManager;
|
|
19
|
+
//# sourceMappingURL=resourceManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resourceManager.js","sourceRoot":"","sources":["../../../src/core/resourceManager.ts"],"names":[],"mappings":";;;AAAA;IAGE;QACE,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC,kBAAkB;IAC3C,CAAC;IAED,eAAe;IACf,qCAAW,GAAX,UAAY,IAAY,EAAE,IAAY;QACpC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,CAAC;IAED,aAAa;IACb,yCAAe,GAAf,UAAgB,IAAY;QAC1B,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IACH,sBAAC;AAAD,CAAC,AAhBD,IAgBC;AAhBY,0CAAe"}
|