pacmad.pas 41.9 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
    lives: byte;
64
    dotCount: word;
bocianu's avatar
bocianu committed
65

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

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

79 80 81 82 83 84 85
    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
86
{$I interrupts.inc}
bocianu's avatar
bocianu committed
87 88
{$I dlist.inc}

bocianu's avatar
bocianu committed
89

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

92

93 94 95 96 97 98 99 100 101 102 103 104
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
105
    mva #scr40 _atari._sdmctl_
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    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
128 129 130 131
procedure ClearTxT;
begin
    FillByte(pointer(TXT_RAM),$400,0);
end;
bocianu's avatar
bocianu committed
132 133 134

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

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
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
165 166
procedure UpdateFood;
begin
bocianu's avatar
bocianu committed
167
    Str(dotCount, s);
bocianu's avatar
bocianu committed
168 169 170 171 172 173
    asm {
        ldy #1
@       lda adr.s,y
        sub #32
        sta TXT_RAM+21,y
        iny
174
        dec adr.s
bocianu's avatar
bocianu committed
175 176 177 178 179 180 181 182
        bne @-
        lda #0
        sta TXT_RAM+21,y
    };
end;

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

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
procedure UpdateEndLevel;
begin
    Str(currentLevel+1, s);
    asm {
        ldy #1
@       lda adr.s,y
        sub #32
        sta TXT_RAM+36,y
        iny
        dec adr.s
        bne @-
        lda #0
        sta TXT_RAM+36,y
    };
end;

bocianu's avatar
bocianu committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
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
229 230 231
procedure SongStart(pattern:byte);
begin
    msx.Init(pattern);
bocianu's avatar
bocianu committed
232
    music := true;
bocianu's avatar
bocianu committed
233 234 235 236 237
end;

procedure SongStop;
begin
    msx.Init(SONG_SILENCE);
bocianu's avatar
bocianu committed
238
    music := false;
bocianu's avatar
bocianu committed
239
end;
240

241 242 243 244 245 246 247 248
procedure GhostColorsDefault;
begin
    _PCOLR0_ := COLOR_G1;
    _PCOLR1_ := COLOR_G2;
    _PCOLR2_ := COLOR_G3;
    _PCOLR3_ := COLOR_G4;
end;

249
procedure GhostColorsEscape(color: byte);
250 251 252 253 254 255 256
begin
    _PCOLR0_ := color;
    _PCOLR1_ := color;
    _PCOLR2_ := color;
    _PCOLR3_ := color;
end;

bocianu's avatar
bocianu committed
257 258 259
procedure putChar(dest:word;letter:word);
var src1,src2:word;
    x:byte;
260 261 262 263 264 265 266 267 268 269 270 271 272
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
273
var dest:word;
274
begin
bocianu's avatar
bocianu committed
275
    dest:=TXT_RAM+(y*10)+x;
276
    for i:=0 to size-1 do begin
bocianu's avatar
bocianu committed
277 278
        putChar(dest,Dpeek(a));
        Inc(dest,2);
279 280 281 282 283
        Inc(a,2);
    end;
end;

procedure putNumber(x,y:byte;num:TString);
bocianu's avatar
bocianu committed
284
var dest:word;
285
begin
bocianu's avatar
bocianu committed
286
    dest:=TXT_RAM+(y*10)+x;
287
    for i:=1 to byte(num[0]) do begin
bocianu's avatar
bocianu committed
288 289 290 291 292 293 294 295 296 297 298 299
        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);
300 301 302
    end
end;

bocianu's avatar
bocianu committed
303

304 305 306
(********************************************** BOARD ROUTINES ******************************************)


Wojciech Bociański's avatar
Wojciech Bociański committed
307
procedure PaintOnBoard(x, y, t: byte);
308
begin
309
    vOffset := dlOffset[y] + byte(x shl 1) - 1;
