このファイル: /home/web6047/www/cgi-bin/prj/20210612-RPG/作成/20210626-任意の矩形に何かを描くクラス試作2適用バージョン - snapshot 20210817/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class Utl {
74
static arrayForward( array, element ) {
75
76
77
let idx = array.indexOf( element );
78
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( ) {
88
89
90
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 ) {
101
102
103
for( let element of array ) {
104
object[ keyMaker( element ) ] = element;
105
}
106
}
107
108
static objectFrom( array, keyMaker ) {
109
110
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 ) {
118
119
return encodeURI( str ).replace( /%20/, "*" ).replace( /%..%..%../g, "**" ).length;
120
}
121
static lengthBinDiv2( str ) {
122
123
return Math.ceil( Utl.lengthBin( str ) / 2 );
124
}
125
static maxLength( strArray ) {
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 ) {
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 ) {
140
141
return Math.ceil( Utl.maxLengthBin( strArray ) / 2 );
142
}
143
static copyObjectKai( object, history ) {
144
145
146
if( history == undefined ) {
147
history = {
148
origin : new Array(),
149
copy : new Array(),
150
}
151
}
152
if( object instanceof Object ) {
153
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
172
173
174
175
176
if( object[ name ].copy ) {
177
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
}
191
192
class Element {
193
constructor( style, app ) {
194
this.app = app ? app : this;
195
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 ) {
220
}
221
senseKey( key ) {
222
}
223
draw( cc ) {
224
this.drawWindow( cc );
225
this.drawChildren( cc );
226
}
227
drawWindow( cc ) {
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 ) {
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() {
259
}
260
focus() {
261
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 ) {
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
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 ) {
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
313
if( child == this.app.messageWindow ) {
314
delete this.app.messageWindow;
315
}
316
317
return child;
318
}
319
removeAllElements() {
320
for( let child of this.childs ) {
321
child.removeAllElements();
322
}
323
this.parent.removeChild( this );
324
}
325
326
}
327
328
329
330
class App extends Element {
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
346
347
348
349
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 );
356
}
357
358
let gridCellSize = canvasWidth / 32;
359
360
super( {
361
gridsPerInnerCell : 2,
362
363
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
380
async initialize() {
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
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
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
521
argz.dougu.property.limit --;
522
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
],
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
],
597
},
598
assistMenuBattle : this.menusrcz[ "つかう" ],
599
},
600
}
601
602
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
612
613
614
615
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
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
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
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
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
}
740
741
742
743
load( type, name, src ) {
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
782
783
}
784
promiseOk();
785
}.bind( this );
786
object.src = src;
787
object.load();
788
break;
789
}
790
}.bind( this ) );
791
}
792
793
onkeydownx( e ) {
794
795
796
if( e.which == 79 ) {
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
812
onkeyupx( e ) {
813
let idx = this.keys.indexOf( e.which );
814
if( idx > -1 ) this.keys[ idx ] *= -1;
815
816
}
817
818
addMetronome( name ) {
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" :
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
}
857
858
859
removeMetronome( metronome ) {
860
delete this.metronomez[ metronome.name ];
861
}
862
863
start() {
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
}
895
896
897
stop() {
898
}
899
900
901
startFrame() {
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
910
stopFrame() {
911
cancelAnimationFrame( this.timerId );
912
this.keys.length = 0;
913
onkeydown = null;
914
onkeyup = null;
915
}
916
917
918
frame( tm ) {
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
928
if( metronome.time >= metronome.maxTime ) {
929
metronome.time = 0;
930
metronome.toggle = ! metronome.toggle;
931
metronome.changed = true;
932
metronome.index ++;
933
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
945
if( key < 0 ) this.keys.splice( i, 1 );
946
}
947
948
949
if( this.mapScreen )
950
if( this.mapScreen.scroll.isAnimating ) {
951
this.mapScreen.frameForScroll();
952
}
953
954
this.storyCheck();
955
956
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
967
async storyCheck() {
968
969
let stories = this.currentElement.searchStory();
970
971
if( ! stories ) return;
972
for( let story of stories ) {
973
let res = await story.story.call( this, story.action );
974
975
if( res ) break;
976
}
977
}
978
979
openMessage( style ) {
980
981
982
983
style = Object.assign( {
984
gridCol : 2,
985
gridRow : 18,
986
gridCols : 28,
987
gridRows : 8,
988
innerCellSize : this.style.innerCellSize,
989
}, style );
990
991
if( this.messageWindow ) {
992
this.closeMessage();
993
}
994
this.messageWindow = new MessageWindow( style, this.app );
995
this.appendChild( this.messageWindow );
996
}
997
998
999
closeMessage() {
1000
this.messageWindow.close();
1001
}
1002
1003
1004
async writeMessage( message, flgs ) {
1005
1006
if( ! flgs ) flgs = {};
1007
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 ) {
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
1037
1038
async shop( menusrc, wordingz ) {
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
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() {
1089
this.battle();
1090
}
1091
1092
1093
1094
async battle() {
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
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
1154
if( ! command ) {
1155
i -= i == 0 ? 1 : 2;
1156
continue;
1157
}
1158
1159
command.argz.self = character;
1160
1161
commandz[ character.name ] = command;
1162
}
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
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
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
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 ) {
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() {
1239
1240
}
1241
effect_flash( color ) {
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
1264
if( cnt == 10 ) {
1265
1266
this.removeChild( effect );
1267
promiseOk();
1268
return true;
1269
}
1270
} );
1271
}.bind( this ) );
1272
}
1273
debug_dir( object, tab, idx ) {
1274
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
}
1296
1297
1298
1299
class MessageWindow extends Element {
1300
constructor( style, app ) {
1301
1302
style = Object.assign( {
1303
gridCols : app.style.gridCols - 2,
1304
gridRows : app.style.gridRows - 2,
1305
}, style );
1306
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() {
1322
this.parent.removeChild( this );
1323
}
1324
clear() {
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 ) {
1333
1334
if( ! flgs ) {
1335
1336
flgs = {
1337
clear : false,
1338
rapid : false,
1339
wait : false,
1340
}
1341
}
1342
1343
this.endedByOutside = false;
1344
1345
this.message = message;
1346
this.flgs = flgs;
1347
this.seek = 0;
1348
this.isWaitingKeyNow = false;
1349
1350
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
1363
this.app.anms.push( function() {
1364
1365
1366
if( ! this.metronomeForWrite.changed ) return;
1367
1368
1369
if( this.endedByOutside || ! this.writeChar() ) {
1370
1371
if( ! this.flgs.wait ) {
1372
console.log( "promiseOk 'message typing'" );
1373
this.promiseOk();
1374
} else {
1375
this.isWaitingKeyNow = true;
1376
1377
}
1378
1379
return true;
1380
}
1381
}.bind( this ) );
1382
}.bind( this ) ).then( function() {
1383
1384
console.log( "promise 'message typing' completed" );
1385
this.app.removeMetronome( this.metronomeForWrite );
1386
this.app.removeMetronome( this.metronomeForCursor );
1387
}.bind( this ) );
1388
}
1389
1390
1391
writeChar() {
1392
1393
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
1406
if( this.innerCursorCol == this.style.innerCols ) kaigyo = true;
1407
}
1408
1409
1410
if( kaigyo ) {
1411
this.innerCursorCol = 0;
1412
1413
if( this.innerCursorRow < this.style.innerRows - 1 ) {
1414
this.innerCursorRow ++;
1415
} else {
1416
1417
1418
this.inner.shift();
1419
this.inner.push( new Array() );
1420
}
1421
}
1422
1423
this.seek ++;
1424
1425
return this.seek < this.message.length;
1426
1427
}
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
}
1440
draw( cc ) {
1441
super.draw( cc );
1442
cc.save();
1443
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
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
}
1468
}
1469
1470
1471
1472
class List extends Element {
1473
constructor( src, style, app ) {
1474
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
1489
if( style.gridCols == undefined ) {
1490
1491
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
1506
if( src.title != undefined ) {
1507
style.gridCols = Math.max( Utl.lengthBinDiv2( src.title ), style.gridCols );
1508
}
1509
1510
if( style.gridPaddingLeft != undefined ) style.gridCols += style.gridPaddingLeft;
1511
}
1512
1513
if( style.gridRows == undefined ) {
1514
style.gridRows = items.length;
1515
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 ) {
1525
1526
this.drawWindow( cc );
1527
1528
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
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
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
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
}
1578
}
1579
cc.restore();
1580
}
1581
translateToItemArea( cc ) {
1582
if( this.src.title ) cc.translate( 0, this.style.innerCellSize );
1583
cc.translate( this.style.gridPaddingLeft * this.style.innerCellSize, 0 );
1584
}
1585
}
1586
1587
1588
1589
class Menu extends List {
1590
constructor( mode, context, src, style, app ) {
1591
1592
if( src.columns == undefined ) {
1593
src.columns = [ { key : "name" } ];
1594
}
1595
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
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() {
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 ) {
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
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:
1645
this.app.artz.soundz.pushA.rapidPlay();
1646
1647
this.selectedItem = this.items[ this.cursorY ];
1648
1649
1650
let menusrc;
1651
1652
if( this.selectedItem.items ) {
1653
menusrc = this.selectedItem;
1654
} else if( this.src.assistMenu ) {
1655
1656
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
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
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:
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
}
1703
}
1704
}
1705
1706
1707
getResult() {
1708
let result = new Object();
1709
result.menus = new Array();
1710
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
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
return result;
1739
}
1740
async execute() {
1741
let result = this.getResult();
1742
1743
1744
if( ! result.story ) {
1745
alert( "storyが未定義の状態です。at menu.execute()" );
1746
}
1747
1748
await result.story.call( this.app, result.argz );
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
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
1769
}
1770
}
1771
}
1772
draw( cc ) {
1773
super.draw( cc );
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
}
1799
}
1800
1801
class TitleScreen extends Element {
1802
constructor( style, app ) {
1803
super( style, app );
1804
}
1805
async typeKey( key ) {
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 );
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 ) {
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
}
1844
1845
1846
1847
class MapScreen extends Element {
1848
constructor( style, app ) {
1849
1850
1851
1852
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
1878
tweakColForLooping( targetCol ) {
1879
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 ) {
1889
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 ) {
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
1906
async typeKey( key ) {
1907
if( key == 32 ) {
1908
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
1937
senseKey( key ) {
1938
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
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
1962
if( Utl.accessBy( this.storyBits, destinRow, destinCol, "sprite" ) ) return;
1963
1964
1965
this.scroll.addX = addX;
1966
this.scroll.addY = addY;
1967
this.scroll.count = 0;
1968
this.scroll.isAnimating = true;
1969
1970
}
1971
1972
1973
frameForScroll() {
1974
1975
1976
this.scroll.tweakGx -= this.scroll.addX;
1977
this.scroll.tweakGy -= this.scroll.addY;
1978
1979
1980
1981
this.scroll.count ++;
1982
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
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
}
2003
}
2004
2005
2006
playerMapMoveTo( mapdatafunc ) {
2007
2008
2009
2010
2011
if( this.app.bgm ) {
2012
this.app.bgm.pause();
2013
this.app.bgm.currentTime = 0;
2014
}
2015
2016
2017
let commentData = mapdatafunc.toString().match( /\/\*[\r\n]+([\s\S]+)[\r\n]+\*\// )[ 1 ];
2018
2019
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
2039
if( mapBit.match( /[0-9a-z ]/i ) ) {
2040
let shortcutId = mapBit + line.substr( x + 1, 1 );
2041
shortcutId = shortcutId.replace( / /, "" );
2042
2043
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++;
2058
}
2059
2060
this.mapBits[ y ].push( mapBit );
2061
}
2062
}
2063
2064
this.mapCols = this.mapBits[ 0 ].length;
2065
this.mapRows = this.mapBits.length;
2066
2067
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
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
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
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
2114
let shortcut = arguments[ 1 ];
2115
this.playerCol = this.cfg.shortcuts[ shortcut ].x;
2116
this.playerRow = this.cfg.shortcuts[ shortcut ].y;
2117
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
2125
this.playerCol = arguments[ 1 ];
2126
this.playerRow = arguments[ 2 ];
2127
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
}
2137
2138
2139
playerMapMoveTo_fx( mapdatafunc ) {
2140
2141
2142
2143
let argArray = Array.from( arguments );
2144
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
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
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
2181
if( ! count ) {
2182
this.keyLock = false;
2183
this.app.removeMetronome( metronome );
2184
return true;
2185
}
2186
}
2187
}.bind( this ) );
2188
}
2189
2190
searchStory() {
2191
let hits = new Array();
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
for( let action of this.actions ) {
2210
let story;
2211
2212
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
2224
if( story = Utl.accessBy( this.storyBits, action.y, action.x, "story", action.type ) ) {
2225
hits.push( { action : action, story : story } );
2226
}
2227
2228
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 ) {
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
2249
2250
2251
cc.font = this.style.innerCellSize + "px ''";
2252
cc.fillStyle = "rgb(0,255,0)";
2253
2254
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
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
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
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
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
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
}
2329
}
2330
2331
2332
class BattleView extends Element {
2333
constructor( monsters, style, app ) {
2334
super( style, app );
2335
this.monsters = monsters;
2336
}
2337
draw( cc ) {
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
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
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
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;
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
}
2472
}
2473
2474
2475
App.townmap = function() {
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
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 : "刀剣屋 'ロバートウッドテイル'",
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 : "道具屋 'セリーヌ・デ・イオン'",
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
}
2645
}