pacmad.pas 41.7 KB
Newer Older
bocianu's avatar
bocianu committed
1
program pacmad;
bocianu's avatar
bocianu committed
2

3 4
uses datamatrix, _atari, mypmg, dlist, rmt;

bocianu's avatar
bocianu committed
5 6 7 8
{$I globals.inc}
{$I assets/sprites.inc}

var
bocianu's avatar
bocianu committed
9 10 11
    pac: pacT;
    dir: byte;
    joy: byte;
12
    frame: byte absolute 20;
bocianu's avatar
bocianu committed
13 14

    board: boardT absolute BOARDBASE;
15
    boardOffset: array [0..BOARDHEIGHT] of word;
16
    boardLastLine: byte;
bocianu's avatar
bocianu committed
17

18
    dlOffset: array [0..BOARDHEIGHT] of word;
bocianu's avatar
bocianu committed
19
    dltop: word;
20 21 22
    viewportTop: byte;
    viewportMoveDir: shortint;
    vscrollstep: shortint;
bocianu's avatar
bocianu committed
23 24
    vOffset: smallInt;
    bOffset: smallInt;
bocianu's avatar
bocianu committed
25

bocianu's avatar
bocianu committed
26
    strings: array [0..0] of pointer absolute STRINGS_ADDRESS;
bocianu's avatar
bocianu committed
27 28 29
    levels: array [0..0] of word absolute LEVELS_ADDRESS;
    levelData: array [0..0] of byte;
    level: levelT;
bocianu's avatar
bocianu committed
30 31 32
    levelCount: byte;
    currentLevel: byte;

Wojciech Bociański's avatar
Wojciech Bociański committed
33 34
    ghost1, ghost2, ghost3, ghost4: ghostT;
    g: ^ghostT;
bocianu's avatar
bocianu committed
35 36
    ghosts: array [0..3] of pointer;
    ghostCount: byte;
bocianu's avatar
bocianu committed
37 38
    ghostFrame: byte;
    ghostSpritesheets: array [0..3] of pointer;
bocianu's avatar
bocianu committed
39

40
    pillTime: word;
Wojciech Bociański's avatar
Wojciech Bociański committed
41 42
    pillCounter: word;
    pillBonus: word;
43

44
    collider: byte;
45
    titleStage: byte;
46 47
    gameState: gameStateT;

bocianu's avatar
bocianu committed
48
    spawners: array [0..SPAWNERS_NUM - 1] of smallInt;
49 50
    spawnerCount: byte;
    spawnerActive: byte;
51
    spawnDelay: word;
52
    spawnCounter: word;
53

bocianu's avatar
bocianu committed
54 55 56
    bonusPosPicked:boolean;
    bonusX:byte;
    bonusY:byte;
bocianu's avatar
bocianu committed
57
    bonusCountdown:word;
bocianu's avatar
bocianu committed
58 59
    bonusDelay:word;

bocianu's avatar
bocianu committed
60
    score: cardinal;
bocianu's avatar
bocianu committed
61
    hiscore: cardinal;
bocianu's avatar
bocianu committed
62
    liveBonus: word;
bocianu's avatar
bocianu committed
63 64
    food: word;
    lives: byte;
65
    dotCount: word;
bocianu's avatar
bocianu committed
66

Wojciech Bociański's avatar
Wojciech Bociański committed
67
    s: TString;
68 69
    i: byte;
    flag:boolean;
bocianu's avatar
bocianu committed
70 71 72 73
    
    secret:array [0..SECRET_LEN-1] of byte;
	secret_id:byte;
	cheat_mode:boolean;
bocianu's avatar
bocianu committed
74 75
	
	startingLevel:byte;
bocianu's avatar
bocianu committed
76

bocianu's avatar
bocianu committed
77
    msx: TRMT;
bocianu's avatar
bocianu committed
78
    music: boolean;
bocianu's avatar
bocianu committed
79

80 81 82 83 84 85 86
    vblVector, dliVector: pointer;

{$R assets/chars.rc}
{$R assets/logo.rc}
{$R assets/asm_assets.rc}
{$r assets/rmt_play.rc}

bocianu's avatar
bocianu committed
87
{$I interrupts.inc}
bocianu's avatar
bocianu committed
88 89
{$I dlist.inc}

bocianu's avatar
bocianu committed
90

91
(********************************************** GUI ROUTINES ******************************************)
bocianu's avatar
bocianu committed
92

93

94 95 96 97 98 99 100 101 102 103 104 105
procedure SystemOff;
begin
asm
{
    txa:pha
    lda:cmp:req 20
    sei
    mva #0 _ATARI._NMIEN
    mva #$fe _ATARI._PORTB
    mva >CHARSET_TITLE_ADDRESS _ATARI._CHBASE
    mwa #nmi _ATARI._NMIVEC
    mwa vblVector vblvec
bocianu's avatar
bocianu committed
106
    mva #scr40 _atari._sdmctl_
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
    mva #$c0 _ATARI._NMIEN
    bne stop
nmi
    bit _ATARI._NMIST
    bpl vbl
    jmp (_ATARI._VDSLST)
    rti

vbl
    jmp $ffff
vblvec  equ *-2
stop
    pla:tax
};
end;

procedure setDli(dliptr: pointer);
begin
    dliVector := dliptr;
    _VDSLST := word(dliptr);
end;

bocianu's avatar
bocianu committed
129 130 131 132
procedure ClearTxT;
begin
    FillByte(pointer(TXT_RAM),$400,0);
end;
bocianu's avatar
bocianu committed
133 134 135

procedure UpdateScore;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
136
    Str(score, s);
bocianu's avatar
bocianu committed
137 138 139 140 141 142
    asm {
        ldy #1
@       lda adr.s,y
        sub #32
        sta TXT_RAM+6,y
        iny
143
        dec adr.s
bocianu's avatar
bocianu committed
144 145 146 147 148 149
        bne @-
        lda #0
        sta TXT_RAM+6,y
    };
end;

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
procedure UpdateHiscore;
begin
    Str(hiscore, s);
    asm {
        ldy #1
@       lda adr.s,y
        sub #32
        sta TXT_RAM+21,y
        iny
        dec adr.s
        bne @-
        lda #0
        sta TXT_RAM+6,y
    };
end;

bocianu's avatar
bocianu committed
166 167
procedure UpdateFood;
begin
bocianu's avatar
bocianu committed
168
    Str(dotCount, s);
bocianu's avatar
bocianu committed
169 170 171 172 173 174
    asm {
        ldy #1
@       lda adr.s,y
        sub #32
        sta TXT_RAM+21,y
        iny
175
        dec adr.s
bocianu's avatar
bocianu committed
176 177 178 179 180 181 182 183
        bne @-
        lda #0
        sta TXT_RAM+21,y
    };
end;

procedure UpdateLives;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
184
    Str(lives, s);
bocianu's avatar
bocianu committed
185 186 187 188 189 190
    asm {
        ldy #1
@       lda adr.s,y
        sub #32
        sta TXT_RAM+36,y
        iny
191
        dec adr.s
bocianu's avatar
bocianu committed
192 193 194 195 196 197
        bne @-
        lda #0
        sta TXT_RAM+36,y
    };
end;

bocianu's avatar
bocianu committed
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
procedure UpdateLevel;
begin
    Str(startingLevel+1, s);
    asm {
        ldy #1
@       lda adr.s,y
        sub #32
        sta TXT_RAM+40*15+30,y
        iny
        dec adr.s
        bne @-
        lda #0
        sta TXT_RAM+40*15+30,y
    };
end;