Wojciech Bociański's avatar
Wojciech Bociański committed
310
    Dpoke(vOffset, tiles[t]);
311 312
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
313
function GetBoard(x, y: byte): byte;
bocianu's avatar
bocianu committed
314 315
var pos: word;
begin
316
    pos := boardOffset[y] + x;
bocianu's avatar
bocianu committed
317 318 319
    result := board[pos];
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
320
procedure SetBoard(x, y, v: byte);
bocianu's avatar
bocianu committed
321 322
var pos: word;
begin
323
    pos := boardOffset[y] + x;
bocianu's avatar
bocianu committed
324
    board[pos] := v;
Wojciech Bociański's avatar
Wojciech Bociański committed
325
    PaintOnBoard(x, y, v);
bocianu's avatar
bocianu committed
326 327
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
328
function CanMoveTo(x, y: byte): boolean;
329
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
330
    Result := true;
331 332 333 334
    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);
335 336
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
337 338
function GetNeighbours(x, y: byte): byte;
var counter: byte;
339
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
340 341 342
    counter := 0;
    result := 0;
    if canMoveTo(x-1, y) then begin  // left
343
        Inc(result, %0100);
344 345
        Inc(counter);
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
346
    if canMoveTo(x+1, y) then begin
347
        Inc(result, %1000);    // right
348 349
        Inc(counter);
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
350
    if canMoveTo(x, y+1) then begin
351
        Inc(result, %0010);    // down
352 353
        Inc(counter);
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
354
    if canMoveTo(x, y-1) then begin
355
        Inc(result, %0001);    // up
356 357
        Inc(counter);
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
358 359
    counter := counter shl 4;
    result := result or counter;
360 361
end;

bocianu's avatar
bocianu committed
362 363 364 365
procedure AddScore(val:word);
begin
    Inc(score, val);
    Inc(liveBonus, val);
366
    if (liveBonus >= LIFE_BONUS) then begin
bocianu's avatar
bocianu committed
367 368
        Inc(lives);
        UpdateLives;
bocianu's avatar
bocianu committed
369
        msx.Sfx(3, 2, 24);
370
        Dec(liveBonus, LIFE_BONUS);
bocianu's avatar
bocianu committed
371 372 373 374
    end;
    UpdateScore;
end;

bocianu's avatar
bocianu committed
375
procedure CheckBoardAction;
Wojciech Bociański's avatar
Wojciech Bociański committed
376
var cell: byte;
bocianu's avatar
bocianu committed
377
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
378
    cell := GetBoard(pac.x, pac.y);
379 380 381
    case cell of
        TILE_DOT: begin
            AddScore(10);
382
            if dotCount<>0 then begin
bocianu's avatar
bocianu committed
383 384 385
				Dec(dotCount);
				if dotCount=0 then msx.Sfx(4, 2, 12);
			end;
386 387 388 389 390 391 392 393 394 395 396 397 398 399
            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
400
        end;
401 402 403 404 405 406 407 408 409
        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
410 411 412 413
        TILE_BONUS, TILE_BONUS+1, TILE_BONUS+2: begin
            AddScore(500);
            bonusCountdown:=100;
            SetBoard(bonusX,bonusY,TILE_500);
bocianu's avatar
bocianu committed
414
            msx.Sfx(5, 2, 12);
bocianu's avatar
bocianu committed
415
        end;
416
    end;
bocianu's avatar
bocianu committed
417 418
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
419
procedure BoardRect(x, y, w, h, f: byte);
bocianu's avatar
bocianu committed
420 421 422
begin
    Dec(h);
    Dec(w);
Wojciech Bociański's avatar
Wojciech Bociański committed
423 424 425
    for i := x to x + w do begin
        SetBoard(i, y, f);
        SetBoard(i, y + h, f);
bocianu's avatar
bocianu committed
426
    end;
427 428
    if h > 0 then begin
        Dec(h);
