shitty title
[mmd.git] / engine.d
blob25a9bd7b26f8a72c7f89910f96eee8aa942c0f22
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
3 * Partially based on Andy Noble's Manic Miner PC
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module engine;
20 import x11gfx;
21 import wadarc;
23 __gshared bool debugColdet = false;
24 __gshared bool singleStep = false;
25 __gshared bool singleStepWait = false;
28 // ////////////////////////////////////////////////////////////////////////// //
29 private __gshared VColor[256] palette;
30 __gshared ubyte[][string] gameData;
31 __gshared Room[] gameRooms;
32 __gshared string gameLevelsName;
35 loadfile (GFXDATA "air", GFXair, 2048);
36 loadfile (GFXDATA "fant", GFXfant, 8 * 96);
37 loadfile (GFXDATA "final", GFXfinal, 256 * 64);
38 loadfile (GFXDATA "fontb", fontb, 8 * 96);
39 loadfile (GFXDATA "fonts", fonts, 6 * 64);
40 loadfile (GFXDATA "house", GFXhouse, 1024);
41 loadfile (GFXDATA "load", GFXload, 32 * 8 * 2);
42 loadfile (GFXDATA "over", GFXover, 256 * 2);
43 loadfile (GFXDATA "piano", GFXpiano, 256 * 64);
44 loadfile (GFXDATA "pkeys", GFXpkeys, 128 * 8);
45 loadfile (GFXDATA "switch", GFXswitch, 2 * 64);
46 loadfile (GFXDATA "tplate", GFXtplate, 32 * 64);
47 loadfile (GFXDATA "win", GFXwin, 4864);
51 private void loadRooms (const(ubyte)[] data) {
52 gameRooms.length = 0;
53 if (data.length < 8) throw new Exception("invalid levels");
54 if (data[0..8] != "MANIC\x0d\x0a\x1a") throw new Exception("invalid levels");
55 data = data[8..$];
56 if (data.length < 16) throw new Exception("invalid levels");
57 char[16] lname = (cast(const(char)[])data)[0..16];
58 data = data[16..$];
59 const(char)[] nn;
60 foreach (immutable idx, char ch; lname[]) {
61 if (ch == 0) break;
62 nn = lname[0..idx];
64 while (nn.length && nn[$-1] <= ' ') nn = nn[0..$-1];
65 gameLevelsName = nn.idup;
66 while (data.length > 0) {
67 gameRooms.length += 1;
68 gameRooms[$-1].roomIdx = cast(int)gameRooms.length-1;
69 gameRooms[$-1].load(data);
74 public void loadGameData () {
76 auto pp = wadLoadFile("palmain");
77 foreach (immutable cc; 0..256) {
78 ubyte r = cast(ubyte)(255*pp[cc*3+0]/63);
79 ubyte g = cast(ubyte)(255*pp[cc*3+1]/63);
80 ubyte b = cast(ubyte)(255*pp[cc*3+2]/63);
81 palette[cc] = rgbcol(r, g, b);
85 forEachWadFile(delegate (string name, uint size) {
86 if (name == "palmain") return;
87 auto buf = wadLoadFile(name);
88 if (name == "levels") {
89 loadRooms(buf);
90 } else {
91 gameData[name] = buf;
93 });
95 if (gameRooms.length == 0) throw new Exception("no levels");
99 // ////////////////////////////////////////////////////////////////////////// //
100 // image builders
101 public VColor palcol (ubyte c) nothrow @trusted @nogc { pragma(inline, true); return palette[c]; }
104 // ////////////////////////////////////////////////////////////////////////// //
105 public X11Image buildImageMasked (const(ubyte)[] darr, int w, int h) {
106 assert(w > 0 && h > 0);
107 int dpos = 0;
108 auto img = new X11Image(w, h);
109 foreach (immutable y; 0..h) {
110 foreach (immutable x; 0..w) {
111 ubyte c = darr[dpos++];
112 if (c) {
113 img.setPixel(x, y, palcol(c));
114 } else {
115 img.setPixel(x, y, Transparent);
119 return img;
123 public X11Image buildImageMaskedShiny (const(ubyte)[] darr, int brightness, int w, int h) {
124 assert(w > 0 && h > 0);
125 int dpos = 0;
126 auto img = new X11Image(w, h);
127 foreach (immutable y; 0..h) {
128 foreach (immutable x; 0..w) {
129 ubyte c = darr[dpos++];
130 if (c) {
131 int b = (c&15)-brightness;
132 if (b < 0) b = 0; else if (b > 15) b = 15;
133 img.setPixel(x, y, palcol(cast(ubyte)((c&240)|b)));
134 } else {
135 img.setPixel(x, y, Transparent);
139 return img;
143 public X11Image buildImageMaskedInk (const(ubyte)[] darr, int ink, int w, int h) {
144 assert(w > 0 && h > 0);
145 int dpos = 0;
146 auto img = new X11Image(w, h);
147 if (ink < 0) ink = 0;
148 ink *= 16;
149 foreach (immutable y; 0..h) {
150 foreach (immutable x; 0..w) {
151 ubyte c = darr[dpos++];
152 if (c) {
153 img.setPixel(x, y, palcol(cast(ubyte)(c+ink)));
154 } else {
155 img.setPixel(x, y, Transparent);
159 return img;
163 // ////////////////////////////////////////////////////////////////////////// //
164 public X11Image buildImageBrick (const(ubyte)[] darr, int ink, int paper, int skipy=0) {
165 assert(skipy >= 0);
166 enum { w = 8, h = 8 }
167 int dpos = 0;
168 auto img = new X11Image(w, h);
169 ink *= 16;
170 foreach (immutable y; 0..h) {
171 foreach (immutable x; 0..w) {
172 ubyte c = (y >= skipy ? darr[dpos++] : 0);
173 img.setPixel(x, y, palcol(cast(ubyte)(c ? ink+c : paper)));
176 return img;
180 // ////////////////////////////////////////////////////////////////////////// //
181 // willy jump offsets
182 private:
183 static immutable int[19] willyJ = [0, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4];
185 struct SkyLabXY { int x, y; }
186 static immutable SkyLabXY[4][3] skylabCoords = [
187 [{x: 8,y:0}, {x: 72,y:0}, {x:136,y:0}, {x:200,y:0}],
188 [{x:40,y:0}, {x:104,y:0}, {x:168,y:0}, {x:232,y:0}],
189 [{x:24,y:0}, {x: 88,y:0}, {x:152,y:0}, {x:216,y:0}]
193 // ////////////////////////////////////////////////////////////////////////// //
194 // suitable to "unjson"
195 public struct Room {
196 private enum SRZIgnore;
197 enum { Width = 32, Height = 16 }
198 static struct WillyStart { int x; int y; int sd; }
199 static struct ExitGfx { ubyte gfx; int x; int y; }
200 static struct CommonGfx { ubyte gfx; ubyte ink; ubyte paper; }
201 static struct Conveyor { int x; int y; int d; int l; ubyte gfx; ubyte ink; ubyte paper; @SRZIgnore int frame; }
202 static struct KeyPos { int x; int y; int s; }
203 static struct Key { ubyte gfx; KeyPos[] info; }
204 static struct SwitchPos { int x; int y; int s; }
205 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; }
206 static struct SkyLabEnemy { int p, s; ubyte ink; int max; int m; int frame; }
207 static struct SkyLabXY { int x, y, frame; ubyte ink; int m; int s; int max; int p; }
208 static struct SPGRay { int x, y; }
209 @SRZIgnore int roomIdx;
210 string title;
211 WillyStart willy;
212 ExitGfx exit;
213 int air;
214 ubyte[Height*Width] map; // 16 rows, 32 cols
215 ubyte border, ink, paper;
216 CommonGfx[] platforms;
217 CommonGfx wall;
218 CommonGfx crumb;
219 CommonGfx[] deadlies;
220 Conveyor conveyor;
221 Key keys;
222 SwitchPos[] switches;
223 Enemy[] enemies;
225 @SRZIgnore int air8 = 8;
227 @SRZIgnore Enemy eugene;
229 @SRZIgnore int holeLen, holeY;
230 @SRZIgnore Enemy kong;
232 @SRZIgnore SkyLabEnemy[] skylabEnemies;
233 @SRZIgnore SkyLabXY[] skylab;
235 @SRZIgnore SPGRay[] spg; // solar power generator
237 @SRZIgnore private {
238 int willyX, willyY, willyDir, willyJump, willyJumpDir;
239 int willyLastMoveDir, willyFall, willyConv, willyStall;
240 public bool willyDead;
241 int frameNo;
242 public int score;
243 // keys
244 bool kLeft, kRight, kJump, kUp, kDown;
245 bool kLeftDown, kRightDown, kJumpDown, kUpDown, kDownDown;
248 @SRZIgnore private {
249 X11Image finalSpr;
250 X11Image sunSpr;
251 X11Image[] brickCache;
252 X11Image[] convCache;
253 X11Image[] exitCache;
254 X11Image[] keyCache;
255 X11Image[][] monsterCache;
256 int[][] monsterOfsCache;
257 X11Image[] eugeneSpr;
258 X11Image[] kongSpr;
259 X11Image[] skylabSpr;
260 X11Image[] switchesSpr;
261 X11Image willySpr;
262 X11Image gfxAir;
265 ubyte saveKeyState () {
266 return
267 ((kLeftDown ? 1 : 0)<<0)|
268 ((kRightDown ? 1 : 0)<<1)|
269 ((kUpDown ? 1 : 0)<<2)|
270 ((kDownDown ? 1 : 0)<<3)|
271 ((kJumpDown ? 1 : 0)<<4)|
275 void restoreKeyState (ubyte b) {
276 keyLeft = ((b&(1<<0)) != 0);
277 keyRight = ((b&(1<<1)) != 0);
278 keyUp = ((b&(1<<2)) != 0);
279 keyDown = ((b&(1<<3)) != 0);
280 keyJump = ((b&(1<<4)) != 0);
283 void initRoom () {
284 void initWilly () {
285 willyX = willy.x;
286 willyY = willy.y;
287 willyDir = (willy.sd > 0 ? -1 : 1);
288 willyJump = willyFall = willyConv = willyStall = 0;
289 willyDead = false;
290 willyLastMoveDir = 0;
291 kLeft = kRight = kJump = kUp = kDown = false;
292 kLeftDown = kRightDown = kJumpDown = kUpDown = kDownDown = false;
295 buildWilly();
296 buildBrickImages();
297 buildConvImages();
298 buildExitImages();
299 buildKeysImages();
300 buildMonsterImages();
301 buildEugeneImages();
302 buildKongImages();
303 buildSkyLabImages();
304 buildSwitchImages();
306 finalSpr = buildImageMasked(gameData["final"], 256, 64);
307 sunSpr = buildImageMasked(gameData["sun"], 24, 16);
308 gfxAir = buildImageMasked(gameData["air"], 256, 8);
310 initWilly();
312 frameNo = 0;
313 foreach (ref c; map) {
314 if (c == 4) c = 8; // 'crumb'
315 else if (c < 1 || c > 7) c = 0; // emptyness
318 // rebuild keys
319 KeyPos[] kinfo;
320 foreach (const ref ki; keys.info) {
321 if (ki.s == 0) continue;
322 kinfo ~= ki;
324 keys.info = kinfo;
326 platforms = platforms.dup;
327 deadlies = deadlies.dup;
328 switches = switches.dup;
329 enemies = enemies.dup;
331 // Eugene?
332 if (roomIdx == 4) {
333 Enemy[] a = [{ x:120, y:1, d/*ir*/:0, min:1, max:87, ink:6 }];
334 eugene = a[0];
336 // Kong?
337 if (roomIdx == 7 || roomIdx == 11) {
338 Enemy[] a = [{ x:120, y:0, max:104, frame:0, ink:2, m:0 }];
339 kong = a[0];
340 holeLen = 2*8;
341 holeY = 0;
343 // SkyLab?
344 if (roomIdx == 13) {
345 enemies = null;
346 SkyLabEnemy[] a = [
347 {p:0, s:4, ink:6, max:72, m:0, frame:0},
348 {p:2, s:3, ink:5, max:56, m:0, frame:0},
349 {p:1, s:1, ink:4, max:32, m:0, frame:0}
351 skylabEnemies = a.dup;
352 foreach (immutable f, const ref se; skylabEnemies) {
353 skylab ~= SkyLabXY(
354 skylabCoords[f][se.p].x, skylabCoords[f][se.p].y,
355 se.frame, se.ink, se.m, se.s, se.max, se.p,
359 // Solar Power Generator?
360 if (roomIdx == 18) buildSPG();
363 void keyLeft (bool pressed) { if (pressed) kLeft = true; kLeftDown = pressed; }
364 void keyRight (bool pressed) { if (pressed) kRight = true; kRightDown = pressed; }
365 void keyUp (bool pressed) { if (pressed) kUp = true; kUpDown = pressed; }
366 void keyDown (bool pressed) { if (pressed) kDown = true; kDownDown = pressed; }
367 void keyJump (bool pressed) { if (pressed) kJump = true; kJumpDown = pressed; }
369 void stepGame () {
370 kLeft = kLeft||kLeftDown;
371 kRight = kRight||kRightDown;
372 kUp = kUp||kUpDown;
373 kDown = kDown||kDownDown;
374 kJump = kJump||kJumpDown;
375 scope(exit) kLeft = kRight = kJump = kUp = kDown = false;
377 ++frameNo;
378 conveyor.frame += (conveyor.d ? -1 : 1);
379 if (conveyor.frame < 0) conveyor.frame = 3; else if (conveyor.frame > 3) conveyor.frame = 0;
381 if (!willyJump) stepCrumb();
382 stepMonsters();
383 stepWillyActions();
384 stepKeys();
385 stepSwitch();
386 buildSPG();
388 if (--air8 <= 0) {
389 air8 = 8;
390 if (--air <= 33) {
391 air = 33;
392 willyDead = true;
397 void cheatRemoveKeys () { keys.info = null; }
399 // ////////////////////////////////////////////////////////////////////// //
400 // checkers
402 // x & y: in pixels
403 ubyte getBlockAt (int x, int y, bool simplify=false) const nothrow @nogc {
404 x = x>>3;
405 y = y>>3;
406 if (x < 0 || y < 0 || x > 31 || y > 15) return 0; // empty
407 ubyte b = map[y*32+x];
408 if (simplify) {
409 if (b > 15) b = 0;
410 else if (b > 7) b = 4;
411 else if (b == 6) b = 5;
412 else if (b == 2) b = 1;
414 return b;
417 bool checkExit () const nothrow @nogc {
418 if (keys.info.length != 0) return false;
419 auto x = exit.x;
420 auto y = exit.y;
421 return (willyX >= x-2 && willyX+10 <= x+18 && willyY >= y-5 && willyY+16 <= y+22);
424 bool checkMonsters () const nothrow {
425 foreach (immutable f, const ref m; enemies) {
426 auto cc = monsterOfsCache[f];
427 if (m.x < 0 || m.y < 0) continue;
428 if (m.vert) {
429 if (pixelCheckMonster(m.x, m.y, gameData["vrobo"], cc[m.anim])) return true;
430 } else {
431 if (pixelCheckMonster(m.x&248, m.y, gameData["hrobo"], cc[((m.x&m.anim)&0xfe)+m.d/*ir*/])) return true;
434 // Eugene?
435 if (eugene.x >= 0) {
436 enum curLSet = 0;
437 if (pixelCheckMonster(eugene.x, eugene.y, gameData["eugene"], 256*curLSet)) return true;
439 // Kong?
440 if (kong.x >= 0) {
441 if (pixelCheckMonster(kong.x, kong.y, gameData["kong"], 256*kong.frame)) return true;
443 // SkyLab?
444 if (skylab.length) {
445 foreach (const ref sk; skylab) {
446 if (pixelCheckMonster(sk.x, sk.y, gameData["sky"], 256*sk.frame)) return true;
449 return false;
452 void checkSwitch () nothrow @nogc {
453 foreach (ref ss; switches) {
454 if (ss.s/*tate*/ != 1) continue;
455 auto x = ss.x;
456 auto y = ss.y;
457 if (x+7 >= willyX && y+7 >= willyY && x < willyX+8 && y < willyY+16) ss.s/*tate*/ = 1+1;
461 bool checkSPG () const nothrow @nogc {
462 foreach (const ref sp; spg) {
463 auto x = sp.x*8;
464 auto y = sp.y*8;
465 if (x < 0 || y < 0) break;
466 if (x+7 >= willyX && x < willyX+8 && y+7 >= willyY && y < willyY+16) return true;
468 return false;
471 X11Image draw (X11Image img=null) {
472 if (img is null) img = new X11Image(8*Room.Width, 8*Room.Height);
473 int pos = 0;
474 foreach (immutable y; 0..img.height) {
475 foreach (immutable x; 0..img.width) {
476 img.setPixel(x, y, palcol(this.paper));
479 foreach (immutable y; 0..Room.Height) {
480 foreach (immutable x; 0..Room.Width) {
481 ubyte blk = this.map[pos++];
482 if (blk < 1 || blk == 7) continue;
483 if (blk == 4) blk = 8;
484 if (blk < brickCache.length && brickCache[blk] !is null) {
485 brickCache[blk].blitTo(img, x*8, y*8);
489 if (this.roomIdx == 19) {
490 finalSpr.blitTo(img, 0, 0);
491 sunSpr.blitTo(img, 60, 32);
494 void drawConveyor () {
495 auto conv = &this.conveyor;
496 if (conv.l < 1) return;
497 auto y = conv.y;
498 if (y <= 0) return;
499 convCache[conv.frame].blitTo(img, conv.x, conv.y, conv.l);
502 void drawExit () {
503 int br;
504 if (this.keys.info.length != 0) {
505 br = 0;
506 } else {
507 br = this.frameNo&31;
508 if (br > 15) br = 31-br;
509 ++br;
511 exitCache[br].blitTo(img, this.exit.x, this.exit.y);
514 void drawKeys () {
515 auto ff = this.frameNo%16;
516 if (ff >= 8) ff = 15-ff;
517 auto keyimg = keyCache[ff];
518 foreach_reverse (const ref ki; this.keys.info) {
519 if (ki.x < 0 || ki.y < 0) continue;
520 //eraseRect(ki.x, ki.y, 8, 8);
521 if (!ki.s) continue;
522 keyimg.blitTo(img, ki.x&248, ki.y);
526 void drawMonsters () {
527 foreach (immutable f, const ref m; this.enemies) {
528 if (m.x < 0 || m.y < 0) continue;
529 auto slist = monsterCache[f];
530 auto x = m.x;
531 if (m.vert) {
532 //for (var c = 0; c <= m.anim; c++) r.push(buildImageMaskedInk(me.data.vrobo, (m.gfx+c)*256, m.ink-1, 16, 16, false));
533 slist[m.anim].blitTo(img, x, m.y);
534 } else {
535 auto sidx = ((x&m.anim)&0xfe)+m.d/*ir*/;
536 if (sidx < slist.length) {
537 slist[sidx].blitTo(img, x&248, m.y);
538 } else {
539 import core.stdc.stdio : stderr, fprintf;
540 stderr.fprintf("monster #%u is fucked: sidx=%u; max=%u", cast(uint)f, cast(uint)sidx, cast(uint)slist.length);
544 if (this.eugene.x >= 0) {
545 enum curLSet = 0;
546 eugeneSpr[curLSet*8+this.eugene.ink].blitTo(img, this.eugene.x, this.eugene.y);
548 if (this.kong.x >= 0) {
549 kongSpr[this.kong.frame*8+this.kong.ink].blitTo(img, this.kong.x, this.kong.y);
551 if (this.skylab.length) {
552 foreach (const ref sk; this.skylab) {
553 skylabSpr[sk.frame*8+sk.ink].blitTo(img, sk.x, sk.y);
558 void drawSwitch () {
559 foreach (const ref ss; this.switches) {
560 if (ss.s == 0) continue;
561 switchesSpr[ss.s-1].blitTo(img, ss.x, ss.y);
565 void drawSPG () {
566 foreach (const ref sp; this.spg) {
567 auto x = sp.x*8;
568 auto y = sp.y*8;
569 if (x < 0 || y < 0) break;
570 //ctx.fillStyle = mainPal[noerase?6:this.paper];
571 //ctx.fillRect(x, y, 8, 8);
572 foreach (immutable dy; 0..8) {
573 foreach (immutable dx; 0..8) {
574 img.setPixel(x+dx, y+dy, palcol(6));
580 void drawWilly () {
581 auto willyPos = (this.willyX&15)>>1;
582 if (this.willyDir < 0) willyPos += 8;
583 if (debugColdet) {
584 auto wy = 0;
585 if (this.checkMonsters()) wy = 16;
586 willySpr.blitTo(willyPos*16, wy, 16, 16, img, this.willyX&248, this.willyY);
587 } else {
588 willySpr.blitTo(willyPos*16, 0, 16, 16, img, this.willyX&248, this.willyY);
592 void drawAir () {
593 gfxAir.blitFast(0, 136);
594 printAt(1, 136, "AIR", 9);
595 //for (int x = 3; x >= 0; --x) PlotXY (30, 138 + x, GFXair + 512 + 256 * x + 30, 226, 1);
596 setPixel(32, 138, palcol(122));
597 setPixel(32, 139, palcol(116));
598 setPixel(32, 140, palcol(119));
599 setPixel(32, 141, palcol(123));
600 if (air > 33) {
601 drawBar8(33, 138, air-33, 1, 120);
602 drawBar8(33, 139, air-33, 1, 114);
603 drawBar8(33, 140, air-33, 1, 117);
604 drawBar8(33, 141, air-33, 1, 121);
606 if (air > 32) {
607 setPixel(air-1, 138, palcol(122));
608 setPixel(air-1, 139, palcol(116));
609 setPixel(air-1, 140, palcol(119));
610 setPixel(air-1, 141, palcol(123));
614 drawConveyor();
615 drawKeys();
616 drawMonsters();
617 drawSwitch();
618 drawWilly();
619 drawExit();
620 drawSPG();
622 drawAir();
624 drawStr(128-cast(int)title.length*6/2, 128, title, rgbcol(255, 255, 0));
626 return img;
629 private:
630 // ////////////////////////////////////////////////////////////////////// //
631 // pixel-perfect collision detector
632 // fully stolen from Andy's sources %-)
633 bool pixelCheckMonster (int rx, int ry, const(ubyte)[] darr, int dpos=0) const nothrow {
634 static ubyte[256] mpcGrid;
635 assert(dpos >= 0);
636 //int x, y, w;
637 int x, w;
638 rx -= willyX&248;
639 ry -= willyY;
640 if (rx < -15 || rx > 15 || ry < -15 || ry > 15) return false;
641 // clear grid
642 mpcGrid[] = 0;
643 if (rx < 0) { x = 0; rx = -rx; w = 16-rx; } else { x = rx; rx = 0; w = 16-x; }
644 // partial plot monster
645 for (int y = ry+15; y >= ry; --y) {
646 if (y >= 0 && y < 16) {
647 int gp = y*16+x;
648 int rp = dpos+(y-ry)*16+rx;
649 for (int dx = 0; dx < w; ++dx, ++gp, ++rp) mpcGrid[gp] = darr[rp];
652 auto warr = gameData["willy"];
653 int wptr = ((willyX&15)>>1)*256+(willyDir < 0 ? 2048 : 0);
654 // check for collision
655 int mp = 0;
656 for (x = 255; x >= 0; --x, ++wptr, ++mp) if (warr[wptr] && mpcGrid[mp]) return true;
657 return false;
660 // ////////////////////////////////////////////////////////////////////// //
661 bool isWillyFalling () const nothrow @nogc {
662 if (willyY&7) return true;
663 for (int dx = 0; dx <= 8; dx += 8) {
664 ubyte b = getBlockAt(willyX+dx, willyY+16, true);
665 if (b > 0 && b != 5) return false;
667 return true;
670 bool isWillyInDeadly () const nothrow @nogc {
671 for (int dx = 0; dx <= 8; dx += 8) {
672 for (int dy = 0; dy <= 16; dy += 8) {
673 if (getBlockAt(willyX+dx, willyY+dy, true) == 5) return true;
676 return false;
679 bool isWillyOnConv () const nothrow @nogc {
680 if (willyY&7) return false;
681 ubyte b0 = getBlockAt(willyX, willyY+16);
682 ubyte b1 = getBlockAt(willyX+8, willyY+16);
683 return (b0 == 7 || b1 == 7);
686 // ////////////////////////////////////////////////////////////////////// //
687 // called on each frame
688 void buildSPG (bool forced=false) {
689 if (!forced && roomIdx != 18) {
690 spg = null;
691 return;
694 bool spgCheckMonster (int x, int y) {
695 x *= 8;
696 y *= 8;
697 foreach (const ref cm; enemies) {
698 auto mx = cm.x;
699 auto my = cm.y;
700 if (x+7 >= mx && x < mx+15 && y+7 >= my && y < my+15) return true;
702 return false;
705 int x = 23, y = 0;
706 bool done = false;
707 int dir = 0, idx = 0;
709 if (spg.length) {
710 spg.length = 0;
711 spg.assumeSafeAppend;
714 void addXY (int x, int y) {
715 ++idx;
716 while (spg.length < idx) spg ~= SPGRay(-1, -1);
717 spg[idx-1].x = x;
718 spg[idx-1].y = y;
721 do {
722 ubyte blockhit = map[y*32+x];
723 bool robohit = spgCheckMonster(x, y);
725 if (blockhit && robohit) {
726 addXY(-1, -1);
727 done = true;
728 } else if (!blockhit && robohit) {
729 if (idx && spg[idx-1].x == x && spg[idx-1].y == y) {
730 spg[idx-1].x = spg[idx-1].y = -1;
731 done = true;
732 } else {
733 addXY(x, y);
735 dir ^= 1;
736 } else if (!blockhit && !robohit) {
737 addXY(x, y);
738 } else if (blockhit && !robohit) {
739 if (idx && spg[idx-1].x == x && spg[idx-1].y == y) {
740 spg[idx-1].x = spg[idx-1].y = -1;
741 done = true;
743 dir ^= 1;
746 if (!blockhit) {
747 if (!dir) {
748 ++y;
749 blockhit = map[y*32+x];
750 if (y == 15 || blockhit) done = true;
751 } else {
752 --x;
753 blockhit = map[y*32+x];
754 if (x == 0 || blockhit) done = true;
756 } else {
757 if (!dir) { --x; if (!x) done = true; }
758 else { ++y; if (++y == 15) done = true; }
760 } while (!done);
761 addXY(-1, -1);
764 void stepMonsters () {
765 foreach (ref m; enemies) {
766 if (m.x < 0 || m.y < 0) continue;
767 if (m.vert) {
768 auto y = m.y;
769 auto spd = m.s/*peed*/;
770 if (m.d/*ir*/ != 0) {
771 // up
772 y -= spd;
773 if (y < m.min || y < 0) { y += spd; m.d/*ir*/ = 0; }
774 } else {
775 // down
776 y += spd;
777 if (y > m.max) { y -= spd; m.d/*ir*/ = 1; }
779 m.y = y;
780 m.anim = (m.anim+1)&3;
781 } else {
782 auto x = m.x;
783 auto spd = (2>>m.s/*peed*/);
784 if (m.d/*ir*/ != 0) {
785 // left
786 x -= spd;
787 if (x < m.min) { m.d/*ir*/ = 0; x += spd; }
788 } else {
789 // right
790 x += spd;
791 if (x > m.max+6) { m.d/*ir*/ = 1; x -= spd; }
793 m.x = x;
796 // Eugene
797 if (eugene.x >= 0) {
798 if (keys.info.length == 0) {
799 // no keys, Eugene tries to block the exit
800 eugene.ink = (eugene.ink+1)&7;
801 eugene.y += (eugene.y < eugene.max ? 1 : 0);
802 } else {
803 if (eugene.d/*ir*/ != 0) {
804 // up
805 --eugene.y;
806 if (eugene.y < eugene.min) { eugene.d/*ir*/ = 0; ++eugene.y; }
807 } else {
808 // down
809 ++eugene.y;
810 if (eugene.y > eugene.max) { eugene.d/*ir*/ = 1; --eugene.y; }
814 // Kong
815 if (kong.x >= 0) {
816 switch (kong.falling) {
817 case 1: // just started
818 map[2*32+15] = 0;
819 map[2*32+16] = 0;
820 //eraseRect(16, 120, 16, 8);
821 kong.falling = 2;
822 break;
823 case 2:
824 kong.ink = 4;
825 kong.frame += 2;
826 kong.falling = 3;
827 break;
828 case 3:
829 kong.y += 4;
830 if (kong.y >= kong.max) { kong.x = -1; score += 100; }
831 if (!kong.delay) { kong.delay = 4; kong.frame = ((kong.frame-1)&1)+2; } else --kong.delay;
832 break;
833 default:
834 if (!kong.delay) { kong.delay = 8; kong.frame = (kong.frame+1)&1; } else --kong.delay;
835 break;
838 // SkyLab
839 foreach (immutable idx, ref sk; skylab) {
840 switch (sk.m) {
841 case 0:
842 sk.y += sk.s;
843 if (sk.y > sk.max) {
844 sk.y = sk.max;
845 sk.m = 1;
846 ++sk.frame;
848 break;
849 case 1:
850 ++sk.frame;
851 if (sk.frame == 7) sk.m = 2;
852 break;
853 case 2:
854 sk.p = (sk.p+1)&3;
855 sk.x = skylabCoords[idx][sk.p].x;
856 sk.y = skylabCoords[idx][sk.p].y;
857 sk.frame = sk.m = 0;
858 break;
859 default:
864 void stepCrumb () {
865 if (willyY&7) return;
866 for (int f = 0; f <= 8; f += 8) {
867 int x = willyX+f;
868 int y = willyY+16;
869 ubyte b = getBlockAt(x, y);
870 if (b == 4) b = 8;
871 if (b < 8) continue;
872 x >>= 3;
873 y >>= 3;
874 if (++b > 15) b = 0;
875 map[y*32+x] = b;
876 //eraseRect(x*8, y*8, 8, 8);
880 void stepKeys () {
881 while (keys.info.length) {
882 bool again = false;
883 foreach (immutable idx; 0..keys.info.length) {
884 auto ki = &keys.info[idx];
885 assert(ki.s);
886 int kx = ki.x;
887 int ky = ki.y;
888 if (kx+7 >= willyX && kx < willyX+10 && ky+7 >= willyY && ky < willyY+16) {
889 score += 100;
890 foreach (immutable c; idx+1..keys.info.length) keys.info[c-1] = keys.info[c];
891 keys.info.length -= 1;
892 again = true;
893 break;
896 if (!again) break;
900 void stepSwitch () {
901 // hole?
902 if (holeLen > 0 && switches.length && switches[0].s/*tate*/ > 1) {
903 if (holeLen < 0) {
904 //FIXME
905 enemies[1].max += 24;
906 holeLen = 0;
907 } else {
908 ++holeY;
909 if (holeY == 16) {
910 map[11*32+17] = 0;
911 map[12*32+17] = 0;
912 holeLen = -1;
916 // Kong?
917 if (kong.x >= 0 && !kong.falling && switches.length > 1 && switches[1].s/*tate*/ > 1) kong.falling = 1;
920 void stepWillyActions () {
921 bool doWillyLeft () {
922 if (willyDir > 0) { willyDir = -1; return true; }
923 auto xx = willyX-2;
924 auto b0 = getBlockAt(xx, willyY);
925 auto b1 = getBlockAt(xx, willyY+8);
926 auto b2 = (willyY&7 ? getBlockAt(xx, willyY+16) : b1);
927 if (b0 == 3 || b1 == 3 || b2 == 3) return false;
928 willyX -= 2;
929 if (willyX < 0) willyX += 240;
930 willyLastMoveDir = -1;
931 return true;
934 bool doWillyRight () {
935 if (willyDir < 0) { willyDir = 1; return true; }
936 if (willyX > 245) return false;
937 auto xx = willyX+10;
938 auto b0 = getBlockAt(xx, willyY);
939 auto b1 = getBlockAt(xx, willyY+8);
940 auto b2 = (willyY&7 ? getBlockAt(xx, willyY+16) : b1);
941 if (b0 == 3 || b1 == 3 || b2 == 3) return false;
942 willyX += 2;
943 if (willyX > 240) willyX -= 240;
944 willyLastMoveDir = 1;
945 return true;
948 void doWillyJump () {
949 if (!willyJump) return;
950 willyY += willyJ[willyJump];
951 auto x = willyX;
952 auto mv = false;
953 if (willyJumpDir < 0) mv = doWillyLeft(); else if (willyJumpDir > 0) mv = doWillyRight();
954 if (willyJump < 9) {
955 willyFall = 0;
956 // up
957 auto b0 = getBlockAt(x, willyY);
958 auto b1 = getBlockAt(x+8, willyY);
959 if (b0 == 3 || b1 == 3) {
960 // headboom! (apstenu %-)
961 willyX = x;
962 willyY -= willyJ[willyJump];
963 willyJump = 0; // enough flying
964 return;
966 } else {
967 // down
968 if (willyJump > 12) willyFall += willyJ[willyJump];
969 if ((willyY&7) == 0) {
970 auto b0 = getBlockAt(willyX, willyY+16);
971 auto b1 = getBlockAt(willyX+8, willyY+16);
972 if (b0 || b1) {
973 if (b0 == 3 || b1 == 3) willyX = x;
974 willyFall = 0; // can't fall too deep while jumping
975 willyJump = 0; // enough flying
976 if (b0 == 7 || b1 == 7) willyStall = 1; // conveyor?
977 return;
981 ++willyJump;
982 if (willyJump > 18) willyJump = 0;
986 if (willyDead) return;
987 checkSwitch();
988 if (isWillyInDeadly()) { willyDead = true; return; }
989 if (!debugColdet) {
990 if (checkMonsters()) { willyDead = true; return; }
991 } else {
992 if (checkMonsters()) singleStep = singleStepWait = true;
995 auto wasJump = false;
996 if (willyJump) {
997 willyLastMoveDir = 0;
998 doWillyJump();
999 if (willyJump) return;
1000 wasJump = true;
1003 auto falling = isWillyFalling();
1004 if (!kDown && falling) {
1005 willyConv = willyStall = willyLastMoveDir = 0;
1006 willyFall += 4;
1007 willyY += 4;
1008 if (willyY > 112) willyY -= 112;
1009 return;
1012 if (!falling && willyFall > 34) willyDead = true; // too high!
1013 auto lfall = willyFall;
1014 willyFall = 0;
1016 if (willyDead) return;
1018 auto dx = (kLeft ? -1 : kRight ? 1 : 0);
1019 if (isWillyOnConv()) {
1020 auto cdir = (conveyor.d ? 1 : -1);
1021 //dx==cdir,!dx,lastmove==cdir
1022 if (willyLastMoveDir == cdir || dx == cdir || !dx) { willyConv = cdir; willyStall = 0; } // was moving in conv. dir or standing
1023 if (!willyConv) {
1024 // Willy just steps on the conveyor, and Willy walking to the opposite side
1025 willyStall = 0;
1026 if (wasJump && willyLastMoveDir == -cdir) {
1027 willyConv = dx; // from jump, can do opposite
1028 } else {
1029 willyConv = dx;
1030 if (lfall > 0 || !willyLastMoveDir) { dx = 0; willyStall = 1; } // lands on conveyor, not from jump
1032 } else {
1033 // Willy was on conveyor
1034 dx = (willyStall ? 0 : willyConv);
1036 } else {
1037 willyConv = willyStall = 0;
1040 //if (willyConv) dx = willyConv;
1041 if (kUp && !wasJump) {
1042 willyConv = willyStall = willyLastMoveDir = 0;
1043 willyJumpDir = dx;
1044 willyJump = 1;
1045 doWillyJump();
1046 return;
1048 if (kDown) willyY -= 8;
1049 willyLastMoveDir = 0;
1050 if (dx < 0) doWillyLeft(); else if (dx > 0) doWillyRight();
1053 // ////////////////////////////////////////////////////////////////////// //
1054 void buildWilly () {
1055 auto img = new X11Image(16*16, 16+16);
1056 auto ww = gameData["willy"];
1057 foreach (immutable f; 0..16) {
1058 foreach (immutable y; 0..16) {
1059 foreach (immutable x; 0..16) {
1060 ubyte c = ww[f*256+y*16+x];
1061 if (!c) {
1062 img.setPixel(f*16+x, y, Transparent);
1063 } else {
1064 img.setPixel(f*16+x, y, palcol(c));
1065 // white
1066 img.setPixel(f*16+x, y+16, palcol(15));
1071 willySpr = img;
1074 void buildBrickImages () {
1075 auto buildBrickImage (in ref Room.CommonGfx brk, int skipy=0) {
1076 return buildImageBrick(gameData["blocks"][brk.gfx*64..$], brk.ink, this.paper, skipy);
1078 brickCache = null;
1079 foreach (immutable f; 0..8) {
1080 X11Image img;
1081 switch (f) {
1082 //case 0: case 7: img = buildBrickImage(this.wall, 16); break;
1083 case 1: img = buildBrickImage(this.platforms[0]); break;
1084 case 2: img = buildBrickImage(this.platforms[1]); break;
1085 case 3: img = buildBrickImage(this.wall); break;
1086 //case 4: img = buildBrickImage(this.crumb); break;
1087 case 5: img = buildBrickImage(this.deadlies[0]); break;
1088 case 6: img = buildBrickImage(this.deadlies[1]); break;
1089 default:
1091 brickCache ~= img;
1093 foreach (immutable f; 0..9) brickCache ~= buildBrickImage(this.crumb, f);
1096 void buildConvImages () {
1097 convCache = null;
1098 auto conv = &this.conveyor;
1099 if (conv.y <= 0 || conv.l < 1) return;
1100 foreach (immutable f; 0..4) {
1101 convCache ~= buildImageBrick(gameData["conv"][conv.gfx*256+f*64..$], conv.ink, this.paper);
1105 void buildExitImages () {
1106 exitCache = null;
1107 exitCache ~= buildImageMasked(gameData["exits"][this.exit.gfx*256..$], 16, 16);
1108 foreach (immutable f; 0..16) exitCache ~= buildImageMaskedShiny(gameData["exits"][this.exit.gfx*256..$], f, 16, 16);
1111 void buildKeysImages () {
1112 keyCache = null;
1113 foreach (immutable f; 0..16) keyCache ~= buildImageMaskedShiny(gameData["keys"][this.keys.gfx*64..$], f, 8, 8);
1116 void buildMonsterImages () {
1117 monsterCache = null;
1118 monsterOfsCache = null;
1119 foreach (immutable f, const ref m; this.enemies) {
1120 if (m.x < 0 || m.y < 0) {
1121 monsterOfsCache ~= null;
1122 monsterCache ~= null;
1123 continue;
1125 X11Image[] r;
1126 int[] cc;
1127 if (m.vert) {
1128 foreach (immutable c; 0..4) {
1129 auto n = (m.gfx+c)*256;
1130 cc ~= n;
1131 r ~= buildImageMaskedInk(gameData["vrobo"][n..$], m.ink-1, 16, 16);
1133 } else {
1134 foreach (immutable c; 0..(m.anim>>1)+1) {
1135 auto n = (m.gfx+c)*256;
1136 cc ~= n;
1137 r ~= buildImageMaskedInk(gameData["hrobo"][n..$], m.ink-1, 16, 16);
1138 n += m.flip*256;
1139 cc ~= n;
1140 r ~= buildImageMaskedInk(gameData["hrobo"][n..$], m.ink-1, 16, 16);
1143 monsterOfsCache ~= cc;
1144 monsterCache ~= r;
1148 void buildEugeneImages () {
1149 eugeneSpr = null;
1150 for (int f = 0; f <= 256; f += 256) {
1151 foreach (immutable c; 0..8) {
1152 eugeneSpr ~= buildImageMaskedInk(gameData["eugene"][f..$], c, 16, 16);
1157 void buildKongImages () {
1158 kongSpr = null;
1159 foreach (immutable f; 0..4) {
1160 foreach (immutable c; 0..8) {
1161 kongSpr ~= buildImageMaskedInk(gameData["kong"][f*256..$], c, 16, 16);
1166 void buildSkyLabImages () {
1167 skylabSpr = null;
1168 foreach (immutable f; 0..8) {
1169 foreach (immutable c; 0..8) {
1170 skylabSpr ~= buildImageMaskedInk(gameData["sky"][f*256..$], c, 16, 16);
1175 void buildSwitchImages () {
1176 switchesSpr = null;
1177 ubyte[] swg;
1178 if (auto pp = "switches" in gameData) swg = *pp; else swg = gameData["switch"];
1179 foreach (immutable f; 0..2) {
1180 switchesSpr ~= buildImageBrick(swg[f*64..$], this.platforms[1].ink, this.paper);
1184 private:
1185 void load (ref const(ubyte)[] data) {
1186 void readBuf (void[] buf) {
1187 import core.stdc.string : memcpy;
1188 if (buf.length == 0) return;
1189 if (data.length < buf.length) throw new Exception("invalid level");
1190 memcpy(buf.ptr, data.ptr, buf.length);
1191 data = data[buf.length..$];
1194 ubyte readU8 () {
1195 if (data.length < 1) throw new Exception("invalid level");
1196 ubyte res = data[0];
1197 data = data[1..$];
1198 return res;
1201 short readS16 () {
1202 if (data.length < 2) throw new Exception("invalid level");
1203 short res = cast(short)(data[0]|(data[1]<<8));
1204 data = data[2..$];
1205 return res;
1208 char[33] title;
1209 readBuf(map[]);
1210 readBuf(title[]);
1211 const(char)[] tit;
1212 foreach (immutable idx, char ch; title[]) {
1213 if (ch == 0) break;
1214 tit = title[0..idx];
1216 while (tit.length && tit[0] <= ' ') tit = tit[1..$];
1217 while (tit.length && tit[$-1] <= ' ') tit = tit[0..$-1];
1218 this.title = tit.idup;
1220 border = readU8;
1221 ink = readU8;
1222 paper = readU8;
1224 platforms.length = 2;
1225 foreach (immutable idx; 0..2) {
1226 CommonGfx g;
1227 g.ink = readU8;
1228 g.paper = readU8;
1229 g.gfx = readU8;
1230 platforms[idx] = g;
1233 wall.ink = readU8;
1234 wall.paper = readU8;
1235 wall.gfx = readU8;
1237 crumb.ink = readU8;
1238 crumb.paper = readU8;
1239 crumb.gfx = readU8;
1241 deadlies.length = 2;
1242 foreach (immutable idx; 0..2) {
1243 CommonGfx g;
1244 g.ink = readU8;
1245 g.paper = readU8;
1246 g.gfx = readU8;
1247 deadlies[idx] = g;
1250 conveyor.ink = readU8;
1251 conveyor.paper = readU8;
1252 conveyor.gfx = readU8;
1254 willy.x = readS16;
1255 willy.y = readS16;
1256 willy.sd = readU8;
1258 conveyor.x = readS16;
1259 conveyor.y = readS16;
1260 conveyor.d = readU8;
1261 conveyor.l = readU8;
1263 keys.info.length = 5;
1264 foreach (immutable idx; 0..5) keys.info[idx].x = readS16;
1265 foreach (immutable idx; 0..5) keys.info[idx].y = readS16;
1266 keys.gfx = readU8;
1267 foreach (immutable idx; 0..5) keys.info[idx].s = readU8;
1269 switches.length = 2;
1270 foreach (immutable idx; 0..2) switches[idx].x = readS16;
1271 foreach (immutable idx; 0..2) switches[idx].y = readS16;
1272 foreach (immutable idx; 0..2) switches[idx].s = readU8;
1274 exit.x = readS16;
1275 exit.y = readS16;
1276 exit.gfx = readU8;
1278 air = readU8;
1280 enemies.length = 4*2;
1282 foreach (immutable idx; 0..4) enemies[idx].vert = false;
1283 foreach (immutable idx; 0..4) enemies[idx].ink = readU8;
1284 foreach (immutable idx; 0..4) enemies[idx].paper = readU8;
1285 foreach (immutable idx; 0..4) enemies[idx].x = readS16;
1286 foreach (immutable idx; 0..4) enemies[idx].y = readS16;
1287 foreach (immutable idx; 0..4) enemies[idx].min = readS16;
1288 foreach (immutable idx; 0..4) enemies[idx].max = readS16;
1289 foreach (immutable idx; 0..4) enemies[idx].d = readU8;
1290 foreach (immutable idx; 0..4) enemies[idx].s = readU8;
1291 foreach (immutable idx; 0..4) enemies[idx].gfx = readS16;
1292 foreach (immutable idx; 0..4) enemies[idx].flip = readU8;
1293 foreach (immutable idx; 0..4) enemies[idx].anim = readU8;
1295 foreach (immutable idx; 4..8) enemies[idx].vert = true;
1296 foreach (immutable idx; 4..8) enemies[idx].ink = readU8;
1297 foreach (immutable idx; 4..8) enemies[idx].paper = readU8;
1298 foreach (immutable idx; 4..8) enemies[idx].x = readS16;
1299 foreach (immutable idx; 4..8) enemies[idx].y = readS16;
1300 foreach (immutable idx; 4..8) enemies[idx].min = readS16;
1301 foreach (immutable idx; 4..8) enemies[idx].max = readS16;
1302 foreach (immutable idx; 4..8) enemies[idx].d = readU8;
1303 foreach (immutable idx; 4..8) enemies[idx].s = readU8;
1304 foreach (immutable idx; 4..8) enemies[idx].gfx = readS16;
1305 foreach (immutable idx; 4..8) enemies[idx].anim = readU8;
1310 // ////////////////////////////////////////////////////////////////////////// //
1311 public void blitTo (X11Image src, X11Image dst, int x, int y, int repx=1) {
1312 foreach (immutable r; 0..repx) {
1313 foreach (immutable dy; 0..src.height) {
1314 foreach (immutable dx; 0..src.width) {
1315 int xx = x+dx;
1316 int yy = y+dy;
1317 if (xx < 0 || yy < 0 || xx >= dst.width || yy >= dst.height) continue;
1318 auto c = src.getPixel(dx, dy);
1319 if (isTransparent(c)) continue;
1320 dst.setPixel(xx, yy, c);
1323 x += src.width;
1328 public void blitTo (X11Image src, int x0, int y0, int ww, int hh, X11Image dst, int x, int y) {
1329 foreach (immutable dy; 0..ww) {
1330 foreach (immutable dx; 0..hh) {
1331 int xx = x+dx;
1332 int yy = y+dy;
1333 if (xx < 0 || yy < 0 || xx >= dst.width || yy >= dst.height) continue;
1334 auto c = src.getPixel(x0+dx, y0+dy);
1335 if (isTransparent(c)) continue;
1336 dst.setPixel(xx, yy, c);
1342 // ////////////////////////////////////////////////////////////////////////// //
1343 public void setPixel8 (int x, int y, ubyte col) {
1344 setPixel(x, y, palcol(col));
1348 public void drawBar8 (int x, int y, int w, int h, ubyte col) {
1349 foreach (immutable dy; 0..h) {
1350 foreach (immutable dx; 0..w) {
1351 setPixel(x+dx, y+dy, palcol(col));
1357 // ////////////////////////////////////////////////////////////////////////// //
1358 public void drawWilly4 (int ofs) {
1359 auto gw = gameData["willy"][256*ofs..$];
1360 enum x = 232, y = 72;
1361 foreach (immutable dy; 0..16) {
1362 foreach (immutable dx; 0..16) {
1363 ubyte c = gw[0];
1364 gw = gw[1..$];
1365 setPixel8(x+dx, y+dy, (c ? c+0*16 : 39));
1371 // ////////////////////////////////////////////////////////////////////////// //
1372 public void printAt (int x, int y, const(char)[] text, ubyte col) {
1373 if (text.length == 0) return;
1375 int count = 0, count2, count3;
1376 int currentx, currenty;
1377 const(ubyte)* fonty, fonty2;
1380 currentx = ((x&127)*8)|((x&128) ? 4 : 0);
1381 currenty = ((y&127)*8)|((y&128) ? 4 : 0);
1382 { import std.stdio; writeln(currentx, "; ", currenty); }
1384 currentx = x;
1385 currenty = y;
1387 auto gfxFant = gameData["fant"];
1388 auto fontb = gameData["fontb"];
1390 while (text.length) {
1391 int alpha = text[0];
1392 text = text[1..$];
1393 if (alpha == 170) {
1394 if (text.length == 0) break;
1395 currentx = 0;
1396 currenty += 8;
1397 alpha = text[0];
1398 text = text[1..$];
1400 alpha -= 32;
1401 if (++count == 33) return;
1403 fonty = fontb.ptr+alpha*8;
1404 fonty2 = gfxFant.ptr+alpha*8;
1406 y = currenty;
1407 for (count2 = 0; count2 < 8; ++count2) {
1408 x = currentx;
1409 for (count3 = 0; count3 < 8; ++count3) {
1410 if (fonty[0]&(1<<count3)) {
1411 setPixel(x, y, palcol(col));
1412 } else if (fonty2[0]&(1<<count3)) {
1414 BYTE data = mm_gfx_getpixel (x, y);
1415 BYTE data2 = (data & 15) + 3;
1416 setPixel(x, y, palcol((data&240)|(data2 > 15 ? 15 : data2)));
1418 putPixel(x, y, palcol(col)|(0x7f<<vlAShift));
1419 //ubyte data2 = 15;
1420 //setPixel(x, y, palcol(data2 > 15 ? 15 : data2));
1422 //setPixel(x, y, rgbcol(255, 127, 0));
1423 ++x;
1425 ++fonty;
1426 ++fonty2;
1427 ++y;
1429 currentx += 8;