bocianu's avatar
bocianu committed
214 215 216
procedure SongStart(pattern:byte);
begin
    msx.Init(pattern);
bocianu's avatar
bocianu committed
217
    music := true;
bocianu's avatar
bocianu committed
218 219 220 221 222
end;

procedure SongStop;
begin
    msx.Init(SONG_SILENCE);
bocianu's avatar
bocianu committed
223
    music := false;
bocianu's avatar
bocianu committed
224
end;
225

226 227 228 229 230 231 232 233
procedure GhostColorsDefault;
begin
    _PCOLR0_ := COLOR_G1;
    _PCOLR1_ := COLOR_G2;
    _PCOLR2_ := COLOR_G3;
    _PCOLR3_ := COLOR_G4;
end;

234
procedure GhostColorsEscape(color: byte);
235 236 237 238 239 240 241
begin
    _PCOLR0_ := color;
    _PCOLR1_ := color;
    _PCOLR2_ := color;
    _PCOLR3_ := color;
end;

bocianu's avatar
bocianu committed
242 243 244
procedure putChar(dest:word;letter:word);
var src1,src2:word;
    x:byte;
245 246 247 248 249 250 251 252 253 254 255 256 257
begin
    src1:=CHARSET_GAME_ADDRESS+Lo(letter)*8;
    src2:=CHARSET_GAME_ADDRESS+Hi(letter)*8;
    for x:=0 to 7 do begin
        Poke(dest,Peek(src1));
        Poke(dest+1,Peek(src2));
        Inc(src1);
        Inc(src2);
        Inc(dest,10);
    end;
end;

procedure putArray(x,y,size:byte;a:word);
bocianu's avatar
bocianu committed
258
var dest:word;
259
begin
bocianu's avatar
bocianu committed
260
    dest:=TXT_RAM+(y*10)+x;
261
    for i:=0 to size-1 do begin
bocianu's avatar
bocianu committed
262 263
        putChar(dest,Dpeek(a));
        Inc(dest,2);
264 265 266 267 268
        Inc(a,2);
    end;
end;

procedure putNumber(x,y:byte;num:TString);
bocianu's avatar
bocianu committed
269
var dest:word;
270
begin
bocianu's avatar
bocianu committed
271
    dest:=TXT_RAM+(y*10)+x;
272
    for i:=1 to byte(num[0]) do begin
bocianu's avatar
bocianu committed
273 274 275 276 277 278 279 280 281 282 283 284
        putChar(dest,txt_numbers[byte(num[i])-48]);
        Inc(dest,2);
    end
end;

procedure putNumber2(x,y:byte;num:TString);
var dest:word;
begin
    dest:=GFX_RAM+(y*40)+x;
    for i:=1 to byte(num[0]) do begin
        Dpoke(dest,txt_numbers[byte(num[i])-48]);
        Inc(dest,2);
285 286 287
    end
end;

bocianu's avatar
bocianu committed
288

289 290 291
(********************************************** BOARD ROUTINES ******************************************)


Wojciech Bociański's avatar
Wojciech Bociański committed
292
procedure PaintOnBoard(x, y, t: byte);
293
begin
294
    vOffset := dlOffset[y] + byte(x shl 1) - 1;
Wojciech Bociański's avatar
Wojciech Bociański committed
295
    Dpoke(vOffset, tiles[t]);
296 297
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
298
function GetBoard(x, y: byte): byte;
bocianu's avatar
bocianu committed
299 300
var pos: word;
begin
301
    pos := boardOffset[y] + x;
bocianu's avatar
bocianu committed
302 303 304
    result := board[pos];
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
305
procedure SetBoard(x, y, v: byte);
bocianu's avatar
bocianu committed
306 307
var pos: word;
begin
308
    pos := boardOffset[y] + x;
bocianu's avatar
bocianu committed
309
    board[pos] := v;
Wojciech Bociański's avatar
Wojciech Bociański committed
310
    PaintOnBoard(x, y, v);
bocianu's avatar
bocianu committed
311 312
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
313
function CanMoveTo(x, y: byte): boolean;
314
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
315
    Result := true;
316 317 318 319
    if GetBoard(x, y) = TILE_VOID then exit(false);
    if x >= BOARDWIDTH then exit(false);
    if y >= BOARDHEIGHT then exit(false);
    if GetBoard(x, y) = TILE_SPAWNER then exit(false);
320 321
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
322 323
function GetNeighbours(x, y: byte): byte;
var counter: byte;
324
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
325 326 327
    counter := 0;
    result := 0;
    if canMoveTo(x-1, y) then begin  // left
328
        Inc(result, %0100);
329 330
        Inc(counter);
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
331
    if canMoveTo(x+1, y) then begin
332
        Inc(result, %1000);    // right
333 334
        Inc(counter);
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
335
    if canMoveTo(x, y+1) then begin
336
        Inc(result, %0010);    // down
337 338
        Inc(counter);
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
339
    if canMoveTo(x, y-1) then begin
340
        Inc(result, %0001);    // up
341 342
        Inc(counter);
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
343 344
    counter := counter shl 4;
    result := result or counter;
345 346
end;

bocianu's avatar
bocianu committed
347 348 349 350
procedure AddScore(val:word);
begin
    Inc(score, val);
    Inc(liveBonus, val);
351
    if (liveBonus >= LIFE_BONUS) then begin
bocianu's avatar
bocianu committed
352 353
        Inc(lives);
        UpdateLives;
bocianu's avatar
bocianu committed
354
        msx.Sfx(3, 2, 24);
355
        Dec(liveBonus, LIFE_BONUS);
bocianu's avatar
bocianu committed
356 357 358 359
    end;
    UpdateScore;
end;

bocianu's avatar
bocianu committed
360
procedure CheckBoardAction;
Wojciech Bociański's avatar
Wojciech Bociański committed
361
var cell: byte;
bocianu's avatar
bocianu committed
362
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
363
    cell := GetBoard(pac.x, pac.y);
364 365 366 367
    case cell of
        TILE_DOT: begin
            AddScore(10);
            Inc(food);
bocianu's avatar
bocianu committed
368 369 370 371
            if dotCount>0 then begin
				Dec(dotCount);
				if dotCount=0 then msx.Sfx(4, 2, 12);
			end;
372 373 374 375 376 377 378 379 380 381 382 383 384 385
            UpdateFood;
            SetBoard(pac.x, pac.y, TILE_EMPTY);
        end;
        TILE_PILL: begin
            AddScore(50);
            SetBoard(pac.x, pac.y, TILE_EMPTY);
            gameState := GAME_ESCAPE;
            GhostColorsEscape(COLOR_ESCAPE);
            pillCounter := pillTime;
            pillBonus := 0;
            SongStart(SONG_PILL);
        end;
        TILE_WARP_LEFT: begin
            pac.x := 20;
bocianu's avatar
bocianu committed
386
        end;
387 388 389 390 391 392 393 394 395
        TILE_WARP_RIGHT: begin
            pac.x := 0;
        end;
        TILE_EXIT: begin
            if dotCount = 0 then begin
                gameState := GAME_WIN;
                frame := 0;
            end;
        end;
bocianu's avatar
bocianu committed
396 397 398 399
        TILE_BONUS, TILE_BONUS+1, TILE_BONUS+2: begin
            AddScore(500);
            bonusCountdown:=100;
            SetBoard(bonusX,bonusY,TILE_500);
bocianu's avatar
bocianu committed
400
            msx.Sfx(5, 2, 12);
bocianu's avatar
bocianu committed
401
        end;
402
    end;