Wojciech Bociański's avatar
Wojciech Bociański committed
429 430 431
        for i := y + 1 to y + h do begin
            SetBoard(x, i, f);
            SetBoard(x+w ,i, f);
bocianu's avatar
bocianu committed
432
        end;
433
    end;
bocianu's avatar
bocianu committed
434 435
end;

bocianu's avatar
bocianu committed
436
procedure PaintBoard;
bocianu's avatar
bocianu committed
437
var row, col, cell, t: byte;
438
    voffset, boffset, tile: word;
Wojciech Bociański's avatar
Wojciech Bociański committed
439 440 441
begin
    voffset := GFX_RAM;
    boffset := 0;
442
    boardLastLine := 0;
443
    dotCount := 0;
444
    for row := 0 to BOARDHEIGHT - 1 do begin
bocianu's avatar
bocianu committed
445 446 447 448
        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
449 450 451 452 453 454
            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
455 456 457 458 459 460
            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
461 462 463
                tile := tiles[cell];
                if (cell = TILE_DOT) then Inc(dotCount);
                boardLastLine := row;
464
            end;
465
            Dpoke(voffset, tile);
bocianu's avatar
bocianu committed
466
            _COLOR0 := Lo(bOffset);
467
            Inc(voffset, 2);
468
            Inc(boffset);
469
        end;
470 471 472 473 474 475 476
        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
477
    end;
478
    Inc(boardLastLine,2);
bocianu's avatar
bocianu committed
479
    if GetBoard(level.exitX,level.exitY) = TILE_DOT then Dec(dotCount);
480
    SetBoard(level.exitX,level.exitY,TILE_EXIT);
bocianu's avatar
bocianu committed
481 482
end;

bocianu's avatar
bocianu committed
483 484 485 486 487
procedure ClearFeatures;
begin
    for i := 0 to SPAWNERS_NUM - 1 do spawners[i] := -1;
end;

488
procedure InitTables;
Wojciech Bociański's avatar
Wojciech Bociański committed
489 490 491 492 493 494 495 496 497
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;
498
        Inc(voffset, SCREEN_WIDTH);
Wojciech Bociański's avatar
Wojciech Bociański committed
499
        Inc(boffset, BOARDWIDTH);
500
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
501 502 503 504
    ghosts[0] := @ghost1;
    ghosts[1] := @ghost2;
    ghosts[2] := @ghost3;
    ghosts[3] := @ghost4;
bocianu's avatar
bocianu committed
505
    hiscore := 0;
bocianu's avatar
bocianu committed
506 507 508 509
    levelCount := 0;
    repeat
        Inc(levelCount)
    until levels[levelCount] = 0;
bocianu's avatar
bocianu committed
510 511 512 513 514 515
    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
516
	startingLevel := 0;
bocianu's avatar
bocianu committed
517 518
end;

bocianu's avatar
bocianu committed
519 520
procedure MoveViewport;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
521 522 523 524
    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
525
        end;
Wojciech Bociański's avatar
Wojciech Bociański committed
526 527
        if (vscrollstep = 0) and (viewportMoveDir = 1) then begin // if vscroll ends
            viewportTop := viewportTop + viewportMoveDir;
bocianu's avatar
bocianu committed
528 529 530 531 532
        end;
    end;
    dltop := dlOffset[viewportTop];
end;

533 534 535 536 537 538
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
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
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
565

bocianu's avatar
bocianu committed
566
{********************************************** GHOST ROUTINES ******************************************}
bocianu's avatar
bocianu committed
567

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

bocianu's avatar
bocianu committed
569 570
procedure ClearGhosts;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
571
    for i := 0 to GHOST_NUM-1 do begin
bocianu's avatar
bocianu committed
572 573
        g := ghosts[i];
        g^.dir := DIR_NONE;
bocianu's avatar
bocianu committed
574
        g^.deadCount := 0;
bocianu's avatar
bocianu committed
575
    end;
