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
;
16 __gshared string gameLevelsName
;
19 public void loadGameData () {
20 //gameData.txtunser(VFile("data/data.js"));
22 foreach (const ref de; vfsAllFiles()) {
23 auto fi
= VFile(de.name
);
24 if (fi
.size
== 0 || fi
.size
> 1024*1024*8) continue;
25 auto data
= new ubyte[](cast(uint)fi
.size
);
26 fi
.rawReadExact(data
[]);
27 gameData
[de.name
.idup
] = data
;
30 //gameRooms.txtunser(VFile("data/levelset0.js"));
31 //foreach (immutable idx, ref room; gameRooms) room.roomIdx = cast(int)idx;
32 gameRooms
.length
= 20;
35 auto fi
= VFile("levels");
36 fi
.rawReadExact(sign
[]);
37 if (sign
!= "MANIC\x0d\x0a\x1a") throw new Exception("invalid levels");
39 fi
.rawReadExact(lname
[]);
41 foreach (immutable idx
, char ch
; lname
[]) {
45 while (nn
.length
&& nn
[$-1] <= ' ') nn
= nn
[0..$-1];
46 gameLevelsName
= nn
.idup
;
47 foreach (immutable ridx
; 0..20) {
48 gameRooms
[ridx
].roomIdx
= cast(int)ridx
;
49 gameRooms
[ridx
].load(fi
);
55 // ////////////////////////////////////////////////////////////////////////// //
57 VColor
palcol (ubyte c
) {
58 static VColor
[256] palette
;
59 static bool palset
= false;
61 auto pp
= gameData
["palmain"];
62 foreach (immutable cc
; 0..256) {
63 ubyte r
= cast(ubyte)(255*pp
[cc
*3+0]/63);
64 ubyte g
= cast(ubyte)(255*pp
[cc
*3+1]/63);
65 ubyte b
= cast(ubyte)(255*pp
[cc
*3+2]/63);
66 palette
[cc
] = rgbcol(r
, g
, b
);
74 // ////////////////////////////////////////////////////////////////////////// //
75 private X11Image
buildImageMasked (const(ubyte)[] darr
, int w
, int h
) {
76 assert(w
> 0 && h
> 0);
78 auto img
= new X11Image(w
, h
);
79 foreach (immutable y
; 0..h
) {
80 foreach (immutable x
; 0..w
) {
81 ubyte c
= darr
[dpos
++];
83 img
.setPixel(x
, y
, palcol(c
));
85 img
.setPixel(x
, y
, Transparent
);
93 private X11Image
buildImageMaskedShiny (const(ubyte)[] darr
, int brightness
, int w
, int h
) {
94 assert(w
> 0 && h
> 0);
96 auto img
= new X11Image(w
, h
);
97 foreach (immutable y
; 0..h
) {
98 foreach (immutable x
; 0..w
) {
99 ubyte c
= darr
[dpos
++];
101 int b
= (c
&15)-brightness
;
102 if (b
< 0) b
= 0; else if (b
> 15) b
= 15;
103 img
.setPixel(x
, y
, palcol(cast(ubyte)((c
&240)|b
)));
105 img
.setPixel(x
, y
, Transparent
);
113 private X11Image
buildImageMaskedInk (const(ubyte)[] darr
, int ink
, int w
, int h
) {
114 assert(w
> 0 && h
> 0);
116 auto img
= new X11Image(w
, h
);
117 if (ink
< 0) ink
= 0;
119 foreach (immutable y
; 0..h
) {
120 foreach (immutable x
; 0..w
) {
121 ubyte c
= darr
[dpos
++];
123 img
.setPixel(x
, y
, palcol(cast(ubyte)(c
+ink
)));
125 img
.setPixel(x
, y
, Transparent
);
133 // ////////////////////////////////////////////////////////////////////////// //
134 private X11Image
buildImageBrick (const(ubyte)[] darr
, int ink
, int paper
, int skipy
=0) {
136 enum { w
= 8, h
= 8 }
138 auto img
= new X11Image(w
, h
);
140 foreach (immutable y
; 0..h
) {
141 foreach (immutable x
; 0..w
) {
142 ubyte c
= (y
>= skipy ? darr
[dpos
++] : 0);
143 img
.setPixel(x
, y
, palcol(cast(ubyte)(c ? ink
+c
: paper
)));
150 // ////////////////////////////////////////////////////////////////////////// //
151 // willy jump offsets
153 static immutable int[19] willyJ
= [0, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4];
155 struct SkyLabXY
{ int x
, y
; }
156 static immutable SkyLabXY
[4][3] skylabCoords
= [
157 [{x
: 8,y
:0}, {x
: 72,y
:0}, {x
:136,y
:0}, {x
:200,y
:0}],
158 [{x
:40,y
:0}, {x
:104,y
:0}, {x
:168,y
:0}, {x
:232,y
:0}],
159 [{x
:24,y
:0}, {x
: 88,y
:0}, {x
:152,y
:0}, {x
:216,y
:0}]
163 // ////////////////////////////////////////////////////////////////////////// //
164 // suitable to "unjson"
166 enum { Width
= 32, Height
= 16 }
167 static struct WillyStart
{ int x
; int y
; int sd
; }
168 static struct ExitGfx
{ ubyte gfx
; int x
; int y
; }
169 static struct CommonGfx
{ ubyte gfx
; ubyte ink
; ubyte paper
; }
170 static struct Conveyor
{ int x
; int y
; int d
; int l
; ubyte gfx
; ubyte ink
; ubyte paper
; @SRZIgnore int frame
; }
171 static struct KeyPos
{ int x
; int y
; int s
; }
172 static struct Key
{ ubyte gfx
; KeyPos
[] info
; }
173 static struct SwitchPos
{ int x
; int y
; int s
; }
174 static struct Enemy
{ bool vert
; ushort 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
; }
175 static struct SkyLabEnemy
{ int p
, s
; ubyte ink
; int max
; int m
; int frame
; }
176 static struct SkyLabXY
{ int x
, y
, frame
; ubyte ink
; int m
; int s
; int max
; int p
; }
177 static struct SPGRay
{ int x
, y
; }
178 @SRZIgnore int roomIdx
;
183 ubyte[Height
*Width
] map
; // 16 rows, 32 cols
184 ubyte border
, ink
, paper
;
185 CommonGfx
[] platforms
;
188 CommonGfx
[] deadlies
;
191 SwitchPos
[] switches
;
194 @SRZIgnore Enemy eugene
;
196 @SRZIgnore int holeLen
, holeY
;
197 @SRZIgnore Enemy kong
;
199 @SRZIgnore SkyLabEnemy
[] skylabEnemies
;
200 @SRZIgnore SkyLabXY
[] skylab
;
202 @SRZIgnore SPGRay
[] spg
; // solar power generator
205 int willyX
, willyY
, willyDir
, willyJump
, willyJumpDir
;
206 int willyLastMoveDir
, willyFall
, willyConv
, willyStall
;
207 public bool willyDead
;
211 bool kLeft
, kRight
, kJump
, kUp
, kDown
;
212 bool kLeftDown
, kRightDown
, kJumpDown
, kUpDown
, kDownDown
;
218 X11Image
[] brickCache
;
219 X11Image
[] convCache
;
220 X11Image
[] exitCache
;
222 X11Image
[][] monsterCache
;
223 int[][] monsterOfsCache
;
224 X11Image
[] eugeneSpr
;
226 X11Image
[] skylabSpr
;
227 X11Image
[] switchesSpr
;
231 ubyte saveKeyState () {
233 ((kLeftDown ?
1 : 0)<<0)|
234 ((kRightDown ?
1 : 0)<<1)|
235 ((kUpDown ?
1 : 0)<<2)|
236 ((kDownDown ?
1 : 0)<<3)|
237 ((kJumpDown ?
1 : 0)<<4)|
241 void restoreKeyState (ubyte b
) {
242 keyLeft
= ((b
&(1<<0)) != 0);
243 keyRight
= ((b
&(1<<1)) != 0);
244 keyUp
= ((b
&(1<<2)) != 0);
245 keyDown
= ((b
&(1<<3)) != 0);
246 keyJump
= ((b
&(1<<4)) != 0);
253 willyDir
= (willy
.sd
> 0 ?
-1 : 1);
254 willyJump
= willyFall
= willyConv
= willyStall
= 0;
256 willyLastMoveDir
= 0;
257 kLeft
= kRight
= kJump
= kUp
= kDown
= false;
258 kLeftDown
= kRightDown
= kJumpDown
= kUpDown
= kDownDown
= false;
266 buildMonsterImages();
271 finalSpr
= buildImageMasked(gameData
["final"], 256, 64);
272 sunSpr
= buildImageMasked(gameData
["sun"], 24, 16);
277 foreach (ref c
; map
) {
278 if (c
== 4) c
= 8; // 'crumb'
279 else if (c
< 1 || c
> 7) c
= 0; // emptyness
284 foreach (const ref ki
; keys
.info
) {
285 if (ki
.s
== 0) continue;
290 platforms
= platforms
.dup
;
291 deadlies
= deadlies
.dup
;
292 switches
= switches
.dup
;
293 enemies
= enemies
.dup
;
297 Enemy
[] a
= [{ x
:120, y
:1, d
/*ir*/:0, min
:1, max
:87, ink
:6 }];
301 if (roomIdx
== 7 || roomIdx
== 11) {
302 Enemy
[] a
= [{ x
:120, y
:0, max
:104, frame
:0, ink
:2, m
:0 }];
311 {p
:0, s
:4, ink
:6, max
:72, m
:0, frame
:0},
312 {p
:2, s
:3, ink
:5, max
:56, m
:0, frame
:0},
313 {p
:1, s
:1, ink
:4, max
:32, m
:0, frame
:0}
315 skylabEnemies
= a
.dup
;
316 foreach (immutable f
, const ref se
; skylabEnemies
) {
318 skylabCoords
[f
][se
.p
].x
, skylabCoords
[f
][se
.p
].y
,
319 se
.frame
, se
.ink
, se
.m
, se
.s
, se
.max
, se
.p
,
323 // Solar Power Generator?
324 if (roomIdx
== 18) buildSPG();
327 void keyLeft (bool pressed
) { if (pressed
) kLeft
= true; kLeftDown
= pressed
; }
328 void keyRight (bool pressed
) { if (pressed
) kRight
= true; kRightDown
= pressed
; }
329 void keyUp (bool pressed
) { if (pressed
) kUp
= true; kUpDown
= pressed
; }
330 void keyDown (bool pressed
) { if (pressed
) kDown
= true; kDownDown
= pressed
; }
331 void keyJump (bool pressed
) { if (pressed
) kJump
= true; kJumpDown
= pressed
; }
334 kLeft
= kLeft||kLeftDown
;
335 kRight
= kRight||kRightDown
;
337 kDown
= kDown||kDownDown
;
338 kJump
= kJump||kJumpDown
;
339 scope(exit
) kLeft
= kRight
= kJump
= kUp
= kDown
= false;
341 conveyor
.frame
+= (conveyor
.d ?
-1 : 1);
342 if (conveyor
.frame
< 0) conveyor
.frame
= 3; else if (conveyor
.frame
> 3) conveyor
.frame
= 0;
344 if (!willyJump
) stepCrumb();
352 void cheatRemoveKeys () { keys
.info
= null; }
354 // ////////////////////////////////////////////////////////////////////// //
358 ubyte getBlockAt (int x
, int y
, bool simplify
=false) const nothrow @nogc {
361 if (x
< 0 || y
< 0 || x
> 31 || y
> 15) return 0; // empty
362 ubyte b
= map
[y
*32+x
];
365 else if (b
> 7) b
= 4;
366 else if (b
== 6) b
= 5;
367 else if (b
== 2) b
= 1;
372 bool checkExit () const nothrow @nogc {
373 if (keys
.info
.length
!= 0) return false;
376 return (willyX
>= x
-2 && willyX
+10 <= x
+18 && willyY
>= y
-5 && willyY
+16 <= y
+22);
379 bool checkMonsters () const nothrow {
380 foreach (immutable f
, const ref m
; enemies
) {
381 auto cc
= monsterOfsCache
[f
];
382 if (m
.x
< 0 || m
.y
< 0) continue;
384 if (pixelCheckMonster(m
.x
, m
.y
, gameData
["vrobo"], cc
[m
.anim
])) return true;
386 if (pixelCheckMonster(m
.x
&248, m
.y
, gameData
["hrobo"], cc
[((m
.x
&m
.anim
)&0xfe)+m
.d
/*ir*/])) return true;
392 if (pixelCheckMonster(eugene
.x
, eugene
.y
, gameData
["eugene"], 256*curLSet
)) return true;
396 if (pixelCheckMonster(kong
.x
, kong
.y
, gameData
["kong"], 256*kong
.frame
)) return true;
400 foreach (const ref sk
; skylab
) {
401 if (pixelCheckMonster(sk
.x
, sk
.y
, gameData
["sky"], 256*sk
.frame
)) return true;
407 void checkSwitch () nothrow @nogc {
408 foreach (ref ss
; switches
) {
409 if (ss
.s
/*tate*/ != 1) continue;
412 if (x
+7 >= willyX
&& y
+7 >= willyY
&& x
< willyX
+8 && y
< willyY
+16) ss
.s
/*tate*/ = 1+1;
416 bool checkSPG () const nothrow @nogc {
417 foreach (const ref sp
; spg
) {
420 if (x
< 0 || y
< 0) break;
421 if (x
+7 >= willyX
&& x
< willyX
+8 && y
+7 >= willyY
&& y
< willyY
+16) return true;
426 X11Image
draw (X11Image img
=null) {
427 if (img
is null) img
= new X11Image(8*Room
.Width
, 8*Room
.Height
);
429 foreach (immutable y
; 0..img
.height
) {
430 foreach (immutable x
; 0..img
.width
) {
431 img
.setPixel(x
, y
, palcol(this.paper
));
434 foreach (immutable y
; 0..Room
.Height
) {
435 foreach (immutable x
; 0..Room
.Width
) {
436 ubyte blk
= this.map
[pos
++];
437 if (blk
< 1 || blk
== 7) continue;
438 if (blk
== 4) blk
= 8;
439 if (blk
< brickCache
.length
&& brickCache
[blk
] !is null) {
440 brickCache
[blk
].blitTo(img
, x
*8, y
*8);
444 if (this.roomIdx
== 19) {
445 finalSpr
.blitTo(img
, 0, 0);
446 sunSpr
.blitTo(img
, 60, 32);
449 void drawConveyor () {
450 auto conv
= &this.conveyor
;
451 if (conv
.l
< 1) return;
454 convCache
[conv
.frame
].blitTo(img
, conv
.x
, conv
.y
, conv
.l
);
459 if (this.keys
.info
.length
!= 0) {
462 br
= this.frameNo
&31;
463 if (br
> 15) br
= 31-br
;
466 exitCache
[br
].blitTo(img
, this.exit
.x
, this.exit
.y
);
470 auto ff
= this.frameNo
%16;
471 if (ff
>= 8) ff
= 15-ff
;
472 auto keyimg
= keyCache
[ff
];
473 foreach_reverse (const ref ki
; this.keys
.info
) {
474 if (ki
.x
< 0 || ki
.y
< 0) continue;
475 //eraseRect(ki.x, ki.y, 8, 8);
477 keyimg
.blitTo(img
, ki
.x
&248, ki
.y
);
481 void drawMonsters () {
482 foreach (immutable f
, const ref m
; this.enemies
) {
483 if (m
.x
< 0 || m
.y
< 0) continue;
484 auto slist
= monsterCache
[f
];
487 //for (var c = 0; c <= m.anim; c++) r.push(buildImageMaskedInk(me.data.vrobo, (m.gfx+c)*256, m.ink-1, 16, 16, false));
488 slist
[m
.anim
].blitTo(img
, x
, m
.y
);
490 auto sidx
= ((x
&m
.anim
)&0xfe)+m
.d
/*ir*/;
491 if (sidx
< slist
.length
) {
492 slist
[sidx
].blitTo(img
, x
&248, m
.y
);
494 import core
.stdc
.stdio
: stderr
, fprintf
;
495 stderr
.fprintf("monster #%u is fucked: sidx=%u; max=%u", cast(uint)f
, cast(uint)sidx
, cast(uint)slist
.length
);
499 if (this.eugene
.x
>= 0) {
501 eugeneSpr
[curLSet
*8+this.eugene
.ink
].blitTo(img
, this.eugene
.x
, this.eugene
.y
);
503 if (this.kong
.x
>= 0) {
504 kongSpr
[this.kong
.frame
*8+this.kong
.ink
].blitTo(img
, this.kong
.x
, this.kong
.y
);
506 if (this.skylab
.length
) {
507 foreach (const ref sk
; this.skylab
) {
508 skylabSpr
[sk
.frame
*8+sk
.ink
].blitTo(img
, sk
.x
, sk
.y
);
514 foreach (const ref ss
; this.switches
) {
515 if (ss
.s
== 0) continue;
516 switchesSpr
[ss
.s
-1].blitTo(img
, ss
.x
, ss
.y
);
521 foreach (const ref sp
; this.spg
) {
524 if (x
< 0 || y
< 0) break;
525 //ctx.fillStyle = mainPal[noerase?6:this.paper];
526 //ctx.fillRect(x, y, 8, 8);
527 foreach (immutable dy
; 0..8) {
528 foreach (immutable dx
; 0..8) {
529 img
.setPixel(x
+dx
, y
+dy
, palcol(6));
536 auto willyPos
= (this.willyX
&15)>>1;
537 if (this.willyDir
< 0) willyPos
+= 8;
540 if (this.checkMonsters()) wy
= 16;
541 willySpr
.blitTo(willyPos
*16, wy
, 16, 16, img
, this.willyX
&248, this.willyY
);
543 willySpr
.blitTo(willyPos
*16, 0, 16, 16, img
, this.willyX
&248, this.willyY
);
559 // ////////////////////////////////////////////////////////////////////// //
560 // pixel-perfect collision detector
561 // fully stolen from Andy's sources %-)
562 bool pixelCheckMonster (int rx
, int ry
, const(ubyte)[] darr
, int dpos
=0) const nothrow {
563 static ubyte[256] mpcGrid
;
569 if (rx
< -15 || rx
> 15 || ry
< -15 || ry
> 15) return false;
572 if (rx
< 0) { x
= 0; rx
= -rx
; w
= 16-rx
; } else { x
= rx
; rx
= 0; w
= 16-x
; }
573 // partial plot monster
574 for (int y
= ry
+15; y
>= ry
; --y
) {
575 if (y
>= 0 && y
< 16) {
577 int rp
= dpos
+(y
-ry
)*16+rx
;
578 for (int dx
= 0; dx
< w
; ++dx
, ++gp
, ++rp
) mpcGrid
[gp
] = darr
[rp
];
581 auto warr
= gameData
["willy"];
582 int wptr
= ((willyX
&15)>>1)*256+(willyDir
< 0 ?
2048 : 0);
583 // check for collision
585 for (x
= 255; x
>= 0; --x
, ++wptr
, ++mp
) if (warr
[wptr
] && mpcGrid
[mp
]) return true;
589 // ////////////////////////////////////////////////////////////////////// //
590 bool isWillyFalling () const nothrow @nogc {
591 if (willyY
&7) return true;
592 for (int dx
= 0; dx
<= 8; dx
+= 8) {
593 ubyte b
= getBlockAt(willyX
+dx
, willyY
+16, true);
594 if (b
> 0 && b
!= 5) return false;
599 bool isWillyInDeadly () const nothrow @nogc {
600 for (int dx
= 0; dx
<= 8; dx
+= 8) {
601 for (int dy
= 0; dy
<= 16; dy
+= 8) {
602 if (getBlockAt(willyX
+dx
, willyY
+dy
, true) == 5) return true;
608 bool isWillyOnConv () const nothrow @nogc {
609 if (willyY
&7) return false;
610 ubyte b0
= getBlockAt(willyX
, willyY
+16);
611 ubyte b1
= getBlockAt(willyX
+8, willyY
+16);
612 return (b0
== 7 || b1
== 7);
615 // ////////////////////////////////////////////////////////////////////// //
616 // called on each frame
617 void buildSPG (bool forced
=false) {
618 if (!forced
&& roomIdx
!= 18) {
623 bool spgCheckMonster (int x
, int y
) {
626 foreach (const ref cm
; enemies
) {
629 if (x
+7 >= mx
&& x
< mx
+15 && y
+7 >= my
&& y
< my
+15) return true;
636 int dir
= 0, idx
= 0;
640 spg
.assumeSafeAppend
;
643 void addXY (int x
, int y
) {
645 while (spg
.length
< idx
) spg
~= SPGRay(-1, -1);
651 ubyte blockhit
= map
[y
*32+x
];
652 bool robohit
= spgCheckMonster(x
, y
);
654 if (blockhit
&& robohit
) {
657 } else if (!blockhit
&& robohit
) {
658 if (idx
&& spg
[idx
-1].x
== x
&& spg
[idx
-1].y
== y
) {
659 spg
[idx
-1].x
= spg
[idx
-1].y
= -1;
665 } else if (!blockhit
&& !robohit
) {
667 } else if (blockhit
&& !robohit
) {
668 if (idx
&& spg
[idx
-1].x
== x
&& spg
[idx
-1].y
== y
) {
669 spg
[idx
-1].x
= spg
[idx
-1].y
= -1;
678 blockhit
= map
[y
*32+x
];
679 if (y
== 15 || blockhit
) done
= true;
682 blockhit
= map
[y
*32+x
];
683 if (x
== 0 || blockhit
) done
= true;
686 if (!dir
) { --x
; if (!x
) done
= true; }
687 else { ++y
; if (++y
== 15) done
= true; }
693 void stepMonsters () {
694 foreach (ref m
; enemies
) {
695 if (m
.x
< 0 || m
.y
< 0) continue;
698 auto spd
= m
.s
/*peed*/;
699 if (m
.d
/*ir*/ != 0) {
702 if (y
< m
.min || y
< 0) { y
+= spd
; m
.d
/*ir*/ = 0; }
706 if (y
> m
.max
) { y
-= spd
; m
.d
/*ir*/ = 1; }
709 m
.anim
= (m
.anim
+1)&3;
712 auto spd
= (2>>m
.s
/*peed*/);
713 if (m
.d
/*ir*/ != 0) {
716 if (x
< m
.min
) { m
.d
/*ir*/ = 0; x
+= spd
; }
720 if (x
> m
.max
+6) { m
.d
/*ir*/ = 1; x
-= spd
; }
727 if (keys
.info
.length
== 0) {
728 // no keys, Eugene tries to block the exit
729 eugene
.ink
= (eugene
.ink
+1)&7;
730 eugene
.y
+= (eugene
.y
< eugene
.max ?
1 : 0);
732 if (eugene
.d
/*ir*/ != 0) {
735 if (eugene
.y
< eugene
.min
) { eugene
.d
/*ir*/ = 0; ++eugene
.y
; }
739 if (eugene
.y
> eugene
.max
) { eugene
.d
/*ir*/ = 1; --eugene
.y
; }
745 switch (kong
.falling
) {
746 case 1: // just started
749 //eraseRect(16, 120, 16, 8);
759 if (kong
.y
>= kong
.max
) { kong
.x
= -1; score
+= 100; }
760 if (!kong
.delay
) { kong
.delay
= 4; kong
.frame
= ((kong
.frame
-1)&1)+2; } else --kong
.delay
;
763 if (!kong
.delay
) { kong
.delay
= 8; kong
.frame
= (kong
.frame
+1)&1; } else --kong
.delay
;
768 foreach (immutable idx
, ref sk
; skylab
) {
780 if (sk
.frame
== 7) sk
.m
= 2;
784 sk
.x
= skylabCoords
[idx
][sk
.p
].x
;
785 sk
.y
= skylabCoords
[idx
][sk
.p
].y
;
794 if (willyY
&7) return;
795 for (int f
= 0; f
<= 8; f
+= 8) {
798 ubyte b
= getBlockAt(x
, y
);
805 //eraseRect(x*8, y*8, 8, 8);
810 while (keys
.info
.length
) {
812 foreach (immutable idx
; 0..keys
.info
.length
) {
813 auto ki
= &keys
.info
[idx
];
817 if (kx
+7 >= willyX
&& kx
< willyX
+10 && ky
+7 >= willyY
&& ky
< willyY
+16) {
819 foreach (immutable c
; idx
+1..keys
.info
.length
) keys
.info
[c
-1] = keys
.info
[c
];
820 keys
.info
.length
-= 1;
831 if (holeLen
> 0 && switches
.length
&& switches
[0].s
/*tate*/ > 1) {
834 enemies
[1].max
+= 24;
846 if (kong
.x
>= 0 && !kong
.falling
&& switches
.length
> 1 && switches
[1].s
/*tate*/ > 1) kong
.falling
= 1;
849 void stepWillyActions () {
850 bool doWillyLeft () {
851 if (willyDir
> 0) { willyDir
= -1; return true; }
853 auto b0
= getBlockAt(xx
, willyY
);
854 auto b1
= getBlockAt(xx
, willyY
+8);
855 auto b2
= (willyY
&7 ?
getBlockAt(xx
, willyY
+16) : b1
);
856 if (b0
== 3 || b1
== 3 || b2
== 3) return false;
858 if (willyX
< 0) willyX
+= 240;
859 willyLastMoveDir
= -1;
863 bool doWillyRight () {
864 if (willyDir
< 0) { willyDir
= 1; return true; }
865 if (willyX
> 245) return false;
867 auto b0
= getBlockAt(xx
, willyY
);
868 auto b1
= getBlockAt(xx
, willyY
+8);
869 auto b2
= (willyY
&7 ?
getBlockAt(xx
, willyY
+16) : b1
);
870 if (b0
== 3 || b1
== 3 || b2
== 3) return false;
872 if (willyX
> 240) willyX
-= 240;
873 willyLastMoveDir
= 1;
877 void doWillyJump () {
878 if (!willyJump
) return;
879 willyY
+= willyJ
[willyJump
];
882 if (willyJumpDir
< 0) mv
= doWillyLeft(); else if (willyJumpDir
> 0) mv
= doWillyRight();
886 auto b0
= getBlockAt(x
, willyY
);
887 auto b1
= getBlockAt(x
+8, willyY
);
888 if (b0
== 3 || b1
== 3) {
889 // headboom! (apstenu %-)
891 willyY
-= willyJ
[willyJump
];
892 willyJump
= 0; // enough flying
897 if (willyJump
> 12) willyFall
+= willyJ
[willyJump
];
898 if ((willyY
&7) == 0) {
899 auto b0
= getBlockAt(willyX
, willyY
+16);
900 auto b1
= getBlockAt(willyX
+8, willyY
+16);
902 if (b0
== 3 || b1
== 3) willyX
= x
;
903 willyFall
= 0; // can't fall too deep while jumping
904 willyJump
= 0; // enough flying
905 if (b0
== 7 || b1
== 7) willyStall
= 1; // conveyor?
911 if (willyJump
> 18) willyJump
= 0;
915 if (willyDead
) return;
917 if (isWillyInDeadly()) { willyDead
= true; return; }
919 if (checkMonsters()) { willyDead
= true; return; }
921 if (checkMonsters()) singleStep
= singleStepWait
= true;
924 auto wasJump
= false;
926 willyLastMoveDir
= 0;
928 if (willyJump
) return;
932 auto falling
= isWillyFalling();
933 if (!kDown
&& falling
) {
934 willyConv
= willyStall
= willyLastMoveDir
= 0;
937 if (willyY
> 112) willyY
-= 112;
941 if (!falling
&& willyFall
> 34) willyDead
= true; // too high!
942 auto lfall
= willyFall
;
945 if (willyDead
) return;
947 auto dx
= (kLeft ?
-1 : kRight ?
1 : 0);
948 if (isWillyOnConv()) {
949 auto cdir
= (conveyor
.d ?
1 : -1);
950 //dx==cdir,!dx,lastmove==cdir
951 if (willyLastMoveDir
== cdir || dx
== cdir ||
!dx
) { willyConv
= cdir
; willyStall
= 0; } // was moving in conv. dir or standing
953 // Willy just steps on the conveyor, and Willy walking to the opposite side
955 if (wasJump
&& willyLastMoveDir
== -cdir
) {
956 willyConv
= dx
; // from jump, can do opposite
959 if (lfall
> 0 ||
!willyLastMoveDir
) { dx
= 0; willyStall
= 1; } // lands on conveyor, not from jump
962 // Willy was on conveyor
963 dx
= (willyStall ?
0 : willyConv
);
966 willyConv
= willyStall
= 0;
969 //if (willyConv) dx = willyConv;
970 if (kUp
&& !wasJump
) {
971 willyConv
= willyStall
= willyLastMoveDir
= 0;
977 if (kDown
) willyY
-= 8;
978 willyLastMoveDir
= 0;
979 if (dx
< 0) doWillyLeft(); else if (dx
> 0) doWillyRight();
982 // ////////////////////////////////////////////////////////////////////// //
984 auto img
= new X11Image(16*16, 16+16);
985 auto ww
= gameData
["willy"];
986 foreach (immutable f
; 0..16) {
987 foreach (immutable y
; 0..16) {
988 foreach (immutable x
; 0..16) {
989 ubyte c
= ww
[f
*256+y
*16+x
];
991 img
.setPixel(f
*16+x
, y
, Transparent
);
993 img
.setPixel(f
*16+x
, y
, palcol(c
));
995 img
.setPixel(f
*16+x
, y
+16, palcol(15));
1003 void buildBrickImages () {
1004 auto buildBrickImage (in ref Room
.CommonGfx brk
, int skipy
=0) {
1005 return buildImageBrick(gameData
["blocks"][brk
.gfx
*64..$], brk
.ink
, this.paper
, skipy
);
1008 foreach (immutable f
; 0..8) {
1011 //case 0: case 7: img = buildBrickImage(this.wall, 16); break;
1012 case 1: img
= buildBrickImage(this.platforms
[0]); break;
1013 case 2: img
= buildBrickImage(this.platforms
[1]); break;
1014 case 3: img
= buildBrickImage(this.wall
); break;
1015 //case 4: img = buildBrickImage(this.crumb); break;
1016 case 5: img
= buildBrickImage(this.deadlies
[0]); break;
1017 case 6: img
= buildBrickImage(this.deadlies
[1]); break;
1022 foreach (immutable f
; 0..9) brickCache
~= buildBrickImage(this.crumb
, f
);
1025 void buildConvImages () {
1027 auto conv
= &this.conveyor
;
1028 if (conv
.y
<= 0 || conv
.l
< 1) return;
1029 foreach (immutable f
; 0..4) {
1030 convCache
~= buildImageBrick(gameData
["conv"][conv
.gfx
*256+f
*64..$], conv
.ink
, this.paper
);
1034 void buildExitImages () {
1036 exitCache
~= buildImageMasked(gameData
["exits"][this.exit
.gfx
*256..$], 16, 16);
1037 foreach (immutable f
; 0..16) exitCache
~= buildImageMaskedShiny(gameData
["exits"][this.exit
.gfx
*256..$], f
, 16, 16);
1040 void buildKeysImages () {
1042 foreach (immutable f
; 0..16) keyCache
~= buildImageMaskedShiny(gameData
["keys"][this.keys
.gfx
*64..$], f
, 8, 8);
1045 void buildMonsterImages () {
1046 monsterCache
= null;
1047 monsterOfsCache
= null;
1048 foreach (immutable f
, const ref m
; this.enemies
) {
1049 if (m
.x
< 0 || m
.y
< 0) {
1050 monsterOfsCache
~= null;
1051 monsterCache
~= null;
1057 foreach (immutable c
; 0..4) {
1058 auto n
= (m
.gfx
+c
)*256;
1060 r
~= buildImageMaskedInk(gameData
["vrobo"][n
..$], m
.ink
-1, 16, 16);
1063 foreach (immutable c
; 0..(m
.anim
>>1)+1) {
1064 auto n
= (m
.gfx
+c
)*256;
1066 r
~= buildImageMaskedInk(gameData
["hrobo"][n
..$], m
.ink
-1, 16, 16);
1069 r
~= buildImageMaskedInk(gameData
["hrobo"][n
..$], m
.ink
-1, 16, 16);
1072 monsterOfsCache
~= cc
;
1077 void buildEugeneImages () {
1079 for (int f
= 0; f
<= 256; f
+= 256) {
1080 foreach (immutable c
; 0..8) {
1081 eugeneSpr
~= buildImageMaskedInk(gameData
["eugene"][f
..$], c
, 16, 16);
1086 void buildKongImages () {
1088 foreach (immutable f
; 0..4) {
1089 foreach (immutable c
; 0..8) {
1090 kongSpr
~= buildImageMaskedInk(gameData
["kong"][f
*256..$], c
, 16, 16);
1095 void buildSkyLabImages () {
1097 foreach (immutable f
; 0..8) {
1098 foreach (immutable c
; 0..8) {
1099 skylabSpr
~= buildImageMaskedInk(gameData
["sky"][f
*256..$], c
, 16, 16);
1104 void buildSwitchImages () {
1107 if (auto pp
= "switches" in gameData
) swg
= *pp
; else swg
= gameData
["switch"];
1108 foreach (immutable f
; 0..2) {
1109 switchesSpr
~= buildImageBrick(swg
[f
*64..$], this.platforms
[1].ink
, this.paper
);
1114 void load (VFile fl
) {
1116 fl
.rawReadExact(map
[]);
1117 fl
.rawReadExact(title
[]);
1119 foreach (immutable idx
, char ch
; title
[]) {
1121 tit
= title
[0..idx
];
1123 while (tit
.length
&& tit
[0] <= ' ') tit
= tit
[1..$];
1124 while (tit
.length
&& tit
[$-1] <= ' ') tit
= tit
[0..$-1];
1125 this.title
= tit
.idup
;
1127 border
= fl
.readNum
!ubyte;
1128 ink
= fl
.readNum
!ubyte;
1129 paper
= fl
.readNum
!ubyte;
1131 platforms
.length
= 2;
1132 foreach (immutable idx
; 0..2) {
1134 g
.ink
= fl
.readNum
!ubyte;
1135 g
.paper
= fl
.readNum
!ubyte;
1136 g
.gfx
= fl
.readNum
!ubyte;
1140 wall
.ink
= fl
.readNum
!ubyte;
1141 wall
.paper
= fl
.readNum
!ubyte;
1142 wall
.gfx
= fl
.readNum
!ubyte;
1144 crumb
.ink
= fl
.readNum
!ubyte;
1145 crumb
.paper
= fl
.readNum
!ubyte;
1146 crumb
.gfx
= fl
.readNum
!ubyte;
1148 deadlies
.length
= 2;
1149 foreach (immutable idx
; 0..2) {
1151 g
.ink
= fl
.readNum
!ubyte;
1152 g
.paper
= fl
.readNum
!ubyte;
1153 g
.gfx
= fl
.readNum
!ubyte;
1157 conveyor
.ink
= fl
.readNum
!ubyte;
1158 conveyor
.paper
= fl
.readNum
!ubyte;
1159 conveyor
.gfx
= fl
.readNum
!ubyte;
1161 willy
.x
= fl
.readNum
!short;
1162 willy
.y
= fl
.readNum
!short;
1163 willy
.sd
= fl
.readNum
!ubyte;
1165 conveyor
.x
= fl
.readNum
!short;
1166 conveyor
.y
= fl
.readNum
!short;
1167 conveyor
.d
= fl
.readNum
!ubyte;
1168 conveyor
.l
= fl
.readNum
!ubyte;
1170 keys
.info
.length
= 5;
1171 foreach (immutable idx
; 0..5) keys
.info
[idx
].x
= fl
.readNum
!short;
1172 foreach (immutable idx
; 0..5) keys
.info
[idx
].y
= fl
.readNum
!short;
1173 keys
.gfx
= fl
.readNum
!ubyte;
1174 foreach (immutable idx
; 0..5) keys
.info
[idx
].s
= fl
.readNum
!ubyte;
1176 switches
.length
= 2;
1177 foreach (immutable idx
; 0..2) switches
[idx
].x
= fl
.readNum
!short;
1178 foreach (immutable idx
; 0..2) switches
[idx
].y
= fl
.readNum
!short;
1179 foreach (immutable idx
; 0..2) switches
[idx
].s
= fl
.readNum
!ubyte;
1181 exit
.x
= fl
.readNum
!short;
1182 exit
.y
= fl
.readNum
!short;
1183 exit
.gfx
= fl
.readNum
!ubyte;
1185 air
= fl
.readNum
!ubyte;
1187 enemies
.length
= 4*2;
1189 foreach (immutable idx
; 0..4) enemies
[idx
].vert
= false;
1190 foreach (immutable idx
; 0..4) enemies
[idx
].ink
= fl
.readNum
!ubyte;
1191 foreach (immutable idx
; 0..4) enemies
[idx
].paper
= fl
.readNum
!ubyte;
1192 foreach (immutable idx
; 0..4) enemies
[idx
].x
= fl
.readNum
!short;
1193 foreach (immutable idx
; 0..4) enemies
[idx
].y
= fl
.readNum
!short;
1194 foreach (immutable idx
; 0..4) enemies
[idx
].min
= fl
.readNum
!short;
1195 foreach (immutable idx
; 0..4) enemies
[idx
].max
= fl
.readNum
!short;
1196 foreach (immutable idx
; 0..4) enemies
[idx
].d
= fl
.readNum
!ubyte;
1197 foreach (immutable idx
; 0..4) enemies
[idx
].s
= fl
.readNum
!ubyte;
1198 foreach (immutable idx
; 0..4) enemies
[idx
].gfx
= fl
.readNum
!short;
1199 foreach (immutable idx
; 0..4) enemies
[idx
].flip
= fl
.readNum
!ubyte;
1200 foreach (immutable idx
; 0..4) enemies
[idx
].anim
= fl
.readNum
!ubyte;
1202 foreach (immutable idx
; 4..8) enemies
[idx
].vert
= true;
1203 foreach (immutable idx
; 4..8) enemies
[idx
].ink
= fl
.readNum
!ubyte;
1204 foreach (immutable idx
; 4..8) enemies
[idx
].paper
= fl
.readNum
!ubyte;
1205 foreach (immutable idx
; 4..8) enemies
[idx
].x
= fl
.readNum
!short;
1206 foreach (immutable idx
; 4..8) enemies
[idx
].y
= fl
.readNum
!short;
1207 foreach (immutable idx
; 4..8) enemies
[idx
].min
= fl
.readNum
!short;
1208 foreach (immutable idx
; 4..8) enemies
[idx
].max
= fl
.readNum
!short;
1209 foreach (immutable idx
; 4..8) enemies
[idx
].d
= fl
.readNum
!ubyte;
1210 foreach (immutable idx
; 4..8) enemies
[idx
].s
= fl
.readNum
!ubyte;
1211 foreach (immutable idx
; 4..8) enemies
[idx
].gfx
= fl
.readNum
!short;
1212 foreach (immutable idx
; 4..8) enemies
[idx
].anim
= fl
.readNum
!ubyte;
1217 // ////////////////////////////////////////////////////////////////////////// //
1218 public void blitTo (X11Image src
, X11Image dst
, int x
, int y
, int repx
=1) {
1219 foreach (immutable r
; 0..repx
) {
1220 foreach (immutable dy
; 0..src
.height
) {
1221 foreach (immutable dx
; 0..src
.width
) {
1224 if (xx
< 0 || yy
< 0 || xx
>= dst
.width || yy
>= dst
.height
) continue;
1225 auto c
= src
.getPixel(dx
, dy
);
1226 if (isTransparent(c
)) continue;
1227 dst
.setPixel(xx
, yy
, c
);
1235 public void blitTo (X11Image src
, int x0
, int y0
, int ww
, int hh
, X11Image dst
, int x
, int y
) {
1236 foreach (immutable dy
; 0..ww
) {
1237 foreach (immutable dx
; 0..hh
) {
1240 if (xx
< 0 || yy
< 0 || xx
>= dst
.width || yy
>= dst
.height
) continue;
1241 auto c
= src
.getPixel(x0
+dx
, y0
+dy
);
1242 if (isTransparent(c
)) continue;
1243 dst
.setPixel(xx
, yy
, c
);