bocianu's avatar
bocianu committed
403 404
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
405
procedure BoardRect(x, y, w, h, f: byte);
bocianu's avatar
bocianu committed
406 407 408
begin
    Dec(h);
    Dec(w);
Wojciech Bociański's avatar
Wojciech Bociański committed
409 410 411
    for i := x to x + w do begin
        SetBoard(i, y, f);
        SetBoard(i, y + h, f);
bocianu's avatar
bocianu committed
412
    end;
413 414
    if h > 0 then begin
        Dec(h);
Wojciech Bociański's avatar
Wojciech Bociański committed
415 416 417
        for i := y + 1 to y + h do begin
            SetBoard(x, i, f);
            SetBoard(x+w ,i, f);
bocianu's avatar
bocianu committed
418
        end;
419
    end;
bocianu's avatar
bocianu committed
420 421
end;

bocianu's avatar
bocianu committed
422
procedure PaintBoard;
bocianu's avatar
bocianu committed
423
var row, col, cell, t: byte;
424
    voffset, boffset, tile: word;
Wojciech Bociański's avatar
Wojciech Bociański committed
425 426 427
begin
    voffset := GFX_RAM;
    boffset := 0;
428
    boardLastLine := 0;
429
    dotCount := 0;
430
    for row := 0 to BOARDHEIGHT - 1 do begin
bocianu's avatar
bocianu committed
431 432 433 434
        if (board[boffset] = TILE_VOID) then begin
            tile := fences[GetNeighbours(0, row) and 15];
			if (Hi(tile) = $4f) and canMoveTo(1, row+1) then tile := fences[16];
        end else
435 436 437 438 439 440
            tile := tiles[board[boffset]];
        Poke(voffset, Hi(tile));
        Inc(voffset);
        Inc(boffset);
        for col := 1 to BOARDWIDTH - 2 do begin
            cell := board[boffset];
bocianu's avatar
bocianu committed
441 442 443 444 445 446
            if cell = TILE_VOID then begin
				t := GetNeighbours(col,row) and 15;
                tile := fences[t];
                if (Hi(tile) = $4f) and CanMoveTo(col+1, row+1) then 
					tile := (tile and $00ff) or $5000;
            end else begin
447 448 449
                tile := tiles[cell];
                if (cell = TILE_DOT) then Inc(dotCount);
                boardLastLine := row;
450
            end;
451
            Dpoke(voffset, tile);
bocianu's avatar
bocianu committed
452
            _COLOR0 := Lo(bOffset);
453
            Inc(voffset, 2);
454
            Inc(boffset);
455
        end;
456 457 458 459 460 461 462
        if (board[boffset] = TILE_VOID) then
            tile := fences[GetNeighbours(BOARDWIDTH - 1,row) and 15]
        else
            tile := tiles[board[boffset]];
        Poke(voffset, Lo(tile));
        Inc(voffset);
        Inc(boffset);
bocianu's avatar
bocianu committed
463
    end;
464
    Inc(boardLastLine,2);
bocianu's avatar
bocianu committed
465
    if GetBoard(level.exitX,level.exitY) = TILE_DOT then Dec(dotCount);
466
    SetBoard(level.exitX,level.exitY,TILE_EXIT);
bocianu's avatar
bocianu committed
467 468
end;

bocianu's avatar
bocianu committed
469 470 471 472 473
procedure ClearFeatures;
begin
    for i := 0 to SPAWNERS_NUM - 1 do spawners[i] := -1;
end;

474
procedure InitTables;
Wojciech Bociański's avatar
Wojciech Bociański committed
475 476 477 478 479 480 481 482 483
var row: byte;
    voffset: word;
    boffset: word;
begin
    voffset := GFX_RAM;
    boffset := 0;
    for row := 0 to BOARDHEIGHT - 1 do begin
        dlOffset[row] := voffset;
        boardOffset[row] := boffset;
484
        Inc(voffset, SCREEN_WIDTH);
Wojciech Bociański's avatar
Wojciech Bociański committed
485
        Inc(boffset, BOARDWIDTH);
486
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
487 488 489 490
    ghosts[0] := @ghost1;
    ghosts[1] := @ghost2;
    ghosts[2] := @ghost3;
    ghosts[3] := @ghost4;
bocianu's avatar
bocianu committed
491
    hiscore := 0;
bocianu's avatar
bocianu committed
492 493 494 495
    levelCount := 0;
    repeat
        Inc(levelCount)
    until levels[levelCount] = 0;
bocianu's avatar
bocianu committed
496 497 498 499 500 501
    msx.player := pointer(rmt_player);
    msx.modul := pointer(rmt_modul);
    music := false;
    secret_id := 0;
	secret[0] := 0;
	cheat_mode := false;
bocianu's avatar
bocianu committed
502
	startingLevel := 0;
bocianu's avatar
bocianu committed
503 504
end;

bocianu's avatar
bocianu committed
505 506
procedure MoveViewport;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
507 508 509 510
    if (viewportMoveDir <> 0) then begin     // if vscroll in progress jump to another line
        vscrollstep := (vscrollstep + viewportMoveDir) and 7;
        if (vscrollstep = 7) and (viewportMoveDir = -1) then begin // if vscroll starts up
            viewportTop := viewportTop + viewportMoveDir;
bocianu's avatar
bocianu committed
511
        end;
Wojciech Bociański's avatar
Wojciech Bociański committed
512 513
        if (vscrollstep = 0) and (viewportMoveDir = 1) then begin // if vscroll ends
            viewportTop := viewportTop + viewportMoveDir;
bocianu's avatar
bocianu committed
514 515 516 517 518
        end;
    end;
    dltop := dlOffset[viewportTop];
end;

519 520 521 522 523 524
procedure BlinkExit;
begin
    if (frame and 4) = 0 then PaintOnBoard(level.exitX, level.exitY, TILE_EXIT)
    else PaintOnBoard(level.exitX, level.exitY, TILE_EXIT_OPEN);
end;

bocianu's avatar
bocianu committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
procedure PickBonusPos;
var x,y:byte;
begin
    x:=random(BOARDWIDTH);
    y:=random(BOARDHEIGHT);
    if GetBoard(x,y) = TILE_EMPTY then begin
        bonusX := x;
        bonusY := y;
        bonusPosPicked := true;
    end;
end;

procedure RemoveBonus;
begin
    SetBoard(bonusX,bonusY,TILE_EMPTY);
    bonusPosPicked := false;
    bonusDelay := BONUS_DELAY * 50;
end;

procedure PlaceBonus;
begin
    i:=TILE_BONUS + Random(BONUS_COUNT);
    SetBoard(bonusX,bonusY,i);
    bonusCountdown := BONUS_COUNTDOWN * 50;
end;

bocianu's avatar
bocianu committed
551

bocianu's avatar
bocianu committed
552
{********************************************** GHOST ROUTINES ******************************************}
bocianu's avatar
bocianu committed
553

Wojciech Bociański's avatar
Wojciech Bociański committed
554

bocianu's avatar
bocianu committed
555 556
procedure ClearGhosts;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
557
    for i := 0 to GHOST_NUM-1 do begin
bocianu's avatar
bocianu committed
558 559
        g := ghosts[i];
        g^.dir := DIR_NONE;
bocianu's avatar
bocianu committed
560
        g^.deadCount := 0;
bocianu's avatar
bocianu committed
561
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
562
    ghostCount := 0;
563
end;
bocianu's avatar
bocianu committed
564

