1 module engine
is aliced
;
8 __gshared
bool debugColdet
= false;
9 __gshared
bool singleStep
= false;
10 __gshared
bool singleStepWait
= false;
13 // ////////////////////////////////////////////////////////////////////////// //
14 __gshared
ubyte[][string
] gameData
;
15 __gshared Room
[] gameRooms
;
18 public void loadGameData () {
19 gameRooms
.txtunser(VFile("data/levelset0.js"));
20 foreach (immutable idx
, ref room
; gameRooms
) room
.roomIdx
= cast(int)idx
;
21 gameData
.txtunser(VFile("data/data.js"));
25 // ////////////////////////////////////////////////////////////////////////// //
27 VColor
palcol (ubyte c
) {
28 static VColor
[256] palette
;
29 static bool palset
= false;
31 auto pp
= gameData
["palmain"];
32 foreach (immutable cc
; 0..256) {
33 ubyte r
= cast(ubyte)(255*pp
[cc
*3+0]/63);
34 ubyte g
= cast(ubyte)(255*pp
[cc
*3+1]/63);
35 ubyte b
= cast(ubyte)(255*pp
[cc
*3+2]/63);
36 palette
[cc
] = rgbcol(r
, g
, b
);
44 // ////////////////////////////////////////////////////////////////////////// //
45 private X11Image
buildImageMasked (const(ubyte)[] darr
, int w
, int h
) {
46 assert(w
> 0 && h
> 0);
48 auto img
= new X11Image(w
, h
);
49 foreach (immutable y
; 0..h
) {
50 foreach (immutable x
; 0..w
) {
51 ubyte c
= darr
[dpos
++];
53 img
.setPixel(x
, y
, palcol(c
));
55 img
.setPixel(x
, y
, Transparent
);
63 private X11Image
buildImageMaskedShiny (const(ubyte)[] darr
, int brightness
, int w
, int h
) {
64 assert(w
> 0 && h
> 0);
66 auto img
= new X11Image(w
, h
);
67 foreach (immutable y
; 0..h
) {
68 foreach (immutable x
; 0..w
) {
69 ubyte c
= darr
[dpos
++];
71 int b
= (c
&15)-brightness
;
72 if (b
< 0) b
= 0; else if (b
> 15) b
= 15;
73 img
.setPixel(x
, y
, palcol(cast(ubyte)((c
&240)|b
)));
75 img
.setPixel(x
, y
, Transparent
);
83 private X11Image
buildImageMaskedInk (const(ubyte)[] darr
, int ink
, int w
, int h
) {
84 assert(w
> 0 && h
> 0);
86 auto img
= new X11Image(w
, h
);
89 foreach (immutable y
; 0..h
) {
90 foreach (immutable x
; 0..w
) {
91 ubyte c
= darr
[dpos
++];
93 img
.setPixel(x
, y
, palcol(cast(ubyte)(c
+ink
)));
95 img
.setPixel(x
, y
, Transparent
);
103 // ////////////////////////////////////////////////////////////////////////// //
104 private X11Image
buildImageBrick (const(ubyte)[] darr
, int ink
, int paper
, int skipy
=0) {
106 enum { w
= 8, h
= 8 }
108 auto img
= new X11Image(w
, h
);
110 foreach (immutable y
; 0..h
) {
111 foreach (immutable x
; 0..w
) {
112 ubyte c
= (y
>= skipy ? darr
[dpos
++] : 0);
113 img
.setPixel(x
, y
, palcol(cast(ubyte)(c ? ink
+c
: paper
)));
120 // ////////////////////////////////////////////////////////////////////////// //
121 // willy jump offsets
123 static immutable int[19] willyJ
= [0, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4];
125 struct SkyLabXY
{ int x
, y
; }
126 static immutable SkyLabXY
[4][3] skylabCoords
= [
127 [{x
: 8,y
:0}, {x
: 72,y
:0}, {x
:136,y
:0}, {x
:200,y
:0}],
128 [{x
:40,y
:0}, {x
:104,y
:0}, {x
:168,y
:0}, {x
:232,y
:0}],
129 [{x
:24,y
:0}, {x
: 88,y
:0}, {x
:152,y
:0}, {x
:216,y
:0}]
133 // ////////////////////////////////////////////////////////////////////////// //
134 // suitable to "unjson"
136 enum { Width
= 32, Height
= 16 }
137 static struct WillyStart
{ int x
; int y
; int sd
; }
138 static struct ExitGfx
{ ubyte gfx
; int x
; int y
; }
139 static struct CommonGfx
{ ubyte gfx
; ubyte ink
; ubyte paper
; }
140 static struct Conveyor
{ int x
; int y
; int d
; int l
; ubyte gfx
; ubyte ink
; ubyte paper
; @SRZIgnore int frame
; }
141 static struct KeyPos
{ int x
; int y
; int s
; }
142 static struct Key
{ ubyte gfx
; KeyPos
[] info
; }
143 static struct SwitchPos
{ int x
; int y
; int s
; }
144 static struct Enemy
{ bool vert
; ubyte gfx
; ubyte ink
; ubyte paper
; int x
=-1; int y
; int min
; int max
; int d
; int s
; int flip
; int anim
; @SRZIgnore int frame
; @SRZIgnore int m
; @SRZIgnore int falling
; @SRZIgnore int delay
; }
145 static struct SkyLabEnemy
{ int p
, s
; ubyte ink
; int max
; int m
; int frame
; }
146 static struct SkyLabXY
{ int x
, y
, frame
; ubyte ink
; int m
; int s
; int max
; int p
; }
147 static struct SPGRay
{ int x
, y
; }
148 @SRZIgnore int roomIdx
;
153 ubyte[Height
*Width
] map
; // 16 rows, 32 cols
154 ubyte border
, ink
, paper
;
155 CommonGfx
[] platforms
;
158 CommonGfx
[] deadlies
;
161 SwitchPos
[] switches
;
164 @SRZIgnore Enemy eugene
;
166 @SRZIgnore int holeLen
, holeY
;
167 @SRZIgnore Enemy kong
;
169 @SRZIgnore SkyLabEnemy
[] skylabEnemies
;
170 @SRZIgnore SkyLabXY
[] skylab
;
172 @SRZIgnore SPGRay
[] spg
; // solar power generator
175 int willyX
, willyY
, willyDir
, willyJump
, willyJumpDir
;
176 int willyLastMoveDir
, willyFall
, willyConv
, willyStall
;
177 public bool willyDead
;
181 bool kLeft
, kRight
, kJump
, kUp
, kDown
;
182 bool kLeftDown
, kRightDown
, kJumpDown
, kUpDown
, kDownDown
;
188 X11Image
[] brickCache
;
189 X11Image
[] convCache
;
190 X11Image
[] exitCache
;
192 X11Image
[][] monsterCache
;
193 int[][] monsterOfsCache
;
194 X11Image
[] eugeneSpr
;
196 X11Image
[] skylabSpr
;
197 X11Image
[] switchesSpr
;
201 ubyte saveKeyState () {
203 ((kLeftDown ?
1 : 0)<<0)|
204 ((kRightDown ?
1 : 0)<<1)|
205 ((kUpDown ?
1 : 0)<<2)|
206 ((kDownDown ?
1 : 0)<<3)|
207 ((kJumpDown ?
1 : 0)<<4)|
211 void restoreKeyState (ubyte b
) {
212 keyLeft
= ((b
&(1<<0)) != 0);
213 keyRight
= ((b
&(1<<1)) != 0);
214 keyUp
= ((b
&(1<<2)) != 0);
215 keyDown
= ((b
&(1<<3)) != 0);
216 keyJump
= ((b
&(1<<4)) != 0);
223 willyDir
= (willy
.sd
> 0 ?
-1 : 1);
224 willyJump
= willyFall
= willyConv
= willyStall
= 0;
226 willyLastMoveDir
= 0;
227 kLeft
= kRight
= kJump
= kUp
= kDown
= false;
228 kLeftDown
= kRightDown
= kJumpDown
= kUpDown
= kDownDown
= false;
236 buildMonsterImages();
241 finalSpr
= buildImageMasked(gameData
["final"], 256, 64);
242 sunSpr
= buildImageMasked(gameData
["sun"], 24, 16);
247 foreach (ref c
; map
) {
248 if (c
== 4) c
= 8; // 'crumb'
249 else if (c
< 1 || c
> 7) c
= 0; // emptyness
254 foreach (const ref ki
; keys
.info
) {
255 if (ki
.s
== 0) continue;
260 platforms
= platforms
.dup
;
261 deadlies
= deadlies
.dup
;
262 switches
= switches
.dup
;
263 enemies
= enemies
.dup
;
267 Enemy
[] a
= [{ x
:120, y
:1, d
/*ir*/:0, min
:1, max
:87, ink
:6 }];
271 if (roomIdx
== 7 || roomIdx
== 11) {
272 Enemy
[] a
= [{ x
:120, y
:0, max
:104, frame
:0, ink
:2, m
:0 }];
281 {p
:0, s
:4, ink
:6, max
:72, m
:0, frame
:0},
282 {p
:2, s
:3, ink
:5, max
:56, m
:0, frame
:0},
283 {p
:1, s
:1, ink
:4, max
:32, m
:0, frame
:0}
285 skylabEnemies
= a
.dup
;
286 foreach (immutable f
, const ref se
; skylabEnemies
) {
288 skylabCoords
[f
][se
.p
].x
, skylabCoords
[f
][se
.p
].y
,
289 se
.frame
, se
.ink
, se
.m
, se
.s
, se
.max
, se
.p
,
293 // Solar Power Generator?
294 if (roomIdx
== 18) buildSPG();
297 void keyLeft (bool pressed
) { if (pressed
) kLeft
= true; kLeftDown
= pressed
; }
298 void keyRight (bool pressed
) { if (pressed
) kRight
= true; kRightDown
= pressed
; }
299 void keyUp (bool pressed
) { if (pressed
) kUp
= true; kUpDown
= pressed
; }
300 void keyDown (bool pressed
) { if (pressed
) kDown
= true; kDownDown
= pressed
; }
301 void keyJump (bool pressed
) { if (pressed
) kJump
= true; kJumpDown
= pressed
; }
304 kLeft
= kLeft||kLeftDown
;
305 kRight
= kRight||kRightDown
;
307 kDown
= kDown||kDownDown
;
308 kJump
= kJump||kJumpDown
;
309 scope(exit
) kLeft
= kRight
= kJump
= kUp
= kDown
= false;
311 conveyor
.frame
+= (conveyor
.d ?
-1 : 1);
312 if (conveyor
.frame
< 0) conveyor
.frame
= 3; else if (conveyor
.frame
> 3) conveyor
.frame
= 0;
314 if (!willyJump
) stepCrumb();
322 void cheatRemoveKeys () { keys
.info
= null; }
324 // ////////////////////////////////////////////////////////////////////// //
328 ubyte getBlockAt (int x
, int y
, bool simplify
=false) const nothrow @nogc {
331 if (x
< 0 || y
< 0 || x
> 31 || y
> 15) return 0; // empty
332 ubyte b
= map
[y
*32+x
];
335 else if (b
> 7) b
= 4;
336 else if (b
== 6) b
= 5;
337 else if (b
== 2) b
= 1;
342 bool checkExit () const nothrow @nogc {
343 if (keys
.info
.length
!= 0) return false;
346 return (willyX
>= x
-2 && willyX
+10 <= x
+18 && willyY
>= y
-5 && willyY
+16 <= y
+22);
349 bool checkMonsters () const nothrow {
350 foreach (immutable f
, const ref m
; enemies
) {
351 auto cc
= monsterOfsCache
[f
];
352 if (m
.x
< 0 || m
.y
< 0) continue;
354 if (pixelCheckMonster(m
.x
, m
.y
, gameData
["vrobo"], cc
[m
.anim
])) return true;
356 if (pixelCheckMonster(m
.x
&248, m
.y
, gameData
["hrobo"], cc
[((m
.x
&m
.anim
)&0xfe)+m
.d
/*ir*/])) return true;
362 if (pixelCheckMonster(eugene
.x
, eugene
.y
, gameData
["eugene"], 256*curLSet
)) return true;
366 if (pixelCheckMonster(kong
.x
, kong
.y
, gameData
["kong"], 256*kong
.frame
)) return true;
370 foreach (const ref sk
; skylab
) {
371 if (pixelCheckMonster(sk
.x
, sk
.y
, gameData
["sky"], 256*sk
.frame
)) return true;
377 void checkSwitch () nothrow @nogc {
378 foreach (ref ss
; switches
) {
379 if (ss
.s
/*tate*/ != 1) continue;
382 if (x
+7 >= willyX
&& y
+7 >= willyY
&& x
< willyX
+8 && y
< willyY
+16) ss
.s
/*tate*/ = 1+1;
386 bool checkSPG () const nothrow @nogc {
387 foreach (const ref sp
; spg
) {
390 if (x
< 0 || y
< 0) break;
391 if (x
+7 >= willyX
&& x
< willyX
+8 && y
+7 >= willyY
&& y
< willyY
+16) return true;
396 X11Image
draw (X11Image img
=null) {
397 if (img
is null) img
= new X11Image(8*Room
.Width
, 8*Room
.Height
);
399 foreach (immutable y
; 0..img
.height
) {
400 foreach (immutable x
; 0..img
.width
) {
401 img
.setPixel(x
, y
, palcol(this.paper
));
404 foreach (immutable y
; 0..Room
.Height
) {
405 foreach (immutable x
; 0..Room
.Width
) {
406 ubyte blk
= this.map
[pos
++];
407 if (blk
< 1 || blk
== 7) continue;
408 if (blk
== 4) blk
= 8;
409 if (blk
< brickCache
.length
&& brickCache
[blk
] !is null) {
410 brickCache
[blk
].blitTo(img
, x
*8, y
*8);
414 if (this.roomIdx
== 19) {
415 finalSpr
.blitTo(img
, 0, 0);
416 sunSpr
.blitTo(img
, 60, 32);
419 void drawConveyor () {
420 auto conv
= &this.conveyor
;
421 if (conv
.l
< 1) return;
424 convCache
[conv
.frame
].blitTo(img
, conv
.x
, conv
.y
, conv
.l
);
429 if (this.keys
.info
.length
!= 0) {
432 br
= this.frameNo
&31;
433 if (br
> 15) br
= 31-br
;
436 exitCache
[br
].blitTo(img
, this.exit
.x
, this.exit
.y
);
440 auto ff
= this.frameNo
%16;
441 if (ff
>= 8) ff
= 15-ff
;
442 auto keyimg
= keyCache
[ff
];
443 foreach_reverse (const ref ki
; this.keys
.info
) {
444 if (ki
.x
< 0 || ki
.y
< 0) continue;
445 //eraseRect(ki.x, ki.y, 8, 8);
447 keyimg
.blitTo(img
, ki
.x
&248, ki
.y
);
451 void drawMonsters () {
452 foreach (immutable f
, const ref m
; this.enemies
) {
453 if (m
.x
< 0 || m
.y
< 0) continue;
454 auto slist
= monsterCache
[f
];
457 //for (var c = 0; c <= m.anim; c++) r.push(buildImageMaskedInk(me.data.vrobo, (m.gfx+c)*256, m.ink-1, 16, 16, false));
458 slist
[m
.anim
].blitTo(img
, x
, m
.y
);
460 auto sidx
= ((x
&m
.anim
)&0xfe)+m
.d
/*ir*/;
461 if (sidx
< slist
.length
) {
462 slist
[sidx
].blitTo(img
, x
&248, m
.y
);
464 import core
.stdc
.stdio
: stderr
, fprintf
;
465 stderr
.fprintf("monster #%u is fucked: sidx=%u; max=%u", cast(uint)f
, cast(uint)sidx
, cast(uint)slist
.length
);
469 if (this.eugene
.x
>= 0) {
471 eugeneSpr
[curLSet
*8+this.eugene
.ink
].blitTo(img
, this.eugene
.x
, this.eugene
.y
);
473 if (this.kong
.x
>= 0) {
474 kongSpr
[this.kong
.frame
*8+this.kong
.ink
].blitTo(img
, this.kong
.x
, this.kong
.y
);
476 if (this.skylab
.length
) {
477 foreach (const ref sk
; this.skylab
) {
478 skylabSpr
[sk
.frame
*8+sk
.ink
].blitTo(img
, sk
.x
, sk
.y
);
484 foreach (const ref ss
; this.switches
) {
485 if (ss
.s
== 0) continue;
486 switchesSpr
[ss
.s
-1].blitTo(img
, ss
.x
, ss
.y
);
491 foreach (const ref sp
; this.spg
) {
494 if (x
< 0 || y
< 0) break;
495 //ctx.fillStyle = mainPal[noerase?6:this.paper];
496 //ctx.fillRect(x, y, 8, 8);
497 foreach (immutable dy
; 0..8) {
498 foreach (immutable dx
; 0..8) {
499 img
.setPixel(x
+dx
, y
+dy
, palcol(6));
506 auto willyPos
= (this.willyX
&15)>>1;
507 if (this.willyDir
< 0) willyPos
+= 8;
510 if (this.checkMonsters()) wy
= 16;
511 willySpr
.blitTo(willyPos
*16, wy
, 16, 16, img
, this.willyX
&248, this.willyY
);
513 willySpr
.blitTo(willyPos
*16, 0, 16, 16, img
, this.willyX
&248, this.willyY
);
529 // ////////////////////////////////////////////////////////////////////// //
530 // pixel-perfect collision detector
531 // fully stolen from Andy's sources %-)
532 bool pixelCheckMonster (int rx
, int ry
, const(ubyte)[] darr
, int dpos
=0) const nothrow {
533 static ubyte[256] mpcGrid
;
539 if (rx
< -15 || rx
> 15 || ry
< -15 || ry
> 15) return false;
542 if (rx
< 0) { x
= 0; rx
= -rx
; w
= 16-rx
; } else { x
= rx
; rx
= 0; w
= 16-x
; }
543 // partial plot monster
544 for (int y
= ry
+15; y
>= ry
; --y
) {
545 if (y
>= 0 && y
< 16) {
547 int rp
= dpos
+(y
-ry
)*16+rx
;
548 for (int dx
= 0; dx
< w
; ++dx
, ++gp
, ++rp
) mpcGrid
[gp
] = darr
[rp
];
551 auto warr
= gameData
["willy"];
552 int wptr
= ((willyX
&15)>>1)*256+(willyDir
< 0 ?
2048 : 0);
553 // check for collision
555 for (x
= 255; x
>= 0; --x
, ++wptr
, ++mp
) if (warr
[wptr
] && mpcGrid
[mp
]) return true;
559 // ////////////////////////////////////////////////////////////////////// //
560 bool isWillyFalling () const nothrow @nogc {
561 if (willyY
&7) return true;
562 for (int dx
= 0; dx
<= 8; dx
+= 8) {
563 ubyte b
= getBlockAt(willyX
+dx
, willyY
+16, true);
564 if (b
> 0 && b
!= 5) return false;
569 bool isWillyInDeadly () const nothrow @nogc {
570 for (int dx
= 0; dx
<= 8; dx
+= 8) {
571 for (int dy
= 0; dy
<= 16; dy
+= 8) {
572 if (getBlockAt(willyX
+dx
, willyY
+dy
, true) == 5) return true;
578 bool isWillyOnConv () const nothrow @nogc {
579 if (willyY
&7) return false;
580 ubyte b0
= getBlockAt(willyX
, willyY
+16);
581 ubyte b1
= getBlockAt(willyX
+8, willyY
+16);
582 return (b0
== 7 || b1
== 7);
585 // ////////////////////////////////////////////////////////////////////// //
586 // called on each frame
587 void buildSPG (bool forced
=false) {
588 if (!forced
&& roomIdx
!= 18) {
593 bool spgCheckMonster (int x
, int y
) {
596 foreach (const ref cm
; enemies
) {
599 if (x
+7 >= mx
&& x
< mx
+15 && y
+7 >= my
&& y
< my
+15) return true;
606 int dir
= 0, idx
= 0;
610 spg
.assumeSafeAppend
;
613 void addXY (int x
, int y
) {
615 while (spg
.length
< idx
) spg
~= SPGRay(-1, -1);
621 ubyte blockhit
= map
[y
*32+x
];
622 bool robohit
= spgCheckMonster(x
, y
);
624 if (blockhit
&& robohit
) {
627 } else if (!blockhit
&& robohit
) {
628 if (idx
&& spg
[idx
-1].x
== x
&& spg
[idx
-1].y
== y
) {
629 spg
[idx
-1].x
= spg
[idx
-1].y
= -1;
635 } else if (!blockhit
&& !robohit
) {
637 } else if (blockhit
&& !robohit
) {
638 if (idx
&& spg
[idx
-1].x
== x
&& spg
[idx
-1].y
== y
) {
639 spg
[idx
-1].x
= spg
[idx
-1].y
= -1;
648 blockhit
= map
[y
*32+x
];
649 if (y
== 15 || blockhit
) done
= true;
652 blockhit
= map
[y
*32+x
];
653 if (x
== 0 || blockhit
) done
= true;
656 if (!dir
) { --x
; if (!x
) done
= true; }
657 else { ++y
; if (++y
== 15) done
= true; }
663 void stepMonsters () {
664 foreach (ref m
; enemies
) {
665 if (m
.x
< 0 || m
.y
< 0) continue;
668 auto spd
= m
.s
/*peed*/;
669 if (m
.d
/*ir*/ != 0) {
672 if (y
< m
.min || y
< 0) { y
+= spd
; m
.d
/*ir*/ = 0; }
676 if (y
> m
.max
) { y
-= spd
; m
.d
/*ir*/ = 1; }
679 m
.anim
= (m
.anim
+1)&3;
682 auto spd
= (2>>m
.s
/*peed*/);
683 if (m
.d
/*ir*/ != 0) {
686 if (x
< m
.min
) { m
.d
/*ir*/ = 0; x
+= spd
; }
690 if (x
> m
.max
+6) { m
.d
/*ir*/ = 1; x
-= spd
; }
697 if (keys
.info
.length
== 0) {
698 // no keys, Eugene tries to block the exit
699 eugene
.ink
= (eugene
.ink
+1)&7;
700 eugene
.y
+= (eugene
.y
< eugene
.max ?
1 : 0);
702 if (eugene
.d
/*ir*/ != 0) {
705 if (eugene
.y
< eugene
.min
) { eugene
.d
/*ir*/ = 0; ++eugene
.y
; }
709 if (eugene
.y
> eugene
.max
) { eugene
.d
/*ir*/ = 1; --eugene
.y
; }
715 switch (kong
.falling
) {
716 case 1: // just started
719 //eraseRect(16, 120, 16, 8);
729 if (kong
.y
>= kong
.max
) { kong
.x
= -1; score
+= 100; }
730 if (!kong
.delay
) { kong
.delay
= 4; kong
.frame
= ((kong
.frame
-1)&1)+2; } else --kong
.delay
;
733 if (!kong
.delay
) { kong
.delay
= 8; kong
.frame
= (kong
.frame
+1)&1; } else --kong
.delay
;
738 foreach (immutable idx
, ref sk
; skylab
) {
750 if (sk
.frame
== 7) sk
.m
= 2;
754 sk
.x
= skylabCoords
[idx
][sk
.p
].x
;
755 sk
.y
= skylabCoords
[idx
][sk
.p
].y
;
764 if (willyY
&7) return;
765 for (int f
= 0; f
<= 8; f
+= 8) {
768 ubyte b
= getBlockAt(x
, y
);
775 //eraseRect(x*8, y*8, 8, 8);
780 while (keys
.info
.length
) {
782 foreach (immutable idx
; 0..keys
.info
.length
) {
783 auto ki
= &keys
.info
[idx
];
787 if (kx
+7 >= willyX
&& kx
< willyX
+10 && ky
+7 >= willyY
&& ky
< willyY
+16) {
789 foreach (immutable c
; idx
+1..keys
.info
.length
) keys
.info
[c
-1] = keys
.info
[c
];
790 keys
.info
.length
-= 1;
801 if (holeLen
> 0 && switches
.length
&& switches
[0].s
/*tate*/ > 1) {
804 enemies
[1].max
+= 24;
816 if (kong
.x
>= 0 && !kong
.falling
&& switches
.length
> 1 && switches
[1].s
/*tate*/ > 1) kong
.falling
= 1;
819 void stepWillyActions () {
820 bool doWillyLeft () {
821 if (willyDir
> 0) { willyDir
= -1; return true; }
823 auto b0
= getBlockAt(xx
, willyY
);
824 auto b1
= getBlockAt(xx
, willyY
+8);
825 auto b2
= (willyY
&7 ?
getBlockAt(xx
, willyY
+16) : b1
);
826 if (b0
== 3 || b1
== 3 || b2
== 3) return false;
828 if (willyX
< 0) willyX
+= 240;
829 willyLastMoveDir
= -1;
833 bool doWillyRight () {
834 if (willyDir
< 0) { willyDir
= 1; return true; }
835 if (willyX
> 245) return false;
837 auto b0
= getBlockAt(xx
, willyY
);
838 auto b1
= getBlockAt(xx
, willyY
+8);
839 auto b2
= (willyY
&7 ?
getBlockAt(xx
, willyY
+16) : b1
);
840 if (b0
== 3 || b1
== 3 || b2
== 3) return false;
842 if (willyX
> 240) willyX
-= 240;
843 willyLastMoveDir
= 1;
847 void doWillyJump () {
848 if (!willyJump
) return;
849 willyY
+= willyJ
[willyJump
];
852 if (willyJumpDir
< 0) mv
= doWillyLeft(); else if (willyJumpDir
> 0) mv
= doWillyRight();
856 auto b0
= getBlockAt(x
, willyY
);
857 auto b1
= getBlockAt(x
+8, willyY
);
858 if (b0
== 3 || b1
== 3) {
859 // headboom! (apstenu %-)
861 willyY
-= willyJ
[willyJump
];
862 willyJump
= 0; // enough flying
867 if (willyJump
> 12) willyFall
+= willyJ
[willyJump
];
868 if ((willyY
&7) == 0) {
869 auto b0
= getBlockAt(willyX
, willyY
+16);
870 auto b1
= getBlockAt(willyX
+8, willyY
+16);
872 if (b0
== 3 || b1
== 3) willyX
= x
;
873 willyFall
= 0; // can't fall too deep while jumping
874 willyJump
= 0; // enough flying
875 if (b0
== 7 || b1
== 7) willyStall
= 1; // conveyor?
881 if (willyJump
> 18) willyJump
= 0;
885 if (willyDead
) return;
887 if (isWillyInDeadly()) { willyDead
= true; return; }
889 if (checkMonsters()) { willyDead
= true; return; }
891 if (checkMonsters()) singleStep
= singleStepWait
= true;
894 auto wasJump
= false;
896 willyLastMoveDir
= 0;
898 if (willyJump
) return;
902 auto falling
= isWillyFalling();
903 if (!kDown
&& falling
) {
904 willyConv
= willyStall
= willyLastMoveDir
= 0;
907 if (willyY
> 112) willyY
-= 112;
911 if (!falling
&& willyFall
> 34) willyDead
= true; // too high!
912 auto lfall
= willyFall
;
915 if (willyDead
) return;
917 auto dx
= (kLeft ?
-1 : kRight ?
1 : 0);
918 if (isWillyOnConv()) {
919 auto cdir
= (conveyor
.d ?
1 : -1);
920 //dx==cdir,!dx,lastmove==cdir
921 if (willyLastMoveDir
== cdir || dx
== cdir ||
!dx
) { willyConv
= cdir
; willyStall
= 0; } // was moving in conv. dir or standing
923 // Willy just steps on the conveyor, and Willy walking to the opposite side
925 if (wasJump
&& willyLastMoveDir
== -cdir
) {
926 willyConv
= dx
; // from jump, can do opposite
929 if (lfall
> 0 ||
!willyLastMoveDir
) { dx
= 0; willyStall
= 1; } // lands on conveyor, not from jump
932 // Willy was on conveyor
933 dx
= (willyStall ?
0 : willyConv
);
936 willyConv
= willyStall
= 0;
939 //if (willyConv) dx = willyConv;
940 if (kUp
&& !wasJump
) {
941 willyConv
= willyStall
= willyLastMoveDir
= 0;
947 if (kDown
) willyY
-= 8;
948 willyLastMoveDir
= 0;
949 if (dx
< 0) doWillyLeft(); else if (dx
> 0) doWillyRight();
952 // ////////////////////////////////////////////////////////////////////// //
954 auto img
= new X11Image(16*16, 16+16);
955 auto ww
= gameData
["willy"];
956 foreach (immutable f
; 0..16) {
957 foreach (immutable y
; 0..16) {
958 foreach (immutable x
; 0..16) {
959 ubyte c
= ww
[f
*256+y
*16+x
];
961 img
.setPixel(f
*16+x
, y
, Transparent
);
963 img
.setPixel(f
*16+x
, y
, palcol(c
));
965 img
.setPixel(f
*16+x
, y
+16, palcol(15));
973 void buildBrickImages () {
974 auto buildBrickImage (in ref Room
.CommonGfx brk
, int skipy
=0) {
975 return buildImageBrick(gameData
["blocks"][brk
.gfx
*64..$], brk
.ink
, this.paper
, skipy
);
978 foreach (immutable f
; 0..8) {
981 //case 0: case 7: img = buildBrickImage(this.wall, 16); break;
982 case 1: img
= buildBrickImage(this.platforms
[0]); break;
983 case 2: img
= buildBrickImage(this.platforms
[1]); break;
984 case 3: img
= buildBrickImage(this.wall
); break;
985 //case 4: img = buildBrickImage(this.crumb); break;
986 case 5: img
= buildBrickImage(this.deadlies
[0]); break;
987 case 6: img
= buildBrickImage(this.deadlies
[1]); break;
992 foreach (immutable f
; 0..9) brickCache
~= buildBrickImage(this.crumb
, f
);
995 void buildConvImages () {
997 auto conv
= &this.conveyor
;
998 if (conv
.y
<= 0 || conv
.l
< 1) return;
999 foreach (immutable f
; 0..4) {
1000 convCache
~= buildImageBrick(gameData
["conv"][conv
.gfx
*256+f
*64..$], conv
.ink
, this.paper
);
1004 void buildExitImages () {
1006 exitCache
~= buildImageMasked(gameData
["exits"][this.exit
.gfx
*256..$], 16, 16);
1007 foreach (immutable f
; 0..16) exitCache
~= buildImageMaskedShiny(gameData
["exits"][this.exit
.gfx
*256..$], f
, 16, 16);
1010 void buildKeysImages () {
1012 foreach (immutable f
; 0..16) keyCache
~= buildImageMaskedShiny(gameData
["keys"][this.keys
.gfx
*64..$], f
, 8, 8);
1015 void buildMonsterImages () {
1016 monsterCache
= null;
1017 monsterOfsCache
= null;
1018 foreach (immutable f
, const ref m
; this.enemies
) {
1019 if (m
.x
< 0 || m
.y
< 0) {
1020 monsterOfsCache
~= null;
1021 monsterCache
~= null;
1027 foreach (immutable c
; 0..4) {
1028 auto n
= (m
.gfx
+c
)*256;
1030 r
~= buildImageMaskedInk(gameData
["vrobo"][n
..$], m
.ink
-1, 16, 16);
1033 foreach (immutable c
; 0..(m
.anim
>>1)+1) {
1034 auto n
= (m
.gfx
+c
)*256;
1036 r
~= buildImageMaskedInk(gameData
["hrobo"][n
..$], m
.ink
-1, 16, 16);
1039 r
~= buildImageMaskedInk(gameData
["hrobo"][n
..$], m
.ink
-1, 16, 16);
1042 monsterOfsCache
~= cc
;
1047 void buildEugeneImages () {
1049 for (int f
= 0; f
<= 256; f
+= 256) {
1050 foreach (immutable c
; 0..8) {
1051 eugeneSpr
~= buildImageMaskedInk(gameData
["eugene"][f
..$], c
, 16, 16);
1056 void buildKongImages () {
1058 foreach (immutable f
; 0..4) {
1059 foreach (immutable c
; 0..8) {
1060 kongSpr
~= buildImageMaskedInk(gameData
["kong"][f
*256..$], c
, 16, 16);
1065 void buildSkyLabImages () {
1067 foreach (immutable f
; 0..8) {
1068 foreach (immutable c
; 0..8) {
1069 skylabSpr
~= buildImageMaskedInk(gameData
["sky"][f
*256..$], c
, 16, 16);
1074 void buildSwitchImages () {
1076 foreach (immutable f
; 0..2) {
1077 switchesSpr
~= buildImageBrick(gameData
["switches"][f
*64..$], this.platforms
[1].ink
, this.paper
);
1083 // ////////////////////////////////////////////////////////////////////////// //
1084 public void blitTo (X11Image src
, X11Image dst
, int x
, int y
, int repx
=1) {
1085 foreach (immutable r
; 0..repx
) {
1086 foreach (immutable dy
; 0..src
.height
) {
1087 foreach (immutable dx
; 0..src
.width
) {
1090 if (xx
< 0 || yy
< 0 || xx
>= dst
.width || yy
>= dst
.height
) continue;
1091 auto c
= src
.getPixel(dx
, dy
);
1092 if (isTransparent(c
)) continue;
1093 dst
.setPixel(xx
, yy
, c
);
1101 public void blitTo (X11Image src
, int x0
, int y0
, int ww
, int hh
, X11Image dst
, int x
, int y
) {
1102 foreach (immutable dy
; 0..ww
) {
1103 foreach (immutable dx
; 0..hh
) {
1106 if (xx
< 0 || yy
< 0 || xx
>= dst
.width || yy
>= dst
.height
) continue;
1107 auto c
= src
.getPixel(x0
+dx
, y0
+dy
);
1108 if (isTransparent(c
)) continue;
1109 dst
.setPixel(xx
, yy
, c
);