1 module mminer
is aliced
;
3 import arsd
.simpledisplay
;
12 // ////////////////////////////////////////////////////////////////////////// //
14 static immutable int[19] willyJ
= [0, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4];
16 struct SkyLabXY
{ int x
, y
; }
17 static immutable SkyLabXY
[4][3] skylabCoords
= [
18 [{x
: 8,y
:0}, {x
: 72,y
:0}, {x
:136,y
:0}, {x
:200,y
:0}],
19 [{x
:40,y
:0}, {x
:104,y
:0}, {x
:168,y
:0}, {x
:232,y
:0}],
20 [{x
:24,y
:0}, {x
: 88,y
:0}, {x
:152,y
:0}, {x
:216,y
:0}]
24 // ////////////////////////////////////////////////////////////////////////// //
25 // suitable to "unjson"
27 enum { Width
= 32, Height
= 16 }
28 static struct WillyStart
{ int x
; int y
; int sd
; }
29 static struct ExitGfx
{ ubyte gfx
; int x
; int y
; }
30 static struct CommonGfx
{ ubyte gfx
; ubyte ink
; ubyte paper
; }
31 static struct Conveyor
{ int x
; int y
; int d
; int l
; ubyte gfx
; ubyte ink
; ubyte paper
; @SRZIgnore int frame
; }
32 static struct KeyPos
{ int x
; int y
; int s
; }
33 static struct Key
{ ubyte gfx
; KeyPos
[] info
; }
34 static struct SwitchPos
{ int x
; int y
; int s
; }
35 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
; }
36 static struct SkyLabEnemy
{ int p
, s
; ubyte ink
; int max
; int m
; int frame
; }
37 static struct SkyLabXY
{ int x
, y
, frame
; ubyte ink
; int m
; int s
; int max
; int p
; }
38 static struct SPGRay
{ int x
, y
; }
39 @SRZIgnore int roomIdx
;
44 ubyte[Height
*Width
] map
; // 16 rows, 32 cols
45 ubyte border
, ink
, paper
;
46 CommonGfx
[] platforms
;
55 @SRZIgnore Enemy eugene
;
57 @SRZIgnore int holeLen
, holeY
;
58 @SRZIgnore Enemy kong
;
60 @SRZIgnore SkyLabEnemy
[] skylabEnemies
;
61 @SRZIgnore SkyLabXY
[] skylab
;
63 @SRZIgnore SPGRay
[] spg
; // solar power generator
66 int willyX
, willyY
, willyDir
, willyJump
, willyJumpDir
;
67 int willyLastMoveDir
, willyFall
, willyConv
, willyStall
;
72 bool kLeft
, kRight
, kJump
, kUp
, kDown
;
79 willyDir
= (willy
.sd
> 0 ?
-1 : 1);
80 willyJump
= willyFall
= willyConv
= willyStall
= 0;
84 kLeft
= kRight
= kJump
= kUp
= kDown
= false;
91 foreach (ref c
; map
) {
92 if (c
== 4) c
= 8; // 'crumb'
93 else if (c
< 1 || c
> 7) c
= 0; // emptyness
98 foreach (const ref ki
; keys
.info
) {
99 if (ki
.s
== 0) continue;
104 platforms
= platforms
.dup
;
105 deadlies
= deadlies
.dup
;
106 switches
= switches
.dup
;
107 enemies
= enemies
.dup
;
111 Enemy
[] a
= [{ x
:120, y
:1, d
/*ir*/:0, min
:1, max
:87, ink
:6 }];
115 if (roomIdx
== 7 || roomIdx
== 11) {
116 Enemy
[] a
= [{ x
:120, y
:0, max
:104, frame
:0, ink
:2, m
:0 }];
125 {p
:0, s
:4, ink
:6, max
:72, m
:0, frame
:0},
126 {p
:2, s
:3, ink
:5, max
:56, m
:0, frame
:0},
127 {p
:1, s
:1, ink
:4, max
:32, m
:0, frame
:0}
129 skylabEnemies
= a
.dup
;
130 foreach (immutable f
, const ref se
; skylabEnemies
) {
132 skylabCoords
[f
][se
.p
].x
, skylabCoords
[f
][se
.p
].y
,
133 se
.frame
, se
.ink
, se
.m
, se
.s
, se
.max
, se
.p
,
137 // Solar Power Generator?
138 if (roomIdx
== 18) buildSPG();
141 // called on each frame
142 void buildSPG (bool forced
=false) {
143 if (!forced
&& roomIdx
!= 18) {
148 bool spgCheckMonster (int x
, int y
) {
151 foreach (const ref cm
; enemies
) {
154 if (x
+7 >= mx
&& x
< mx
+15 && y
+7 >= my
&& y
< my
+15) return true;
161 int dir
= 0, idx
= 0;
165 spg
.assumeSafeAppend
;
168 void addXY (int x
, int y
) {
170 while (spg
.length
< idx
) spg
~= SPGRay(-1, -1);
176 ubyte blockhit
= map
[y
*32+x
];
177 bool robohit
= spgCheckMonster(x
, y
);
179 if (blockhit
&& robohit
) {
182 } else if (!blockhit
&& robohit
) {
183 if (idx
&& spg
[idx
-1].x
== x
&& spg
[idx
-1].y
== y
) {
184 spg
[idx
-1].x
= spg
[idx
-1].y
= -1;
190 } else if (!blockhit
&& !robohit
) {
192 } else if (blockhit
&& !robohit
) {
193 if (idx
&& spg
[idx
-1].x
== x
&& spg
[idx
-1].y
== y
) {
194 spg
[idx
-1].x
= spg
[idx
-1].y
= -1;
203 blockhit
= map
[y
*32+x
];
204 if (y
== 15 || blockhit
) done
= true;
207 blockhit
= map
[y
*32+x
];
208 if (x
== 0 || blockhit
) done
= true;
211 if (!dir
) { --x
; if (!x
) done
= true; }
212 else { ++y
; if (++y
== 15) done
= true; }
218 void stepMonsters () {
219 foreach (ref m
; enemies
) {
220 if (m
.x
< 0 || m
.y
< 0) continue;
223 auto spd
= m
.s
/*peed*/;
224 if (m
.d
/*ir*/ != 0) {
227 if (y
< m
.min || y
< 0) { y
+= spd
; m
.d
/*ir*/ = 0; }
231 if (y
> m
.max
) { y
-= spd
; m
.d
/*ir*/ = 1; }
234 m
.anim
= (m
.anim
+1)&3;
237 auto spd
= (2>>m
.s
/*peed*/);
238 if (m
.d
/*ir*/ != 0) {
241 if (x
< m
.min
) { m
.d
/*ir*/ = 0; x
+= spd
; }
245 if (x
> m
.max
+6) { m
.d
/*ir*/ = 1; x
-= spd
; }
252 if (keys
.info
.length
== 0) {
253 // no keys, Eugene tries to block the exit
254 eugene
.ink
= (eugene
.ink
+1)&7;
255 eugene
.y
+= (eugene
.y
< eugene
.max ?
1 : 0);
257 if (eugene
.d
/*ir*/ != 0) {
260 if (eugene
.y
< eugene
.min
) { eugene
.d
/*ir*/ = 0; ++eugene
.y
; }
264 if (eugene
.y
> eugene
.max
) { eugene
.d
/*ir*/ = 1; --eugene
.y
; }
270 switch (kong
.falling
) {
271 case 1: // just started
274 //eraseRect(16, 120, 16, 8);
284 if (kong
.y
>= kong
.max
) { kong
.x
= -1; score
+= 100; }
285 if (!kong
.delay
) { kong
.delay
= 4; kong
.frame
= ((kong
.frame
-1)&1)+2; } else --kong
.delay
;
288 if (!kong
.delay
) { kong
.delay
= 8; kong
.frame
= (kong
.frame
+1)&1; } else --kong
.delay
;
293 foreach (immutable idx
, ref sk
; skylab
) {
305 if (sk
.frame
== 7) sk
.m
= 2;
309 sk
.x
= skylabCoords
[idx
][sk
.p
].x
;
310 sk
.y
= skylabCoords
[idx
][sk
.p
].y
;
319 if (willyY
&7) return;
320 for (int f
= 0; f
<= 8; f
+= 8) {
323 ubyte b
= getBlockAt(x
, y
);
330 //eraseRect(x*8, y*8, 8, 8);
335 while (keys
.info
.length
) {
337 foreach (immutable idx
; 0..keys
.info
.length
) {
338 auto ki
= &keys
.info
[idx
];
342 if (kx
+7 >= willyX
&& kx
< willyX
+10 && ky
+7 >= willyY
&& ky
< willyY
+16) {
344 foreach (immutable c
; idx
+1..keys
.info
.length
) keys
.info
[c
-1] = keys
.info
[c
];
345 keys
.info
.length
-= 1;
356 if (holeLen
> 0 && switches
.length
&& switches
[0].s
/*tate*/ > 1) {
359 enemies
[1].max
+= 24;
363 //eraseRect(136, 88, 8, 16);
364 //ctx.fillStyle = mainPal[curRoom.paper];
365 //ctx.fillRect(136, 88+16-holeY, 8, holeY);
374 if (kong
.x
>= 0 && !kong
.falling
&& switches
.length
> 1 && switches
[1].s
/*tate*/ > 1) kong
.falling
= 1;
377 void stepWillyActions () {
378 bool doWillyLeft () {
379 if (willyDir
> 0) { willyDir
= -1; return true; }
381 auto b0
= getBlockAt(xx
, willyY
);
382 auto b1
= getBlockAt(xx
, willyY
+8);
383 auto b2
= (willyY
&7 ?
getBlockAt(xx
, willyY
+16) : b1
);
384 if (b0
== 3 || b1
== 3 || b2
== 3) return false;
386 if (willyX
< 0) willyX
+= 240;
387 willyLastMoveDir
= -1;
391 bool doWillyRight () {
392 if (willyDir
< 0) { willyDir
= 1; return true; }
393 if (willyX
> 245) return false;
395 auto b0
= getBlockAt(xx
, willyY
);
396 auto b1
= getBlockAt(xx
, willyY
+8);
397 auto b2
= (willyY
&7 ?
getBlockAt(xx
, willyY
+16) : b1
);
398 if (b0
== 3 || b1
== 3 || b2
== 3) return false;
400 if (willyX
> 240) willyX
-= 240;
401 willyLastMoveDir
= 1;
405 void doWillyJump () {
406 if (!willyJump
) return;
407 willyY
+= willyJ
[willyJump
];
410 if (willyJumpDir
< 0) mv
= doWillyLeft(); else if (willyJumpDir
> 0) mv
= doWillyRight();
414 auto b0
= getBlockAt(x
, willyY
);
415 auto b1
= getBlockAt(x
+8, willyY
);
416 if (b0
== 3 || b1
== 3) {
417 // headboom! (apstenu %-)
419 willyY
-= willyJ
[willyJump
];
420 willyJump
= 0; // enough flying
425 if (willyJump
> 12) willyFall
+= willyJ
[willyJump
];
426 if ((willyY
&7) == 0) {
427 auto b0
= getBlockAt(willyX
, willyY
+16);
428 auto b1
= getBlockAt(willyX
+8, willyY
+16);
430 if (b0
== 3 || b1
== 3) willyX
= x
;
431 willyFall
= 0; // can't fall too deep while jumping
432 willyJump
= 0; // enough flying
433 if (b0
== 7 || b1
== 7) willyStall
= 1; // conveyor?
439 if (willyJump
> 18) willyJump
= 0;
443 if (willyDead
) return;
445 if (isWillyInDeadly()) { willyDead
= true; return; }
446 /*if (!DEBUG_COLDET)*/ {
447 if (checkMonsters()) { willyDead
= true; return; }
450 auto wasJump
= false;
452 willyLastMoveDir
= 0;
454 if (willyJump
) return;
458 auto falling
= isWillyFalling();
459 if (!kDown
&& falling
) {
460 willyConv
= willyStall
= willyLastMoveDir
= 0;
463 if (willyY
> 112) willyY
-= 112;
467 if (!falling
&& willyFall
> 34) willyDead
= true; // too high!
468 auto lfall
= willyFall
;
471 if (willyDead
) return;
473 auto dx
= (kLeft ?
-1 : kRight ?
1 : 0);
474 if (isWillyOnConv()) {
475 auto cdir
= (conveyor
.d ?
1 : -1);
476 //dx==cdir,!dx,lastmove==cdir
477 if (willyLastMoveDir
== cdir || dx
== cdir ||
!dx
) { willyConv
= cdir
; willyStall
= 0; } // was moving in conv. dir or standing
479 // Willy just steps on the conveyor, and Willy walking to the opposite side
481 if (wasJump
&& willyLastMoveDir
== -cdir
) {
482 willyConv
= dx
; // from jump, can do opposite
485 if (lfall
> 0 ||
!willyLastMoveDir
) { dx
= 0; willyStall
= 1; } // lands on conveyor, not from jump
488 // Willy was on conveyor
489 dx
= (willyStall ?
0 : willyConv
);
492 willyConv
= willyStall
= 0;
495 //if (willyConv) dx = willyConv;
496 if (kUp
&& !wasJump
) {
497 willyConv
= willyStall
= willyLastMoveDir
= 0;
503 if (kDown
) willyY
-= 8;
504 willyLastMoveDir
= 0;
505 if (dx
< 0) doWillyLeft(); else if (dx
> 0) doWillyRight();
510 conveyor
.frame
+= (conveyor
.d ?
-1 : 1);
511 if (conveyor
.frame
< 0) conveyor
.frame
= 3; else if (conveyor
.frame
> 3) conveyor
.frame
= 0;
513 // must erase SPG, Willy and monsters here -- before coords change
517 eraseRect(willyX&248, willyY, 16, 16);
519 if (!willyJump
) stepCrumb();
524 if (checkMonsters()) {
525 dbgSingleStep = true; dbgNF = false;
526 dbgColDetected = true;
527 } else dbgColDetected = false;
539 if (gameTID) clearInterval(gameTID), gameTID = null;
541 message("you are dead!");
542 setTimeout(startRoom, 1000);
549 if (gameTID) clearInterval(gameTID), gameTID = null;
551 message(curRoom.title+" complete!");
553 if (curRoomNo >= me.lsets[curLSet].rooms.length) {
556 if (curLSet >= me.lsets.length) curLSet = 0;
558 setTimeout(startRoom, 2000);
564 // ////////////////////////////////////////////////////////////////////// //
568 ubyte getBlockAt (int x
, int y
, bool simplify
=false) const nothrow @nogc {
571 if (x
< 0 || y
< 0 || x
> 31 || y
> 15) return 0; // empty
572 ubyte b
= map
[y
*32+x
];
575 else if (b
> 7) b
= 4;
576 else if (b
== 6) b
= 5;
577 else if (b
== 2) b
= 1;
582 bool isWillyFalling () const nothrow @nogc {
583 if (willyY
&7) return true;
584 for (int dx
= 0; dx
<= 8; dx
+= 8) {
585 ubyte b
= getBlockAt(willyX
+dx
, willyY
+16, true);
586 if (b
> 0 && b
!= 5) return false;
591 bool isWillyInDeadly () const nothrow @nogc {
592 for (int dx
= 0; dx
<= 8; dx
+= 8) {
593 for (int dy
= 0; dy
<= 16; dy
+= 8) {
594 if (getBlockAt(willyX
+dx
, willyY
+dy
, true) == 5) return true;
600 bool isWillyOnConv () const nothrow @nogc {
601 if (willyY
&7) return false;
602 ubyte b0
= getBlockAt(willyX
, willyY
+16);
603 ubyte b1
= getBlockAt(willyX
+8, willyY
+16);
604 return (b0
== 7 || b1
== 7);
607 bool checkExit () const nothrow @nogc {
608 if (keys
.info
.length
!= 0) return false;
611 return (willyX
>= x
-2 && willyX
+10 <= x
+18 && willyY
>= y
-5 && willyY
+16 <= y
+22);
615 // pixel-perfect collision detector
616 // fully stolen from Andy's sources %-)
618 bool pixelCheckMonster (int rx
, int ry
, const(ubyte)[] darr
, int dpos
=0) const nothrow {
619 static ubyte[256] mpcGrid
;
625 if (rx
< -15 || rx
> 15 || ry
< -15 || ry
> 15) return false;
628 if (rx
< 0) { x
= 0; rx
= -rx
; w
= 16-rx
; } else { x
= rx
; rx
= 0; w
= 16-x
; }
629 // partial plot monster
630 for (int y
= ry
+15; y
>= ry
; --y
) {
631 if (y
>= 0 && y
< 16) {
633 int rp
= dpos
+(y
-ry
)*16+rx
;
634 for (int dx
= 0; dx
< w
; ++dx
, ++gp
, ++rp
) mpcGrid
[gp
] = darr
[rp
];
637 auto warr
= gameData
["willy"];
638 int wptr
= ((willyX
&15)>>1)*256+(willyDir
< 0 ?
2048 : 0);
639 // check for collision
641 for (x
= 255; x
>= 0; --x
, ++wptr
, ++mp
) if (warr
[wptr
] && mpcGrid
[mp
]) return true;
645 bool checkMonsters () const nothrow {
646 foreach (immutable f
, const ref m
; enemies
) {
647 auto cc
= monsterOfsCache
[f
];
648 if (m
.x
< 0 || m
.y
< 0) continue;
650 if (pixelCheckMonster(m
.x
, m
.y
, gameData
["vrobo"], cc
[m
.anim
])) return true;
652 if (pixelCheckMonster(m
.x
&248, m
.y
, gameData
["hrobo"], cc
[((m
.x
&m
.anim
)&0xfe)+m
.d
/*ir*/])) return true;
658 if (pixelCheckMonster(eugene
.x
, eugene
.y
, gameData
["eugene"], 256*curLSet
)) return true;
662 if (pixelCheckMonster(kong
.x
, kong
.y
, gameData
["kong"], 256*kong
.frame
)) return true;
666 foreach (const ref sk
; skylab
) {
667 if (pixelCheckMonster(sk
.x
, sk
.y
, gameData
["sky"], 256*sk
.frame
)) return true;
673 void checkSwitch () nothrow @nogc {
674 foreach (ref ss
; switches
) {
675 if (ss
.s
/*tate*/ != 1) continue;
678 if (x
+7 >= willyX
&& y
+7 >= willyY
&& x
< willyX
+8 && y
< willyY
+16) ss
.s
/*tate*/ = 1+1;
682 bool checkSPG () const nothrow @nogc {
683 foreach (const ref sp
; spg
) {
686 if (x
< 0 || y
< 0) break;
687 if (x
+7 >= willyX
&& x
< willyX
+8 && y
+7 >= willyY
&& y
< willyY
+16) return true;
693 __gshared Room
[] gameRooms
;
696 // ////////////////////////////////////////////////////////////////////////// //
697 __gshared
ubyte[][string
] gameData
;
700 // ////////////////////////////////////////////////////////////////////////// //
702 VColor
palcol (ubyte c
) {
703 static VColor
[256] palette
;
704 static bool palset
= false;
706 //palette = gameData["palmain"][];
707 auto pp
= gameData
["palmain"];
708 foreach (immutable cc
; 0..256) {
709 ubyte r
= cast(ubyte)(255*pp
[cc
*3+0]/63);
710 ubyte g
= cast(ubyte)(255*pp
[cc
*3+1]/63);
711 ubyte b
= cast(ubyte)(255*pp
[cc
*3+2]/63);
712 palette
[cc
] = rgbcol(r
, g
, b
);
720 // ////////////////////////////////////////////////////////////////////////// //
721 X11Image
buildImageMasked (const(ubyte)[] darr
, int w
, int h
) {
722 assert(w
> 0 && h
> 0);
724 auto img
= new X11Image(w
, h
);
725 foreach (immutable y
; 0..h
) {
726 foreach (immutable x
; 0..w
) {
727 ubyte c
= darr
[dpos
++];
729 img
.setPixel(x
, y
, palcol(c
));
731 img
.setPixel(x
, y
, Transparent
);
739 X11Image
buildImageMaskedShiny (const(ubyte)[] darr
, int brightness
, int w
, int h
) {
740 assert(w
> 0 && h
> 0);
742 auto img
= new X11Image(w
, h
);
743 foreach (immutable y
; 0..h
) {
744 foreach (immutable x
; 0..w
) {
745 ubyte c
= darr
[dpos
++];
747 int b
= (c
&15)-brightness
;
748 if (b
< 0) b
= 0; else if (b
> 15) b
= 15;
749 img
.setPixel(x
, y
, palcol(cast(ubyte)((c
&240)|b
)));
751 img
.setPixel(x
, y
, Transparent
);
759 X11Image
buildImageMaskedInk (const(ubyte)[] darr
, int ink
, int w
, int h
) {
760 assert(w
> 0 && h
> 0);
762 auto img
= new X11Image(w
, h
);
763 if (ink
< 0) ink
= 0;
765 foreach (immutable y
; 0..h
) {
766 foreach (immutable x
; 0..w
) {
767 ubyte c
= darr
[dpos
++];
769 img
.setPixel(x
, y
, palcol(cast(ubyte)(c
+ink
)));
771 img
.setPixel(x
, y
, Transparent
);
779 // ////////////////////////////////////////////////////////////////////////// //
780 X11Image
buildImageBrick (const(ubyte)[] darr
, int ink
, int paper
, int skipy
=0) {
782 enum { w
= 8, h
= 8 }
784 auto img
= new X11Image(w
, h
);
786 foreach (immutable y
; 0..h
) {
787 foreach (immutable x
; 0..w
) {
788 ubyte c
= (y
>= skipy ? darr
[dpos
++] : 0);
789 img
.setPixel(x
, y
, palcol(cast(ubyte)(c ? ink
+c
: paper
)));
796 // ////////////////////////////////////////////////////////////////////////// //
799 X11Image
[] brickCache
;
800 X11Image
[] convCache
;
801 X11Image
[] exitCache
;
803 X11Image
[][] monsterCache
;
804 int[][] monsterOfsCache
;
805 X11Image
[] eugeneSpr
;
807 X11Image
[] skylabSpr
;
808 X11Image
[] switchesSpr
;
812 // ////////////////////////////////////////////////////////////////////////// //
813 void buildWilly (in ref Room curRoom
) {
814 auto img
= new X11Image(16*16, 16+16);
815 auto ww
= gameData
["willy"];
816 foreach (immutable f
; 0..16) {
817 foreach (immutable y
; 0..16) {
818 foreach (immutable x
; 0..16) {
819 ubyte c
= ww
[f
*256+y
*16+x
];
821 img
.setPixel(f
*16+x
, y
, Transparent
);
823 img
.setPixel(f
*16+x
, y
, palcol(c
));
825 img
.setPixel(f
*16+x
, y
+16, palcol(15));
834 void buildBrickImages (in ref Room curRoom
) {
835 auto buildBrickImage (in ref Room
.CommonGfx brk
, int skipy
=0) {
836 return buildImageBrick(gameData
["blocks"][brk
.gfx
*64..$], brk
.ink
, curRoom
.paper
, skipy
);
839 foreach (immutable f
; 0..8) {
842 //case 0: case 7: img = buildBrickImage(curRoom.wall, 16); break;
843 case 1: img
= buildBrickImage(curRoom
.platforms
[0]); break;
844 case 2: img
= buildBrickImage(curRoom
.platforms
[1]); break;
845 case 3: img
= buildBrickImage(curRoom
.wall
); break;
846 //case 4: img = buildBrickImage(curRoom.crumb); break;
847 case 5: img
= buildBrickImage(curRoom
.deadlies
[0]); break;
848 case 6: img
= buildBrickImage(curRoom
.deadlies
[1]); break;
853 foreach (immutable f
; 0..9) brickCache
~= buildBrickImage(curRoom
.crumb
, f
);
857 void buildConvImages (in ref Room curRoom
) {
859 auto conv
= &curRoom
.conveyor
;
860 if (conv
.y
<= 0 || conv
.l
< 1) return;
861 foreach (immutable f
; 0..4) {
862 convCache
~= buildImageBrick(gameData
["conv"][conv
.gfx
*256+f
*64..$], conv
.ink
, curRoom
.paper
);
867 void buildExitImages (in ref Room curRoom
) {
869 exitCache
~= buildImageMasked(gameData
["exits"][curRoom
.exit
.gfx
*256..$], 16, 16);
870 foreach (immutable f
; 0..16) exitCache
~= buildImageMaskedShiny(gameData
["exits"][curRoom
.exit
.gfx
*256..$], f
, 16, 16);
874 void buildKeysImages (in ref Room curRoom
) {
876 foreach (immutable f
; 0..16) keyCache
~= buildImageMaskedShiny(gameData
["keys"][curRoom
.keys
.gfx
*64..$], f
, 8, 8);
880 void buildMonsterImages (in ref Room curRoom
) {
882 monsterOfsCache
= null;
883 //auto l = curRoom.enemies.length;
884 foreach (immutable f
, const ref m
; curRoom
.enemies
) {
885 if (m
.x
< 0 || m
.y
< 0) {
886 monsterOfsCache
~= null;
887 monsterCache
~= null;
893 foreach (immutable c
; 0..4) {
894 auto n
= (m
.gfx
+c
)*256;
896 r
~= buildImageMaskedInk(gameData
["vrobo"][n
..$], m
.ink
-1, 16, 16);
899 foreach (immutable c
; 0..(m
.anim
>>1)+1) {
900 auto n
= (m
.gfx
+c
)*256;
902 r
~= buildImageMaskedInk(gameData
["hrobo"][n
..$], m
.ink
-1, 16, 16);
905 r
~= buildImageMaskedInk(gameData
["hrobo"][n
..$], m
.ink
-1, 16, 16);
908 monsterOfsCache
~= cc
;
914 void buildEugeneImages (in ref Room curRoom
) {
916 for (int f
= 0; f
<= 256; f
+= 256) {
917 foreach (immutable c
; 0..8) {
918 eugeneSpr
~= buildImageMaskedInk(gameData
["eugene"][f
..$], c
, 16, 16);
924 void buildKongImages (in ref Room curRoom
) {
926 foreach (immutable f
; 0..4) {
927 foreach (immutable c
; 0..8) {
928 kongSpr
~= buildImageMaskedInk(gameData
["kong"][f
*256..$], c
, 16, 16);
934 void buildSkyLabImages (in ref Room curRoom
) {
936 foreach (immutable f
; 0..8) {
937 foreach (immutable c
; 0..8) {
938 skylabSpr
~= buildImageMaskedInk(gameData
["sky"][f
*256..$], c
, 16, 16);
944 void buildSwitchImages (in ref Room curRoom
) {
946 foreach (immutable f
; 0..2) {
947 switchesSpr
~= buildImageBrick(gameData
["switches"][f
*64..$], curRoom
.platforms
[1].ink
, curRoom
.paper
);
952 // ////////////////////////////////////////////////////////////////////////// //
953 void blitTo (X11Image src
, X11Image dst
, int x
, int y
, int repx
=1) {
954 foreach (immutable r
; 0..repx
) {
955 foreach (immutable dy
; 0..src
.height
) {
956 foreach (immutable dx
; 0..src
.width
) {
959 if (xx
< 0 || yy
< 0 || xx
>= dst
.width || yy
>= dst
.height
) continue;
960 auto c
= src
.getPixel(dx
, dy
);
961 if (isTransparent(c
)) continue;
962 dst
.setPixel(xx
, yy
, c
);
970 void blitTo (X11Image src
, int x0
, int y0
, int ww
, int hh
, X11Image dst
, int x
, int y
) {
971 foreach (immutable dy
; 0..ww
) {
972 foreach (immutable dx
; 0..hh
) {
975 if (xx
< 0 || yy
< 0 || xx
>= dst
.width || yy
>= dst
.height
) continue;
976 auto c
= src
.getPixel(x0
+dx
, y0
+dy
);
977 if (isTransparent(c
)) continue;
978 dst
.setPixel(xx
, yy
, c
);
984 X11Image
drawRoom (in ref Room curRoom
, X11Image img
=null) {
985 if (img
is null) img
= new X11Image(8*Room
.Width
, 8*Room
.Height
);
987 foreach (immutable y
; 0..img
.height
) {
988 foreach (immutable x
; 0..img
.width
) {
989 img
.setPixel(x
, y
, palcol(curRoom
.paper
));
992 foreach (immutable y
; 0..Room
.Height
) {
993 foreach (immutable x
; 0..Room
.Width
) {
994 ubyte blk
= curRoom
.map
[pos
++];
995 if (blk
< 1 || blk
== 7) continue;
996 if (blk
== 4) blk
= 8;
997 brickCache
[blk
].blitTo(img
, x
*8, y
*8);
1000 if (curRoom
.roomIdx
== 19) {
1001 finalSpr
.blitTo(img
, 0, 0);
1002 sunSpr
.blitTo(img
, 60, 32);
1005 void drawConveyor () {
1006 auto conv
= &curRoom
.conveyor
;
1007 if (conv
.l
< 1) return;
1010 convCache
[conv
.frame
].blitTo(img
, conv
.x
, conv
.y
, conv
.l
);
1015 if (curRoom
.keys
.info
.length
!= 0) {
1018 br
= curRoom
.frameNo
&31;
1019 if (br
> 15) br
= 31-br
;
1022 exitCache
[br
].blitTo(img
, curRoom
.exit
.x
, curRoom
.exit
.y
);
1026 auto ff
= curRoom
.frameNo
%16;
1027 if (ff
>= 8) ff
= 15-ff
;
1028 auto keyimg
= keyCache
[ff
];
1029 foreach_reverse (const ref ki
; curRoom
.keys
.info
) {
1030 if (ki
.x
< 0 || ki
.y
< 0) continue;
1031 //eraseRect(ki.x, ki.y, 8, 8);
1032 if (!ki
.s
) continue;
1033 keyimg
.blitTo(img
, ki
.x
&248, ki
.y
);
1037 void drawMonsters () {
1038 foreach (immutable f
, const ref m
; curRoom
.enemies
) {
1039 if (m
.x
< 0 || m
.y
< 0) continue;
1040 auto slist
= monsterCache
[f
];
1043 //for (var c = 0; c <= m.anim; c++) r.push(buildImageMaskedInk(me.data.vrobo, (m.gfx+c)*256, m.ink-1, 16, 16, false));
1044 slist
[m
.anim
].blitTo(img
, x
, m
.y
);
1046 auto sidx
= ((x
&m
.anim
)&0xfe)+m
.d
/*ir*/;
1047 if (sidx
< slist
.length
) {
1048 slist
[sidx
].blitTo(img
, x
&248, m
.y
);
1050 writeln("monster #", f
, " is fucked: sidx=", sidx
, "; max=", slist
.length
);
1054 if (curRoom
.eugene
.x
>= 0) {
1056 eugeneSpr
[curLSet
*8+curRoom
.eugene
.ink
].blitTo(img
, curRoom
.eugene
.x
, curRoom
.eugene
.y
);
1058 if (curRoom
.kong
.x
>= 0) {
1059 kongSpr
[curRoom
.kong
.frame
*8+curRoom
.kong
.ink
].blitTo(img
, curRoom
.kong
.x
, curRoom
.kong
.y
);
1061 if (curRoom
.skylab
.length
) {
1062 foreach (const ref sk
; curRoom
.skylab
) {
1063 skylabSpr
[sk
.frame
*8+sk
.ink
].blitTo(img
, sk
.x
, sk
.y
);
1068 void drawSwitch () {
1069 foreach (const ref ss
; curRoom
.switches
) {
1070 if (ss
.s
== 0) continue;
1071 switchesSpr
[ss
.s
-1].blitTo(img
, ss
.x
, ss
.y
);
1076 foreach (const ref sp
; curRoom
.spg
) {
1079 if (x
< 0 || y
< 0) break;
1080 //ctx.fillStyle = mainPal[noerase?6:curRoom.paper];
1081 //ctx.fillRect(x, y, 8, 8);
1082 foreach (immutable dy
; 0..8) {
1083 foreach (immutable dx
; 0..8) {
1084 img
.setPixel(x
+dx
, y
+dy
, palcol(6));
1091 auto willyPos
= (curRoom
.willyX
&15)>>1;
1092 if (curRoom
.willyDir
< 0) willyPos
+= 8;
1094 //if (DEBUG_COLDET && dbgColDetected) wy = 16;
1095 willySpr
.blitTo(willyPos
*16, wy
, 16, 16, img
, curRoom
.willyX
&248, curRoom
.willyY
);
1102 if (curRoom
.keys
.info
.length
) drawExit();
1104 if (!curRoom
.keys
.info
.length
) drawExit();
1111 // ////////////////////////////////////////////////////////////////////////// //
1112 void main (string
[] args
) {
1113 gameRooms
.txtunser(VFile("levelset0.js"));
1114 foreach (immutable idx
, ref room
; gameRooms
) room
.roomIdx
= cast(int)idx
;
1115 gameData
.txtunser(VFile("data.js"));
1117 finalSpr
= buildImageMasked(gameData
["final"], 256, 64);
1118 sunSpr
= buildImageMasked(gameData
["sun"], 24, 16);
1123 void loadRoom (int idx
) {
1124 curRoom
= gameRooms
[idx
];
1126 buildWilly(curRoom
);
1127 buildBrickImages(curRoom
);
1128 buildConvImages(curRoom
);
1129 buildExitImages(curRoom
);
1130 buildKeysImages(curRoom
);
1131 buildMonsterImages(curRoom
);
1132 buildEugeneImages(curRoom
);
1133 buildKongImages(curRoom
);
1134 buildSkyLabImages(curRoom
);
1135 buildSwitchImages(curRoom
);
1141 auto sdwin
= new SimpleWindow(vbufW
, vbufH
, "Manic Miner", OpenGlOptions
.no
, Resizablity
.fixedSize
);
1144 Image vbimg
= new Image(vbufW
, vbufH
);
1148 sdwin
.eventLoop(1000/20,
1151 if (sdwin
.closed
) return;
1153 if (!curRoom
.willyDead
) {
1155 if (!curRoom
.willyDead
&& curRoom
.checkExit()) {
1156 if (gameRooms
.length
-curRoom
.roomIdx
> 1) {
1157 loadRoom(curRoom
.roomIdx
+1);
1158 curRoom
.willyDead
= true;
1162 //clear(rgbcol(255, 127, 0));
1165 roomImg
= drawRoom(curRoom
, roomImg
);
1166 roomImg
.blit2xTV(0, 0);
1169 drawStr(10, vbufH
-10, curRoom
.title
, rgbcol(255, 255, 0));
1173 auto painter
= sdwin
.draw();
1174 painter
.drawImage(Point(0, 0), vbimg
);
1175 //map.drawMap(painter, player);
1179 delegate (KeyEvent evt
) {
1181 case Key
.Left
: curRoom
.kLeft
= evt
.pressed
; break;
1182 case Key
.Right
: curRoom
.kRight
= evt
.pressed
; break;
1183 case Key
.Up
: curRoom
.kUp
= evt
.pressed
; break;
1184 case Key
.Down
: curRoom
.kDown
= evt
.pressed
; break;
1185 case Key
.Ctrl
: curRoom
.kJump
= evt
.pressed
; break;
1187 if (evt
.pressed
&& curRoom
.willyDead
) {
1188 loadRoom(curRoom
.roomIdx
);
1191 case Key
.Q
: case Key
.Escape
: doQuit
= true; break;
1193 if (!evt
.pressed
) break;
1194 if (curRoom
.roomIdx
> 0) {
1195 loadRoom(curRoom
.roomIdx
-1);
1199 if (!evt
.pressed
) break;
1200 if (gameRooms
.length
-curRoom
.roomIdx
> 1) {
1201 loadRoom(curRoom
.roomIdx
+1);
1206 if (doQuit
) { sdwin
.close(); return; }
1209 delegate (MouseEvent evt
) {
1212 delegate (dchar ch
) {