Skin:
[NORMAL]
[BLUE] [DOS] [LIGHT]  / コピーするための表示 / 実行
このファイル: /home/web6047/www/cgi-bin/prj/20210612-RPG/作成/20210626-任意の矩形に何かを描くクラス試作2適用バージョン - snapshot 20210817/app.js
1 /* memo.
2 名前のすみわけ(理想であって、実際はそうなっていないかもしれない)
3 "イベント"
4 イベントドリブンのイベント イベント という
5 シナリオスクリプトのイベント ストーリー という
6
7 "ストーリー"
8 以下のような、その場所でどんな行動をとったらこの関数、という構造のオブジェクトをstoryと呼ぶ。
9 {
10 "walk" : function() {...},
11 "outer.goton" : function() {...},
12 }
13 storyの配列をstoriesと呼ぶ。
14 storyを構成する story.walk や story[ "outer,goton" ] をshortStoryと呼ぶ。
15
16 "アイテム"
17 もちもの トレジャー
18 メニュー項目 アイテム
19
20 キャラの状態
21 直近の状態 status 毒を受けたとか
22 恒常的な状態 property 力がどれだけあるとか
23
24 座標
25 グラフィクス gx, gy, gw, gh
26 マス目のある画面 col, row, cols, rows
27 データ上の位置 x, y, w, h
28 マス目 1マスの名称 座標
29 画面のマス目 tile style.gridCol,style.gridRow
30 要素内に作成したマス目 inner cell col,row
31
32 配列変数の名前と、同じデータを持つ連想配列変数の名前
33 配列 -s たとえばcharacters
34 連想配列 -z たとえばcharacterz
35
36 そのとき、たとえばsprites、spritezの読み方について
37 配列時 sprites スプライトス
38 連想配列時 spritez スプライトス
39 ※どちらも-スと読む。つまり-zは見た目の区別でしかない
40
41 また、childの複数形childrenについて -z を表現できないので、
42 childs, childz とする。
43 連想配列のkeyとキー入力のkey
44 連想配列 name
45 キー入力 key
46 言葉の意味
47 tweak 調整する
48 strategy 広い視野で物事を見て全体の方向性を決めていく
49 tactic 実務レベルでどのように対処するのかという部分的な一つの計画
50
51 async関数とawait
52
53 awaitはasync関数を暗黙的に分割します。
54
55 async test() {
56 処理1
57 await 関数など
58 処理2
59 }
60 処理1までは通常の関数と同じように動作し、
61 awaitの後の処理2はawaitの行が終わる(= Promiseの解決が履行される)までは実行されません。
62 また、処理2が終わった後は、test関数が呼び出しされた場所には戻りません。
63 test関数の最初のawaitの時点で、プログラムは2つ(並列)に枝分かれします。
64 片方はtest()を出てtest()を呼び出した次の行へ進み、もう片方はawait以降を実行します。
65 await以降を実行した後は、どこにも戻らず、終了します。
66 このRPGのプログラムにおいては、枝分かれすることはあまり価値がありません。
67 1つの関数を途中で止めることができるawaitに価値があります。
68 */
69
70 // |
71 // V
72 //---■■■ 1 Utl ■■■
73 class Utl {//c
74 static arrayForward( array, element ) {//m
75 //配列内の要素を末尾に置く
76
77 let idx = array.indexOf( element );
78 //check.
79 if( idx == -1 ) {
80 alert( "Utl.arrayForward() 失敗した" );
81 return;
82 }
83
84 array.splice( idx, 1 );
85 array.push( element );
86 }
87 static accessBy( /**/ ) {//m
88 //test.test2.test3のようなアクセスで、test2がnullだとエラーで止まる。
89 //accessBy( test, "test2", "test3" )とすれば止まらず、nullを返す。
90 //try,catchもあるが、なじみがないので。
91 let array = Array.from( arguments );
92 let object = array.shift();
93 do {
94 if( object === null ) return null;
95 if( object === undefined ) return undefined;
96 object = object[ array.shift() ];
97 } while( array.length );
98 return object;
99 }
100 static array2object( array, object, keyMaker ) {//m
101 //配列を連想配列に変換する。その際の名前はkeyMakerが作る
102 //objectは作成済みの場合
103 for( let element of array ) {
104 object[ keyMaker( element ) ] = element;
105 }
106 }
107
108 static objectFrom( array, keyMaker ) {//m
109 //配列を連想配列に変換する。その際の名前はkeyMakerが作る
110 //objectは作成済みではない場合
111 let object = new Object();
112 for( let element of array ) {
113 object[ keyMaker( element ) ] = element;
114 }
115 return object;
116 }
117 static lengthBin( str ) {//m
118 //漢字を2バイト、半角を1バイトとしてバイト数を計算 (UTF8を前提)
119 return encodeURI( str ).replace( /%20/, "*" ).replace( /%..%..%../g, "**" ).length;
120 }
121 static lengthBinDiv2( str ) {//m
122 //lengthBinの結果を2で割り、小数点切り上げ
123 return Math.ceil( Utl.lengthBin( str ) / 2 );
124 }
125 static maxLength( strArray ) {//m
126 //文字列配列中で、一番長い文字列の文字数を返す
127 let v = 0;
128 for( let str of strArray ) v = Math.max( v, String( str ).length );
129 return v;
130 }
131 static maxLengthBin( strArray ) {//m
132 //そのバイト版
133 let v = 0;
134 for( let str of strArray ) {
135 v = Math.max( v, Utl.lengthBin( String( str ) ) );
136 }
137 return v;
138 }
139 static maxLengthBinDiv2( strArray ) {//m
140 //その、2で割り、小数点切り上げ版
141 return Math.ceil( Utl.maxLengthBin( strArray ) / 2 );
142 }
143 static copyObjectKai( object, history ) {//m
144 //オブジェクトを厳密にコピーするの改造版
145 //check. 最初の呼び出し
146 if( history == undefined ) {
147 history = {
148 origin : new Array(),
149 copy : new Array(),
150 }
151 }
152 if( object instanceof Object ) {
153 //check. すでにコピーしたことがある(無限ループ検知)
154 if( history.origin.includes( object ) ) {
155 let idx = history.origin.indexOf( object );
156 return history.copy[ idx ];
157 }
158 history.origin.push( object );
159 let copy;
160 if( object instanceof Array ) {
161 copy = new Array();
162 history.copy.push( copy );
163 for( let i = 0; i < object.length; i++ ) {
164 copy[ i ] = Utl.copyObjectKai( object[ i ], history );
165 }
166 } else {
167 copy = new Object();
168 history.copy.push( copy );
169 for( let name in object ) {
170 //改造箇所はここだけ
171 //オブジェクトのメンバでcopy:trueを持つ場合に限り
172 //厳密にコピーをする。
173 //copy:trueがないと、コピー元のオブジェクトの参照になる。
174 //モンスターやトレジャーはカタログをコピーするが
175 //HPや使用回数だけはカタログへの参照ではなくコピーにしたい。
176 if( object[ name ].copy ) {
177 //HPや使用回数はこちら
178 copy[ name ] = Utl.copyObjectKai( object[ name ], history );
179 } else {
180 //その他定義は読むだけなので参照でよい
181 copy[ name ] = object[ name ];
182 }
183 }
184 }
185 return copy;
186 } else {
187 return object;
188 }
189 }
190 }//Utl
191 //---■■■ 2 Element ■■■
192 class Element {//c
193 constructor( style, app ) {
194 this.app = app ? app : this;
195 //check.
196 if( this == app && style.gridCellSize == undefined )
197 alert( "appに限り、styleにgridCellSizeの定義が必要です。" );
198
199 //デフォルト値と上書き
200 this.style = {
201 gridCol : 0,
202 gridRow : 0,
203 gridCols : 5,
204 gridRows : 5,
205 };
206 this.assignStyle( style );
207
208 this.name = this.constructor.name;
209 this.childs = new Array();
210 this.prevFocus = null;
211 }
212 assignStyle( newStyle ) {
213 Object.assign( this.style, newStyle );
214 this.x = this.style.gridCol * this.app.style.gridCellSize;
215 this.y = this.style.gridRow * this.app.style.gridCellSize;
216 this.w = this.style.gridCols * this.app.style.gridCellSize;
217 this.h = this.style.gridRows * this.app.style.gridCellSize;
218 }
219 typeKey( key ) {//m
220 }
221 senseKey( key ) {//m
222 }
223 draw( cc ) {//m
224 this.drawWindow( cc );
225 this.drawChildren( cc );
226 }
227 drawWindow( cc ) {//m
228
229 //ウィンドウ
230 let x = - this.app.style.gridCellSize / 2;
231 let y = - this.app.style.gridCellSize / 2;
232 let w = this.w + this.app.style.gridCellSize;
233 let h = this.h + this.app.style.gridCellSize;
234
235 cc.fillStyle = "black";
236 cc.fillRect( x, y, w, h );
237
238 //ウィンドウ 面
239 cc.fillStyle = "white";
240 cc.fillRect( x, y, w, h );
241
242 //ウィンドウ 枠
243 if( this.app.currentElement == this ) {
244 cc.strokeStyle = "yellow";
245 cc.strokeRect( x + 1, y + 1, w - 2, h - 2 );
246 }
247 cc.strokeStyle = "black";
248 cc.strokeRect( x, y, w, h );
249 }
250 drawChildren( cc ) {//m
251 for( let child of this.childs ) {
252 cc.save();
253 cc.translate( child.x, child.y );
254 child.draw( cc );
255 cc.restore();
256 }
257 }
258 searchStory() {//m
259 }
260 focus() {//m
261 //check. すでにフォーカスされている。
262 if( this.app.currentElement == this ) {
263 console.log( "already", this.name, "focused" );
264 return;
265 }
266
267 if( this.parent )
268 console.log( "focus", this.name, "at", this.parent.name );
269 else
270 console.log( "focus", this.name );
271
272 if( this.prevFocus == undefined )
273 this.prevFocus = this.app.currentElement;
274 this.app.currentElement = this;
275
276 //前面に移動
277 if( this.parent ) {
278 let idx = this.parent.childs.indexOf( this );
279 this.parent.childs.splice( idx, 1 );
280 this.parent.childs.push( this );
281 }
282 }
283 appendChild( child, nofocus ) {//m
284 this.childs.push( child );
285 child.setParent( this );
286 if( ! nofocus ) child.focus();
287 return child;
288 }
289 appendFloat( child ) {
290 this.appendChild( child, true );
291 }
292 setParent( parent ) {
293 this.parent = parent;
294 //check.
295 for( let name in this.style ) {
296 if( this.style[ name ] == "inherit" ) {
297 this.style[ name ] = parent.style[ name ];
298 }
299 }
300 }
301 removeChild( child ) {//m
302 console.log( "remove", child.name, "from", this.name );
303 let idx;
304
305 if( ( idx = this.childs.indexOf( child ) ) > -1 ) {
306 this.childs.splice( idx, 1 );
307 if( child.prevFocus ) child.prevFocus.focus();
308 } else {
309 alert( "警告:正体不明のオブジェクトをelement.removeChild()しようとしました." );
310 }
311
312 //メッセージウィンドウの場合はthis.appからも削除
313 if( child == this.app.messageWindow ) {
314 delete this.app.messageWindow;
315 }
316
317 return child;
318 }
319 removeAllElements() {//m
320 for( let child of this.childs ) {
321 child.removeAllElements();
322 }
323 this.parent.removeChild( this );
324 }
325
326 }//Element
327 // |
328 // V
329 //---▼▼▼ 3 App ▼▼▼
330 class App extends Element {//c
331 constructor( canvasId ) {
332
333 let canvas = document.getElementById( canvasId );
334 let cc = canvas.getContext( "2d", { alpha : false } );
335
336 let logiczoom = 1;
337 let canvasWidth = 256 / logiczoom; //プログラム上の論理的サイズ
338 let canvasHeight = 224 / logiczoom;
339 canvas.width = canvasWidth;
340 canvas.height = canvasHeight;
341 if( 0 ) canvas.style.imageRendering = "crisp-edges"; //アンチエイリアス
342
343 /* 解像度変更機能:
344
345 mozaic canvasWidth canvas.width scale
346 0.5 256 2048 8 さらにきめ細かい
347 1 256 1024 4 きめ細かい(標準)
348 2 256 512 2 少し荒い
349 4 256 256 1 ファミコン風
350 */
351 if( 1 ) {
352 let mozaic = 1; //見た目の粒の大きさ
353 canvas.width = canvasWidth / mozaic * 4; //物理的
354 canvas.height = canvasHeight / mozaic * 4;
355 cc.scale( 4 / mozaic, 4 / mozaic ); //物理1ピクセル当たりの論理ピクセル数
356 }
357
358 let gridCellSize = canvasWidth / 32;
359
360 super( {
361 gridsPerInnerCell : 2, //内部1セルあたり、何gridか。
362 //gridはアプリ内での絶対的なマス目。
363 //例えば、gridが8ピクセルで、マップチップが16ピクセルなら、チップ当たり2grid
364 //という意味。
365 gridCol : 0,
366 gridRow : 0,
367 gridCols : Math.floor( canvasWidth / gridCellSize ),
368 gridRows : Math.floor( canvasHeight / gridCellSize ),
369
370 gridCellSize : gridCellSize, //全画面共通の最小単位。
371 innerCellSize : gridCellSize * 2, //その画面の単位。マップチップ等
372 }, null );
373
374 this.cc = cc;
375 this.cc.canvasWidth = canvasWidth;
376 this.cc.canvasHeight = canvasHeight;
377 }
378 // |
379 // V
380 async initialize() {//m
381
382
383 //---メトロノーム
384 this.metronomez = new Object();
385 this.addMetronome( "t1000_i2" );
386 this.addMetronome( "t500_i2" );
387 this.addMetronome( "t250_i2" );
388 //カーソル専用(手動追加)
389 this.metronomez.forCursor = {
390 maxTime : 500,
391 maxIndex : 0,
392 time : 0,
393 index : 0,
394 toggle : true,
395 changed : false,
396 }
397 /*
398 メトロノームの各フラグの変化タイミング図解(タイミングチャート)
399 maxTime : 250,
400 maxIndex : 3, の場合の各フラグの動き↓
401
402 (経過時間) 0 ... 250 ... 500 ... 750 ... 1000 ...(ms)
403
404 time : 0, 0 ..249 0 ..249 0 ..249 0 ..249 0 .. ...(ms)
405
406 index : 0, 000000001111111122222222000000001111 ...
407
408 toggle : true, ~~~~~~~~________~~~~~~~~________~~~~ ...
409
410 changed : false, ________~_______~_______~_______~___ ...
411
412 ~ : true
413 _ : false
414 ... : 略
415 ※このグラフ中の1文字単位の縦1列はrequestAnimationFrameに相当する
416
417 つまり、requestAnimationFrameごとに、
418 timeは経過時間を増やしていきmaxTimeになると0に戻る
419 indexはtimeがmaxTimeになるごとに1増え、maxIndexになると0に戻る
420 toggleはtimeがmaxTimeになるごとに真偽を反転する
421 changedはtimeがmaxTimeになったときだけ真になる
422
423 maxTimeでアニメのパラパラのスピード決め
424 toggleやchangedでアニメのパラパラの実行を判断
425 indexでアニメのパラパラの画の切り替え
426 */
427
428 this.anms = new Array();
429
430 this.spritez = {
431 player : {
432 images : [ "←", "-", "↑", "|", "→", "-", "↓", "|" ],
433 metronome : this.metronomez.t500_i2,
434 },
435 }
436
437 this.artz = {
438 spritez : this.spritez,
439 bgms : new Object(),
440 soundz : new Object(),
441 imagez : new Object(),
442 }
443 //---素材のプリロード
444 let dir = location.pathname.replace( /%20/g, " " ).replace( /\/[a-z0-9_ .-]*$/i, "" ) + "/";
445
446 this.cc.fillStyle = "cyan";
447 let str = "want a moment"
448 this.cc.fillText( str, ( this.cc.canvasWidth - this.cc.measureText( str ).width ) / 2, 100 );
449
450 await Promise.all( [
451 this.load( "bgm", "field", dir + "_bgm/街/MusMus-CT-NV-24/MusMus-CT-NV-24.mp3" ),
452 this.load( "bgm", "town", dir + "_bgm/街/MusMus-BGM-078/MusMus-BGM-078.mp3" ),
453 this.load( "sound", "pushA", dir + "_sound/pushA.mp3" ),
454 this.load( "sound", "pushWall", dir + "_sound/pushWall.mp3" ),
455 this.load( "sound", "walk", dir + "_sound/walk.wav" ),
456 this.load( "sound", "damage", dir + "_sound/musmus_other_set/othr10.mp3" ),
457 this.load( "image", "slime", dir + "_img/slime.png" ),
458 this.load( "image", "battleBG", dir + "_img/battle.png" ),
459 ] );
460 console.log( "素材のロード完了." );
461
462 this.monsters = new Array(); //戦闘中のモンスター
463 this.characters = new Array();
464 this.characterz = new Object();
465 this.treasureCatalog = new Object();
466
467 //---■メニュー
468 this.menusrcs = [
469 {
470 name : "yesno",
471 title : "",
472 items : [
473 {
474 name : "はい",
475 },
476 {
477 name : "いいえ",
478 }
479 ],
480 },
481 {
482 name : "camp",
483 title : "コマンド?",
484 argzName : "cmd",
485 items : [
486 {
487 name : "どうぐ",
488 title : "だれの?",
489 items : this.characters,
490 argzName : "char1",
491 assistMenu : {
492 title : "どれ?",
493 argzName : "dougu",
494 row : 1, //だれでも表示高さを固定して表示
495 },
496 },
497 {
498 name : "しらべる",
499 },
500 {
501 name : "まほう",
502 title : "だれの?",
503 items : this.characters,
504 argzName : "char1",
505 },
506 ],
507 },
508 //---
509 {
510 name : "つかう",
511 title : "だれに?",
512 items : this.characters,
513 argzName : "char2",
514 story : async function( argz ) {
515 this.openMessage();
516 await this.writeMessage( argz.char1.name + "は" + argz.char2.name + "に" + argz.dougu.name + "を使った!", { wait : true } );
517
518 let lostFlg = true;
519 if( argz.dougu.property.limit != undefined ) {
520 //limitが0になったらなくなるタイプ
521 argz.dougu.property.limit --;
522 //check.
523 lostFlg = argz.dougu.property.limit == 0;
524 if( lostFlg ) {
525 await this.writeMessage( "なんと " + argz.dougu.name + " は壊れてしまった!", { clear : true, wait : true } );
526 }
527 }
528 if( lostFlg ) {
529 let idx = argz.char1[ argz.cmd.name ].indexOf( argz.dougu );
530 argz.char1[ argz.cmd.name ].splice( idx, 1 );
531 }
532 this.closeMessage();
533 },
534 },
535 {
536 name : "わたす",
537 title : "だれに?",
538 items : this.characters,
539 argzName : "char2",
540 story : async function( cmd, char1, dougu, cmd2, char2 ) {
541 this.openMessage();
542 await this.writeMessage( char1.name + "は" + char2.name + "に" + dougu.name + "を渡した!", { wait : true } );
543 this.closeMessage();
544 },
545 },
546 {
547 name : "すてる",
548 story : async function( cmd, char1, dougu, cmd2 ) {
549 this.openMessage();
550 await this.writeMessage( char1.name + "は" + dougu.name + "を捨てた!", { wait : true } );
551 this.closeMessage();
552 },
553 },
554 ]
555 this.menusrcz = Utl.objectFrom( this.menusrcs, element => element.name );
556 //---■トレジャー
557 this.treasureCatalog = {
558 "どうけん" : {
559
560 },
561 "せいどうけん" : {
562
563 },
564 "てっけん" : {
565
566 },
567 "こうてつけん" : {
568
569 },
570 "ひほう" : {
571 property : {
572 copy : true,
573 limit : 3,
574 },
575 assistMenuCamp : {
576 title : "どうする?",
577 argzName : "dousuru",
578 items : [
579 this.menusrcz[ "つかう" ],
580 this.menusrcz[ "わたす" ],
581 this.menusrcz[ "すてる" ],
582 ],//items
583 },
584 },
585 "なんこう" : {
586 property : {
587 copy : true,
588 },
589 assistMenuCamp : {
590 title : "どうする?",
591 argzName : "dousuru",
592 items : [
593 this.menusrcz[ "つかう" ],
594 this.menusrcz[ "わたす" ],
595 this.menusrcz[ "すてる" ],
596 ],//items
597 },//nextMenu
598 assistMenuBattle : this.menusrcz[ "つかう" ],
599 },//なんこう
600 }//treasureCatalog
601
602 //tweak. キーを名前として要素内に登録
603 for( let name in this.treasureCatalog ) {
604 this.treasureCatalog[ name ].name = name;
605 }
606
607 //---■戦闘思考ルーチン
608 this.tacticz = {
609 slime : function( ourselves, enemies, battleLog ) {
610 /*
611 result = {
612 menus
613 argz
614 story
615 selectedItem
616 }
617 */
618 return {
619 argz : {
620 enemy : enemies[ 0 ],
621 },
622 story : this.battleStories[ "たたかう" ],
623 }
624 },
625 }
626 //---■モンスター
627 this.monsterCatalog = {
628 "モモスラ" : {
629 image : this.artz.imagez.slime,
630 property : {
631 copy : true,
632 hp : 10,
633 atack : 5,
634 defence : 5,
635 },
636 tactic : this.tacticz.slime,
637 }
638 }
639 //tweak. キーを名前として要素内に登録
640 for( let name in this.monsterCatalog ) {
641 this.monsterCatalog[ name ].name = name;
642 }
643
644 //---■戦闘コマンド 実行
645
646 this.battleStories = {
647 "たたかう" : async function( argz ) {
648 await this.writeMessage( argz.self.name + "は " + argz.enemy.name + " にこうげき!\n", { clear : true, } );
649 //check.
650 if( argz.enemy.property.hp == 0 ) {
651 await this.writeMessage( "しかし" + argz.enemy.name + "はすでに倒れている!", { wait : true, } );
652 return;
653 }
654 let enemyDamage = argz.self.property.atack;
655 enemyDamage -= argz.enemy.property.defence;
656
657 if( enemyDamage > 0 ) {
658 await this.effect_imageBure();
659 await this.writeMessage( argz.enemy.name + "に " + enemyDamage + " ポイントのダメージをあたえた!", { wait : true, } );
660 argz.enemy.property.hp -= enemyDamage;
661 //check.
662 if( argz.enemy.property.hp <= 0 ) {
663 argz.enemy.property.hp = 0;
664 await this.writeMessage( "\n" + argz.enemy.name + "は倒れた!", { wait : true, } );
665 }
666 } else {
667 await this.writeMessage( "ミス!ダメージをあたえられない!", { wait : true, } );
668 }
669 }
670 }
671
672 //---■戦闘コマンド
673 this.batcomz = {
674 "たたかう" : {
675 title : "あいては?",
676 items : function() {
677 return this.monsters.filter( m => m.property.hp > 0 );
678 },
679 argzName : "enemy",
680 row : 1,
681 story : this.battleStories[ "たたかう" ],
682 },
683 };
684 //tweak. キーを名前として要素内に登録
685 for( let key in this.batcomz ) {
686 this.batcomz[ key ].name = key;
687 }
688
689 //---■キャラクター
690
691 this.characters.push( {
692 name : "キャラ1",
693 property : {
694 hp : 20,
695 atack : 10,
696 defence : 10,
697 },
698 "どうぐ" : [
699 { name : "つるぎ" },
700 this.treasureCatalog[ "なんこう" ],
701 Utl.copyObjectKai( this.treasureCatalog[ "ひほう" ] ),
702 { name : "つるぎ" },
703 ],
704 batcoms : [
705 this.batcomz[ "たたかう" ],
706 ],
707 } );
708
709 this.characters.push( {
710 name : "キャラ2",
711 property : {
712 atack : 10,
713 defence : 10,
714 },
715 "どうぐ" : [
716 { name : "なんこう2" },
717 { name : "つるぎ" },
718 ],
719 batcoms : [
720 this.batcomz[ "たたかう" ],
721 ],
722 } );
723 this.characters.push( {
724 name : "キャラ3",
725 property : {
726 atack : 10,
727 defence : 10,
728 },
729 "どうぐ" : [
730 { name : "なんこう3" },
731 { name : "つるぎ" },
732 ],
733 batcoms : [
734 this.batcomz[ "たたかう" ],
735 ],
736 } );
737 Utl.array2object( this.characters, this.characterz, element => element.name );
738
739 }//initialize()
740 // |
741 // V
742 //プリロード
743 load( type, name, src ) {//m
744 return new Promise( function( promiseOk ) {
745 let object;
746 console.log( type, name, "loading.." );
747 switch( type ) {
748 case "image":
749 object = new Image();
750 this.artz.imagez[ name ] = object;
751 object.onload = function() {
752 console.log( "\t", type, name, "loaded." );
753 promiseOk();
754 }
755 object.src = src;
756 break;
757 case "bgm":
758 object = new Audio();
759 object.oncanplaythrough = function() {
760 this.artz.bgms[ name ] = object;
761 object.oncanplaythrough = null;
762 console.log( "\t", type, name, "loaded." );
763 promiseOk();
764 }.bind( this );
765 object.src = src;
766 object.load();
767 break;
768 case "sound":
769 object = new Audio();
770 object.oncanplaythrough = function() {
771 this.artz.soundz[ name ] = object;
772 object.oncanplaythrough = null;
773 console.log( "\t", type, name, "loaded." );
774 object.rapidPlay = function() {
775 object.pause();
776 object.currentTime = 0;
777 object.play();
778 /*
779 同じ音楽ファイルの連続演奏時は
780 最初にリセットをかける必要がある。
781 rapid=急速
782 */
783 }
784 promiseOk();
785 }.bind( this );
786 object.src = src;
787 object.load();
788 break;
789 }
790 }.bind( this ) );
791 }//load()
792
793 onkeydownx( e ) {//m
794
795 //debug. 現在の階層構造を表示
796 if( e.which == 79 ) { //'o'キー
797 console.log( "=== 階層構造 ===" );
798 console.log( this.debug_dir( this ) );
799 console.log( "=== /階層構造 ===" );
800 return;
801 }
802
803 if( this.keys.indexOf( e.which ) == -1 ) {
804 this.keys.push( e.which ); //キーセンス オン
805
806 this.currentElement.typeKey( e.which );
807 //キータイプ 実行
808 }
809 }
810 // |
811 // V
812 onkeyupx( e ) {//m
813 let idx = this.keys.indexOf( e.which );
814 if( idx > -1 ) this.keys[ idx ] *= -1;
815 //キーセンス オフ要求(同値のマイナスで示す)
816 }
817
818 addMetronome( name /*other args*/ ) {//m
819 //オーバーロード(メソッド名が同じで引数のパターンが異なる)の模造
820 //”関数の先頭で引数の型を判定する条件分岐で対応”
821 let argsId = "";
822 for( let arg of Array.from( arguments ) ) {
823 argsId += "," + ( typeof arg ).substr( 0, 1 );
824 }
825 let metronome;
826 switch( argsId ) {
827 case ",s" : //フォーマット名称 t時間_iインデックス上限
828 let tokens = name.split( /_/ );
829 metronome = {
830 maxTime : Number( tokens[ 0 ].substr( 1 ) ),
831 maxIndex : Number( tokens[ 1 ].substr( 1 ) ),
832 time : 0,
833 index : 0,
834 toggle : false,
835 changed : false,
836 }
837 this.metronomez[ name ] = metronome;
838 break;
839 case ",s,n" : //自由な名称、時間
840 let tm = arguments[ 1 ];
841 metronome = {
842 maxTime : tm,
843 maxIndex : 0,
844 time : 0,
845 index : 0,
846 toggle : false,
847 changed : false,
848 }
849 this.metronomez[ name ] = metronome;
850 break;
851 default:
852 alert( "addMetronome()にて、未定義の引数リスト:[" + args + "]" );
853 }
854 return metronome;
855
856 }//addMetronome()
857 // |
858 // v
859 removeMetronome( metronome ) {//m
860 delete this.metronomez[ metronome.name ];
861 }
862
863 start() {//m
864 //------------
865
866 this.focus();
867 this.keys = new Array();
868
869 this.memoryCard = {
870 player : {
871 position : {
872 map : App.worldmap,
873 x : 0,
874 y : 0,
875 },
876 direction : 0,
877 gold : 200,
878 },
879 }
880
881 //タイトル画面から始める
882 let titleScreen = new TitleScreen( {
883 innerCellSize : this.style.gridCellSize * 2,
884 gridCol : 0,
885 gridRow : 0,
886 gridCols : this.style.gridCols,
887 gridRows : this.style.gridRows,
888 }, this );
889 this.appendChild( titleScreen );
890
891
892 this.startFrame();
893
894 }//start()
895 // |
896 // V
897 stop() {//m
898 }
899 // |
900 // V
901 startFrame() {//m
902 this.keys.length = 0;
903 this.beforeTime = 0;
904 this.frame( 0 );
905 onkeydown = this.onkeydownx.bind( this );
906 onkeyup = this.onkeyupx.bind( this );
907 }
908 // |
909 // V
910 stopFrame() {//m
911 cancelAnimationFrame( this.timerId );
912 this.keys.length = 0;
913 onkeydown = null;
914 onkeyup = null;
915 }
916 // |
917 // V
918 frame( tm ) {//m
919 //経過時間
920 let diff = tm - this.beforeTime;
921 this.beforeTime = tm;
922
923 //メトロノーム更新
924 for( let metronome of Object.values( this.metronomez ) ) {
925 metronome.time += diff
926
927 //check. そのメトロノームは時を刻んだ
928 if( metronome.time >= metronome.maxTime ) {
929 metronome.time = 0;
930 metronome.toggle = ! metronome.toggle;
931 metronome.changed = true;
932 metronome.index ++;
933 //check. インデックスリセット
934 if( metronome.index == metronome.maxIndex ) metronome.index = 0;
935 } else {
936 metronome.changed = false;
937 }
938 }
939
940 //キーセンス 処理
941 for( let i = this.keys.length - 1; i >= 0; i-- ) {
942 let key = this.keys[ i ];
943 this.currentElement.senseKey( Math.abs( key ) );
944 //check. キーセンス オフ
945 if( key < 0 ) this.keys.splice( i, 1 );
946 }
947
948 //1ドットスクロール実行
949 if( this.mapScreen )
950 if( this.mapScreen.scroll.isAnimating ) {
951 this.mapScreen.frameForScroll();
952 }
953
954 this.storyCheck();
955
956 //アニメ処理 (forの途中で要素削除するので逆順)
957 for( let i = this.anms.length - 1; i >= 0; i-- ) {
958 if( this.anms[ i ].call( this ) ) this.anms.splice( i, 1 );
959 }
960
961 this.draw( this.cc );
962
963 this.timerId = requestAnimationFrame( this.frame.bind( this ) );
964 }
965 // |
966 // V
967 async storyCheck() {//m
968 //ストーリー検索&実行
969 let stories = this.currentElement.searchStory();
970 //check.
971 if( ! stories ) return;
972 for( let story of stories ) {
973 let res = await story.story.call( this, story.action );
974 //check. 真を返したら以降を打ち切り(町に入るところで戦闘発生等防ぐため)
975 if( res ) break;
976 }
977 }
978
979 openMessage( style ) {//m
980 // this.artz.soundz.pushA.rapidPlay();
981
982 //check. デフォルト値と上書き
983 style = Object.assign( {
984 gridCol : 2,
985 gridRow : 18,
986 gridCols : 28,
987 gridRows : 8,
988 innerCellSize : this.style.innerCellSize,
989 }, style );
990 //check. メッセージウィンドウがすでにあるときは一度閉じる
991 if( this.messageWindow ) {
992 this.closeMessage();
993 }
994 this.messageWindow = new MessageWindow( style, this.app );
995 this.appendChild( this.messageWindow );
996 }
997 // |
998 // V
999 closeMessage() {//m
1000 this.messageWindow.close();
1001 }
1002 // |
1003 // V
1004 async writeMessage( message, flgs ) {//m
1005 //check.
1006 if( ! flgs ) flgs = {};
1007 //check.
1008 if( flgs.clear ) this.messageWindow.clear();
1009 if( flgs.wait ) this.messageWindow.focus();
1010
1011 await this.messageWindow.write( ...arguments );
1012 }
1013
1014 yesno( closeFlg ) {//m
1015 this.artz.soundz.pushA.rapidPlay();
1016
1017 let yesnoMenu = new Menu( "selectmode", "", this.menusrcz.yesno, {
1018 gridCol : 26,
1019 gridRow : 25,
1020 }, this );
1021 this.appendChild( yesnoMenu );
1022
1023 return new Promise( function( promiseOk ) {
1024 console.log( "promised 'yesno'" );
1025 yesnoMenu.promiseOk = promiseOk;
1026 }.bind( this ) ).then( function( result ) {
1027 console.log( "promise 'yesno' completed" );
1028 this.removeChild( yesnoMenu );
1029 if( Utl.accessBy( result, "selectedItem" ) ) {
1030 return result.selectedItem.name == "はい";
1031 } else
1032 return false;
1033 }.bind( this ) );
1034 }
1035 // |
1036 // V
1037 //---●●● shop ●●●
1038 async shop( menusrc, wordingz ) {//m
1039 let treasureMenu = new Menu( "selectmode", "", menusrc, {
1040 gridCol : 2,
1041 gridRow : 2,
1042 }, this );
1043 this.mapScreen.appendChild( treasureMenu );
1044
1045 //所持金表示
1046 let stat = new List( {
1047 columns : [ { key : "title" }, { key : "value", align : "right" } ],
1048 items : [
1049 {
1050 title : "エン",
1051 value : function() { return this.memoryCard.player.gold; },
1052 },
1053 ],
1054
1055 }, {
1056 gridCol : treasureMenu.style.gridCols + 2,
1057 gridRow : 0,
1058 }, this.app );
1059 treasureMenu.appendFloat( stat );
1060
1061 this.openMessage();
1062
1063 while( 1 ) {
1064 //商品を選ぶ
1065 await this.writeMessage( wordingz[ "何を買うんだ?" ] + "\n" );
1066 treasureMenu.focus();
1067 let result = await treasureMenu.select();
1068
1069 //check. お店から退出
1070 if( ! result ) break;
1071
1072 let selectedItem = result.selectedItem;
1073
1074 this.messageWindow.focus(); //←見た目のため
1075 await this.writeMessage( selectedItem.name + wordingz[ "だな。" ] + "\n" );
1076 await this.writeMessage( selectedItem.price + "エン" + wordingz[ "だ。買うかね?" ] + "\n" );
1077 if( await this.yesno() ) {
1078 this.memoryCard.player.gold -= selectedItem.price;
1079 await this.writeMessage( wordingz[ "まいどあり!" ] + "\n", { wait : true } );
1080 await this.writeMessage( "\n" );
1081 }
1082 }
1083 await this.writeMessage( wordingz[ "ありがとうございました!" ], { wait : true } );
1084 this.closeMessage();
1085 this.mapScreen.removeChild( treasureMenu );
1086 }
1087
1088 encounter() {//m
1089 this.battle();
1090 }
1091 // |
1092 // V
1093 //---●●● battle ●●●
1094 async battle() {//m
1095 this.monsters = new Array();
1096 let max = Math.floor( Math.random() * 5 ) + 1;
1097 console.log( max );
1098 for( let i = 0; i < max; i++ ) {
1099 this.monsters[ i ] = Utl.copyObjectKai( this.monsterCatalog[ "モモスラ" ] ),
1100 this.monsters[ i ].name += i + 1;
1101 }
1102 let masterOfMonsters = null;
1103
1104 let battleLog = new Array();
1105
1106 let battleView = new BattleView( this.monsters, {
1107 gridCol : 6,
1108 gridRow : 6,
1109 gridCols : 20,
1110 gridRows : 12,
1111 }, this );
1112
1113 await this.effect_battleStart( battleView );
1114
1115 this.mapScreen.appendFloat( battleView );
1116
1117 this.openMessage();
1118 let m = this.monsters.map( monster => monster.name ).join( "、" );
1119 await this.writeMessage( m + "があらわれた!", { wait : 1 } );
1120 this.closeMessage();
1121
1122 let theEndOfTheBattle;
1123 do {
1124 let commandz = new Object();
1125
1126 this.openMessage( {
1127 gridCol : 9,
1128 gridRow : 18,
1129 gridCols : 21,
1130 gridRows : 8,
1131 } );
1132
1133 //プレイヤー側のコマンド決定
1134 for( let i = 0; i < this.characters.length; i++ ) {
1135 let character = this.characters[ i ];
1136 //check.
1137 if( character.property.hp == 0 ) continue;
1138
1139 let cmdMenu = new Menu( "selectmode", "", {
1140 name : "batcom",
1141 title : character.name,
1142 items : character.batcoms,
1143 }, {
1144 gridCol : 1,
1145 gridRow : 18,
1146 }, this.app );
1147 this.appendChild( cmdMenu );
1148
1149 this.writeMessage( character.name + "はどうする?", { rapid : true, clear : true } );
1150
1151 let command = await cmdMenu.select();
1152 cmdMenu.removeAllElements();
1153 //check.
1154 if( ! command ) {
1155 i -= i == 0 ? 1 : 2;
1156 continue;
1157 }
1158 //tweak.
1159 command.argz.self = character;
1160
1161 commandz[ character.name ] = command;
1162 }//for character
1163 this.closeMessage();
1164
1165 //モンスター側のコマンド決定
1166 if( masterOfMonsters ) {
1167 //リーダーが思考する場合 未使用 未デバッグ
1168 let resultz = masterOfMonsters.strategy.call( this, this.monsters, this.characters, battleLog );
1169 Object.assign( commandz, resultz );
1170 } else {
1171 //それぞれが思考する場合
1172 for( let monster of this.monsters ) {
1173 //check.
1174 if( monster.property.hp == 0 ) continue;
1175 commandz[ monster.name ] = monster.tactic.call( this, this.monsters, this.characters, battleLog );
1176 commandz[ monster.name ].argz.self = monster;
1177 }
1178 }
1179
1180 this.openMessage();
1181 for( let name in commandz ) {
1182 //check.
1183 if( commandz[ name ].argz.self.property.hp == 0 ) continue;
1184 let command = commandz[ name ];
1185
1186 await command.story.call( this, command.argz );
1187
1188 //check. 味方の生存者いない
1189 if( ! this.characters.some( c => c.property.hp > 0 ) ) {
1190 await this.writeMessage( "\n" + characters[ 0 ].name + "たちは全滅した…", { wait : true } );
1191 theEndOfTheBattle = true;
1192 break;
1193 } else if( ! this.monsters.some( m => m.property.hp > 0 ) ) {
1194 //敵の生存者いない
1195 await this.writeMessage( "\nモンスターたちを倒した!", { wait : true } );
1196 theEndOfTheBattle = true;
1197 break;
1198 } else {
1199 theEndOfTheBattle = false;
1200 }
1201 }
1202 this.closeMessage();
1203
1204 } while( ! theEndOfTheBattle );
1205
1206 this.mapScreen.removeChild( battleView );
1207 }
1208 async delay( ms ) {
1209 await new Promise( promiseOk => setTimeout( promiseOk, ms ) );
1210 }
1211 async effect_battleStart( element ) {//m
1212 await this.effect_flash( "red" );
1213 await this.effect_flash( "yellow" );
1214 await this.effect_flash( "red" );
1215 this.stopFrame();
1216
1217 let cc = this.cc;
1218 let step = 13;
1219 let movl = 200
1220 for( let i = 1; i <= step; i ++ ) {
1221 let theta = Math.PI * 2 / step * i;
1222 let alpha = 1 / step * i;
1223 let movx = movl - movl / step * i;
1224 this.draw( cc );
1225 cc.save();
1226 cc.globalAlpha = alpha;
1227 cc.translate( element.x + movx, element.y );
1228 cc.rotate( theta );
1229
1230 element.draw( cc );
1231 cc.restore();
1232
1233 await this.delay( 60 );
1234 }
1235
1236 this.startFrame();
1237 }
1238 effect_imageBure() {//m
1239
1240 }
1241 effect_flash( color ) {//m
1242 let effect = new Element( {}, this );
1243 //描画
1244 effect.draw = function( cc ) {
1245 cc.save();
1246 cc.globalCompositeOperation = "difference";
1247 cc.fillStyle = color;
1248 cc.fillRect( 0, 0, cc.canvasWidth, cc.canvasHeight );
1249 cc.restore();
1250 }
1251
1252 //エフェクト開始
1253
1254 let cnt = 0;
1255 this.appendChild( effect );
1256
1257 //音声
1258 this.artz.soundz.damage.rapidPlay();
1259
1260 return new Promise( function( promiseOk ) {
1261 this.anms.push( function(a) {
1262 cnt++;
1263 //check.
1264 if( cnt == 10 ) { //何フレーム上記drawを行うか
1265 //エフェクト終了
1266 this.removeChild( effect );
1267 promiseOk();
1268 return true;
1269 }
1270 } );
1271 }.bind( this ) );
1272 }
1273 debug_dir( object, tab, idx ) {//m
1274 //check.
1275 if( tab == undefined ) tab = "";
1276 let str = tab;
1277 if( object != this ) {
1278 str += "→ childs[";
1279 str += idx;
1280 str += "] = ";
1281 }
1282 str += object.name;
1283 if( object == this.currentElement ) str += "*";
1284 if( object.prevFocus ) str += " (prev:" + object.prevFocus.name + ") ";
1285 str += "\n";
1286
1287 tab += " ";
1288
1289 for( let i = 0; i < object.childs.length; i++ ) {
1290 let child = object.childs[ i ];
1291 str += this.debug_dir( child, tab, i );
1292 }
1293 return str;
1294 }
1295 }//App
1296 //---▲▲▲ 3 App ▲▲▲
1297 //---
1298 //---◆◆◆ 4 MessageWindow ◆◆◆
1299 class MessageWindow extends Element {//c
1300 constructor( style, app ) {
1301 //check. デフォルト値と上書き
1302 style = Object.assign( {
1303 gridCols : app.style.gridCols - 2,
1304 gridRows : app.style.gridRows - 2,
1305 }, style );
1306 //下記で自身のgridCols,gridRowsを参照しているので、2つに分けて上記であらかじめ定義
1307 style = Object.assign( {
1308 gridCol : 1,
1309 gridRow : 1,
1310 innerCols : style.gridCols / app.style.gridsPerInnerCell,
1311 innerRows : style.gridRows / app.style.gridsPerInnerCell,
1312 }, style );
1313
1314 super( style, app );
1315
1316 this.clear();
1317
1318 //フラグ
1319 this.endedByOutside = false;
1320 }
1321 close() {//m
1322 this.parent.removeChild( this );
1323 }
1324 clear() {//m
1325 this.inner = new Array();
1326 for( let r = 0; r < this.style.innerRows; r++ ) {
1327 this.inner.push( new Array() );
1328 }
1329 this.innerCursorCol = 0;
1330 this.innerCursorRow = 0;
1331 }
1332 write( message, flgs ) {//m
1333 //check.
1334 if( ! flgs ) {
1335
1336 flgs = {
1337 clear : false, //メッセージ表示前文字を全消去したいときtrue
1338 rapid : false, //メッセージを一気に表示したいときtrue
1339 wait : false, //メッセージ表示後キー入力待ちしたいときtrue
1340 }
1341 }
1342 //check. "外部により終了させられた"フラグ(1文字ずつ表示を一気に等)
1343 this.endedByOutside = false;
1344
1345 this.message = message;
1346 this.flgs = flgs;
1347 this.seek = 0;
1348 this.isWaitingKeyNow = false;
1349
1350 //check.
1351 if( flgs.rapid ) {
1352 while( this.writeChar() );
1353 return;
1354 }
1355
1356 return new Promise( function( promiseOk ) {
1357 console.log( "promised 'message typing'" );
1358 this.promiseOk = promiseOk;
1359 this.metronomeForWrite = this.app.addMetronome( "writeCharTiming", 100 );
1360 this.metronomeForCursor = this.app.addMetronome( "messageCursor", 400 );
1361
1362 //1文字ずつ表示するアニメーション
1363 this.app.anms.push( function() {
1364
1365 //check. メトロノームに合わせる
1366 if( ! this.metronomeForWrite.changed ) return;
1367
1368 //終了チェック兼1文字出力
1369 if( this.endedByOutside || ! this.writeChar() ) {
1370 //check. キー待ちしない
1371 if( ! this.flgs.wait ) {
1372 console.log( "promiseOk 'message typing'" );
1373 this.promiseOk();
1374 } else {
1375 this.isWaitingKeyNow = true;
1376 //キー待ちする場合は、typeKey()のほうでthis.promiseOk()される
1377 }
1378
1379 return true; //アニメ終了の意
1380 }
1381 }.bind( this ) );//anm
1382 }.bind( this ) ).then( function() {
1383 //promiseOk()されたらここに来る
1384 console.log( "promise 'message typing' completed" );
1385 this.app.removeMetronome( this.metronomeForWrite );
1386 this.app.removeMetronome( this.metronomeForCursor );
1387 }.bind( this ) );//new Promise
1388 }//write()
1389 // |
1390 // V
1391 writeChar() {//m
1392 /* memo.
1393 messageの末尾時にfalseを返す
1394 */
1395 let ch = this.message.substr( this.seek, 1 );
1396 let kaigyo = false;
1397
1398 switch( ch ) {
1399 case "\n":
1400 kaigyo = true;
1401 break;
1402 default:
1403 this.inner[ this.innerCursorRow ][ this.innerCursorCol ] = ch;
1404 this.innerCursorCol ++;
1405 //check. 右端
1406 if( this.innerCursorCol == this.style.innerCols ) kaigyo = true;
1407 }//switch
1408
1409 //check. 改行
1410 if( kaigyo ) {
1411 this.innerCursorCol = 0;
1412 //ウィンドウの途中行
1413 if( this.innerCursorRow < this.style.innerRows - 1 ) {
1414 this.innerCursorRow ++;
1415 } else {
1416 //ウィンドウの最下行
1417 //1行スクロール
1418 this.inner.shift();
1419 this.inner.push( new Array() );
1420 }
1421 }
1422
1423 this.seek ++;
1424
1425 return this.seek < this.message.length; //messageの末尾時false
1426
1427 }//writeChar()
1428 typeKey( key ) {
1429 if( this.seek < this.message.length ) {
1430 //文字出力中のときは文字出力を一気に行う
1431 for( let i = this.seek; i < this.message.length; i++ ) {
1432 this.writeChar();
1433 }
1434 this.endedByOutside = true;
1435 } else if( this.flgs.wait ) {
1436 //文字出力終了のときは、呼び出し元に処理終了を知らせる。
1437 this.promiseOk();
1438 }
1439 }//typeKey()
1440 draw( cc ) {//m
1441 super.draw( cc );
1442 cc.save();
1443 // cc.translate( this.x, this.y );
1444
1445 //メッセージを描画
1446 cc.font = this.style.innerCellSize + "px ''";
1447 cc.fillStyle = "black";
1448 for( let r = 0; r < this.style.innerRows; r++ ) {
1449 let gy = this.style.innerCellSize * r;
1450 for( let c = 0; c < this.style.innerCols; c++ ) {
1451 let gx = this.style.innerCellSize * c;
1452 let ch = this.inner[ r ][ c ];
1453 //check. 行末
1454 if( ! ch ) break;
1455 cc.fillText( ch, gx, gy + this.style.innerCellSize );
1456 }
1457 }
1458
1459 //キー待ちを示すカーソルを描画
1460 if( this.isWaitingKeyNow && this.metronomeForCursor.toggle ) {
1461 let gx = this.innerCursorCol * this.style.innerCellSize;
1462 let gy = this.innerCursorRow * this.style.innerCellSize;
1463 cc.fillText( "▼", gx, gy + this.style.innerCellSize );
1464 }
1465
1466 cc.restore();
1467 }//draw()
1468 }//MessageWindow
1469 // |
1470 // V
1471 //---■■■ 5 List ■■■
1472 class List extends Element {//c
1473 constructor( src, style, app ) {
1474 //check. styleのデフォルト値と上書き
1475 style = Object.assign( {
1476 innerCellSize : app.style.gridCellSize,
1477 gridCol : 0,
1478 gridRow : 0,
1479 }, style );
1480
1481 //リストの項目
1482 let items;
1483 if( src.items instanceof Function )
1484 items = src.items.call( app ); //関数から得る
1485 else
1486 items = src.items;
1487
1488 //check. リストの列数
1489 if( style.gridCols == undefined ) {
1490 //各列において、その全行の文字列の中での最大文字数を得る
1491 //その最大文字数をstyle.gridColsへ加算していき、ウィンドウの横幅とする
1492 style.gridCols = 0;
1493 for( let column of src.columns ) {
1494 let strings = new Array();
1495 for( let item of items ) {
1496 let string = ( item[ column.key ] instanceof Function )
1497 ? item[ column.key ].call( app )
1498 : item[ column.key ];
1499 strings.push( string );
1500 }
1501 column.cols = Utl.maxLengthBinDiv2( strings ) + 1; //その列の幅
1502 column.col = style.gridCols; //その列の位置
1503 style.gridCols += column.cols;
1504 }
1505 //check. タイトルのほうが長ければ
1506 if( src.title != undefined ) {
1507 style.gridCols = Math.max( Utl.lengthBinDiv2( src.title ), style.gridCols );
1508 }
1509 //check.
1510 if( style.gridPaddingLeft != undefined ) style.gridCols += style.gridPaddingLeft;
1511 }
1512 //check. リストの行数
1513 if( style.gridRows == undefined ) {
1514 style.gridRows = items.length;
1515 //check.
1516 if( src.title ) style.gridRows++;
1517 }
1518
1519 super( style, app );
1520
1521 this.src = src;
1522 this.items = items;
1523 }
1524 draw( cc ) {//m
1525
1526 this.drawWindow( cc );
1527
1528 //debug. テキストエリアに水色枠
1529 if( 0 ) {
1530 cc.strokeStyle = "cyan";
1531 cc.strokeRect( 0, 0, this.w, this.h );
1532 }
1533
1534 cc.fillStyle = "black";
1535 cc.font = this.style.innerCellSize + "px ''";
1536
1537 //タイトル
1538 if( this.src.title ) {
1539 let gx = ( this.style.gridPaddingLeft - 0.3 ) * this.app.style.gridCellSize;
1540 let gy = this.style.innerCellSize * 0.65;
1541 cc.fillText( this.src.title, gx, gy );
1542 }
1543
1544 //リスト
1545 cc.save();
1546 this.translateToItemArea( cc );
1547
1548 //行
1549 for( let i = 0; i < this.items.length; i++ ) {
1550 let item = this.items[ i ];
1551 let gy = this.style.innerCellSize * i;
1552 //列
1553 for( let col = 0; col < this.src.columns.length; col++ ) {
1554 let column = this.src.columns[ col ];
1555 //値が関数になっている場合に対応 たとえば、shopの「所持金」表示
1556 let string = ( item[ column.key ] instanceof Function )
1557 ? item[ column.key ].call( this.app )
1558 : item[ column.key ];
1559 let gx = column.col * this.style.innerCellSize;
1560 //check. 右寄せ指定 たとえば数値表示
1561 if( Utl.accessBy( column, "align" ) == "right" ) {
1562 gx += column.cols * this.style.innerCellSize;
1563 gx -= cc.measureText( string ).width;
1564 }
1565
1566 cc.fillText( string, gx, gy + this.style.innerCellSize );
1567 //debug. 列に緑枠
1568 if( 0 ) {
1569 cc.strokeStyle = "green";
1570 cc.strokeRect(
1571 column.col * this.style.innerCellSize,
1572 gy,
1573 column.cols * this.style.innerCellSize,
1574 this.style.innerCellSize
1575 );
1576 }
1577 }//for
1578 }//for
1579 cc.restore();
1580 }//draw()
1581 translateToItemArea( cc ) {//m
1582 if( this.src.title ) cc.translate( 0, this.style.innerCellSize );
1583 cc.translate( this.style.gridPaddingLeft * this.style.innerCellSize, 0 );
1584 }
1585 }//List
1586 // |
1587 // V
1588 //---◆◆◆ 6 Menu ◆◆◆
1589 class Menu extends List {//c
1590 constructor( mode, context, src, style, app ) {
1591 //check. 表の列設定が未定義なら1列でnameを並べるだけの通常メニュー
1592 if( src.columns == undefined ) {
1593 src.columns = [ { key : "name" } ];
1594 }
1595 //check. 左端にカーソル用の空白エリア
1596 if( style.gridPaddingLeft == undefined ) style.gridPaddingLeft = 1;
1597
1598 super( src, style, app );
1599
1600 this.mode = mode;
1601 this.context = context;
1602 this.src = src;
1603 //check. contextをthisで参照できるように
1604 if( this.src.context ) this.context = this.src.context;
1605
1606 //カーソルが横にも動く場合のため(未対応中)
1607 this.itemCols = 1; //現在使用していない。
1608 this.itemRows = this.items.length; //縦方向は基本なのでこれは使用している
1609
1610 this.cursorX = 0;
1611 this.cursorY = 0;
1612 }
1613 select() {//m
1614 return new Promise(
1615 function( promiseOk ) {
1616 console.log( "promised 'select'" );
1617 this.promiseOk = promiseOk;
1618 }.bind( this )
1619 ).then(
1620 function( result ) {
1621 console.log( "promise 'select' completed" )
1622 return result;
1623 }
1624 );
1625 }
1626 typeKey( key ) {//m
1627 let addX = ( key == 39 ) - ( key == 37 );
1628 let addY = ( key == 40 ) - ( key == 38 );
1629
1630 if( addX || addY ) {
1631 this.cursorX += addX;
1632 this.cursorY += addY;
1633 //check.
1634 if( this.cursorY < 0 ) this.cursorY = 0;
1635 if( this.cursorY >= this.itemRows ) this.cursorY = this.itemRows - 1;
1636
1637 //カーソル 移動のタイミングで「点滅の点灯開始状態」にする
1638 //こうしないとチラチラして見づらい
1639 this.app.metronomez.forCursor.toggle = true;
1640 this.app.metronomez.forCursor.time = 0;
1641 } else {
1642
1643 switch( key ) {
1644 case 32://SPACE
1645 this.app.artz.soundz.pushA.rapidPlay();
1646
1647 this.selectedItem = this.items[ this.cursorY ];
1648
1649 //選択した項目の形態
1650 let menusrc;
1651 //選択した項目はメニューであるA
1652 if( this.selectedItem.items ) {
1653 menusrc = this.selectedItem;
1654 } else if( this.src.assistMenu ) {
1655 //選択した項目はメニューではないが、
1656 //this.srcにassistMenu指定がある。B
1657 menusrc = Utl.copyObjectKai( this.src.assistMenu );
1658 menusrc.items = this.selectedItem[ this.src.name ];
1659 } else if( this.selectedItem[ "assistMenu" + this.context ] ) {
1660 //選択した項目はメニューではないが、
1661 //this.selectedItem内にcontextMenu指定がある。C
1662 menusrc = this.selectedItem[ "assistMenu" + this.context ];
1663 } else {
1664 //メニューの終了
1665 //ストーリー内でメニューの結果を待っている場合
1666 if( this.mode == "selectmode" ) {
1667 //選択結果を返す
1668 this.promiseOk( this.getResult() );
1669 } else {
1670 //campなどでオペレーションする場合
1671 //埋め込まれたスクリプトをもとに実行する
1672 this.execute();
1673 }
1674 return;
1675 }
1676
1677 //次のメニューを表示
1678 let col = menusrc.col == undefined
1679 ? this.selectedItem.name.length + 2
1680 : menusrc.col;
1681 let row = menusrc.row == undefined
1682 ? this.cursorY + 2
1683 : menusrc.row;
1684 let menu = new Menu( this.mode, this.context, menusrc, {
1685 gridCol : col,
1686 gridRow : row,
1687 }, this.app );
1688 this.appendChild( menu );
1689 menu.promiseOk = this.promiseOk;
1690
1691 break;
1692 case 66://B
1693 //セレクトモードでトップメニューがキャンセルされた
1694 let thisIsTop = ! ( this.parent instanceof Menu );
1695 if( this.mode == "selectmode" && thisIsTop ) {
1696 this.promiseOk( null );
1697 } else {
1698 //それ以外
1699 this.parent.removeChild( this );
1700 }
1701 break;
1702 }//switch
1703 }//if else
1704 }//typeKey()
1705 // |
1706 // V
1707 getResult() {//m
1708 let result = new Object();
1709 result.menus = new Array();
1710 //自分自身から上へさかのぼってmenusへ加えていく
1711 let element = this;
1712 while( element instanceof Menu ) {
1713 result.menus.unshift( element );
1714 element = element.parent;
1715 }
1716 result.argz = new Object();
1717 for( let menu of result.menus ) {
1718 if( menu.src.story ) result.story = menu.src.story; //埋め込みスクリプト
1719 result.argz[ menu.src.argzName ] = menu.selectedItem; //そのための引数
1720 }
1721 result.selectedItem = result.menus[ result.menus.length - 1 ].selectedItem;
1722
1723 /*
1724 メニューの選択結果は、
1725 result.menus たどったメニュー
1726 result.argz たどって得られた引数リスト
1727 result.story たどりの途中で得られた埋め込みスクリプト
1728 result.selectedItem 最後に選択した項目
1729 なお、storyとargzは
1730 story( argz )
1731 のように実行する。
1732 argzはstoryの中で、argz.char1などのように参照する。
1733 storyはこのファイルの最初のほうのメニュー定義部分などで定義されているもの。
1734 argz.char1のchar1などは、同じくメニュー定義部分で、argzKey : "char1"
1735 のように定義されている。
1736 storyの内容も、storyの中でどんな引数を使うかもユーザーが定義する。
1737 */
1738 return result;
1739 }
1740 async execute() {//m
1741 let result = this.getResult();
1742
1743 //check.
1744 if( ! result.story ) {
1745 alert( "storyが未定義の状態です。at menu.execute()" );
1746 }
1747
1748 await result.story.call( this.app, result.argz );
1749
1750 //check. storyによってサブメニューの元となったモノがなくなった。
1751 /*
1752 たとえば、
1753 コマンド?:どうぐ
1754 だれの?:キャラ1
1755 どれ?:なんこう
1756 どうする?:つかう
1757 だれに?:キャラ1
1758 として、なんこうを使ったら、なんこうはなくなるので、
1759 なんこうに由来する どうする? だれに? の2つのメニューは
1760 消さなくてはならない。その処理↓
1761 */
1762 for( let i = result.menus.length - 1; i > 0; i -- ) {
1763 let menu = result.menus[ i ];
1764 let selfItem = menu.parent.selectedItem;
1765 //親メニューに自身の選択項目がない。
1766 if( ! menu.parent.items.includes( selfItem ) ) {
1767 menu.parent.removeChild( menu );
1768 //メニューを1つ消せば以降のサブメニューはchildなので一緒に消える
1769 }
1770 }
1771 }
1772 draw( cc ) {//m
1773 super.draw( cc ); //extends List
1774
1775 cc.save();
1776
1777 this.translateToItemArea( cc );
1778
1779 //カーソルを描画
1780 if( this.app.currentElement != this ||
1781 this.app.metronomez.forCursor.toggle ) {
1782 let w = this.style.innerCellSize * .7;
1783 let h = this.style.innerCellSize * .8;
1784 let gx = - this.style.innerCellSize + ( this.style.innerCellSize - w ) / 2;
1785 let gy = this.cursorY * this.style.innerCellSize + ( this.style.innerCellSize - h ) / 2 + 1;
1786 cc.beginPath();
1787 cc.moveTo( gx, gy );
1788 cc.lineTo( gx + w, gy + h / 2 );
1789 cc.lineTo( gx, gy + h );
1790 cc.closePath();
1791 cc.fillStyle = "black";
1792 cc.fill();
1793 }
1794
1795 cc.restore();
1796
1797 super.drawChildren( cc );
1798 }//draw()
1799 }//Menu
1800 //---■■■ 7 Title ■■■
1801 class TitleScreen extends Element {//c
1802 constructor( style, app ) {
1803 super( style, app );
1804 }
1805 async typeKey( key ) {//m
1806 this.app.artz.soundz.pushA.play();
1807
1808 this.app.removeChild( this );
1809 this.app.mapScreen = new MapScreen( {
1810 innerCellSize : this.app.style.gridCellSize * 2,
1811 gridCol : 0,
1812 gridRow : 0,
1813 gridCols : this.app.style.gridCols + ( this.app.style.gridCols % 2 ? 0 : 1 ),
1814 gridRows : this.app.style.gridRows + ( this.app.style.gridRows % 2 ? 0 : 1 ),
1815 //偶数なら奇数にする
1816 }, this.app );
1817
1818 this.app.appendChild( this.app.mapScreen );
1819
1820 let map = this.app.memoryCard.player.position.map;
1821 let x = this.app.memoryCard.player.position.x;
1822 let y = this.app.memoryCard.player.position.y;
1823 switch( 2 ) {
1824 case 0: this.app.mapScreen.playerMapMoveTo( map, x, y ); //memorycard
1825 break;
1826 case 1: this.app.mapScreen.playerMapMoveTo( map, 3, 8 ); //外
1827 break;
1828 case 2: this.app.mapScreen.playerMapMoveTo( App.townmap, 3, 5 );//町
1829 break;
1830 }
1831
1832 this.app.openMessage();
1833 await this.app.writeMessage( "スタート", { wait : true } );
1834 this.app.closeMessage();
1835 }
1836 draw( cc ) {//m
1837 cc.fillStyle = "black";
1838 cc.fillRect( 0, 0, this.w, this.h );
1839 cc.font = this.style.innerCellSize + "px''";
1840 cc.fillStyle = "white";
1841 cc.fillText( "hit any key", this.w * .35, this.h * .75 );
1842 }
1843 }//TitleScreen
1844 // |
1845 // V
1846 //---◆◆◆ 8 Map ◆◆◆
1847 class MapScreen extends Element {//c
1848 constructor( style, app ) {
1849 // style = Object.assign( {
1850 // gridCols : app.style.gridCols,
1851 // gridRows : app.style.gridRows,
1852 // }, style );
1853 style = Object.assign( {
1854 innerCols : style.gridCols / app.style.gridsPerInnerCell,
1855 innerRows : style.gridRows / app.style.gridsPerInnerCell,
1856 }, style );
1857
1858 super( style, app );
1859
1860 this.actions = new Array();
1861
1862 this.scroll = {
1863 count : 0,
1864 isAnimating : false,
1865 tweakGx : 0,
1866 tweakGy : 0,
1867 }
1868 this.keyLock = false;
1869 }
1870 assignStyle( newStyle ) {
1871 super.assignStyle( newStyle );
1872
1873 this.style.innerColsHalf = Math.floor( this.style.innerCols / 2 );
1874 this.style.innerRowsHalf = Math.floor( this.style.innerRows / 2 );
1875 }
1876 // |
1877 // V
1878 tweakColForLooping( targetCol ) {//m
1879 //tweak. ループ時は範囲外の値を範囲内に調整する
1880 if( this.cfg.maploop ) {
1881 if( targetCol < 0 )
1882 targetCol += this.mapCols;
1883 else if( targetCol >= this.mapCols )
1884 targetCol -= this.mapCols;
1885 }
1886 return targetCol;
1887 }
1888 tweakRowForLooping( targetRow ) {//m
1889 //tweak. ループ時は範囲外の値を範囲内に調整する
1890 if( this.cfg.maploop ) {
1891 if( targetRow < 0 )
1892 targetRow += this.mapRows;
1893 else if( targetRow >= this.mapRows )
1894 targetRow -= this.mapRows;
1895 }
1896 return targetRow;
1897 }
1898 getPositionAwayBy( step ) {//m
1899 let dir = this.app.memoryCard.player.direction;
1900 let x = this.playerCol + ( ( dir == 2 ) - ( dir == 0 ) ) * step;
1901 let y = this.playerRow + ( ( dir == 3 ) - ( dir == 1 ) ) * step;
1902 return { x : x, y : y }
1903 }
1904 // |
1905 // V
1906 async typeKey( key ) {//m
1907 if( key == 32 ) {
1908 //check. 向いている方向に何かがある
1909 let pos = this.getPositionAwayBy( 1 );
1910 let bit;
1911 if( bit = Utl.accessBy( this.mapBits, pos.y, pos.x ) ) {
1912 if( bit == "□" ) {
1913 pos = this.getPositionAwayBy( 2 );
1914 this.actions.push( { x : pos.x, y : pos.y, type : "contact" } );
1915 return;
1916 }
1917 }
1918 if( bit = Utl.accessBy( this.storyBits, pos.y, pos.x ) ) {
1919 if( bit.sprite ) {
1920 this.actions.push( { x : pos.x, y : pos.y, type : "contact" } );
1921 return;
1922 }
1923 }
1924
1925 //コマンドメニューを開く
1926 this.app.artz.soundz.pushA.rapidPlay();
1927 let campMenu = new Menu( "executemode", "Camp", this.app.menusrcz.camp, {
1928 gridCol : 1,
1929 gridRow : 1,
1930 }, this.app );
1931 this.app.appendChild( campMenu );
1932 campMenu.focus();
1933 }
1934 }
1935 // |
1936 // V
1937 senseKey( key ) {//m
1938 //check.
1939 if( this.scroll.isAnimating || this.keyLock ) return;
1940
1941 let addX, addY;
1942
1943 switch( key ) {
1944 case 37: this.app.memoryCard.player.direction = 0; addX = -1; addY = 0; break;
1945 case 38: this.app.memoryCard.player.direction = 1; addX = 0; addY = -1; break;
1946 case 39: this.app.memoryCard.player.direction = 2; addX = 1; addY = 0; break;
1947 case 40: this.app.memoryCard.player.direction = 3; addX = 0; addY = 1; break;
1948 default: return;
1949 }
1950
1951 //移動先
1952 let destinCol = this.tweakColForLooping( this.playerCol + addX );
1953 let destinRow = this.tweakRowForLooping( this.playerRow + addY );
1954 //check. 移動先は壁
1955 let bit;
1956 if( bit = Utl.accessBy( this.mapBits, destinRow, destinCol ) )
1957 if( this.cfg.walls.indexOf( bit ) > -1 ) {
1958 this.app.artz.soundz.pushWall.play();
1959 return;
1960 }
1961 //check. 移動先はスプライト
1962 if( Utl.accessBy( this.storyBits, destinRow, destinCol, "sprite" ) ) return;
1963
1964 //1マススクロールの起動
1965 this.scroll.addX = addX;
1966 this.scroll.addY = addY;
1967 this.scroll.count = 0;
1968 this.scroll.isAnimating = true;
1969
1970 }//senseKey
1971 // |
1972 // V
1973 frameForScroll() {//m
1974 //1マススクロールにおける、1ドット分のスクロール処理
1975
1976 this.scroll.tweakGx -= this.scroll.addX;
1977 this.scroll.tweakGy -= this.scroll.addY;
1978 //tweakGx,Gyが1ドットスクロールの主要のしくみ
1979 //それについてはthis.draw()を参照
1980
1981 this.scroll.count ++;
1982 //check. スクロールの終了
1983 if( this.scroll.count == this.style.innerCellSize ) {
1984 this.scroll.isAnimating = false;
1985
1986 this.scroll.tweakGx = 0;
1987 this.scroll.tweakGy = 0;
1988 //draw()はスクロール終了後も参照するからここで0にする。
1989
1990 this.playerCol = this.tweakColForLooping( this.playerCol + this.scroll.addX );
1991 this.playerRow = this.tweakRowForLooping( this.playerRow + this.scroll.addY );
1992
1993 //マップ外に乗ったストーリー発生
1994 if( ! this.cfg.maploop && (
1995 this.playerCol < 0 || this.playerCol >= this.mapCols ||
1996 this.playerRow < 0 || this.playerRow >= this.mapRows ) ) {
1997 this.actions.push( { type : "exit" } );
1998 } else {
1999 //座標に乗ったストーリー発生
2000 this.actions.push( { x : this.playerCol, y : this.playerRow, type : "walk" } );
2001 }
2002 }//if スクロール終了
2003 }//frameForScroll()
2004 // |
2005 // V
2006 playerMapMoveTo( mapdatafunc /*other args*/ ) {//m
2007
2008 //マップ間移動
2009
2010 //check. 
2011 if( this.app.bgm ) {
2012 this.app.bgm.pause();
2013 this.app.bgm.currentTime = 0;
2014 }
2015
2016 //データを記述した関数からデータを取り出す1
2017 let commentData = mapdatafunc.toString().match( /\/\*[\r\n]+([\s\S]+)[\r\n]+\*\// )[ 1 ];
2018
2019 //データを記述した関数からデータを取り出す2
2020 this.cfg = mapdatafunc();
2021
2022
2023 //取り出したデータを使えるように加工
2024 this.mapBits = new Array();
2025 this.storyBits = new Array(); //位置に設定されたストーリー
2026
2027 //マップデータを読み取る ここから
2028 let shortcutPositionz = new Object();
2029 let lines = commentData.split( /[\r\n]+/ );
2030
2031 for( let y = 0; y < lines.length; y++ ) {
2032 let line = lines[ y ];
2033
2034 this.mapBits[ y ] = new Array();
2035 for( let x = 0; x < line.length; x++ ) {
2036 let mapBit = line.substr( x, 1 );
2037
2038 //check. 半角文字のときはストーリーへのショートカット
2039 if( mapBit.match( /[0-9a-z ]/i ) ) {
2040 let shortcutId = mapBit + line.substr( x + 1, 1 );
2041 shortcutId = shortcutId.replace( / /, "" );
2042
2043 //check. ショートカットがリンク切れ
2044 if( ! this.cfg.shortcuts[ shortcutId ] ) {
2045 alert( "マップ上に配置したショートカットがリンク切れ id:" + shortcutId );
2046 continue;
2047 }
2048
2049 //マップ上のショートカットについては後で処理
2050 shortcutPositionz[ shortcutId ] = {
2051 x : x,
2052 y : y,
2053 };
2054 //ショートカットをマップデータに置き換え
2055 mapBit = this.cfg.shortcuts[ shortcutId ].bit;
2056
2057 x++; //1文字多く読んだから
2058 }
2059
2060 this.mapBits[ y ].push( mapBit );
2061 }
2062 }//for
2063
2064 this.mapCols = this.mapBits[ 0 ].length;
2065 this.mapRows = this.mapBits.length;
2066
2067 //storyBits初期化
2068 for( let y = 0; y < this.mapRows; y++ ) {
2069 this.storyBits[ y ] = new Array();
2070 for( let x = 0; x < this.mapCols; x++ ) {
2071 this.storyBits[ y ][ x ] = null;
2072 }
2073 }
2074 //storyBitsにstoryを配置
2075 for( let shortcutId in shortcutPositionz ) {
2076 let pos =shortcutPositionz[ shortcutId ];
2077 let shortcut = this.cfg.shortcuts[ shortcutId ];
2078 this.storyBits[ pos.y ][ pos.x ] = shortcut;
2079
2080 //tweak. 座標情報をセット
2081 shortcut.x = pos.x;
2082 shortcut.y = pos.y;
2083 }
2084
2085
2086 //マップデータの端を超えるとマップの反対側になる計算
2087 if( this.cfg.maploop ) {
2088 this.patchwork = function( position, size ) { return ( position + size ) % size }
2089 } else {
2090 //町などループしない場合
2091 this.patchwork = function( position ) { return position };
2092 }
2093
2094
2095 //マップデータを読み取る ここまで
2096
2097
2098 //BGM
2099 if( this.cfg.bgmTitle ) {
2100 this.app.bgm = this.app.artz.bgms[ this.cfg.bgmTitle ];
2101 this.app.bgm.loop = true;
2102 this.app.bgm.play();
2103 }
2104
2105 //メソッドの引数によって処理を分ける(オーバーロードみたいに)
2106 let argtypes;
2107 let argArray = Array.from( arguments );
2108 argtypes = argArray.map( arg => ( typeof arg ).substr( 0, 1 ) ).join( "" );
2109
2110 //ショートカットを指定して移動
2111 if( argtypes.indexOf( "fs" ) == 0 ) {
2112
2113 //playerMapMoveTo( function, string [,number] )
2114 let shortcut = arguments[ 1 ];
2115 this.playerCol = this.cfg.shortcuts[ shortcut ].x;
2116 this.playerRow = this.cfg.shortcuts[ shortcut ].y;
2117 //check.
2118 if( typeof arguments[ 2 ] !== "undefined" )
2119 this.app.memoryCard.player.direction = arguments[ 2 ];
2120
2121 } else if( argtypes.indexOf( "fnn" ) == 0 ) {
2122
2123 //移動先位置の座標指定して移動
2124 //playerMapMoveTo( function, number, number [,number] )
2125 this.playerCol = arguments[ 1 ];
2126 this.playerRow = arguments[ 2 ];
2127 //check.
2128 if( typeof arguments[ 3 ] !== "undefined" )
2129 this.app.memoryCard.player.direction = arguments[ 3 ];
2130 } else {
2131
2132 alert( "playerMapMoveTo()の引数リストが想定外 : " + argtypes );
2133
2134 }
2135
2136 }//playerMapMoveTo()
2137 // |
2138 // V
2139 playerMapMoveTo_fx( mapdatafunc /*other args*/ ) {//m
2140
2141 //視覚効果付きのマップ間移動
2142
2143 let argArray = Array.from( arguments );
2144 //check. 歩く音
2145 let donotFootsteps = false;
2146 if( ( typeof argArray[ argArray.length - 1 ] ) == "boolean" ) {
2147 donotFootsteps = argArray.pop();
2148 }
2149 if( ! donotFootsteps ) this.app.artz.soundz.walk.play();
2150
2151 this.keyLock = true;
2152
2153 //クロージャ(関数が終わっても、この関数内で定義した関数内では生き続ける変数)
2154 let countMax = 3;
2155 let count = countMax;
2156 let flg = true;
2157 //マップ間移動時の画面フェードアウト、イン
2158 let metronome = this.app.addMetronome( "forFade", 125 );
2159 this.app.anms.push( function() {
2160 //check. メトロノームに合わせて
2161 if( ! metronome.changed ) return;
2162
2163 if( flg ) {
2164 //フェードアウト
2165 count --;
2166 let value = 100 * count / countMax;
2167 this.app.cc.canvas.style.filter = "brightness( " + value + "% ) ";
2168 //check. フェードアウトの終了
2169 if( ! count ) {
2170 //マップ間移動する
2171 this.playerMapMoveTo( ...argArray );
2172 flg = false;
2173 count = countMax;
2174 }
2175 } else {
2176 //フェードイン
2177 count --;
2178 let value = 100 * ( countMax - count ) / countMax;
2179 this.app.cc.canvas.style.filter = "brightness( " + value + "% ) ";
2180 //check. フェードインの終了(全終了)
2181 if( ! count ) {
2182 this.keyLock = false;
2183 this.app.removeMetronome( metronome );
2184 return true; //trueはアニメの終了の意
2185 }
2186 }
2187 }.bind( this ) );
2188 }//playerMapMoveTo_fx()
2189
2190 searchStory() {//m
2191 let hits = new Array();
2192
2193 /* 優先順位
2194 処理順 1. 2. 3.
2195 action walk>walk  >walk
2196 x,y 全体>ある位置>全体
2197 沼地>ジャンプ>エンカウント
2198
2199 つまり、一歩進んでwalkアクションになると、3種類のストーリーが起こりうる。
2200 たとえば、沼地に入ったら沼地のダメージのストーリーがまず行われ、
2201 つづいて、マップジャンプがそこに設定されていたらジャンプのストーリーが行われる。
2202 最後にエンカウントのストーリーが入るが、ジャンプのストーリーは
2203 打ち切りのフラグをreturnするので、エンカウントは行われない。
2204 優先順位を考慮しないと、マップジャンプしたあとに突然戦闘になるとか、
2205 沼地に踏み込んだのに、ダメージが減らないままマップジャンプするとか
2206 になってしまう。
2207 */
2208
2209 for( let action of this.actions ) {
2210 let story;
2211
2212 //1. マップチップのストーリー検索
2213 let bit;
2214 if( action.y != undefined && action.x != undefined )
2215 bit = this.mapBits[ action.y ][ action.x ];
2216 else
2217 bit = this.cfg.outside;
2218 if( this.cfg.mapBitStoriez[ bit ] )
2219 if( story = this.cfg.mapBitStoriez[ bit ][ action.type ] ) {
2220 hits.push( { action : action, story : story } );
2221 }
2222
2223 //2. マップ上の位置に設定されたストーリー
2224 if( story = Utl.accessBy( this.storyBits, action.y, action.x, "story", action.type ) ) {
2225 hits.push( { action : action, story : story } );
2226 }
2227
2228 //3. マップ自体に設定されたストーリー
2229 if( story = this.cfg.story[ action.type ] ) {
2230 hits.push( { action : action, story : story } );
2231 }
2232 }
2233
2234 this.actions.length = 0;
2235 return hits;
2236 }
2237 draw( cc ) {//m
2238 cc.fillStyle = "black";
2239 cc.fillRect( this.x, this.y, this.w, this.h );
2240
2241 let image;
2242
2243 cc.save();
2244 cc.translate(
2245 -this.style.innerCellSize / 2 + this.scroll.tweakGx,
2246 -this.style.innerCellSize / 2 + this.scroll.tweakGy
2247 );
2248 //tweakX,Yが描画位置を1ドットずつずらすので
2249 //きれいに1ドットスクロールしてるようにみえる
2250
2251 cc.font = this.style.innerCellSize + "px ''";
2252 cc.fillStyle = "rgb(0,255,0)";
2253
2254 //this.playerCol,this.playerRowが画面中央に来るように描画開始座標を調整
2255 let sx = this.playerCol - this.style.innerColsHalf;
2256 let sy = this.playerRow - this.style.innerRowsHalf;
2257
2258 for( let inner_row = 0; inner_row < this.style.innerRows; inner_row++ ) {
2259 let map_row = this.patchwork( sy + inner_row, this.mapRows );//両端ループ処理
2260 let gy = inner_row * this.style.innerCellSize;
2261 for( let inner_col = 0; inner_col < this.style.innerCols; inner_col++ ) {
2262 let map_col = this.patchwork( sx + inner_col, this.mapCols );//両端ループ処理
2263 let gx = inner_col * this.style.innerCellSize;
2264
2265 //マップを描画
2266 let bit = Utl.accessBy( this.mapBits, map_row, map_col );
2267 //check.
2268 if( bit == undefined ) bit = this.cfg.outside;
2269 image = this.cfg.images[ bit ] ? this.cfg.images[ bit ] : bit;
2270 cc.fillText( image, gx, gy + this.style.innerCellSize );
2271
2272 //スプライトを描画
2273 let story = Utl.accessBy( this.storyBits, map_row, map_col );
2274 //check.
2275 if( ! story ) continue;
2276 if( ! story.sprite ) continue;
2277 image = story.sprite;
2278 cc.fillText( image, gx, gy + this.style.innerCellSize );
2279 }
2280 }
2281
2282
2283 //debug. グリッド
2284 if( 0 ) {
2285 cc.strokeStyle = "rgba(255,255,255,.125)";
2286 for( let row = 0; row < this.style.innerRows; row ++ ) {
2287 for( let col = 0; col < this.style.innerCols; col ++ ) {
2288 let x = col * this.style.innerCellSize;
2289 let y = row * this.style.innerCellSize;
2290 cc.strokeRect( x, y, this.style.innerCellSize, this.style.innerCellSize );
2291 }
2292 }
2293 }
2294
2295 cc.restore();
2296
2297 //debug. 画面の十字線
2298 if( 0 ) {
2299 cc.strokeStyle = "red";
2300 cc.beginPath();
2301 cc.moveTo( 0, cc.canvasHeight / 2 );
2302 cc.lineTo( cc.canvasWidth, cc.canvasHeight / 2 );
2303 cc.stroke();
2304 cc.beginPath();
2305 cc.moveTo( cc.canvasWidth / 2, 0 );
2306 cc.lineTo( cc.canvasWidth / 2, cc.canvasHeight );
2307 cc.stroke();
2308 }
2309
2310 //プレイヤーキャラ描画
2311 cc.fillStyle = "cyan";
2312 cc.font = this.style.innerCellSize + "px ''";
2313 let gx = ( cc.canvasWidth - this.style.innerCellSize ) / 2;
2314 let gy = ( cc.canvasHeight - this.style.innerCellSize ) / 2 + this.style.innerCellSize;
2315 let sprite = this.app.artz.spritez.player;
2316 let idx = this.app.memoryCard.player.direction * 2 + sprite.metronome.index;
2317 image = sprite.images[ idx ];
2318 cc.fillText( image, gx, gy );
2319 //debug. 座標表示
2320 if( 0 ) {
2321 cc.fillStyle = "red";
2322 cc.fillText( this.playerCol + "," + this.playerRow, gx, gy );
2323 }
2324
2325
2326 this.drawChildren( cc );
2327
2328 }//draw()
2329 }//MapScreen
2330
2331 //---■■■ 9 BattleView ■■■
2332 class BattleView extends Element {//c
2333 constructor( monsters, style, app ) {
2334 super( style, app );
2335 this.monsters = monsters;
2336 }
2337 draw( cc ) {//m
2338 cc.drawImage( this.app.artz.imagez.battleBG, 0, 0, this.w, this.h );
2339 if( 1 ) {
2340 let step = this.w / ( this.monsters.length );
2341 for( let i = 0; i < this.monsters.length; i++ ) {
2342 let monster = this.monsters[ i ];
2343 //check. 倒れたモンスター
2344 if( monster.property.hp == 0 ) continue;
2345 let zm = 0.1;
2346 let mw = monster.image.width * zm;
2347 let mh = monster.image.height * zm;
2348 let gx = i * step + this.w / this.monsters.length / 2 - mw / 2;
2349 let gy = this.h * 0.9 - mh;
2350 cc.drawImage( monster.image, gx, gy, mw, mh );
2351
2352 }
2353 } else {
2354 let step = this.w / ( this.monsters.length + 1 );
2355 for( let i = 0; i < this.monsters.length; i++ ) {
2356 let monster = this.monsters[ i ];
2357 //check. 倒れたモンスター
2358 if( monster.property.hp == 0 ) continue;
2359 let zm = 0.1;
2360 let mw = monster.image.width * zm;
2361 let mh = monster.image.height * zm;
2362 let gx = ( i + 1 ) * step - mw / 2;
2363 let gy = this.h * 0.9 - mh;
2364 cc.drawImage( monster.image, gx, gy, mw, mh );
2365
2366 }
2367 }
2368 }
2369 }
2370
2371
2372 App.worldmap = function() {/*
2373 ●〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃¥¥¥▲〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2374 ¥山山山山山〃~~~~~~¥¥〃〃〃〃〃〃〃〃〃¥¥山山山¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2375 ¥山山山〃〃〃〃~~~〃〃〃〃〃〃〃〃〃〃〃〃¥¥山山山¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2376 ¥山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥山山山¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2377 ¥¥〃〃1 〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥山山山¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2378 〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥山山山¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2379 〃沼沼沼〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥¥山山山¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2380 〃沼沼沼〃〃〃〃〃山山〃〃〃〃〃〃〃¥¥¥山山山¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2381 〃沼沼沼〃〃〃山山山〃〃〃〃〃〃〃¥¥¥山山山¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2382 〃〃〃〃山山山山山〃〃〃〃〃〃〃〃¥¥山山山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2383 〃〃〃山山山〃〃〃〃〃〃〃〃¥〃〃¥山山山山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2384 〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥¥〃〃〃〃山山山山山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2385 〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥〃〃〃〃〃山山山山山山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2386 〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2387 〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2388 山山山〃〃〃~~~〃〃〃〃〃〃〃〃〃〃山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2389 山山〃〃~~~~¥¥〃〃〃〃〃〃〃〃山山山山〃〃〃〃〃〃〃山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2390 山〃〃~~~¥¥¥〃〃〃〃〃〃〃〃山山山山山山山〃〃〃〃〃山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2391 〃〃〃〃¥¥¥¥〃〃〃〃〃〃〃〃〃山山山山山山山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2392 〃〃〃〃〃〃¥¥¥〃〃〃〃〃〃〃山山山山山山山山山山~~〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2393 〃〃〃〃〃〃〃¥¥¥〃〃〃〃〃〃山山山山山~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2394 〃〃〃〃〃〃〃¥¥¥〃〃〃〃〃〃〃〃〃〃~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2395 〃〃〃〃¥¥¥¥¥¥¥〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2396 〃〃〃¥¥¥¥¥¥¥¥〃〃〃〃〃〃〃〃~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2397 〃〃¥¥¥¥¥¥〃〃〃〃〃〃〃〃〃~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2398 〃¥¥¥¥¥〃〃〃〃〃〃〃〃〃〃~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2399 〃〃¥¥〃〃〃〃〃〃〃〃〃〃~~~~~〃〃〃〃〃〃〃¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2400 〃〃〃〃〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃¥¥〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2401 〃〃〃〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2402 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2403 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2404 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2405 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2406 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2407 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2408 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2409 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2410 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2411 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2412 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2413 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2414 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2415 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2416 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2417 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2418 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2419 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2420 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2421 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2422 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2423 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2424 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2425 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2426 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2427 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2428 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2429 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2430 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2431 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2432 ○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山
2433 */
2434 return {
2435 maploop : true,
2436 bgmTitle : "field",
2437 walls : "~",
2438 shortcuts : {
2439 "1" : {
2440 bit : "凸",
2441 story : {
2442 "walk" : function( act ) {
2443 this.mapScreen.playerMapMoveTo_fx( App.townmap, "s", 2 );
2444 return true; //他のstory実行を打ち切る意
2445 },
2446 },
2447 },
2448 },
2449 story : {
2450 "walk" : function( act ) {
2451 //エンカウント
2452 if( Math.random() > .8 ) {
2453 this.encounter();
2454 }
2455 },
2456 },
2457 mapBitStoriez : {
2458 "沼" : {
2459 "walk" : async function() {
2460 await this.effect_flash( "darkred" );
2461 },
2462 },
2463 },
2464 images : {
2465 "¥" : "🌲",
2466 "凸" : "🏰",
2467 "山" : "⛰",
2468 "~" : "🌊",
2469 "沼" : "☠",
2470 },
2471 }//return
2472 }//worldmap
2473
2474
2475 App.townmap = function() {/*
2476 木木木木木木木〃・・〃木木木木木木木木木〃・・〃〃〃〃〃〃〃
2477 木■■■■■木〃・・〃木■■■■■■■木〃・・〃〃〃〃〃〃〃
2478 木■刀刀刀■木〃・・〃木■■■■■■■木〃・・〃〃〃〃〃〃〃
2479 木■・ws・■木〃・・〃木■■■■■■■木〃・・〃〃〃〃〃〃〃
2480 木■■□■■木〃・・〃木■■■扉■■■木〃・・〃〃〃〃〃〃〃
2481 木木〃・剣木木〃・・〃〃花花花・花花花a2〃・・〃〃〃〃〃〃〃
2482 s ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
2483 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
2484 木木盾・〃木木〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2485 木■■□■■木〃・・〃a1〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2486 木■・ss・■木〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2487 木■盾盾盾■木〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2488 木■■■■■木〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2489 木木木木木木木〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2490 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2491 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2492 〃〃〃〃〃沼沼〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2493 〃〃〃〃沼沼沼〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2494 〃〃〃沼沼沼沼〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2495 〃〃〃沼沼沼沼〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2496 〃〃〃〃〃沼沼沼・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2497 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2498 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2499 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2500 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2501 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2502 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2503 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2504 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2505 〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃
2506 */
2507 return {
2508 maploop : false,
2509 bgmTitle : "town",
2510 shortcuts : {
2511 "s" : {
2512 bit : "・",
2513 },
2514 "ws" : {
2515 bit : "👨‍🔧",
2516 story : {
2517 "contact" : async function( e ) {
2518 this.openMessage();
2519 await this.writeMessage( "ここは刀剣屋だ。\n", { wait : true } );
2520 this.closeMessage();
2521 await this.shop( {
2522 name : "shop",
2523 title : "刀剣屋 'ロバートウッドテイル'",//全13, 半3
2524 items : [
2525 { name : "どうけん", price : 100, },
2526 { name : "せいどうけん", price : 200, },
2527 { name : "てっけん", price : 300, },
2528 { name : "こうてつけん", price : 400, },
2529 { name : "いばらきけん", price : 18000, },
2530 { name : "ちばけん", price : 78000, },
2531 { name : "とっとりけん", price : 10102, },
2532 { name : "ふくしまけん", price : 29400, },
2533 ],
2534 columns : [
2535 {
2536 key : "name",
2537 },
2538 {
2539 key : "price",
2540 align : "right",
2541 }
2542 ],
2543 }, {
2544 "何を買うんだ?" : "何を買うんだ?",
2545 "だな。" : "だな。",
2546 "だ。買うかね?" : "だ。買うかね?",
2547 "だ。買うかね?" : "だ。買うかね?",
2548 "まいどあり!" : "まいどあり!",
2549 "ありがとうございました!" : "ありがとうございました!",
2550 } );
2551 console.log( "shop closed." );
2552 },
2553 },
2554 },
2555 "ss" : {
2556 bit : "👩‍🔧",
2557 story : {
2558 "contact" : async function( e ) {
2559 this.openMessage();
2560 await this.writeMessage( "ここは道具屋よ。\n", { wait : true } );
2561 this.closeMessage();
2562 await this.shop( {
2563 name : "shop",
2564 title : "道具屋 'セリーヌ・デ・イオン'",//全13, 半3
2565 items : [
2566 { name : "なんこう", price : 100, },
2567 { name : "羊皮紙", price : 200, },
2568 { name : "黄金の羊", price : 200000, },
2569 ],
2570 columns : [
2571 {
2572 key : "name",
2573 },
2574 {
2575 key : "price",
2576 align : "right",
2577 }
2578 ],
2579 }, {
2580 "何を買うんだ?" : "何を買いますか?",
2581 "だな。" : "ね。",
2582 "だ。買うかね?" : "です。買いますか?",
2583 "まいどあり!" : "ありがとうございます!",
2584 "ありがとうございました!" : "ありがとうございました!",
2585 } );
2586 console.log( "shop closed." );
2587 },
2588 },
2589 },
2590 "a1" : {
2591 bit : "〃",
2592 sprite : "🕵️‍♂️",
2593 story : {
2594 "contact" : async function( e ) {
2595 this.openMessage();
2596 await this.writeMessage( "ドラゴンなら北のほうへ飛んで行ったぜ", { wait : true } );
2597 this.closeMessage();
2598 },
2599 },
2600 },
2601 "a2" : {
2602 bit : "〃",
2603 sprite : "👩‍🚒",
2604 story : {
2605 "contact" : async function( e ) {
2606 this.openMessage();
2607 await this.writeMessage( "あなたのつるぎの使い方、\nふつうじゃないよね?", { wait : true } );
2608 this.closeMessage();
2609 },
2610 },
2611 },
2612 },
2613 story : {
2614 "exit" : function( e ) {
2615 this.mapScreen.playerMapMoveTo_fx( App.worldmap, "1", 3 );
2616 },
2617 },
2618
2619 outside : "〃",
2620 walls : "■□扉",
2621 mapBitStoriez : {
2622 "沼" : {
2623 "walk" : function() {
2624 this.effect_flash( "darkred" );
2625 },
2626 "exit" : function() {
2627 this.effect_flash( "darkred" );
2628 },
2629 },
2630 },
2631 images : {
2632 "木" : "🌳",
2633 "花" : "🌷",
2634 "扉" : "🚪",
2635 "箱" : "📦",
2636 "座" : "🪑",
2637 "寝" : "🛏",
2638 "∪" : "🏺",
2639 "剣" : "⚔",
2640 "刀" : "🗡",
2641 "盾" : "🛡",
2642 "沼" : "☠",
2643 },
2644 }//return
2645 }//townmap