Skin:
[NORMAL]
[BLUE] [DOS] [LIGHT]  / コピーするための表示 / 実行
このファイル: /home/web6047/www/cgi-bin/prj/20200515-RPG/基本的/20200620-ウィンドウ入力/20200621-ドラクエの戦闘コマンド入力を再現/simple2.html
1 <html><!--ESCAPEPROCESS-->
2 <head>
3 <meta content="text/html; charset=UTF-8" http-equiv="content-type">
4 <script>console.clear();</script>
5 <script>
6 /*
7 ドラクエの戦闘コマンド入力を再現しようとしている。
8 オブジェクト指向にしたほうが、シンプルで分かりやすくなるのでは?
9
10 そのためにやっていること:
11
12 イベントスクリプトにおいてコマンド入力を行う関数を呼ぶ。
13 コマンド入力を行う関数は、Promiseになっており、
14 コマンド入力がすべて完了したらtellOkを実行することでPromiseの停止を解除する。
15 Promiseが停止しているあいだ、
16 controls.winsのsetIntevalが動き、当frameを呼ぶ。
17 当frameは当drawを行う。
18 また、window.onkeydownには当onkeydownが代入され、コマンド入力ウィンドウが操作される。
19 */
20 this.name = "window";
21 function onloadx() {
22 cc = document.getElementById( "test" ).getContext( "2d" );
23
24 //キャラクタデータを作成し、
25 chars = new Array();
26
27 chars.push( {
28 title : "c1",
29 dougus : [
30 { title : "dougu1", items_title : "だれに?", items : chars },
31 { title : "dougu2" },
32 { title : "dougu3" },
33 ],
34 } );
35 chars.push( {
36 title : "c2",
37 dougus : [
38 { title : "dougu4" },
39 { title : "dougu5" },
40 { title : "dougu6" },
41 ],
42 jumons : [
43 { title : "jumon1" },
44 { title : "jumon2" },
45 { title : "jumon3" },
46 ],
47 } );
48 chars.push( {
49 title : "c3",
50 dougus : [
51 { title : "dougu7" },
52 { title : "dougu8" },
53 { title : "dougu9" },
54 ],
55 jumons : [
56 { title : "jumon4" },
57 { title : "jumon5" },
58 { title : "jumon6" },
59 ],
60 } );
61
62 //各キャラに戦闘コマンドを作成。
63 chars[ 0 ].batcom = [
64 { title : "たたかう1" },
65 { title : "にげる" },
66 { title : "ぼうぎょ" },
67 { title : "どうぐ", items : chars[ 0 ].dougus },
68 ];
69 chars[ 1 ].batcom = [
70 { title : "たたかう2" },
71 { title : "じゅもん", items : chars[ 1 ].jumons },
72 { title : "ぼうぎょ" },
73 { title : "どうぐ", items : chars[ 1 ].dougus },
74 ];
75 chars[ 2 ].batcom = [
76 { title : "たたかう3" },
77 { title : "じゅもん", items : chars[ 2 ].jumons },
78 { title : "ぼうぎょ" },
79 { title : "どうぐ", items : chars[ 2 ].dougus },
80 ];
81
82 /*
83 controls
84 wins
85 init
86 open controls.wins.open( {ウィンドウ設定} ); でウィンドウを開く
87 close
88 start 開いたウィンドウのキー入力を開始したり等
89 stop
90 onkeydown_normal キー入力開始時、window.onkeydownに設定
91 onkeydown_wizard キー入力開始時、window.onkeydownに設定
92 frame setIntervalで呼ばれる。
93 draw 描画
94 */
95
96 controls = {
97 wins : {
98 name : "wins",
99 others : new Object(),
100 history : new Array(),
101 init : function( firstwin ) {
102 //使用前初期化
103 if( firstwin instanceof Array ) {
104 //ウィザードを指定された場合
105 this.mode = "wizard";
106 this.wizard = firstwin;
107 this.wizCount = 0;
108 this.wizResult = new Array();
109 for( let i = 0; i < this.wizard.length; i++ )
110 this.wizResult[ i ] = new Array();
111 this.open( this.wizard[ this.wizCount ] );
112 } else {
113 //通常ウィンドウを指定された場合
114 this.mode = "normal";
115 this.open( firstwin );
116 }
117 },
118 open : function( win ) {
119 //ウィンドウを開く
120
121 //分析
122 if( win.items && typeof win.cursorY !== "undefined" )
123 win.type = "menu";
124 else
125 win.type = "info";
126
127 this.history.push( win );
128
129 if( win.type == "menu" ) {
130 this.current = win;
131 this.current.cursorY = 0;
132 }
133 },
134 close : function() {
135 //ウィンドウを閉じる
136 this.history.pop();
137 this.current = this.history[ this.history.length -1 ];
138 },
139 start : function() {
140 //controls.winsの動作開始
141 if( this.mode == "normal" )
142 window.onkeydown = this.onkeydown_normal.bind( this );
143 else
144 window.onkeydown = this.onkeydown_wizard.bind( this );
145 // this.timerId = setInterval( this.frame.bind( this ), 100 );
146 },
147 stop : function() {
148 //controls.winsの動作停止
149 // clearInterval( this.timerId );
150 },
151 onkeydown_normal : function( e ) {
152 switch( e.which ) {
153 case 38: //up
154 if( this.current.cursorY > 0 ) this.current.cursorY --;
155 break;
156 case 40: //down
157 if( this.current.cursorY < this.current.items.length - 1 ) this.current.cursorY ++;
158 break;
159 case 32: //space
160 let selectedItem = this.current.items[ this.current.cursorY ];
161 this.close();
162 this.tellOkToPromise( selectedItem );
163 break;
164 case 88: //x
165 break;
166 }//switch
167 },
168 onkeydown_wizard : function( e ) {
169 /*
170 1つのウィザード
171 wizard = [
172 {トップメニュー1},
173 {トップメニュー2},
174 {トップメニュー3},
175 ]
176 各トップメニューはサブメニューを持ちうる。
177 ウィザードの結果
178 wizResult = [
179 [ selectedItem, selectedItem, ... ], (トップメニュー1がたどった項目の配列)
180 [ selectedItem, selectedItem, ... ], (トップメニュー2がたどった項目の配列)
181 [ selectedItem, selectedItem, ... ], (トップメニュー3がたどった項目の配列)
182 ]
183 */
184 switch( e.which ) {
185 case 38: //up
186 if( this.current.cursorY > 0 ) this.current.cursorY --;
187 break;
188 case 40: //down
189 if( this.current.cursorY < this.current.items.length - 1 ) this.current.cursorY ++;
190 break;
191 case 32: //space
192 let selectedItem = this.current.items[ this.current.cursorY ];
193 if( selectedItem.items ) {
194 //サブメニューがある場合
195 this.wizResult[ this.wizCount ].push( selectedItem );
196 let win = {
197 title : selectedItem.items_title ? selectedItem.items_title : "",
198 cursorY : 0,
199 cx : this.current.cx + this.current.cw,
200 cy : 10,
201 cw : 5,
202 ch : 5,
203 items : selectedItem.items,
204 };
205 this.open( win );
206 } else {
207 //サブメニューがない場合
208 this.wizResult[ this.wizCount ].push( selectedItem );
209 this.history.length = 0;
210 //次のトップメニューへ
211 this.wizCount ++;
212 //check. 全トップメニュー終了?
213 if( this.wizCount == this.wizard.length ) {
214 this.tellOkToPromise( this.wizResult );
215 break;
216 }
217 this.open( this.wizard[ this.wizCount ] );
218 }//if
219 break;
220 case 88: //x
221 if( this.history.length == 1 ) {
222 //キャンセルが、トップメニューにおいて行われたとき
223 if( this.wizCount > 0 ) {
224 //ウィザードが2番目以降のトップメニューを表示しているとき
225 //1つ前のトップメニューへ戻る
226 this.close();
227 this.wizResult[ this.wizCount ].pop();
228 this.wizCount --;
229 this.wizResult[ this.wizCount ].pop();
230 this.open( this.wizard[ this.wizCount ] );
231 }
232 } else {
233 //サブメニューでキャンセルされたとき
234 this.close();
235 this.wizResult[ this.wizCount ].pop();
236 }//if
237 break;
238 }//switch
239 },//onkeydown
240 frame : function() {
241 // this.draw();
242 },
243 draw : function( e ) {
244 cc.clearRect( 0,0, cc.canvas.width, cc.canvas.height );
245
246
247 //wins
248 let _drawFrame = function( win, gx, gy, gw, gh ) {
249 cc.fillStyle = "white";
250 cc.fillRect( gx, gy, gw, gh );
251 cc.strokeStyle = "black";
252 cc.strokeRect( gx, gy, gw, gh );
253 cc.fillStyle = "black";
254 cc.fillText( win.title, gx, gy + 16 );
255 }//drawWin
256 for( let i = 0; i < this.history.length; i++ ) {
257 let win = this.history[ i ];
258 let gx = win.cx * 16;
259 let gy = win.cy * 16;
260 let gw = win.cw * 16;
261 let gh = win.items ? ( win.items.length + 1 ) * 16 : win.ch * 16;
262 _drawFrame( win, gx, gy, gw, gh );
263 cc.save();
264 if( win.type == "menu" ) {
265 cc.translate( gx, gy + 16 );
266 for( let j = 0; j < win.items.length; j++ ) {
267 let item = win.items[ j ];
268 let gx2 = 16;
269 let gy2 = j * 16;
270 if( j == win.cursorY ) {
271 cc.fillStyle = "black";
272 cc.fillRect( gx2, gy2, gw - 32, 16 );
273 cc.fillStyle = "white";
274 } else {
275 cc.fillStyle = "black";
276 }
277 cc.fillText( item.title, gx2, gy2 + 16 );
278 }
279 } else {
280 cc.translate( gx, gy );
281 cc.fillStyle = "black";
282 for( let i = 0; i < win.lines.length; i++ )
283 cc.fillText( win.lines[ i ], 0, ( i + 1 ) * 16 );
284 }
285 cc.restore();
286 }
287 for( let name in this.others ) {
288 let win = this.others[ name ];
289 let gx = win.cx * 16;
290 let gy = win.cy * 16;
291 let gw = win.cw * 16;
292 let gh = win.items ? ( win.items.length + 1 ) * 16 : win.ch * 16;
293 _drawFrame( win, gx, gy, gw, gh );
294 cc.save();
295 cc.restore();
296 }
297 },//draw
298 },//wins
299 }//controls
300
301 timerId = setInterval( frame, 100 );
302
303 eve3();
304
305 }//onloadx()
306
307 function frame() {
308 for( let name in controls ) {
309 controls[ name ].frame();
310 }
311 for( let name in controls ) {
312 controls[ name ].draw();
313 }
314 }
315
316 async function eve3() {
317
318 await message( "勇者ロトの血をひきしものよ! そなたが来るのを待っておった。" );
319 // await message( "その昔 伝説の勇者ロトは神から光の玉を授かり この世界を覆っていた魔物たちを封じこめたという。" );
320 // await message( "しかし いずこともなく現れた悪魔の化身 竜王がその玉を闇に閉ざしてしまったのじゃ!" );
321 // await message( "このままでは世界は闇に飲み込まれ やがて滅んでしまうことだろう。" );
322 // await message( "勇者よ! 竜王を倒し その手から光の玉を取り戻してくれ!" );
323 // await message( "わしからの贈り物じゃ! そこにある宝箱を開けるがよい。そなたの役に立つ物が入っておるはずじゃ。" );
324 // await message( "そしてこの部屋にいる者にたずねれば 旅の心得を教えてくれよう。" );
325
326 eve2();
327 }
328
329 function message( msg ) {
330 return new Promise(
331 async function( tellOk ) {
332 let messwin = tool_searchObjectInArray( controls.wins.history, "id", "messagewindow" );
333 //check. メッセージウィンドウがないなら作成
334 if( ! messwin ) {
335 messwin = {
336 id : "messagewindow",
337 title : "",
338 cx : 1,
339 cy : 20,
340 cw : 35,
341 ch : 5,
342 lines : new Array(),
343 }
344 controls.wins.open( messwin );
345 }
346 let lines = messwin.lines;
347 lines.push( "" );
348 //check. ウィンドウ内最大行数を越えた?
349 if( lines.length > 4 ) lines.shift();
350 //一文字ずつ表示
351 let seek = 0;
352 while( seek < msg.length ) {
353 lines[ lines.length - 1 ] = msg.substr( 0, seek + 1 );
354 seek++;
355 await delay( 100 );
356 }
357 await hitanykey();
358 tellOk();
359 }
360 );
361 }
362 function hitanykey() {
363 return new Promise(
364 function( tellOk ) {
365 window.onkeydown = tellOk;
366 }
367 );
368 }
369
370 function delay( ms ) {
371 return new Promise(
372 function( tellOk ) {
373 setTimeout( tellOk, ms );
374 }
375 );
376 }
377
378 async function eve2() {
379 let result = await yesno( "たたかう?" );
380 if( result ) {
381 eve1();
382 }
383 }
384
385 async function eve1() {
386 let result = await inputBatcom();
387
388 //結果出力
389 let s = "";
390 for( let i = 0; i < result.length; i++ ) {
391 let c = result[ i ];
392 for( let j = 0; j < c.length; j++ ) {
393 let item = c[ j ];
394 s += item.title + ", ";
395 }
396 s += "\n";
397 }
398
399 alert( s );
400 }
401
402 function tool_searchObjectInArray( objectArray, member, key ) {
403 /*
404 オブジェクトの配列の中から、
405 メンバー名とその値をキーにして検索する。
406 */
407 for( let i = 0; i < objectArray.length; i++ ) {
408 let object = objectArray[ i ];
409 if( object[ member ] == key ) return object;
410 }
411 return null;
412 }
413
414 function yesno( message ) {
415 return new Promise(
416 function( tellOk ) {
417 let win = {
418 title : message,
419 cursorY : 0,
420 cx : 32,
421 cy : 19,
422 cw : 5,
423 items : [
424 { title : "はい" },
425 { title : "いいえ" }
426 ],
427 }
428 this.tellOkToPromise = tellOk;
429 this.init( win );
430 this.start();
431 }.bind( controls.wins )
432 ).then( function( selectedItem ) {
433 return selectedItem.title == "はい";
434 } );
435 }
436
437 function inputBatcom() {
438 return new Promise(
439 function( tellOk ) {
440 //この関数はnew Promise後、すぐに実行される。その後tellOkが実行されるまで停止する。
441 let wizard = [
442 {
443 title : "p1",
444 cursorY : 0,
445 cx : 1,
446 cy : 10,
447 cw : 10,
448 items : chars[ 0 ].batcom,
449 },
450 {
451 title : "p2",
452 cursorY : 0,
453 cx : 1,
454 cy : 10,
455 cw : 10,
456 items : chars[ 1 ].batcom,
457 },
458 {
459 title : "p3",
460 cursorY : 0,
461 cx : 1,
462 cy : 10,
463 cw : 10,
464 items : chars[ 2 ].batcom,
465 },
466 ];
467 this.tellOkToPromise = tellOk;
468 this.init( wizard );
469 this.start();
470 }.bind( controls.wins )//function
471 ).then(
472 function( result ) {
473 //この関数はtellOkが実行されたとき実行される。
474 this.stop();
475 return result;
476 }.bind( controls.wins )
477 );//Promise.then
478 }//inputBatcom()
479
480 </script>
481 <style>
482 </style>
483 </head>
484 <body onload="onloadx()">
485 <canvas id="test" width="640" height="480" style="
486 border : solid 1px black;
487 float:left;
488 "></canvas>
489 <div style="float:left; width:512px; padding-left:1em;">
490 メッセージが表示された後、何かキーを押すと、
491 「たたかう?」ときかれます。
492 上下:↑↓キー、決定:スペースキー、キャンセル:xキー
493
494 「はい」を選ぶと、戦闘のコマンド入力が表示されます。
495 一人目の どうぐ→dougu1→C1 でサブメニューの動作を確認できます。
496
497 3人分のコマンドを入力すると、入力内容を表示します。
498 それだけです。
499 </div>
500 <script>
501 div = document.getElementsByTagName( "div" )[ 0 ];
502 div.innerHTML = div.innerHTML.replace( /\n/g, "<BR>" );
503 </script>
504 </body>
505 </html>