Wojciech Bociański's avatar
Wojciech Bociański committed
576
    ghostCount := 0;
577
end;
bocianu's avatar
bocianu committed
578

Wojciech Bociański's avatar
Wojciech Bociański committed
579 580
procedure SpawnGhost(ghostNumber, x, y: byte);
var dir, nx, ny: byte;
bocianu's avatar
bocianu committed
581
begin
bocianu's avatar
bocianu committed
582
    g := ghosts[ghostNumber];
Wojciech Bociański's avatar
Wojciech Bociański committed
583 584
    g^.x := x;
    g^.y := y;
bocianu's avatar
bocianu committed
585
    repeat
Wojciech Bociański's avatar
Wojciech Bociański committed
586 587
        nx := x;
        ny := y;
bocianu's avatar
bocianu committed
588
        dir := Random(4);
Wojciech Bociański's avatar
Wojciech Bociański committed
589 590 591 592 593 594
        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
595
    g^.deadCount := 0;
bocianu's avatar
bocianu committed
596
    g^.despawn := 0;
Wojciech Bociański's avatar
Wojciech Bociański committed
597 598
    ghostSpritesheets[ghostNumber] := ghost_sprites[dir];
    g^.step := 0;
599
    g^.level := level.ai[ghostNumber];
bocianu's avatar
bocianu committed
600 601 602
    Inc(ghostCount);
end;

603 604 605
procedure KillGhost(ghostNumber: byte);
begin
    g := ghosts[ghostNumber];
606
    g^.dx := g^.x;
bocianu's avatar
bocianu committed
607 608
    if g^.dx = 0 then g^.dx := 1
    else if g^.dx = 20 then g^.dx := 19;
609 610 611 612 613 614 615
    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;
616
    PaintOnBoard(g^.dx, g^.dy, g^.reward);
Wojciech Bociański's avatar
Wojciech Bociański committed
617
    FillChar(pointer(PMGBASE + 512 + (ghostNumber shl 7)), 128, 0);
618 619
end;

bocianu's avatar
bocianu committed
620 621 622 623 624 625 626 627 628
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
629
function RandomDir(mask: byte): byte;
bocianu's avatar
bocianu committed
630 631 632 633 634 635 636 637 638 639
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)]];
640 641
end;

642 643 644 645 646 647 648
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
649 650 651
function FollowPac(ghostNumber, mask: byte): byte;
var dx, dy: byte;
    bestdir1, bestdir2: byte;
bocianu's avatar
bocianu committed
652
begin
bocianu's avatar
bocianu committed
653
    g := ghosts[ghostNumber];
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
    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;
674

675 676 677
    if gameState = GAME_ESCAPE then begin
        bestdir1 := Reverse(bestdir1); // reverse directions when ghost escapes
        bestdir2 := Reverse(bestdir2);
bocianu's avatar
bocianu committed
678
    end;
679

680 681 682 683 684
    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
685 686 687
end;

procedure MoveGhost(ghostNumber: byte);
Wojciech Bociański's avatar
Wojciech Bociański committed
688
var exitMask, exitCount, step: byte;
bocianu's avatar
bocianu committed
689 690
begin
    g := ghosts[ghostNumber];
691 692 693 694 695 696 697 698 699
    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
700
    if (g^.dir <> DIR_NONE) then begin
bocianu's avatar
bocianu committed
701
        g^.sy := shortInt(g^.y - viewportTop) shl 3;
bocianu's avatar
bocianu committed
702 703
        g^.sy := g^.sy + 16;
        g^.sy := g^.sy - vscrollstep;
704
        g^.sx := PMG_LEFT_OFFSET + g^.x shl 3;
bocianu's avatar
bocianu committed
705 706 707 708

        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
709 710
        g^.spriteHeight := SPRITE_HEIGHT;
        g^.spriteOffset := 0;