Wojciech Bociański's avatar
Wojciech Bociański committed
565 566
procedure SpawnGhost(ghostNumber, x, y: byte);
var dir, nx, ny: byte;
bocianu's avatar
bocianu committed
567
begin
bocianu's avatar
bocianu committed
568
    g := ghosts[ghostNumber];
Wojciech Bociański's avatar
Wojciech Bociański committed
569 570
    g^.x := x;
    g^.y := y;
bocianu's avatar
bocianu committed
571
    repeat
Wojciech Bociański's avatar
Wojciech Bociański committed
572 573
        nx := x;
        ny := y;
bocianu's avatar
bocianu committed
574
        dir := Random(4);
Wojciech Bociański's avatar
Wojciech Bociański committed
575 576 577 578 579 580
        if (dir = DIR_UP) then Dec(ny);
        if (dir = DIR_DOWN) then Inc(ny);
        if (dir = DIR_LEFT) then Dec(nx);
        if (dir = DIR_RIGHT) then Inc(nx);
    until CanMoveTo(nx, ny);
    g^.dir := dir;
bocianu's avatar
bocianu committed
581
    g^.deadCount := 0;
bocianu's avatar
bocianu committed
582
    g^.despawn := 0;
Wojciech Bociański's avatar
Wojciech Bociański committed
583 584
    ghostSpritesheets[ghostNumber] := ghost_sprites[dir];
    g^.step := 0;
585
    g^.level := level.ai[ghostNumber];
bocianu's avatar
bocianu committed
586 587 588
    Inc(ghostCount);
end;

589 590 591
procedure KillGhost(ghostNumber: byte);
begin
    g := ghosts[ghostNumber];
592
    g^.dx := g^.x;
bocianu's avatar
bocianu committed
593 594
    if g^.dx = 0 then g^.dx := 1
    else if g^.dx = 20 then g^.dx := 19;
595 596 597 598 599 600 601
    g^.dy := g^.y;
    g^.deadCount := 150;
    if pillBonus = 100 then g^.reward := TILE_100;
    if pillBonus = 200 then g^.reward := TILE_200;
    if pillBonus = 300 then g^.reward := TILE_300;
    if pillBonus = 400 then g^.reward := TILE_400;
    if pillBonus = 500 then g^.reward := TILE_500;
Wojciech Bociański's avatar
Wojciech Bociański committed
602
    FillChar(pointer(PMGBASE + 512 + (ghostNumber shl 7)), 128, 0);
603 604
end;

bocianu's avatar
bocianu committed
605 606 607 608 609 610 611 612 613
procedure DespawnGhost(ghostNumber: byte);
begin
    g := ghosts[ghostNumber];
    g^.deadCount := 0;
    g^.dir := DIR_NONE;
    Dec(ghostCount);
    FillChar(pointer(PMGBASE + 512 + (ghostNumber shl 7)), 128, 0);
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
614
function RandomDir(mask: byte): byte;
bocianu's avatar
bocianu committed
615 616 617 618 619 620 621 622 623 624
var a:array[0..3] of byte;
	ai:byte;
begin
	ai:=0;
	for result:=0 to 3 do 
		if dirs[result] and mask <> 0 then begin
			a[ai]:=dirs[result];
			inc(ai)
		end;
    result := n2dir[a[Random(ai)]];
625 626
end;

627 628 629 630 631 632 633
function Reverse(dir: byte): byte;
begin
    result := dir;
    if (dir and 3 <> 0) then result := result xor 3;
    if (dir and 12 <> 0) then result := result xor 12;
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
634 635 636
function FollowPac(ghostNumber, mask: byte): byte;
var dx, dy: byte;
    bestdir1, bestdir2: byte;
bocianu's avatar
bocianu committed
637
begin
bocianu's avatar
bocianu committed
638
    g := ghosts[ghostNumber];
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
    if Random(g^.level) = 0 then exit(RandomDir(mask));
    dx := Abs(shortInt(pac.x - g^.x));
    dy := Abs(shortInt(pac.y - g^.y));
    if dy > dx then begin
        if g^.y < pac.y then bestdir1 := JOY_DOWN
            else bestdir1 := JOY_UP;
        if g^.x < pac.x then bestdir2 := JOY_RIGHT
            else bestdir2 := JOY_LEFT;
    end else begin
        if g^.x < pac.x then bestdir1 := JOY_RIGHT
            else bestdir1 := JOY_LEFT;
        if g^.y < pac.y then bestdir2 := JOY_DOWN
            else bestdir2 := JOY_UP;
    end;
    result := Random(4);  // how often choose second choice
    if result = 0 then begin
        result := bestdir2;
        bestdir2 := bestdir1;
        bestdir1 := result;
    end;
659

660 661 662
    if gameState = GAME_ESCAPE then begin
        bestdir1 := Reverse(bestdir1); // reverse directions when ghost escapes
        bestdir2 := Reverse(bestdir2);
bocianu's avatar
bocianu committed
663
    end;
664

665 666 667 668 669
    result := DIR_NONE;
    if (bestdir2 and mask) <> 0 then result := n2dir[bestdir2];
    if (bestdir1 and mask) <> 0 then result := n2dir[bestdir1];
    if result = DIR_NONE then result := RandomDir(mask);

bocianu's avatar
bocianu committed
670 671 672
end;

procedure MoveGhost(ghostNumber: byte);
Wojciech Bociański's avatar
Wojciech Bociański committed
673
var exitMask, exitCount, step: byte;
bocianu's avatar
bocianu committed
674 675
begin
    g := ghosts[ghostNumber];
676 677 678 679 680 681 682 683 684
    if (g^.deadCount > 0) then begin
        PaintOnBoard(g^.dx, g^.dy, g^.reward);
        g^.deadCount := g^.deadCount - 1;
        if g^.deadCount = 0 then begin
            PaintOnBoard(g^.dx, g^.dy, GetBoard(g^.dx, g^.dy));
            g^.dir := DIR_NONE;
            Dec(ghostCount);
        end;
    end else
Wojciech Bociański's avatar
Wojciech Bociański committed
685
    if (g^.dir <> DIR_NONE) then begin
bocianu's avatar
bocianu committed
686
        g^.sy := shortInt(g^.y - viewportTop) shl 3;
bocianu's avatar
bocianu committed
687 688
        g^.sy := g^.sy + 16;
        g^.sy := g^.sy - vscrollstep;
689
        g^.sx := PMG_LEFT_OFFSET + g^.x shl 3;
bocianu's avatar
bocianu committed
690 691 692 693

        if Odd(g^.dir) then g^.sx := g^.sx + g^.step
        else g^.sy := g^.sy + g^.step;

Wojciech Bociański's avatar
Wojciech Bociański committed
694 695
        g^.spriteHeight := SPRITE_HEIGHT;
        g^.spriteOffset := 0;
bocianu's avatar
bocianu committed
696

Wojciech Bociański's avatar
Wojciech Bociański committed
697 698 699
        if g^.sy < BOARD_TOP then begin   // sprite out of top boundry
            vOffset := BOARD_TOP - g^.sy;
            if vOffset > SPRITE_HEIGHT then g^.spriteHeight := 0
bocianu's avatar
bocianu committed
700
            else begin  // if part visible
Wojciech Bociański's avatar
Wojciech Bociański committed
701 702 703
                g^.sy := BOARD_TOP;
                g^.spriteOffset := vOffset;
                g^.spriteHeight := SPRITE_HEIGHT - vOffset;
bocianu's avatar
bocianu committed
704 705
            end;
        end;
