home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-03-25 | 10.4 KB | 373 lines | [TEXT/PJMM] |
- program CampFire;
-
- {This program is a Pascal port of CAMP FIRE 1.1 by Brian Stone.}
- {Ported by Ingemar Ragnemalm 1996.}
-
- {Possible improvements:}
- {• Change the calculations to work with 127..-128 rather than 0..255.}
- {That would simplify and speed up the code.}
- {The BASE# pointers should really be pointers to bytes (Ptr), to avoid the}
- {overhead of looking up in arrays. The best solution I can think of is to}
- {use *signed* bytes, normal Ptrs, so all calculations are made with the range}
- {127..-128 in mind rather than 0..255. Well, let's fix that for another}
- {version.}
- {• Move as many global variables to be local. (I have moved some.)}
- {That makes the code more readable.}
-
- {NOTE FOR THINK PASCAL USERS: If debug it turned on for this file, it will run VERY slow.}
- {That's normal – don't blame Think for it. It is something like 100 times faster with D/V/R}
- {turned off.}
-
- uses
- {$IFC UNDEFINED THINK_PASCAL}
- Types, QuickDraw, OSUtils, Memory, Events, Resources, Fonts, TextEdit, Dialogs, Processes, ToolUtils,
- {$ENDC}
- Palettes, QDOffScreen;
-
- type
- ByteArr = packed array[0..100000] of Byte;
- ByteArrPtr = ^ByteArr;
- { Global rectangles.}
- var
- WindowRect, OffscreenRect: Rect;
-
- { Graphics world vars, and PixMap stuff.}
- saveWorld: GWorldPtr;
- saveDevice: GDHandle;
- MyWindow: CWindowPtr;
- OffscreenWorldP: GWorldPtr;
- err: QDErr;
-
- { Pix Map stuff}
- OffscreenPixMapH, windowPixMapH: PixMapHandle;
- OffscreenPixMapP, windowPixMapP: PixMapPtr;
- endbyte: Ptr;
- base: ByteArrPtr;
- rb, start: LongInt;
-
- { Misc Vars}
- Resx, Resy: LongInt;
- MaxBlockSize: LongInt;
- resfileID: Integer;
-
- { Vars used for Frame Rate Calculation}
- StartTime, EndTime, TotalTime: LongInt;
- finalTicks: Longint;
- Frames: Longint; {was double}
- FPS: Integer;
-
-
- (****************************)
-
- { Function RangedRdm from THINK Reference.}
- function Rnd (min, max: Longint): Longint;
- var
- qdRdm: Longint;
- range: LongInt;
- (* assume that min is less than max *)
- begin
- qdRdm := BitAnd(Longint(Random), $0000ffff); {Avoid negative values while keeping the full range.}
- range := max - min;
- Rnd := (qdRdm * range) div 65536 + min; (* now 0 <= t <= range *)
- end; {Rnd}
-
- (********************)
-
- procedure CleanUp;
- begin
- if OffscreenWorldP <> nil then
- DisposePtr(Ptr(OffscreenWorldP));
- if MyWindow <> nil then
- DisposeWindow(WindowPtr(MyWindow));
- FlushEvents(everyEvent, 0);
- end; {CleanUp}
-
- (****************************************)
-
- procedure Init;
- var
- WindowResX, WindowResY: LongInt;
- i, j: Integer;
- { Palette and ColorTable stuff}
- CampFirePal: PaletteHandle;
- CampFireClut: CTabHandle;
- { Init the Macintosh environment}
- begin
- {$IFC UNDEFINED THINK_PASCAL}
- InitGraf(@qd.thePort);
- InitFonts;
- FlushEvents(everyEvent - osMask - diskMask, 0);
- InitWindows;
- InitMenus;
- TEInit;
- InitDialogs(nil);
- InitCursor;
- MaxApplZone;
-
- { Randomize Random number Seed}
- GetDateTime(qd.randSeed);
- {$ELSEC}
- { Randomize Random number Seed}
- GetDateTime(randSeed);
- {$ENDC}
-
- { Open resource file}
- resfileID := OpenResFile('Flame.res');
- if resfileID = 0 then
- ExitToShell;
-
- { Get the window palette from the resource file.}
- CampFirePal := GetNewPalette(128);
- { Create a color table for the offscreen world.}
- CampFireClut := GetCTable(8);
- { Give the color table a unique CTseed.}
- CampFireClut^^.ctSeed := GetCTSeed;
- { Copy the palette into the color table.}
- Palette2CTab(CampFirePal, CampFireClut);
-
- { Set Up the Window}
- {$IFC UNDEFINED THINK_PASCAL}
- WindowResX := qd.screenBits.bounds.right;
- WindowResY := qd.screenBits.bounds.bottom;
- {$ELSEC}
- WindowResX := screenBits.bounds.right;
- WindowResY := screenBits.bounds.bottom;
- {$ENDC}
- SetRect(WindowRect, (WindowResX - Resx) div 2, (WindowResY - Resy) div 2, (WindowResX - Resx) div 2 + Resx, (WindowResY - Resy) div 2 + Resy);
- MyWindow := CWindowPtr(NewCWindow(nil, WindowRect, 'Flame!', TRUE, noGrowDocProc, nil, FALSE, 0));
- SelectWindow(WindowPtr(MyWindow));
- SetPort(WindowPtr(MyWindow));
- { Get the Graphics World and Device associated with the window}
- GetGWorld(saveWorld, saveDevice);
- { Set a pointer to the windows PixMap}
- windowPixMapH := GetGWorldPixMap(saveWorld);
- HLockHi(Handle(windowPixMapH));
- if LockPixels(windowPixMapH) then
- ;
- windowPixMapP := windowPixMapH^;
- { Set the palette to the window}
- SetPalette(WindowPtr(MyWindow), CampFirePal, TRUE);
- ActivatePalette(WindowPtr(MyWindow));
-
- { Set Up Offscreen GWorld}
- SetRect(OffscreenRect, 0, 0, Resx, Resy);
- {$IFC UNDEFINED THINK_PASCAL}
- err := NewGWorld(OffscreenWorldP, 0, OffscreenRect, CampFireClut, nil, 0);
- {$ELSEC}
- err := NewGWorld(OffscreenWorldP, 0, OffscreenRect, CampFireClut, nil, []);
- {$ENDC}
- if err <> 0 then
- ExitToShell;
- SetGWorld(OffscreenWorldP, nil);
- { Set a pointer to the Offscreen World's PixMap}
- OffscreenPixMapH := GetGWorldPixMap(OffscreenWorldP);
- HLockHi(Handle(OffscreenPixMapH));
- if LockPixels(OffscreenPixMapH) then
- ;
- OffscreenPixMapP := OffscreenPixMapH^;
- { Set a pionter to the first byte of the ofscreen PixMap}
- base := ByteArrPtr(OffscreenPixMapH^^.baseAddr);
- rb := BitAnd(OffscreenPixMapH^^.rowBytes, $3fff);
-
- { Set every pixle of the offscreen PixMap to colorID 3.}
- for j := 0 to Resy - 1 do
- for i := 0 to Resx - 1 do
- base^[rb * j + i] := 3;
-
- CloseResFile(resfileID);
- end; {Init}
-
-
- (*******************************)
-
- procedure CampFireInit;
- { This function draws in the last two rows of the PixMap}
- { with some random colors to start the flame.}
- { The averaging then takes over and "pulls" these colors}
- { upwards creating the fire effect.}
- var
- blocksize, RndNum: LongInt;
- left, right, halfresx: Real;
- Maxcolor, Mincolor: LongInt;
- tmpBASE2, tmpBASE3, tmpBASE4: Ptr;
- BASE1, BASE2, BASE3, BASE4, BASE5: ByteArrPtr;
- k: Integer;
- i: Longint;
- randomcolor: Integer;
- begin
- { Initilize the pointers to the last two rows.}
- BASE2 := @base^[rb * (Resy - 3)];
- BASE3 := @base^[rb * (Resy - 2)];
- BASE4 := @base^[rb * (Resy - 1)];
- { Init the information required to choose the colors. }
- halfresx := Resx / 2;
- Maxcolor := 255;
- Mincolor := 180;
-
- { Draw the last two rows}
- RndNum := Rnd(5, 60);
- i := 0;
- while i < Resx do
- { This IF statement gives the fire a "pointed" look.}
- begin
- if i < halfresx then
- { We are on the left half of the PixMap}
- begin
- left := (halfresx - (halfresx - i)) / halfresx + 0.5;
- if left > 1 then
- left := 1;
- {$PUSH}
- {$R-}
- randomcolor := Rnd(Trunc(left * Mincolor), Trunc(left * Maxcolor));
- {$POP}
- end
- else { We are on the right half of the PixMap}
- begin
- right := (halfresx + (halfresx - i)) / halfresx + 0.5;
- if right > 1 then
- right := 1;
- {$PUSH}
- {$R-}
- randomcolor := Rnd(Trunc(right * MinColor), Trunc(right * Maxcolor));
- {$POP}
- end;
-
- tmpBASE2 := Ptr(BASE2);
- tmpBASE3 := Ptr(BASE3);
- tmpBASE4 := Ptr(BASE4);
- { Fill in a randomly sized block of color}
- blocksize := Rnd(1, MaxBlockSize);
- if ((blocksize + i) > Resx) then
- blocksize := Resx - i;
- for k := 0 to blocksize - 1 do
- begin
- {$PUSH}
- {$R-}
- tmpBASE2^ := randomcolor;
- tmpBASE3^ := randomcolor;
- tmpBASE4^ := randomcolor;
- {$POP}
- tmpBASE2 := Ptr(Longint(tmpBASE2) + 1);
- tmpBASE3 := Ptr(Longint(tmpBASE3) + 1);
- tmpBASE4 := Ptr(Longint(tmpBASE4) + 1);
- end;
-
- { Increment the PixMap pointers.}
- BASE2 := ByteArrPtr(Longint(BASE2) + RndNum);
- BASE3 := ByteArrPtr(Longint(BASE3) + RndNum);
- BASE4 := ByteArrPtr(Longint(BASE4) + RndNum);
-
- i := i + RndNum;
- end;
- end; {CampFireInit}
-
- (*********************************)
-
- procedure BuildFlame;
- { This function averages each pixel in this manner}
- { *T*}
- { *P* Where pixels marked "P" are averaged together}
- { PPP and the result is stored in pixel "T".}
-
- { Stagger the starting pixel.}
- var
- averagecolor: Integer;
- BASE1, BASE2, BASE3, BASE4, BASE5: ByteArrPtr;
- begin
- start := start + 1;
- if start = 10 then
- start := 0;
-
- { Set all pointers to their first pixel}
- BASE1 := @base^[rb * 0 + start];
- BASE2 := @base^[rb * 1 + start];
- BASE3 := @base^[rb * 2 + start];
- BASE4 := @base^[rb * 2 - 1 + start];
- BASE5 := @base^[rb * 2 + 1 + start];
-
- endbyte := @base^[rb * (Resy - 3) + Resx];
- while Longint(BASE1) < Longint(endbyte) do
- { Calculate the average color.}
- begin
- {Note! I use byte-arrays to access the pixels, but this really needs some}
- {optimizing. As it is now, it isn't as fast as the C version. It still is}
- {fairly fast, but I think it takes some more to access data byte-wise in}
- {Pascal.}
- {As a comfort, a 16-bit version wouldn't need any of these workarounds.}
- averagecolor := BSR(BASE2^[0] + BASE3^[0] + BASE4^[0] + BASE5^[0], 2) - 5;
- { Make sure the color's don't go below the lowest color}
- if averagecolor < 3 then
- averagecolor := 3;
- { Set the color of the target pixel}
- {$PUSH}
- {$R-}
- BASE1^[0] := averagecolor;
- {$POP}
- { Increment all pointers to the next pixels to be used.}
- {BASE4 = BASE5;}
- BASE1 := ByteArrPtr(Longint(BASE1) + 10);
- BASE2 := ByteArrPtr(Longint(BASE2) + 10);
- BASE3 := ByteArrPtr(Longint(BASE3) + 10);
- BASE4 := ByteArrPtr(Longint(BASE4) + 10);
- BASE5 := ByteArrPtr(Longint(BASE5) + 10);
- end;
- end; {BuildFlame}
-
- (********************************)
-
- procedure CampFire;
- { Draw the base of the Camp Fire}
- begin
- CampFireInit;
-
- { Build the flame of the Camp Fire}
- BuildFlame;
-
- { Scroll the Camp Fire up a few pixels to help it out.}
- BlockMove(@base^[rb * 3], @base^[0], rb * (Resy - 1) - rb * 3);
-
- {Set back the port/device! Otherwise the CopyBits might not work. Actually, it didn't work on my 68k Macs.}
- SetGWorld(saveWorld, saveDevice);
-
- { Copy the image to the screen}
- CopyBits(BitMapPtr(OffscreenPixMapP)^, BitMapPtr(windowPixMapP)^, OffscreenRect, OffscreenRect, srcCopy, nil);
- end; {CampFire}
-
-
- (***************************)
- (*** Camp Fire ***)
- (***************************)
- {Procedure main;}
- { Set image resolution}
- begin
- Resx := 240;
- Resy := 320;
- MaxBlockSize := Resx div 20 + 5;
-
- { Init Application}
- Init;
-
- { Init Frame Rate calculations}
- Frames := 0;
- StartTime := TickCount;
-
- { Begin Simulation // }
- while not Button do
- begin
- CampFire;
- Frames := Frames + 1;
- end;
-
- { Calculate Frame Rate, and display. }
- SetGWorld(saveWorld, saveDevice);
- EndTime := TickCount;
- TotalTime := (EndTime - StartTime) div 60;
- FPS := Frames div TotalTime;
- MoveTo(10, 50);
- ForeColor(greenColor);
- DrawString(StringOf(FPS));
- Delay(90, finalTicks);
-
- { Dont forget to wash behind your ears.}
- CleanUp;
- end.