bocianu's avatar
bocianu committed
711

Wojciech Bociański's avatar
Wojciech Bociański committed
712 713 714
        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
715
            else begin  // if part visible
Wojciech Bociański's avatar
Wojciech Bociański committed
716 717 718
                g^.sy := BOARD_TOP;
                g^.spriteOffset := vOffset;
                g^.spriteHeight := SPRITE_HEIGHT - vOffset;
bocianu's avatar
bocianu committed
719 720
            end;
        end;
Wojciech Bociański's avatar
Wojciech Bociański committed
721 722 723 724
        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
725 726
        end;

727
        step:=1;
Wojciech Bociański's avatar
Wojciech Bociański committed
728
        if gameState = GAME_ESCAPE then step := frame and 1; // slow down by skipin steps in escape mode
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
        if step<>0 then begin
			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;

			if Abs(g^.step) = 8 then begin // moved into new position
				if (Abs(shortInt(g^.y-pac.y))<GHOST_DESPAWN_DISTANCE) then g^.despawn := 0
				else g^.despawn := g^.despawn + 1;
				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;
			end;
		end;
Wojciech Bociański's avatar
Wojciech Bociański committed
755
        ghostSpritesheets[ghostNumber] := ghost_sprites[g^.dir];
bocianu's avatar
bocianu committed
756 757 758
    end;
end;

bocianu's avatar
bocianu committed
759
procedure ShowGhost(ghostNumber: byte);
Wojciech Bociański's avatar
Wojciech Bociański committed
760
var s: array[0..0] of word;
bocianu's avatar
bocianu committed
761
begin
bocianu's avatar
bocianu committed
762
    g := ghosts[ghostNumber];
Wojciech Bociański's avatar
Wojciech Bociański committed
763
    if gameState = GAME_ESCAPE then s := @ghost_escape
764
    else s := ghostSpritesheets[ghostNumber];
765
    if (g^.dir <> DIR_NONE) and (g^.deadCount = 0) then begin
766
        if (g^.spriteHeight <> 0) then
Wojciech Bociański's avatar
Wojciech Bociański committed
767
           Move(pointer(s[ghostFrame] + g^.spriteOffset), pointer(PMGBASE + 512 + (ghostNumber shl 7) + g^.sy), g^.spriteHeight)
768
        else
Wojciech Bociański's avatar
Wojciech Bociański committed
769 770
           g^.sx := 0;
        Poke($d000+ghostNumber, g^.sx); // hpos
771
    end;
bocianu's avatar
bocianu committed
772 773
end;

bocianu's avatar
bocianu committed
774 775 776 777 778 779 780 781 782 783 784 785 786 787
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
788
    ghostFrame := (frame shr 4) and 1;
bocianu's avatar
bocianu committed
789 790
end;

791 792
function FreeGhostSlot:byte;
begin
793
    for i := 0 to GHOST_NUM - 1 do begin
794
        g := ghosts[i];
795
        if g^.dir = DIR_NONE then exit(i);
796 797 798
    end;
end;

799
function SelectSpawner: byte;
800
var i,min,dist:byte;
801
begin
802 803 804 805 806 807 808 809
    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;
810 811 812 813 814 815 816 817
end;

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

818
procedure TryToSpawnGhost;
Wojciech Bociański's avatar
Wojciech Bociański committed
819
var ghostNumber: byte;
820
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
821 822
    if ghostCount < 4 then begin
        ghostNumber := FreeGhostSlot;
823 824 825 826 827 828 829
        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;
bocianu's avatar
bocianu committed
830 831 832 833 834 835 836
        if (spawnCounter = 0) then begin 
			Inc(spawnCounter);
			if (pac.frame = ghostNumber + 1) then begin
				PaintOnBoard(Hi(spawners[spawnerActive]),Lo(spawners[spawnerActive]),TILE_SPAWNER);
				SpawnGhost(ghostNumber,Hi(spawners[spawnerActive]),Lo(spawners[spawnerActive]));
				spawnCounter := spawnDelay;
            end;