Wojciech Bociański's avatar
Wojciech Bociański committed
706 707 708 709
        if g^.sy > BOARD_BOTTOM then begin    // sprite out of bottom boundry
            vOffset := g^.sy - BOARD_BOTTOM;
            if vOffset > SPRITE_HEIGHT then g^.spriteHeight := 0
            else g^.spriteHeight := g^.spriteHeight - vOffset;
bocianu's avatar
bocianu committed
710 711
        end;

712
        step:=1;
Wojciech Bociański's avatar
Wojciech Bociański committed
713 714 715
        if gameState = GAME_ESCAPE then step := frame and 1; // slow down by skipin steps in escape mode
        if ((g^.dir = DIR_RIGHT) or (g^.dir = DIR_DOWN)) then g^.step := g^.step + step;
        if ((g^.dir = DIR_LEFT) or (g^.dir = DIR_UP)) then g^.step := g^.step - step;
716

Wojciech Bociański's avatar
Wojciech Bociański committed
717
        if Abs(g^.step) = 8 then begin // moved into new position
bocianu's avatar
bocianu committed
718
            if (Abs(shortInt(g^.y-pac.y))<GHOST_DESPAWN_DISTANCE) then g^.despawn := 0
719
            else g^.despawn := g^.despawn + 1;
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
            if g^.despawn = GHOST_DESPAWN_DELAY then DespawnGhost(ghostNumber)
            else begin
                if g^.step > 0 then g^.step := 1 else g^.step := -1;
                if Odd(g^.dir) then begin // horizontal
                    g^.x := g^.x + g^.step;
                    if not CanMoveTo(g^.x + g^.step,g^.y) then g^.dir := DIR_NONE; // on turns try to follow pac
                end else begin                // vertical
                    g^.y := g^.y + g^.step;
                    if not CanMoveTo(g^.x,g^.y + g^.step) then g^.dir := DIR_NONE; // on turns try to follow pac
                end;
                g^.step := 0;

                exitMask := GetNeighbours(g^.x, g^.y);
                exitCount := exitMask shr 4;
                 // if more than 3 exits or cannot continue move then take decision
                if (exitCount > 2) or (g^.dir = DIR_NONE) then g^.dir := FollowPac(ghostNumber,exitMask);
            end;
bocianu's avatar
bocianu committed
737
        end;
Wojciech Bociański's avatar
Wojciech Bociański committed
738
        ghostSpritesheets[ghostNumber] := ghost_sprites[g^.dir];
bocianu's avatar
bocianu committed
739 740 741
    end;
end;

bocianu's avatar
bocianu committed
742
procedure ShowGhost(ghostNumber: byte);
Wojciech Bociański's avatar
Wojciech Bociański committed
743
var s: array[0..0] of word;
bocianu's avatar
bocianu committed
744
begin
bocianu's avatar
bocianu committed
745
    g := ghosts[ghostNumber];
Wojciech Bociański's avatar
Wojciech Bociański committed
746
    if gameState = GAME_ESCAPE then s := @ghost_escape
747
    else s := ghostSpritesheets[ghostNumber];
748
    if (g^.dir <> DIR_NONE) and (g^.deadCount = 0) then begin
749
        if (g^.spriteHeight <> 0) then
Wojciech Bociański's avatar
Wojciech Bociański committed
750
           Move(pointer(s[ghostFrame] + g^.spriteOffset), pointer(PMGBASE + 512 + (ghostNumber shl 7) + g^.sy), g^.spriteHeight)
751
        else
Wojciech Bociański's avatar
Wojciech Bociański committed
752 753
           g^.sx := 0;
        Poke($d000+ghostNumber, g^.sx); // hpos
754
    end;
bocianu's avatar
bocianu committed
755 756
end;

bocianu's avatar
bocianu committed
757 758 759 760 761 762 763 764 765 766 767 768 769 770
procedure MoveGhosts;
begin
    MoveGhost(0);
    MoveGhost(1);
    MoveGhost(2);
    MoveGhost(3);
end;

procedure ShowGhosts;
begin
    ShowGhost(0);
    ShowGhost(1);
    ShowGhost(2);
    ShowGhost(3);
Wojciech Bociański's avatar
Wojciech Bociański committed
771
    ghostFrame := (frame shr 4) and 1;
bocianu's avatar
bocianu committed
772 773
end;

774 775
function FreeGhostSlot:byte;
begin
776
    for i := 0 to GHOST_NUM - 1 do begin
777
        g := ghosts[i];
778
        if g^.dir = DIR_NONE then exit(i);
779 780 781
    end;
end;

782
function SelectSpawner: byte;
783
var i,min,dist:byte;
784
begin
785 786 787 788 789 790 791 792
    min:=100;
    for i:=0 to spawnerCount-1 do begin
        dist := Abs(shortint(Lo(spawners[i]) - pac.y));
        if dist < min then begin
            result := i;
            min := dist;
        end;
    end;
793 794 795 796 797 798 799 800
end;

procedure CloseAllSpawners;
begin
    for i := 0 to spawnerCount-1 do
        PaintOnBoard(Hi(spawners[i]),Lo(spawners[i]),TILE_SPAWNER);
end;

801
procedure TryToSpawnGhost;
Wojciech Bociański's avatar
Wojciech Bociański committed
802
var ghostNumber: byte;
803
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
804 805
    if ghostCount < 4 then begin
        ghostNumber := FreeGhostSlot;
806 807 808 809 810 811 812
        if spawnCounter = 80 then begin
            spawnerActive := SelectSpawner;
            PaintOnBoard(Hi(spawners[spawnerActive]),Lo(spawners[spawnerActive]),TILE_SPAWNER_OPEN1);
        end;
        if spawnCounter = 40 then begin
            PaintOnBoard(Hi(spawners[spawnerActive]),Lo(spawners[spawnerActive]),TILE_SPAWNER_OPEN2);
        end;
813
        if spawnCounter = word(ghostNumber + 1) then begin
814 815
            PaintOnBoard(Hi(spawners[spawnerActive]),Lo(spawners[spawnerActive]),TILE_SPAWNER);
            SpawnGhost(ghostNumber,Hi(spawners[spawnerActive]),Lo(spawners[spawnerActive]));
Wojciech Bociański's avatar
Wojciech Bociański committed
816
            spawnCounter := spawnDelay;
817 818 819 820
        end;
        Dec(spawnCounter);
    end;
end;
bocianu's avatar
bocianu committed
821

Wojciech Bociański's avatar
Wojciech Bociański committed
822

bocianu's avatar
bocianu committed
823
{********************************************** PAC ROUTINES ******************************************}
bocianu's avatar
bocianu committed
824

bocianu's avatar
bocianu committed
825

Wojciech Bociański's avatar
Wojciech Bociański committed
826
procedure InitPac(x, y: byte);
bocianu's avatar
bocianu committed
827
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
828 829 830 831 832 833 834
    pac.x := x;
    pac.y := y;
    pac.step := 0;
    pac.frame := 0;
    pac.dir := DIR_NONE;
    pac_sprites[DIR_NONE] := pac_sprites[DIR_UP];
    pac.sprite := pac_sprites[pac.dir];
835
    viewportTop := pac.y - 6;
836
    if (pac.y > byte(boardLastLine - 6)) then viewportTop := viewportTop + (boardLastLine-6-pac.y);
837
    if (pac.y < 6) then viewportTop := 0;
bocianu's avatar
bocianu committed
838 839
end;

bocianu's avatar
bocianu committed
840 841
procedure ShowPac;
begin
bocianu's avatar
bocianu committed
842 843 844 845
    _HPOSM3 := pac.sx;
    _HPOSM2 := pac.sx+2;
    _HPOSM1 := pac.sx+4;
    _HPOSM0 := pac.sx+6;
