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("levelset0.js"));
20 foreach (immutable idx
, ref room
; gameRooms
) room
.roomIdx
= cast(int)idx
;
21 gameData
.txtunser(VFile("data.js"));
25 // ////////////////////////////////////////////////////////////////////////// //
27 VColor
palcol (ubyte c
) {
28 static VColor
[256] palette
;
29 static bool palset
= false;
31 //palette = gameData["palmain"][];
32 auto pp
= gameData
["palmain"];
33 foreach (immutable cc
; 0..256) {
34 ubyte r
= cast(ubyte)(255*pp
[cc
*3+0]/63);
35 ubyte g
= cast(ubyte)(255*pp
[cc
*3+1]/63);
36 ubyte b
= cast(ubyte)(255*pp
[cc
*3+2]/63);
37 palette
[cc
] = rgbcol(r
, g
, b
);
45 // ////////////////////////////////////////////////////////////////////////// //
46 private X11Image
buildImageMasked (const(ubyte)[] darr
, int w
, int h
) {
47 assert(w
> 0 && h
> 0);
49 auto img
= new X11Image(w
, h
);
50 foreach (immutable y
; 0..h
) {
51 foreach (immutable x
; 0..w
) {
52 ubyte c
= darr
[dpos
++];
54 img
.setPixel(x
, y
, palcol(c
));
56 img
.setPixel(x
, y
, Transparent
);
64 private X11Image
buildImageMaskedShiny (const(ubyte)[] darr
, int brightness
, int w
, int h
) {
65 assert(w
> 0 && h
> 0);
67 auto img
= new X11Image(w
, h
);
68 foreach (immutable y
; 0..h
) {
69 foreach (immutable x
; 0..w
) {
70 ubyte c
= darr
[dpos
++];
72 int b
= (c
&15)-brightness
;
73 if (b
< 0) b
= 0; else if (b
> 15) b
= 15;
74 img
.setPixel(x
, y
, palcol(cast(ubyte)((c
&240)|b
)));
76 img
.setPixel(x
, y
, Transparent
);
84 private X11Image
buildImageMaskedInk (const(ubyte)[] darr
, int ink
, int w
, int h
) {
85 assert(w
> 0 && h
> 0);
87 auto img
= new X11Image(w
, h
);
90 foreach (immutable y
; 0..h
) {
91 foreach (immutable x
; 0..w
) {
92 ubyte c
= darr
[dpos
++];
94 img
.setPixel(x
, y
, palcol(cast(ubyte)(c
+ink
)));
96 img
.setPixel(x
, y
, Transparent
);
104 // ////////////////////////////////////////////////////////////////////////// //
105 private X11Image
buildImageBrick (const(ubyte)[] darr
, int ink
, int paper
, int skipy
=0) {
107 enum { w
= 8, h
= 8 }
109 auto img
= new X11Image(w
, h
);
111 foreach (immutable y
; 0..h
) {
112 foreach (immutable x
; 0..w
) {
113 ubyte c
= (y
>= skipy ? darr
[dpos
++] : 0);
114 img
.setPixel(x
, y
, palcol(cast(ubyte)(c ? ink
+c
: paper
)));
121 // ////////////////////////////////////////////////////////////////////////// //
122 // willy jump offsets
124 static immutable int[19] willyJ
= [0, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4];
126 struct SkyLabXY
{ int x
, y
; }
127 static immutable SkyLabXY
[4][3] skylabCoords
= [
128 [{x
: 8,y
:0}, {x
: 72,y
:0}, {x
:136,y
:0}, {x
:200,y
:0}],
129 [{x
:40,y
:0}, {x
:104,y
:0}, {x
:168,y
:0}, {x
:232,y
:0}],
130 [{x
:24,y
:0}, {x
: 88,y
:0}, {x
:152,y
:0}, {x
:216,y
:0}]
134 // ////////////////////////////////////////////////////////////////////////// //
135 // suitable to "unjson"
137 enum { Width
= 32, Height
= 16 }
138 static struct WillyStart
{ int x
; int y
; int sd
; }
139 static struct ExitGfx
{ ubyte gfx
; int x
; int y
; }
140 static struct CommonGfx
{ ubyte gfx
; ubyte ink
; ubyte paper
; }
141 static struct Conveyor
{ int x
; int y
; int d
; int l
; ubyte gfx
; ubyte ink
; ubyte paper
; @SRZIgnore int frame
; }
142 static struct KeyPos
{ int x
; int y
; int s
; }
143 static struct Key
{ ubyte gfx
; KeyPos
[] info
; }
144 static struct SwitchPos
{ int x
; int y
; int s
; }
145 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
; }
146 static struct SkyLabEnemy
{ int p
, s
; ubyte ink
; int max
; int m
; int frame
; }
147 static struct SkyLabXY
{ int x
, y
, frame
; ubyte ink
; int m
; int s
; int max
; int p
; }
148 static struct SPGRay
{ int x
, y
; }
149 @SRZIgnore int roomIdx
;
154 ubyte[Height
*Width
] map
; // 16 rows, 32 cols
155 ubyte border
, ink
, paper
;
156 CommonGfx
[] platforms
;
159 CommonGfx
[] deadlies
;
162 SwitchPos
[] switches
;
165 @SRZIgnore Enemy eugene
;
167 @SRZIgnore int holeLen
, holeY
;
168 @SRZIgnore Enemy kong
;
170 @SRZIgnore SkyLabEnemy
[] skylabEnemies
;
171 @SRZIgnore SkyLabXY
[] skylab
;
173 @SRZIgnore SPGRay
[] spg
; // solar power generator
176 int willyX
, willyY
, willyDir
, willyJump
, willyJumpDir
;
177 int willyLastMoveDir
, willyFall
, willyConv
, willyStall
;
178 public bool willyDead
;
182 bool kLeft
, kRight
, kJump
, kUp
, kDown
;
183 bool kLeftDown
, kRightDown
, kJumpDown
, kUpDown
, kDownDown
;
189 X11Image
[] brickCache
;
190 X11Image
[] convCache
;
191 X11Image
[] exitCache
;
193 X11Image
[][] monsterCache
;
194 int[][] monsterOfsCache
;
195 X11Image
[] eugeneSpr
;
197 X11Image
[] skylabSpr
;
198 X11Image
[] switchesSpr
;
207 willyDir
= (willy
.sd
> 0 ?
-1 : 1);
208 willyJump
= willyFall
= willyConv
= willyStall
= 0;
210 willyLastMoveDir
= 0;
211 kLeft
= kRight
= kJump
= kUp
= kDown
= false;
212 kLeftDown
= kRightDown
= kJumpDown
= kUpDown
= kDownDown
= false;
220 buildMonsterImages();
225 finalSpr
= buildImageMasked(gameData
["final"], 256, 64);
226 sunSpr
= buildImageMasked(gameData
["sun"], 24, 16);
231 foreach (ref c
; map
) {
232 if (c
== 4) c
= 8; // 'crumb'
233 else if (c
< 1 || c
> 7) c
= 0; // emptyness
238 foreach (const ref ki
; keys
.info
) {
239 if (ki
.s
== 0) continue;
244 platforms
= platforms
.dup
;
245 deadlies
= deadlies
.dup
;
246 switches
= switches
.dup
;
247 enemies
= enemies
.dup
;
251 Enemy
[] a
= [{ x
:120, y
:1, d
/*ir*/:0, min
:1, max
:87, ink
:6 }];
255 if (roomIdx
== 7 || roomIdx
== 11) {
256 Enemy
[] a
= [{ x
:120, y
:0, max
:104, frame
:0, ink
:2, m
:0 }];
265 {p
:0, s
:4, ink
:6, max
:72, m
:0, frame
:0},
266 {p
:2, s
:3, ink
:5, max
:56, m
:0, frame
:0},
267 {p
:1, s
:1, ink
:4, max
:32, m
:0, frame
:0}
269 skylabEnemies
= a
.dup
;
270 foreach (immutable f
, const ref se
; skylabEnemies
) {
272 skylabCoords
[f
][se
.p
].x
, skylabCoords
[f
][se
.p
].y
,
273 se
.frame
, se
.ink
, se
.m
, se
.s
, se
.max
, se
.p
,
277 // Solar Power Generator?
278 if (roomIdx
== 18) buildSPG();
281 void keyLeft (bool pressed
) { if (pressed
) kLeft
= true; kLeftDown
= pressed
; }
282 void keyRight (bool pressed
) { if (pressed
) kRight
= true; kRightDown
= pressed
; }
283 void keyUp (bool pressed
) { if (pressed
) kUp
= true; kUpDown
= pressed
; }
284 void keyDown (bool pressed
) { if (pressed
) kDown
= true; kDownDown
= pressed
; }
285 void keyJump (bool pressed
) { if (pressed
) kJump
= true; kJumpDown
= pressed
; }
288 kLeft
= kLeft||kLeftDown
;
289 kRight
= kRight||kRightDown
;
291 kDown
= kDown||kDownDown
;
292 kJump
= kJump||kJumpDown
;
293 scope(exit
) kLeft
= kRight
= kJump
= kUp
= kDown
= false;
295 conveyor
.frame
+= (conveyor
.d ?
-1 : 1);
296 if (conveyor
.frame
< 0) conveyor
.frame
= 3; else if (conveyor
.frame
> 3) conveyor
.frame
= 0;
298 if (!willyJump
) stepCrumb();
306 void cheatRemoveKeys () { keys
.info
= null; }
308 // ////////////////////////////////////////////////////////////////////// //
312 ubyte getBlockAt (int x
, int y
, bool simplify
=false) const nothrow @nogc {
315 if (x
< 0 || y
< 0 || x
> 31 || y
> 15) return 0; // empty
316 ubyte b
= map
[y
*32+x
];
319 else if (b
> 7) b
= 4;
320 else if (b
== 6) b
= 5;
321 else if (b
== 2) b
= 1;
326 bool checkExit () const nothrow @nogc {
327 if (keys
.info
.length
!= 0) return false;
330 return (willyX
>= x
-2 && willyX
+10 <= x
+18 && willyY
>= y
-5 && willyY
+16 <= y
+22);
333 bool checkMonsters () const nothrow {
334 foreach (immutable f
, const ref m
; enemies
) {
335 auto cc
= monsterOfsCache
[f
];
336 if (m
.x
< 0 || m
.y
< 0) continue;
338 if (pixelCheckMonster(m
.x
, m
.y
, gameData
["vrobo"], cc
[m
.anim
])) return true;
340 if (pixelCheckMonster(m
.x
&248, m
.y
, gameData
["hrobo"], cc
[((m
.x
&m
.anim
)&0xfe)+m
.d
/*ir*/])) return true;
346 if (pixelCheckMonster(eugene
.x
, eugene
.y
, gameData
["eugene"], 256*curLSet
)) return true;
350 if (pixelCheckMonster(kong
.x
, kong
.y
, gameData
["kong"], 256*kong
.frame
)) return true;
354 foreach (const ref sk
; skylab
) {
355 if (pixelCheckMonster(sk
.x
, sk
.y
, gameData
["sky"], 256*sk
.frame
)) return true;
361 void checkSwitch () nothrow @nogc {
362 foreach (ref ss
; switches
) {
363 if (ss
.s
/*tate*/ != 1) continue;
366 if (x
+7 >= willyX
&& y
+7 >= willyY
&& x
< willyX
+8 && y
< willyY
+16) ss
.s
/*tate*/ = 1+1;
370 bool checkSPG () const nothrow @nogc {
371 foreach (const ref sp
; spg
) {
374 if (x
< 0 || y
< 0) break;
375 if (x
+7 >= willyX
&& x
< willyX
+8 && y
+7 >= willyY
&& y
< willyY
+16) return true;
380 X11Image
draw (X11Image img
=null) {
381 if (img
is null) img
= new X11Image(8*Room
.Width
, 8*Room
.Height
);
383 foreach (immutable y
; 0..img
.height
) {
384 foreach (immutable x
; 0..img
.width
) {
385 img
.setPixel(x
, y
, palcol(this.paper
));
388 foreach (immutable y
; 0..Room
.Height
) {
389 foreach (immutable x
; 0..Room
.Width
) {
390 ubyte blk
= this.map
[pos
++];
391 if (blk
< 1 || blk
== 7) continue;
392 if (blk
== 4) blk
= 8;
393 if (blk
< brickCache
.length
&& brickCache
[blk
] !is null) {
394 brickCache
[blk
].blitTo(img
, x
*8, y
*8);
398 if (this.roomIdx
== 19) {
399 finalSpr
.blitTo(img
, 0, 0);
400 sunSpr
.blitTo(img
, 60, 32);
403 void drawConveyor () {
404 auto conv
= &this.conveyor
;
405 if (conv
.l
< 1) return;
408 convCache
[conv
.frame
].blitTo(img
, conv
.x
, conv
.y
, conv
.l
);
413 if (this.keys
.info
.length
!= 0) {
416 br
= this.frameNo
&31;
417 if (br
> 15) br
= 31-br
;
420 exitCache
[br
].blitTo(img
, this.exit
.x
, this.exit
.y
);
424 auto ff
= this.frameNo
%16;
425 if (ff
>= 8) ff
= 15-ff
;
426 auto keyimg
= keyCache
[ff
];
427 foreach_reverse (const ref ki
; this.keys
.info
) {
428 if (ki
.x
< 0 || ki
.y
< 0) continue;
429 //eraseRect(ki.x, ki.y, 8, 8);
431 keyimg
.blitTo(img
, ki
.x
&248, ki
.y
);
435 void drawMonsters () {
436 foreach (immutable f
, const ref m
; this.enemies
) {
437 if (m
.x
< 0 || m
.y
< 0) continue;
438 auto slist
= monsterCache
[f
];
441 //for (var c = 0; c <= m.anim; c++) r.push(buildImageMaskedInk(me.data.vrobo, (m.gfx+c)*256, m.ink-1, 16, 16, false));
442 slist
[m
.anim
].blitTo(img
, x
, m
.y
);
444 auto sidx
= ((x
&m
.anim
)&0xfe)+m
.d
/*ir*/;
445 if (sidx
< slist
.length
) {
446 slist
[sidx
].blitTo(img
, x
&248, m
.y
);
448 writeln("monster #", f
, " is fucked: sidx=", sidx
, "; max=", slist
.length
);
452 if (this.eugene
.x
>= 0) {
454 eugeneSpr
[curLSet
*8+this.eugene
.ink
].blitTo(img
, this.eugene
.x
, this.eugene
.y
);
456 if (this.kong
.x
>= 0) {
457 kongSpr
[this.kong
.frame
*8+this.kong
.ink
].blitTo(img
, this.kong
.x
, this.kong
.y
);
459 if (this.skylab
.length
) {
460 foreach (const ref sk
; this.skylab
) {
461 skylabSpr
[sk
.frame
*8+sk
.ink
].blitTo(img
, sk
.x
, sk
.y
);
467 foreach (const ref ss
; this.switches
) {
468 if (ss
.s
== 0) continue;
469 switchesSpr
[ss
.s
-1].blitTo(img
, ss
.x
, ss
.y
);
474 foreach (const ref sp
; this.spg
) {
477 if (x
< 0 || y
< 0) break;
478 //ctx.fillStyle = mainPal[noerase?6:this.paper];
479 //ctx.fillRect(x, y, 8, 8);
480 foreach (immutable dy
; 0..8) {
481 foreach (immutable dx
; 0..8) {
482 img
.setPixel(x
+dx
, y
+dy
, palcol(6));
489 auto willyPos
= (this.willyX
&15)>>1;
490 if (this.willyDir
< 0) willyPos
+= 8;
493 if (this.checkMonsters()) wy
= 16;
494 willySpr
.blitTo(willyPos
*16, wy
, 16, 16, img
, this.willyX
&248, this.willyY
);
496 willySpr
.blitTo(willyPos
*16, 0, 16, 16, img
, this.willyX
&248, this.willyY
);
512 // ////////////////////////////////////////////////////////////////////// //
513 // pixel-perfect collision detector
514 // fully stolen from Andy's sources %-)
515 bool pixelCheckMonster (int rx
, int ry
, const(ubyte)[] darr
, int dpos
=0) const nothrow {
516 static ubyte[256] mpcGrid
;
522 if (rx
< -15 || rx
> 15 || ry
< -15 || ry
> 15) return false;
525 if (rx
< 0) { x
= 0; rx
= -rx
; w
= 16-rx
; } else { x
= rx
; rx
= 0; w
= 16-x
; }
526 // partial plot monster
527 for (int y
= ry
+15; y
>= ry
; --y
) {
528 if (y
>= 0 && y
< 16) {
530 int rp
= dpos
+(y
-ry
)*16+rx
;
531 for (int dx
= 0; dx
< w
; ++dx
, ++gp
, ++rp
) mpcGrid
[gp
] = darr
[rp
];
534 auto warr
= gameData
["willy"];
535 int wptr
= ((willyX
&15)>>1)*256+(willyDir
< 0 ?
2048 : 0);
536 // check for collision
538 for (x
= 255; x
>= 0; --x
, ++wptr
, ++mp
) if (warr
[wptr
] && mpcGrid
[mp
]) return true;
542 // ////////////////////////////////////////////////////////////////////// //
543 bool isWillyFalling () const nothrow @nogc {
544 if (willyY
&7) return true;
545 for (int dx
= 0; dx
<= 8; dx
+= 8) {
546 ubyte b
= getBlockAt(willyX
+dx
, willyY
+16, true);
547 if (b
> 0 && b
!= 5) return false;
552 bool isWillyInDeadly () const nothrow @nogc {
553 for (int dx
= 0; dx
<= 8; dx
+= 8) {
554 for (int dy
= 0; dy
<= 16; dy
+= 8) {
555 if (getBlockAt(willyX
+dx
, willyY
+dy
, true) == 5) return true;
561 bool isWillyOnConv () const nothrow @nogc {
562 if (willyY
&7) return false;
563 ubyte b0
= getBlockAt(willyX
, willyY
+16);
564 ubyte b1
= getBlockAt(willyX
+8, willyY
+16);
565 return (b0
== 7 || b1
== 7);
568 // ////////////////////////////////////////////////////////////////////// //
569 // called on each frame
570 void buildSPG (bool forced
=false) {
571 if (!forced
&& roomIdx
!= 18) {
576 bool spgCheckMonster (int x
, int y
) {
579 foreach (const ref cm
; enemies
) {
582 if (x
+7 >= mx
&& x
< mx
+15 && y
+7 >= my
&& y
< my
+15) return true;
589 int dir
= 0, idx
= 0;
593 spg
.assumeSafeAppend
;
596 void addXY (int x
, int y
) {
598 while (spg
.length
< idx
) spg
~= SPGRay(-1, -1);
604 ubyte blockhit
= map
[y
*32+x
];
605 bool robohit
= spgCheckMonster(x
, y
);
607 if (blockhit
&& robohit
) {
610 } else if (!blockhit
&& robohit
) {
611 if (idx
&& spg
[idx
-1].x
== x
&& spg
[idx
-1].y
== y
) {
612 spg
[idx
-1].x
= spg
[idx
-1].y
= -1;
618 } else if (!blockhit
&& !robohit
) {
620 } else if (blockhit
&& !robohit
) {
621 if (idx
&& spg
[idx
-1].x
== x
&& spg
[idx
-1].y
== y
) {
622 spg
[idx
-1].x
= spg
[idx
-1].y
= -1;
631 blockhit
= map
[y
*32+x
];
632 if (y
== 15 || blockhit
) done
= true;
635 blockhit
= map
[y
*32+x
];
636 if (x
== 0 || blockhit
) done
= true;
639 if (!dir
) { --x
; if (!x
) done
= true; }
640 else { ++y
; if (++y
== 15) done
= true; }
646 void stepMonsters () {
647 foreach (ref m
; enemies
) {
648 if (m
.x
< 0 || m
.y
< 0) continue;
651 auto spd
= m
.s
/*peed*/;
652 if (m
.d
/*ir*/ != 0) {
655 if (y
< m
.min || y
< 0) { y
+= spd
; m
.d
/*ir*/ = 0; }
659 if (y
> m
.max
) { y
-= spd
; m
.d
/*ir*/ = 1; }
662 m
.anim
= (m
.anim
+1)&3;
665 auto spd
= (2>>m
.s
/*peed*/);
666 if (m
.d
/*ir*/ != 0) {
669 if (x
< m
.min
) { m
.d
/*ir*/ = 0; x
+= spd
; }
673 if (x
> m
.max
+6) { m
.d
/*ir*/ = 1; x
-= spd
; }
680 if (keys
.info
.length
== 0) {
681 // no keys, Eugene tries to block the exit
682 eugene
.ink
= (eugene
.ink
+1)&7;
683 eugene
.y
+= (eugene
.y
< eugene
.max ?
1 : 0);
685 if (eugene
.d
/*ir*/ != 0) {
688 if (eugene
.y
< eugene
.min
) { eugene
.d
/*ir*/ = 0; ++eugene
.y
; }
692 if (eugene
.y
> eugene
.max
) { eugene
.d
/*ir*/ = 1; --eugene
.y
; }
698 switch (kong
.falling
) {
699 case 1: // just started
702 //eraseRect(16, 120, 16, 8);
712 if (kong
.y
>= kong
.max
) { kong
.x
= -1; score
+= 100; }
713 if (!kong
.delay
) { kong
.delay
= 4; kong
.frame
= ((kong
.frame
-1)&1)+2; } else --kong
.delay
;
716 if (!kong
.delay
) { kong
.delay
= 8; kong
.frame
= (kong
.frame
+1)&1; } else --kong
.delay
;
721 foreach (immutable idx
, ref sk
; skylab
) {
733 if (sk
.frame
== 7) sk
.m
= 2;
737 sk
.x
= skylabCoords
[idx
][sk
.p
].x
;
738 sk
.y
= skylabCoords
[idx
][sk
.p
].y
;
747 if (willyY
&7) return;
748 for (int f
= 0; f
<= 8; f
+= 8) {
751 ubyte b
= getBlockAt(x
, y
);
758 //eraseRect(x*8, y*8, 8, 8);
763 while (keys
.info
.length
) {
765 foreach (immutable idx
; 0..keys
.info
.length
) {
766 auto ki
= &keys
.info
[idx
];
770 if (kx
+7 >= willyX
&& kx
< willyX
+10 && ky
+7 >= willyY
&& ky
< willyY
+16) {
772 foreach (immutable c
; idx
+1..keys
.info
.length
) keys
.info
[c
-1] = keys
.info
[c
];
773 keys
.info
.length
-= 1;
784 if (holeLen
> 0 && switches
.length
&& switches
[0].s
/*tate*/ > 1) {
787 enemies
[1].max
+= 24;
799 if (kong
.x
>= 0 && !kong
.falling
&& switches
.length
> 1 && switches
[1].s
/*tate*/ > 1) kong
.falling
= 1;
802 void stepWillyActions () {
803 bool doWillyLeft () {
804 if (willyDir
> 0) { willyDir
= -1; return true; }
806 auto b0
= getBlockAt(xx
, willyY
);
807 auto b1
= getBlockAt(xx
, willyY
+8);
808 auto b2
= (willyY
&7 ?
getBlockAt(xx
, willyY
+16) : b1
);
809 if (b0
== 3 || b1
== 3 || b2
== 3) return false;
811 if (willyX
< 0) willyX
+= 240;
812 willyLastMoveDir
= -1;
816 bool doWillyRight () {
817 if (willyDir
< 0) { willyDir
= 1; return true; }
818 if (willyX
> 245) return false;
820 auto b0
= getBlockAt(xx
, willyY
);
821 auto b1
= getBlockAt(xx
, willyY
+8);
822 auto b2
= (willyY
&7 ?
getBlockAt(xx
, willyY
+16) : b1
);
823 if (b0
== 3 || b1
== 3 || b2
== 3) return false;
825 if (willyX
> 240) willyX
-= 240;
826 willyLastMoveDir
= 1;
830 void doWillyJump () {
831 if (!willyJump
) return;
832 willyY
+= willyJ
[willyJump
];
835 if (willyJumpDir
< 0) mv
= doWillyLeft(); else if (willyJumpDir
> 0) mv
= doWillyRight();
839 auto b0
= getBlockAt(x
, willyY
);
840 auto b1
= getBlockAt(x
+8, willyY
);
841 if (b0
== 3 || b1
== 3) {
842 // headboom! (apstenu %-)
844 willyY
-= willyJ
[willyJump
];
845 willyJump
= 0; // enough flying
850 if (willyJump
> 12) willyFall
+= willyJ
[willyJump
];
851 if ((willyY
&7) == 0) {
852 auto b0
= getBlockAt(willyX
, willyY
+16);
853 auto b1
= getBlockAt(willyX
+8, willyY
+16);
855 if (b0
== 3 || b1
== 3) willyX
= x
;
856 willyFall
= 0; // can't fall too deep while jumping
857 willyJump
= 0; // enough flying
858 if (b0
== 7 || b1
== 7) willyStall
= 1; // conveyor?
864 if (willyJump
> 18) willyJump
= 0;
868 if (willyDead
) return;
870 if (isWillyInDeadly()) { willyDead
= true; return; }
872 if (checkMonsters()) { willyDead
= true; return; }
874 if (checkMonsters()) singleStep
= singleStepWait
= true;
877 auto wasJump
= false;
879 willyLastMoveDir
= 0;
881 if (willyJump
) return;
885 auto falling
= isWillyFalling();
886 if (!kDown
&& falling
) {
887 willyConv
= willyStall
= willyLastMoveDir
= 0;
890 if (willyY
> 112) willyY
-= 112;
894 if (!falling
&& willyFall
> 34) willyDead
= true; // too high!
895 auto lfall
= willyFall
;
898 if (willyDead
) return;
900 auto dx
= (kLeft ?
-1 : kRight ?
1 : 0);
901 if (isWillyOnConv()) {
902 auto cdir
= (conveyor
.d ?
1 : -1);
903 //dx==cdir,!dx,lastmove==cdir
904 if (willyLastMoveDir
== cdir || dx
== cdir ||
!dx
) { willyConv
= cdir
; willyStall
= 0; } // was moving in conv. dir or standing
906 // Willy just steps on the conveyor, and Willy walking to the opposite side
908 if (wasJump
&& willyLastMoveDir
== -cdir
) {
909 willyConv
= dx
; // from jump, can do opposite
912 if (lfall
> 0 ||
!willyLastMoveDir
) { dx
= 0; willyStall
= 1; } // lands on conveyor, not from jump
915 // Willy was on conveyor
916 dx
= (willyStall ?
0 : willyConv
);
919 willyConv
= willyStall
= 0;
922 //if (willyConv) dx = willyConv;
923 if (kUp
&& !wasJump
) {
924 willyConv
= willyStall
= willyLastMoveDir
= 0;
930 if (kDown
) willyY
-= 8;
931 willyLastMoveDir
= 0;
932 if (dx
< 0) doWillyLeft(); else if (dx
> 0) doWillyRight();
935 // ////////////////////////////////////////////////////////////////////// //
937 auto img
= new X11Image(16*16, 16+16);
938 auto ww
= gameData
["willy"];
939 foreach (immutable f
; 0..16) {
940 foreach (immutable y
; 0..16) {
941 foreach (immutable x
; 0..16) {
942 ubyte c
= ww
[f
*256+y
*16+x
];
944 img
.setPixel(f
*16+x
, y
, Transparent
);
946 img
.setPixel(f
*16+x
, y
, palcol(c
));
948 img
.setPixel(f
*16+x
, y
+16, palcol(15));
956 void buildBrickImages () {
957 auto buildBrickImage (in ref Room
.CommonGfx brk
, int skipy
=0) {
958 return buildImageBrick(gameData
["blocks"][brk
.gfx
*64..$], brk
.ink
, this.paper
, skipy
);
961 foreach (immutable f
; 0..8) {
964 //case 0: case 7: img = buildBrickImage(this.wall, 16); break;
965 case 1: img
= buildBrickImage(this.platforms
[0]); break;
966 case 2: img
= buildBrickImage(this.platforms
[1]); break;
967 case 3: img
= buildBrickImage(this.wall
); break;
968 //case 4: img = buildBrickImage(this.crumb); break;
969 case 5: img
= buildBrickImage(this.deadlies
[0]); break;
970 case 6: img
= buildBrickImage(this.deadlies
[1]); break;
975 foreach (immutable f
; 0..9) brickCache
~= buildBrickImage(this.crumb
, f
);
978 void buildConvImages () {
980 auto conv
= &this.conveyor
;
981 if (conv
.y
<= 0 || conv
.l
< 1) return;
982 foreach (immutable f
; 0..4) {
983 convCache
~= buildImageBrick(gameData
["conv"][conv
.gfx
*256+f
*64..$], conv
.ink
, this.paper
);
987 void buildExitImages () {
989 exitCache
~= buildImageMasked(gameData
["exits"][this.exit
.gfx
*256..$], 16, 16);
990 foreach (immutable f
; 0..16) exitCache
~= buildImageMaskedShiny(gameData
["exits"][this.exit
.gfx
*256..$], f
, 16, 16);
993 void buildKeysImages () {
995 foreach (immutable f
; 0..16) keyCache
~= buildImageMaskedShiny(gameData
["keys"][this.keys
.gfx
*64..$], f
, 8, 8);
998 void buildMonsterImages () {
1000 monsterOfsCache
= null;
1001 foreach (immutable f
, const ref m
; this.enemies
) {
1002 if (m
.x
< 0 || m
.y
< 0) {
1003 monsterOfsCache
~= null;
1004 monsterCache
~= null;
1010 foreach (immutable c
; 0..4) {
1011 auto n
= (m
.gfx
+c
)*256;
1013 r
~= buildImageMaskedInk(gameData
["vrobo"][n
..$], m
.ink
-1, 16, 16);
1016 foreach (immutable c
; 0..(m
.anim
>>1)+1) {
1017 auto n
= (m
.gfx
+c
)*256;
1019 r
~= buildImageMaskedInk(gameData
["hrobo"][n
..$], m
.ink
-1, 16, 16);
1022 r
~= buildImageMaskedInk(gameData
["hrobo"][n
..$], m
.ink
-1, 16, 16);
1025 monsterOfsCache
~= cc
;
1030 void buildEugeneImages () {
1032 for (int f
= 0; f
<= 256; f
+= 256) {
1033 foreach (immutable c
; 0..8) {
1034 eugeneSpr
~= buildImageMaskedInk(gameData
["eugene"][f
..$], c
, 16, 16);
1039 void buildKongImages () {
1041 foreach (immutable f
; 0..4) {
1042 foreach (immutable c
; 0..8) {
1043 kongSpr
~= buildImageMaskedInk(gameData
["kong"][f
*256..$], c
, 16, 16);
1048 void buildSkyLabImages () {
1050 foreach (immutable f
; 0..8) {
1051 foreach (immutable c
; 0..8) {
1052 skylabSpr
~= buildImageMaskedInk(gameData
["sky"][f
*256..$], c
, 16, 16);
1057 void buildSwitchImages () {
1059 foreach (immutable f
; 0..2) {
1060 switchesSpr
~= buildImageBrick(gameData
["switches"][f
*64..$], this.platforms
[1].ink
, this.paper
);
1066 // ////////////////////////////////////////////////////////////////////////// //
1067 public void blitTo (X11Image src
, X11Image dst
, int x
, int y
, int repx
=1) {
1068 foreach (immutable r
; 0..repx
) {
1069 foreach (immutable dy
; 0..src
.height
) {
1070 foreach (immutable dx
; 0..src
.width
) {
1073 if (xx
< 0 || yy
< 0 || xx
>= dst
.width || yy
>= dst
.height
) continue;
1074 auto c
= src
.getPixel(dx
, dy
);
1075 if (isTransparent(c
)) continue;
1076 dst
.setPixel(xx
, yy
, c
);
1084 public void blitTo (X11Image src
, int x0
, int y0
, int ww
, int hh
, X11Image dst
, int x
, int y
) {
1085 foreach (immutable dy
; 0..ww
) {
1086 foreach (immutable dx
; 0..hh
) {
1089 if (xx
< 0 || yy
< 0 || xx
>= dst
.width || yy
>= dst
.height
) continue;
1090 auto c
= src
.getPixel(x0
+dx
, y0
+dy
);
1091 if (isTransparent(c
)) continue;
1092 dst
.setPixel(xx
, yy
, c
);