837 838 839 840
        end;
        Dec(spawnCounter);
    end;
end;
bocianu's avatar
bocianu committed
841

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

bocianu's avatar
bocianu committed
843
{********************************************** PAC ROUTINES ******************************************}
bocianu's avatar
bocianu committed
844

bocianu's avatar
bocianu committed
845

Wojciech Bociański's avatar
Wojciech Bociański committed
846
procedure InitPac(x, y: byte);
bocianu's avatar
bocianu committed
847
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
848 849 850 851 852 853 854
    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];
855
    viewportTop := pac.y - 6;
856
    if (pac.y > byte(boardLastLine - 6)) then viewportTop := viewportTop + (boardLastLine-6-pac.y);
857
    if (pac.y < 6) then viewportTop := 0;
bocianu's avatar
bocianu committed
858 859
end;

bocianu's avatar
bocianu committed
860 861
procedure ShowPac;
begin
bocianu's avatar
bocianu committed
862 863 864 865
    _HPOSM3 := pac.sx;
    _HPOSM2 := pac.sx+2;
    _HPOSM1 := pac.sx+4;
    _HPOSM0 := pac.sx+6;
Wojciech Bociański's avatar
Wojciech Bociański committed
866
    Move(pac.sprite[pac.frame], pointer(PMGBASE + 384 + pac.sy), SPRITE_HEIGHT);
bocianu's avatar
bocianu committed
867 868
end;

869 870
procedure MovePac;
begin
bocianu's avatar
bocianu committed
871
    pac.sy := 16 + byte((pac.y - viewportTop) shl 3) - vscrollstep;  // count coordinates relative to viewport
872
    pac.sx := PMG_LEFT_OFFSET + pac.x shl 3;
873

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

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

bocianu's avatar
bocianu committed
879 880
    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
881

Wojciech Bociański's avatar
Wojciech Bociański committed
882 883
    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
884 885
        if Odd(pac.dir) then  // horizontal
            pac.x := pac.x + pac.step // increment absolute position
886
        else                // vertical
bocianu's avatar
bocianu committed
887
            pac.y := pac.y + pac.step; // increment absolute position
Wojciech Bociański's avatar
Wojciech Bociański committed
888
        pac.step := 0;
889
        CheckBoardAction;
890 891 892
    end;
end;

bocianu's avatar
bocianu committed
893 894
procedure CheckIfPacTurnsBack;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
895
    if _STICK0 <> 15 then begin    // if stick moved then check if pac wants turn back
bocianu's avatar
bocianu committed
896
        joy := not (_STICK0 or %11110000);
Wojciech Bociański's avatar
Wojciech Bociański committed
897
        if joy2dir[joy] = pac.dir then begin
bocianu's avatar
bocianu committed
898
            viewportMoveDir := -viewportMoveDir;
Wojciech Bociański's avatar
Wojciech Bociański committed
899
            pac.dir := (pac.dir + 2) and 3;
bocianu's avatar
bocianu committed
900 901 902 903
        end;
    end;
end;

904 905
procedure CheckIfPacTurns;
begin
Wojciech Bociański's avatar
Wojciech Bociański committed
906
    if (pac.dir <> DIR_NONE) then begin; // if pac was, moving check if it's able to continue movement
907 908 909 910 911 912
        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;
913 914
    end;

Wojciech Bociański's avatar
Wojciech Bociański committed
915
    if joy <> 0 then begin    // if stick is moved try to turn pac
bocianu's avatar
bocianu committed
916
                            // for diagonal stick movement, turning sides has bigger priority than fwd/bck movement
Wojciech Bociański's avatar
Wojciech Bociański committed
917
        dir := pac.dir;
918
        flag := pac.dir = DIR_NONE;