Wojciech Bociański's avatar
Wojciech Bociański committed
846
    Move(pac.sprite[pac.frame], pointer(PMGBASE + 384 + pac.sy), SPRITE_HEIGHT);
bocianu's avatar
bocianu committed
847 848
end;

849 850
procedure MovePac;
begin
bocianu's avatar
bocianu committed
851
    pac.sy := 16 + byte((pac.y - viewportTop) shl 3) - vscrollstep;  // count coordinates relative to viewport
852
    pac.sx := PMG_LEFT_OFFSET + pac.x shl 3;
853

bocianu's avatar
bocianu committed
854 855
    if Odd(pac.dir) then pac.sx := pac.sx + pac.step  // increment coordinates by current pac step
    else pac.sy := pac.sy + pac.step;
856

Wojciech Bociański's avatar
Wojciech Bociański committed
857
    pac.frame:=(pac.frame + 1) and 7; // jump to next animation frame
858

bocianu's avatar
bocianu committed
859 860
    if ((pac.dir = DIR_RIGHT) or (pac.dir = DIR_DOWN)) then Inc(pac.step); // increment step for right or down movement
    if ((pac.dir = DIR_LEFT) or (pac.dir = DIR_UP)) then Dec(pac.step); // decrement for remaining directions
861

Wojciech Bociański's avatar
Wojciech Bociański committed
862 863
    if abs(pac.step) = 8 then begin // moved into new position
        if pac.step > 0 then pac.step := 1 else pac.step := -1;   // get position increment
bocianu's avatar
bocianu committed
864 865
        if Odd(pac.dir) then  // horizontal
            pac.x := pac.x + pac.step // increment absolute position
866
        else                // vertical
bocianu's avatar
bocianu committed
867
            pac.y := pac.y + pac.step; // increment absolute position
Wojciech Bociański's avatar
Wojciech Bociański committed
868
        pac.step := 0;
869 870 871
    end;
end;

bocianu's avatar
bocianu committed
872 873
procedure CheckIfPacTurnsBack;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
874
    if _STICK0 <> 15 then begin    // if stick moved then check if pac wants turn back
bocianu's avatar
bocianu committed
875
        joy := not (_STICK0 or %11110000);
Wojciech Bociański's avatar
Wojciech Bociański committed
876
        if joy2dir[joy] = pac.dir then begin
bocianu's avatar
bocianu committed
877
            viewportMoveDir := -viewportMoveDir;
Wojciech Bociański's avatar
Wojciech Bociański committed
878
            pac.dir := (pac.dir + 2) and 3;
bocianu's avatar
bocianu committed
879 880 881 882
        end;
    end;
end;

883 884
procedure CheckIfPacTurns;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
885
    if (pac.dir <> DIR_NONE) then begin; // if pac was, moving check if it's able to continue movement
886 887 888 889 890 891
        case pac.dir of
            DIR_UP: if (not canMoveTo(pac.x, pac.y - 1)) then pac.dir := DIR_NONE;
            DIR_DOWN: if (not canMoveTo(pac.x, pac.y + 1)) then pac.dir := DIR_NONE;
            DIR_LEFT: if (not canMoveTo(pac.x - 1, pac.y)) then pac.dir := DIR_NONE;
            DIR_RIGHT: if (not canMoveTo(pac.x + 1, pac.y)) then pac.dir := DIR_NONE;
        end;
892 893
    end;

Wojciech Bociański's avatar
Wojciech Bociański committed
894
    if joy <> 0 then begin    // if stick is moved try to turn pac
bocianu's avatar
bocianu committed
895
                            // for diagonal stick movement, turning sides has bigger priority than fwd/bck movement
Wojciech Bociański's avatar
Wojciech Bociański committed
896
        dir := pac.dir;
897
        flag := pac.dir = DIR_NONE;
Wojciech Bociański's avatar
Wojciech Bociański committed
898 899
        if (joy and JOY_UP <> 0) then // up
            if canMoveTo(pac.x, pac.y-1) then
900
                if Odd(pac.dir) or flag then dir := DIR_UP;
Wojciech Bociański's avatar
Wojciech Bociański committed
901 902 903

        if (joy and JOY_DOWN <> 0) then // down
            if canMoveTo(pac.x, pac.y+1) then
904
                if Odd(pac.dir) or flag then dir := DIR_DOWN;
Wojciech Bociański's avatar
Wojciech Bociański committed
905 906 907

        if (joy and JOY_LEFT <> 0) then // left
            if canMoveTo(pac.x-1, pac.y) then
908
                if not Odd(pac.dir) or flag then dir := DIR_LEFT;
Wojciech Bociański's avatar
Wojciech Bociański committed
909 910 911

        if (joy and JOY_RIGHT <> 0) then // right
            if canMoveTo(pac.x+1, pac.y) then
912
                if not Odd(pac.dir) or flag then dir := DIR_RIGHT;
Wojciech Bociański's avatar
Wojciech Bociański committed
913
        pac.dir := dir;
914 915
    end;

Wojciech Bociański's avatar
Wojciech Bociański committed
916
    pac.sprite := pac_sprites[pac.dir];    // set sprite sheet based on direction
917
    pac_sprites[DIR_NONE] := pac.sprite;   // set current sprite sheet for idle
Wojciech Bociański's avatar
Wojciech Bociański committed
918
    viewportMoveDir := 0;                    // reset vscroll
919

Wojciech Bociański's avatar
Wojciech Bociański committed
920
    if (pac.dir <> DIR_NONE) and not Odd(pac.dir) then begin // if pac moves verticaly
921
        if (pac.dir = DIR_DOWN) and (pac.y > 5) and (pac.y < byte(boardLastLine - 6)) then begin  // and up scroll needed
bocianu's avatar
bocianu committed
922
            viewportMoveDir := 1; // start scroll screen up
923
        end;
924
        if (pac.dir = DIR_UP) and (pac.y > 6) and (pac.y < boardLastLine - 5) then begin  // or scroll down needed
bocianu's avatar
bocianu committed
925
            viewportMoveDir := -1; // start scroll screen down
926 927 928 929
        end;
    end;
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
930
function PacColider: byte;
931 932 933 934 935 936
begin
    result := _SIZEP0 or _SIZEP1 or _SIZEP2 or _SIZEP3;
end;

function TouchedGhostNum(collider:byte):byte;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
937 938 939 940
    if (collider and %0001) <> 0 then result := 0;
    if (collider and %0010) <> 0 then result := 1;
    if (collider and %0100) <> 0 then result := 2;
    if (collider and %1000) <> 0 then result := 3;
941 942 943 944 945 946
end;

procedure PacDies;
begin
    Dec(lives);
    UpdateLives;
Wojciech Bociański's avatar
Wojciech Bociański committed
947 948 949
    frame := 1;
    pac.sprite := @pac_dies;
    pac.frame := 0;
950 951
end;

bocianu's avatar
bocianu committed
952

bocianu's avatar
bocianu committed
953 954
{********************************************** GAME ROUTINES ******************************************}

bocianu's avatar
bocianu committed
955 956 957 958

procedure GrabGui;
begin
    PMG_init(Hi(PMGBASE));
959
    _GPRIOR_ := 1 + 16; // 5th player
bocianu's avatar
bocianu committed
960 961
end;

bocianu's avatar
bocianu committed
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
procedure ReadSecret;
var lk:byte;
begin
	if secret[0] <> 0 then begin
		if _KBCODE < 64 then  begin
			lk := keycodes[_KBCODE];
			if lk = secret[secret_id] then begin
				inc(secret_id);
				if secret_id = SECRET_LEN then begin
					secret_id:=0;
					cheat_mode := not cheat_mode;
					if cheat_mode then poke(TXT_RAM,32) 
					else poke(TXT_RAM,0);
				end;
			end;
		end;
	end;
