[Back to GRAPHICS SWAG index] [Back to Main SWAG index] [Original]

    {$A+,B+,D-,E-,F-,G+,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X+,Y-}
    {$M 20000,0,0}

    {Burn V1.0: the original fireroutine was made by
     Frank Jan Sorensen alias Frank Patxi (fjs@lab.jt.dk)}

    {Burn V2.0: interaction, speedup and sparks
                added by Gerhard Piran}

    Program Burn2;           {12.12.95}

    uses  Dos, Crt;

    var   regs: Registers;
          pic: integer;      {drawn pictures}

    {********************************************************}
    procedure SetVideoMode (vMode: byte);

    begin
      regs.ax := vMode;      {Bit 7 = 1: RAM nicht l”schen}
      Intr ($10,regs);
    end;
    {--------------------------------------------------------}
    function GetVideoMode: byte;

    begin
      regs.ah := $0F;
      intr ($10, regs);
      GetVideoMode := regs.al;
    end;
    {*********************************************************}
    type  ColorValue = record R,G,B: byte; end;
          VGAPaletteType = array[0..255] of ColorValue;

    procedure ReadPal (var pal: VGAPaletteType);

    begin
      regs.AX := $1017;
      regs.BX := 0;
      regs.CX := 256;
      regs.ES := Seg(pal);
      regs.DX := Ofs(pal);
      repeat until Port[$03DA] And $08 = $08; {Wait for rescan}
      Intr ($10,regs);
    end;
    {--------------------------------------------------------}
    procedure WritePal (var pal: VGAPaletteType);

    begin
      regs.AX := $1012;
      regs.BX := 0;
      regs.CX := 256;
      regs.ES := Seg(pal);
      regs.DX := Ofs(pal);
      repeat until Port[$03DA] and $08 = $08; {Wait for rescan}
      Intr($10,regs);
    end;
    {*********************************************************}
    { Convert HSI (Hue, Saturation, Intensity) -> RGB }
    {---------------------------------------------------------}
    procedure Hsi2Rgb (H, S, I: Real; var C: ColorValue);

    var   T, Rv, Gv, Bv: Real;

    begin
      T  := H;
      Rv := 1 + S * Sin(T - 2 * Pi / 3);
      Gv := 1 + S * Sin(T);
      Bv := 1 + S * Sin(T + 2 * Pi / 3);
      T  := 63.999 * I / 2;
      c.R := trunc(Rv * T);
      c.G := trunc(Gv * T);
      c.B := trunc(Bv * T);
    end;
    {*********************************************************}
    { fast pixel drawing for graphic mode 320x200x256
    {---------------------------------------------------------}
    procedure PutPixel (x,y: integer; c: byte); assembler;
     asm
      mov ax,y
      mov bx,ax
      shl ax,8
      shl bx,6
      add bx,ax
      add bx,x
      mov ax,0a000h
      mov es,ax
      mov al,c
      mov es:[bx],al
     end;
    {--------------------------------------------------------}
    function GetPixel (x,y: integer): byte;

    begin
     asm
      mov ax,y
      mov bx,ax
      shl ax,8
      shl bx,6
      add bx,ax
      add bx,x
      mov ax,0a000h
      mov es,ax
      mov al,es:[bx]
      mov @result,al
     end;
    end;
    {********************************************************}
    procedure Info;

    begin
      ClrScr;
      WriteLn('Burn V 2.0,   a hot burning stuff'#13#10);
      WriteLn('commands: '#13#10
             +'    ?     this help'#13#10
             +'   + -    change width'#13#10
             +'    C     clear base fire'#13#10
             +'    W     give water into fire'#13#10
             +'    P     draw palette'#13#10
             +'    A     animate values on/off');
      WriteLn('  space   random values'#13#10
             +'  cursor  edit values'#13#10
             +'   ESC    exit demo'#13#10);
      WriteLn('values 1: decrease root of flame'#13#10
             +'       2: how far flames go up'#13#10
             +'       3: more or less fire'#13#10
             +'       4: smooth root of flame'#13#10
             +'       5: limit of start burning'#13#10
             +'       6: burnability (wood..gaz)'#13#10
             +'       7: sparks'#13#10
             +'       8: new flames'#13#10
             +'       9: put water into fire'#13#10);
    end;
    {********************************************************}
    const maxPar = 9;
          actPar: integer = 1;

    procedure StartBurning (xl,yl: integer);

    type  tPar = record min, max, value: integer end;

    const par: array [1..maxPar] of tPar
          =((min:  0;   max: 50;   value: 10)   {0: rootRand}
           ,(min:  0;   max: 50;   value: 15)   {1: decay}
           ,(min: -2;   max: 10;   value: 10)   {2: moreFire}
           ,(min:  0;   max:  9;   value: 10)   {3: smooth}
           ,(min:  0;   max:100;   value: 10)   {4: minFire}
           ,(min:  3;   max: 90;   value: 10)   {5: fireInc}
           ,(min:  0;   max: 10;   value: 10)   {6: sparks}
           ,(min:  0;   max: 20;   value: 10)   {7: new fire}
           ,(min:  0;   max: 20;   value: 10)); {8: put water}

    const maxX = 319;
          maxY = 199;
          bkColor = 16;

    var   vga256: array[0..maxY,0..maxX] of byte absolute $A000:0;
          cb: char;

          rootRand,         {Max/Min decrease of the root of the flames}
          moreFire,         {change fire intensity}
          decay,            {How far should the flames go up on the screen ?}
          smooth,           {How descrete can the flames be?}
          minFire,          {limit between the "starting to burn" and
                             the "is burning" routines }
          fireIncrease,     {3 = Wood, 90 = Gazolin}
          sparks,           {new sparks per picture}
          newFlame,         {create new flame}
          putWater: integer;{put water to fire}

          x1,x2,y1,y2: integer;  {drawing rectangle}

    {********************************************************}
    procedure MakePal;

    const maxColor = 110;

    var   ni: integer;   pal: VGAPaletteType;

    begin
      FillChar (pal, SizeOf (pal), 0);
      for ni := 1 to MaxColor
      do HSI2RGB (4.6-1.5*ni/MaxColor, ni/MaxColor, ni/MaxColor, pal[ni]);
      for ni := MaxColor to 255
      do begin
        pal[ni] := pal[ni-1];
        With pal[ni] do
        begin
          if R < 63 then Inc(R);
          if R < 63 then Inc(R);
          if (ni Mod 2=0) And (G<53) then Inc(G);
          if (ni Mod 2=0) And (B<63) then Inc(B);
        end;
      end;
      WritePal (pal);
    end;

    procedure DrawPaletteScreen;

    var   xi, yi: integer;

    begin
      MakePal;
      for yi := 0 to maxY
      do for xi := 0 to maxX do PutPixel (xi,yi,yi);
    end;

    procedure DrawValues;

    var   ni, yi: integer;

    begin
      for ni := 1 to maxPar
      do begin
        yi := succ(ni) * 3;
        FillChar (vga256[yi,100], 120, 0);
        with par[ni]
        do if actPar = ni
        then FillChar (vga256[yi,100], 1 + longint(value)*119 div 20, 100)
        else FillChar (vga256[yi,100], 1 + longint(value)*119 div 20,  50);
      end;
    end;

    procedure CalcValues;

    begin
      with par[1] do rootRand     :=  min + value * (max - min) div 20;
      with par[2] do decay        :=  max - value * (max - min) div 20;
      with par[3] do moreFire     :=  min + value * (max - min) div 20;
      with par[4] do smooth       :=  min + value * (max - min) div 20;
      with par[5] do minFire      :=  min + value * (max - min) div 20;
      with par[6] do fireIncrease :=  min + sqr (value);
      with par[7] do sparks       :=  min + value * (max - min) div 20;
      with par[8] do newFlame     :=  max - value * (max - min) div 20;
      with par[9] do putWater     :=  max - value * (max - min) div 20;
    end;

    procedure ChangeValue;

    begin
      cb := ReadKey;
      if cb = 'P' {down} then actPar := (actPar mod maxPar) + 1;
      if cb = 'H' {up}   then actPar := (actPar+maxPar-2) mod maxPar + 1;
      with par[actPar]
      do begin
        if cb = 'K' {left}  then if value >  0 then dec (value);
        if cb = 'M' {right} then if value < 20 then inc (value);
      end;
      CalcValues;
      DrawValues;
      cb := #1;
    end;

    procedure RandomValues;

    var   ni: integer;

    begin
      for ni := 1 to maxPar
      do par[ni].value := random(21);
      CalcValues;
      DrawValues;
    end;

    procedure AnimateValues;

    var   ni: integer;

    begin
      ni := 1 + random (maxPar);
      with par[ni]
      do if random (2) = 0
      then if value < 20 then inc (value) else
      else if value >  0 then dec (value);
      CalcValues;
      DrawValues;
    end;

    procedure ChangeSize (dx: integer);

    var   yi: integer;

    begin
      if (dx > 0) and (x1 - dx > 2)
      then repeat
        dec (x1);
        inc (x2);
        dec (dx);
        for yi := y1 to y2
        do begin
          PutPixel (x1,yi,0);
          PutPixel (x2,yi,0);
        end;
      until dx = 0;
      if (dx < 0) and (x1 - dx < 140)
      then repeat
        for yi := y1 to y2
        do begin
          PutPixel (x1, yi, bkColor);
          PutPixel (x2, yi, bkColor);
        end;
        inc (x1);
        dec (x2);
        inc (dx);
      until dx = 0;
      xl := x2 - x1 - 1;
    end;

    procedure Help;

    begin
      SetVideoMode (3);          {TextMode}
      ClrScr;
      Info;
      Write ('Hit any key to start ');
      cb := ReadKey;
      SetVideoMode ($13);
      MakePal;
    end;

    const animValues: boolean = false;

    var   flameArray: array[0..319] of byte;
          x,xi,y,c,v: integer;

    begin
      x1 := (320 - xl) div 2;   x2 := x1 + xl - 1;
      y1 := (200 - yl) div 2;   y2 := y1 + yl - 1;
      Help;
      Randomize;

      FillChar (vga256, SizeOf(vga256), bkColor);
      FillChar (flameArray, SizeOf(flameArray), 0);
      for x := x1 to x2 do for y := y1 to y2 do PutPixel (x,y,0);
      CalcValues;
      pic := 0;
      repeat
        inc (pic);
        if KeyPressed then cb := upcase(ReadKey) else cb := #1;
        if cb = #0 then ChangeValue;
        while KeyPressed do ReadKey;  {empty keyboard buffer}

        {Put the values from flameArray on the bottom line of the screen}
        for x := x1 to x2 do PutPixel (x, y2, flameArray[x]);

        {This loop makes the actual flames}
        for xi := x1 to x2
        do begin
          if      xi = x1 then x := xi
          else if xi < x2 then x := xi - 1
          else                 x := xi - 2;
          for y := y1 + 1 to y2
          do begin
            v := GetPixel (xi,y);
            if (v = 0)
            or (v < decay)
    {        then PutPixel (x,pred(y),0)
            else PutPixel (x-pred(Random(3)),Pred(y),v-Random(decay));
    }       then vga256[pred(y),xi] := 0
            else vga256[pred(y),x+Random(3)] := v-Random(decay);
          end;
        end;

        for xi := 1 to sparks
        do begin
          x := x1 + random (xl);
          y := y2 - random (yl - 10);
          PutPixel (x,y, GetPixel (x,y)+y);
        end;

        if Random(newFlame) = 0       {new fire ?}
        then FillChar (flameArray[x1+Random(xl-5)],5,199);

        if Random(putWater)= 0        {put water ?}
        then FillChar (flameArray[x1+Random(xl-5)],3,0);

        if cb <> #1                   {check input ?}
        then begin
          if      cb = '+' then ChangeSize (+5)
          else if cb = '-' then ChangeSize (-5)
          else if cb = 'R' then RandomValues
          else if cb = ' ' then RandomValues
          else if cb = 'A' then animValues := not animValues
          else if cb = 'C' then FillChar (flameArray, SizeOf(flameArray),0)
          else if cb = 'W' then for x := 1 to xl div 10
                                do flameArray[x1+Random(xl)] := 0
          else if cb = '?' then Help
          else if cb = 'P' then DrawPaletteScreen;
        end;
        if animValues then AnimateValues;

        {This loop controls the "root" of the flames (values in flameArray)}
        for x := x1 to x2 do
        begin
          c := flameArray[x];
          if c < MinFire then    {Increase by the "burnability"}
          begin                  {Starting to burn:}
            if c > 10 then Inc (c, Random (fireIncrease));
          end
          else {Otherwise randomize and increase by intensity (is burning)}
            Inc (c, Random (rootRand * 2 + 1) - rootRand + moreFire);
          if c > 200 then c := 200;  {c too large ?}
          flameArray[x] := c;
        end;

        {Pour a little water on both sides of the fire
         to make it look nice on the sides}
        for x := 1 to xl div 8 do
        begin
          c := Trunc(Sqr(Random)*xl/8);
          flameArray[x1+c] := 0;
          flameArray[x2-c] := 0;
        end;

        {Smoothen the values of FrameArray to avoid "descrete" flames}
        for x := x1+Smooth to x2-Smooth do
        begin
          c := 0;
          for y := -Smooth to Smooth do Inc (c,flameArray[x+y]);
          flameArray[x] := c div (2*Smooth+1);
        end;
      until (cb = #27);
    end;
    {********************************************************}
    var   lastMode: byte;

    begin
      lastMode := GetVideoMode;  {save video mode}
      StartBurning (120, 100);   {fire simulation}
      SetVideoMode (lastMode);   {Restore video mode}
      Info;
    end.

[Back to GRAPHICS SWAG index] [Back to Main SWAG index] [Original]