Wojciech Bociański's avatar
Wojciech Bociański committed
919 920
        if (joy and JOY_UP <> 0) then // up
            if canMoveTo(pac.x, pac.y-1) then
921
                if Odd(pac.dir) or flag then dir := DIR_UP;
Wojciech Bociański's avatar
Wojciech Bociański committed
922 923 924

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

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

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

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

Wojciech Bociański's avatar
Wojciech Bociański committed
941
    if (pac.dir <> DIR_NONE) and not Odd(pac.dir) then begin // if pac moves verticaly
942
        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
943
            viewportMoveDir := 1; // start scroll screen up
944
        end;
945
        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
946
            viewportMoveDir := -1; // start scroll screen down
947 948 949 950
        end;
    end;
end;

Wojciech Bociański's avatar
Wojciech Bociański committed
951
function PacColider: byte;
952 953 954 955 956 957
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
958 959 960 961
    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;
962 963 964 965 966 967
end;

procedure PacDies;
begin
    Dec(lives);
    UpdateLives;
Wojciech Bociański's avatar
Wojciech Bociański committed
968 969 970
    frame := 1;
    pac.sprite := @pac_dies;
    pac.frame := 0;
971 972
end;

bocianu's avatar
bocianu committed
973

bocianu's avatar
bocianu committed
974 975
{********************************************** GAME ROUTINES ******************************************}

bocianu's avatar
bocianu committed
976 977 978 979

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

bocianu's avatar
bocianu committed
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
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;

1002
procedure ShowTitleScreen;
bocianu's avatar
bocianu committed
1003 1004
var selectState,selectState_:byte;
	selectFrame:word;
1005
begin
1006 1007 1008
    Pause;
    setDli(@Dli1T);
    _CHBASE := Hi(CHARSET_TITLE_ADDRESS);
bocianu's avatar
bocianu committed
1009 1010
    Move(DL_title,pointer(DL),64);
    _SDLSTL_ := DL;
bocianu's avatar
bocianu committed
1011
    _COLOR4_ := 0;
1012
    PMG_clear;
bocianu's avatar
bocianu committed
1013
    ClearTxT;
bocianu's avatar
bocianu committed
1014 1015 1016
	secret_id:=0;
	if cheat_mode then poke(TXT_RAM,32) 
	else poke(TXT_RAM,0);
1017

bocianu's avatar
bocianu committed
1018 1019 1020
    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
1021 1022
	selectState_ := _CONSOL and 2;
	selectFrame := 0;
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035

    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;
1036

1037 1038
    titleStage := 0;
    repeat
1039
        if music then msx.Play;
1040 1041
        Pause;
        ghostFrame := (frame shr 4) and 1;
bocianu's avatar
bocianu committed
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
        
        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
1053 1054
		ReadSecret;

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
        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);
1097
                Move(strings[10],pointer(TXT_RAM+40*11+3),35);
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
                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
1115 1116 1117 1118 1119 1120 1121
		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;
1122

bocianu's avatar
bocianu committed
1123
    until (_STRIG0 = 0) or (_CONSOL and 1 = 0);
bocianu's avatar
bocianu committed
1124
    repeat until _STRIG0 = 1
1125 1126
end;

bocianu's avatar
bocianu committed
1127 1128
procedure ShowLevelScreen;
begin
bocianu's avatar
bocianu committed
1129
    ClearTxT;
1130
    Pause;
bocianu's avatar
bocianu committed
1131 1132
    Move(DL_LEVEL,pointer(DL),Length(DL_LEVEL));
    _SDLSTL_ := DL;
bocianu's avatar
bocianu committed
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
    _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
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
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
1191 1192 1193 1194 1195 1196 1197 1198
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
1199
procedure ShowDataMatrix(score:cardinal);
bocianu's avatar
bocianu committed
1200 1201
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
1202
begin
bocianu's avatar
bocianu committed
1203
    p := 0;