end;

981
procedure ShowTitleScreen;
bocianu's avatar
bocianu committed
982 983
var selectState,selectState_:byte;
	selectFrame:word;
984
begin
985 986 987
    Pause;
    setDli(@Dli1T);
    _CHBASE := Hi(CHARSET_TITLE_ADDRESS);
bocianu's avatar
bocianu committed
988 989
    Move(DL_title,pointer(DL),64);
    _SDLSTL_ := DL;
bocianu's avatar
bocianu committed
990
    _COLOR4_ := 0;
991
    PMG_clear;
bocianu's avatar
bocianu committed
992
    ClearTxT;
bocianu's avatar
bocianu committed
993 994 995
	secret_id:=0;
	if cheat_mode then poke(TXT_RAM,32) 
	else poke(TXT_RAM,0);
996

bocianu's avatar
bocianu committed
997 998 999
    UpdateHiScore;
    Move(strings[11],pointer(TXT_RAM+12),9);
    if hiscore = 0 then Move(strings[1],pointer(TXT_RAM+5),30);
bocianu's avatar
bocianu committed
1000 1001
	selectState_ := _CONSOL and 2;
	selectFrame := 0;
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014

    GhostColorsDefault;
    for i:=0 to 3 do begin
        g := ghosts[i];
        g^.dir := DIR_RIGHT;
        g^.spriteHeight := 12;
        g^.spriteOffset := 0;
        g^.deadCount := 0;
        ghostSpritesheets[i] := ghost_sprites[DIR_RIGHT];
        g^.step := 0;
        g^.sy := 53 + i * 12;
        g^.sx := 0;
    end;
1015

1016 1017
    titleStage := 0;
    repeat
1018
        if music then msx.Play;
1019 1020
        Pause;
        ghostFrame := (frame shr 4) and 1;
bocianu's avatar
bocianu committed
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
        
        if selectFrame = 0 then begin 
			Move(strings[12],pointer(TXT_RAM+40*15+8),23);
			UpdateLevel
		end;
        if selectFrame = 150 then Move(strings[2],pointer(TXT_RAM+40*15+11),19);
        if (selectFrame = 100) or (selectFrame = 250) then FillByte(pointer(TXT_RAM+40*15+8),26,0);
		
		Inc(selectFrame);
		if selectFrame=300 then selectFrame:=0;
		
bocianu's avatar
bocianu committed
1032 1033
		ReadSecret;

1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
        case titleStage of
            0: begin
                for i:=0 to 3 do begin
                    g := ghosts[i];
                    g^.sx := g^.sx + 1;
                    if byte(g^.sx) = 106 then titleStage:=1;
                    ShowGhost(i);
                end;
            end;
            1: begin
                frame := 0;
                Move(strings[4],pointer(TXT_RAM+40*2+18),8);
                Move(strings[5],pointer(TXT_RAM+40*5+18),7);
                Move(strings[6],pointer(TXT_RAM+40*8+18),7);
                Move(strings[7],pointer(TXT_RAM+40*11+18),6);
                titleStage:=2;
            end;
            2: begin
                ShowGhosts;
                if frame = 0 then titleStage:=3;
            end;
            3: begin
                FillByte(pointer(TXT_RAM+40*2),400,0);
                titleStage:=6;
            end;
            6: begin
                for i:=0 to 3 do begin
                    g := ghosts[i];
                    g^.sx := g^.sx + 1;
                    if byte(g^.sx) = 0 then titleStage:=7;
                    ShowGhost(i);
                end;
            end;
            7: begin
                frame := 0;
                pac.step := 0;
                pac.frame := 0;
                pac.sprite := pac_sprites[DIR_RIGHT];
                pac.sy := 69;
                Move(strings[3],pointer(TXT_RAM+40*2+5),31);
                Move(strings[8],pointer(TXT_RAM+40*4+8),25);
                Move(strings[9],pointer(TXT_RAM+40*8+8),25);
1076
                Move(strings[10],pointer(TXT_RAM+40*11+3),35);
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
                titleStage:=8;
            end;
            8: begin
                pac.frame:=(pac.frame + 1) and 7;
                pac.sx := frame;
                ShowPac;
                if frame = 0 then titleStage:=9;
            end;
            9: begin
                if frame = 200 then titleStage:=10;
            end;
            10: begin
                FillByte(pointer(TXT_RAM+40*2),400,0);
                titleStage:=0;
            end;

        end;
bocianu's avatar
bocianu committed
1094 1095 1096 1097 1098 1099 1100
		selectState := _CONSOL and 2;
		if (selectState_ <> selectState) and (selectState = 0) then begin
			Inc(startingLevel);
			if startingLevel=levelCount then startingLevel := 0;
			selectFrame:=0;
		end;
		selectState_ := selectState;
1101

bocianu's avatar
bocianu committed
1102
    until (_STRIG0 = 0) or (_CONSOL and 1 = 0);
bocianu's avatar
bocianu committed
1103
    repeat until _STRIG0 = 1
1104 1105
end;

bocianu's avatar
bocianu committed
1106 1107
procedure ShowLevelScreen;
begin
bocianu's avatar
bocianu committed
1108
    ClearTxT;
1109
    Pause;
bocianu's avatar
bocianu committed
1110 1111
    Move(DL_LEVEL,pointer(DL),Length(DL_LEVEL));
    _SDLSTL_ := DL;
bocianu's avatar
bocianu committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
    _COLOR4_ := 15;
    PMG_clear;
end;

procedure ShowDM;
var vptr,data:word;
    row,col,b:byte;
    chunk: array[0..0] of byte;
begin
    data := DM_DATA + $100;
    vptr := TXT_RAM + 2;
    row := 0;
    repeat
        col := 0;
        repeat
            chunk := pointer(data);
            b := chunk[0];
            b := b shl 2;
            b := b or chunk[1];
            b := b shl 2;
            b := b or chunk[2];
            b := b shl 2;
            b := b or chunk[3];
            Poke(vptr,b);
            Inc(data,4);
            Inc(col,4);
            Inc(vptr);
        until col = DM_SIZE;
        Inc(row);
        Inc(vptr,4);
    until row = DM_SIZE;
    Poke(708,0);
end;

bocianu's avatar
bocianu committed
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
procedure PutCharPM(c:byte;offset:word);
var src1:word;
    x:byte;
begin
    src1:=CHARSET_TITLE_ADDRESS+c*8;
    for x:=0 to 7 do begin
        Poke(offset+x,Peek(src1));
        Inc(src1);
    end;
end;

procedure ShowScorePM;
var offset:word;
    digit:byte;
begin
    offset:=PMGBASE + 512 + 64 - byte(length(s) * 5);
    for i:=1 to Length(s) do begin
        digit:=byte(s[i])+16;
        PutCharPM(digit,offset);
        PutCharPM(digit,offset+128);
        Inc(offset,10);
    end;
end;

bocianu's avatar
bocianu committed
1170 1171 1172 1173 1174 1175 1176 1177
procedure RandomizeSecret;
var i:byte;
begin
	for i:=0 to SECRET_LEN-1 do begin
		secret[i]:=Random(26)+97;
	end;
end;