bocianu's avatar
bocianu committed
1204
    ShowLevelScreen;
bocianu's avatar
bocianu committed
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
    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
1222 1223
    CalculateMatrix;
    ShowDM;
bocianu's avatar
bocianu committed
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
    _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
1240 1241
end;

bocianu's avatar
bocianu committed
1242
procedure InitGameScreen;
bocianu's avatar
bocianu committed
1243
begin
1244 1245
    Pause;
    setDli(@Dli1);
bocianu's avatar
bocianu committed
1246 1247
    Move(DL_game,pointer(DLG),Length(DL_game));
    _SDLSTL_ := DLG;
bocianu's avatar
bocianu committed
1248 1249
    _VSCROL := 0;
    PMG_clear;
bocianu's avatar
bocianu committed
1250 1251
end;

bocianu's avatar
bocianu committed
1252 1253
function ReadLevelData(ptr: word): word;
var tile,count,tileSize:byte;
1254
    p:word;
bocianu's avatar
bocianu committed
1255 1256 1257
begin
    tile := levelData[0];
    count := levelData[1];
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
    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
1271

1272 1273 1274 1275 1276 1277 1278
        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
1279

1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
        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
1290

1291 1292 1293 1294 1295 1296 1297 1298 1299
        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;
1300 1301

    end;
1302
    result:=ptr+p;
bocianu's avatar
bocianu committed
1303 1304 1305 1306 1307 1308 1309 1310 1311
end;

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

1316
    Fillchar(@board, BOARDSIZE, TILE_VOID);
bocianu's avatar
bocianu committed
1317 1318 1319 1320 1321 1322 1323

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

bocianu's avatar
bocianu committed
1324 1325
procedure InitGame;
begin
bocianu's avatar
bocianu committed
1326
    lives := GAME_LIVES;
bocianu's avatar
bocianu committed
1327
	if cheat_mode then lives := 99;
bocianu's avatar
bocianu committed
1328
    score := 0;
1329
    liveBonus := 0;
bocianu's avatar
bocianu committed
1330
    currentLevel := startingLevel;
bocianu's avatar
bocianu committed
1331 1332
end;

1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
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
1344
procedure InitLevel(lvl: byte);
bocianu's avatar
bocianu committed
1345
begin
bocianu's avatar
bocianu committed
1346 1347 1348 1349
    ShowLevelScreen;
    PutArray(0,2,5,word(@txt_level));
    Str(lvl+1,s);
    PutNumber(5-byte(s[0]),14,s);
bocianu's avatar
bocianu committed
1350
    LoadLevel(lvl);
bocianu's avatar
bocianu committed
1351 1352 1353 1354 1355 1356
    _COLOR0_ := level.colors[0];
    _COLOR1_ := level.colors[1];
    _COLOR2_ := level.colors[2];
    _COLOR3_ := level.colors[3];
    _COLOR4_ := level.colors[4];

1357
    PaintBoard;
1358
    InitView;
1359
    dotCount := dotCount - level.dotLimit;
bocianu's avatar
bocianu committed
1360
    ClearTxT;
bocianu's avatar
bocianu committed
1361 1362
    bonusPosPicked := false;
    bonusDelay := BONUS_DELAY * 50;
bocianu's avatar
bocianu committed
1363

1364
    Move(strings[0],pointer(TXT_RAM+1),36); // score topbar
bocianu's avatar
bocianu committed
1365 1366 1367
    UpdateScore;
    UpdateFood;
    UpdateLives;
bocianu's avatar
bocianu committed
1368 1369
end;

1370 1371 1372
procedure GameRestore;
begin
    InitView;
bocianu's avatar
bocianu committed
1373
    if bonusPosPicked then RemoveBonus;
1374 1375 1376 1377 1378
    CloseAllSpawners;
    PMG_clear;
    SongStart(SONG_GAME);
end;

1379 1380 1381 1382 1383 1384