bocianu's avatar
bocianu committed
1178
procedure ShowDataMatrix(score:cardinal);
bocianu's avatar
bocianu committed
1179 1180
var p,x,c:byte;
var sine:array [0..31] of byte = (2,2,3,3,3,4,4,4,4,4,4,4,3,3,3,2,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2);
bocianu's avatar
bocianu committed
1181
begin
bocianu's avatar
bocianu committed
1182
    p := 0;
bocianu's avatar
bocianu committed
1183
    ShowLevelScreen;
bocianu's avatar
bocianu committed
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
    if score < 100 then begin
		RandomizeSecret;
		for c := 0 to SECRET_LEN-1 do s[c+1] := char(secret[c]);
		s[0] := char(SECRET_LEN);
		SetMessage(s, DM_DATA);
    end else begin
		if cheat_mode then begin
			s := 'No Hiscore For Cheaters!!!';
			SetMessage(s, DM_DATA);
			Str(score,s);
		end else begin
			Str(score,s);
			SetMessage(HSC_URI, DM_DATA);
			SetMessage(s, DM_DATA + 26);
		end;
		ShowScorePM;
    end;
bocianu's avatar
bocianu committed
1201 1202
    CalculateMatrix;
    ShowDM;
bocianu's avatar
bocianu committed
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
    _SIZEP0 := 1;
    _SIZEP1 := 1;
    repeat
        c:=p+_VCOUNT;
        x:=48 + sine[c and 31];
        _WSYNC := 0;
        _PCOLR0 := c;
        _PCOLR1 := c;
        _HPOS0 := x;
        _HPOS1 := 140 + x;
        if _vcount=126 then inc(p);
    until (_STRIG0 = 0) or (_CONSOL and 1 = 0); // or (_SKSTAT and 4 = 0);

    repeat until _STRIG0 = 1;
    _SIZEP0 := 0;
    _SIZEP1 := 0;
bocianu's avatar
bocianu committed
1219 1220
end;

bocianu's avatar
bocianu committed
1221
procedure InitGameScreen;
bocianu's avatar
bocianu committed
1222
begin
1223 1224
    Pause;
    setDli(@Dli1);
bocianu's avatar
bocianu committed
1225 1226
    Move(DL_game,pointer(DLG),Length(DL_game));
    _SDLSTL_ := DLG;
bocianu's avatar
bocianu committed
1227 1228
    _VSCROL := 0;
    PMG_clear;
bocianu's avatar
bocianu committed
1229 1230
end;

bocianu's avatar
bocianu committed
1231 1232
function ReadLevelData(ptr: word): word;
var tile,count,tileSize:byte;
1233
    p:word;
bocianu's avatar
bocianu committed
1234 1235 1236
begin
    tile := levelData[0];
    count := levelData[1];
1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
    p := 2;
    tileSize := 2;

    case tile of

        TILE_DOT, TILE_EMPTY, TILE_VOID: begin
            tileSize := 4;
            repeat
                BoardRect(levelData[p], levelData[p+1], levelData[p+2], levelData[p+3], tile);
                p := p + tileSize;
                Dec(count)
            until count = 0;
        end;
bocianu's avatar
bocianu committed
1250

1251 1252 1253 1254 1255 1256 1257
        TILE_PILL: begin
            repeat
                SetBoard(levelData[p], levelData[p+1], tile);
                p := p + tileSize;
                Dec(count)
            until count = 0;
        end;
bocianu's avatar
bocianu committed
1258

1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
        TILE_SPAWNER: begin
            spawnerCount := 0;
            repeat
                SetBoard(levelData[p], levelData[p+1], tile);
                spawners[spawnerCount] := (levelData[p] shl 8) + levelData[p+1];
                p := p + tileSize;
                Inc(spawnerCount);
                Dec(count);
            until count = 0;
        end;
bocianu's avatar
bocianu committed
1269

1270 1271 1272 1273 1274 1275 1276 1277 1278
        TILE_WARP_LEFT: begin
            tileSize := 1;
            repeat
                SetBoard(0, levelData[p], TILE_WARP_LEFT);
                SetBoard(20, levelData[p], TILE_WARP_RIGHT);
                p := p + tileSize;
                Dec(count);
            until count = 0;
        end;
1279 1280

    end;
1281
    result:=ptr+p;
bocianu's avatar
bocianu committed
1282 1283 1284 1285 1286 1287 1288 1289 1290
end;

procedure LoadLevel(levelNum: byte);
var levelPtr: word;
begin
    ClearFeatures;
    levelPtr := levels[levelNum];
    Move(pointer(levelPtr), level, SizeOf(level));
    spawnDelay := level.delay * 50;
1291
    pillTime := level.pillTime * 50;
bocianu's avatar
bocianu committed
1292 1293 1294
    levelPtr := levelPtr + SizeOf(level);
    levelData := pointer(levelPtr);

1295
    Fillchar(@board, BOARDSIZE, TILE_VOID);
bocianu's avatar
bocianu committed
1296 1297 1298 1299 1300 1301 1302

    repeat
        levelPtr := ReadLevelData(levelPtr);
        levelData := pointer(levelPtr);
    until levelData[0]=$ff;
end;

bocianu's avatar
bocianu committed
1303 1304
procedure InitGame;
begin
bocianu's avatar
bocianu committed
1305
    lives := GAME_LIVES;
bocianu's avatar
bocianu committed
1306
	if cheat_mode then lives := 99;
bocianu's avatar
bocianu committed
1307
    score := 0;
1308
    liveBonus := 0;
bocianu's avatar
bocianu committed
1309
    currentLevel := startingLevel;
bocianu's avatar
bocianu committed
1310 1311
end;

1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
procedure InitView;
begin
    vscrollstep := 0;
    viewportMoveDir := 0;
    InitPac(level.startX, level.startY);
    ClearGhosts;
    spawnCounter := spawnDelay;
    GhostColorsDefault;
    gameState := GAME_NORMAL;
end;

bocianu's avatar
bocianu committed
1323
procedure InitLevel(lvl: byte);
bocianu's avatar
bocianu committed
1324
begin
bocianu's avatar
bocianu committed
1325 1326 1327 1328
    ShowLevelScreen;
    PutArray(0,2,5,word(@txt_level));
    Str(lvl+1,s);
    PutNumber(5-byte(s[0]),14,s);
bocianu's avatar
bocianu committed
1329
    LoadLevel(lvl);
bocianu's avatar
bocianu committed
1330 1331 1332 1333 1334 1335
    _COLOR0_ := level.colors[0];
    _COLOR1_ := level.colors[1];
    _COLOR2_ := level.colors[2];
    _COLOR3_ := level.colors[3];
    _COLOR4_ := level.colors[4];

1336
    PaintBoard;
1337
    InitView;
1338
    dotCount := dotCount - level.dotLimit;
bocianu's avatar
bocianu committed
1339
    ClearTxT;
Wojciech Bociański's avatar
Wojciech Bociański committed
1340
    food := 0;
bocianu's avatar
bocianu committed
1341 1342
    bonusPosPicked := false;
    bonusDelay := BONUS_DELAY * 50;
bocianu's avatar
bocianu committed
1343

1344
    Move(strings[0],pointer(TXT_RAM+1),36); // score topbar
bocianu's avatar
bocianu committed
1345 1346 1347
    UpdateScore;
    UpdateFood;
    UpdateLives;
bocianu's avatar
bocianu committed
1348 1349
end;

1350 1351 1352
procedure GameRestore;
begin
    InitView;
bocianu's avatar
bocianu committed
1353
    if bonusPosPicked then RemoveBonus;
1354 1355 1356 1357 1358
    CloseAllSpawners;
    PMG_clear;
    SongStart(SONG_GAME);
end;