                  .,::      .:.::::::.  :::::::::::::::::::..   
                  `;;;,  .,;;,;;'```';;,;;;;;;;;'''';;;;``;;;;  
                    '[[,,[[' [[[     [[[\    [[      [[[,/[[['  
                     Y$$$P   "$$c  cc$$$"    $$      $$$$$$c    
                   oP"``"Yo,  "*8bo,Y88b,    88,     888b "88bo,
                ,m"       "Mm,  "*YP" "M"    MMM     MMMM   "W" 
                --------------------------------------------------
                Mystic BBS - MPL Tutor : Screen Handling...
                --------------------------------------------------

  Suppose that we have printed some text and info on our screen/terminal and
we would like to save it, as it is. Or we would like to change the color of 
some ANSI graphics that we displayed. Well, all this and more we can do it 
with two very use full commands of the MPL and some more code.

  First of all lets explain the functions:
  
../ GetCharXY(X,Y:byte)

The GetCharXY function reads the character that is displayed on the screen
at the location (X,Y) we specify and returns it as result. So if we want to 
get the character at position 5,10 of the screen we give something like this:

              ch := GetCharXY(5,10)
              
The Ch is a variable of type Char and now holds whatever was displayed on the
screen.

../ GetAttrXY(X,Y:Byte)
  
  The GetAttrXY function is similar with the GetCharXY, but instead of the 
character it returns the color it. If we wanted to get the color at the same 
position as the example above we would type something like this.

              cl := GetAttrXY(5,10)
            
  Where cl is a variable of type Byte and now stores the foreground and 
background color of that particular position on the screen. 

  A very straight forward use of these two functions and very useful, would
be to save/store all data of the screen to a file or an Array. First lets 
store the screen info into an array. In BBSes the standard width and height of
a screen in characters is 80 x 25. So we will declare an Array with these 
dimensions. We will need two arrays or we could also use only one array but of
a custom variable type... we will see both.

---[ CODE ]-------------------------------------------------------------------

// Two Arrays example
Var Grid    : Array [1..80,1..25] Of Char  // This will store the chars.
Var Attr    : Array [1..80,1..25] of Byte  // This will store the colors

// One Array Example
Type               // We create a Record type
  xChar = Record
    Ch : Char      // This will store one Char.
    Cl : Byte      // And this the color of it.
  end

Var
  Screen : Array[1..80,1..25] of xChar  // This Array now can hold the info
                                        // of the screen

------------------------------------------------------------------------------

  There is not a particular reason to use the one or another method (for 
basic usage/users ;) ). Just the second one is more easy, when writing code to
remember. 

But still we did not see, how to store/grab the screen info...

---[ CODE ]-------------------------------------------------------------------

Procedure GrabScreen
Var X,Y  : Byte                     // We need two variables 
Begin
   For X:=1 to 80 Do Begin          // First we scan the screen by
                                    // width and then...
      For Y:=1 To 25 Do Begin       // ... by height.
         Screen[X,Y].Ch := GetCharXY(X,Y)   // Grab each character on the 
                                            // screen and put it in our Array
         Screen[X,Y].Cl := GetAttrXY(X,Y)   // Same with colors
      End
   End
End           

------------------------------------------------------------------------------

Now the Screen Array, has all the screen data. And now starts the fun part... 
Cause we can manipulate this data and alter the colors or text. But one small
thing... MPL stores foreground and background colors in a simple variable of 
type Byte. So we have to do some math to separate the Foreground and 
Background color when we need to. To get the foreground color we calculate 
this formula:

    FG Color = Value modulus 16     // Logic Implementation
      FG:=Screen[x,y].cl % 16       // MPL Code
    
For the Background color we do this:

    BG Color = Value divide by 16   // Logic Implementation
    BG:= 16 + Screen[x,y].cl / 16   // MPL Code
    
If we would like to change the color of a certain character or area in the 
screen we just alter the Color part of our array and display/write that area
or character to the screen. For example lets change the background color of 
a character in the screen. The Foreground Color will remain the same.

---[ CODE ]-------------------------------------------------------------------
Procedure Highlight(x,y,bgc:byte)  // This procedure will alter the background
                                   // color in a certain character
Var
  fg:byte              // Temp variable to store the foreground
                       // color, as we will use it again
Begin
gotoxy(x,y)                    // Position the cursor at that place
fg:= Screen[x,y].cl % 16       // Get the foreground color as we explained  
textcolor( fg + bgc * 16 )     // Apply the new background color
write(screen[x,y].ch)          // Write the character 
end

 -- - More simple - --

Procedure Highlight(x,y,bgc:byte)                
Begin 
writexy(x,y, (Screen[x,y].cl % 16) + bgc * 16,screen[x,y].ch)
end

// The WriteXY procedure is a combined version of the : Gotoxy, Textcolor,
// Write, so we can use just this command.

------------------------------------------------------------------------------

By executing this procedure ex. Highlight(5,10,23), it will change the 
background color of that character to bright white. You will think that this
is not very useful. So lets write a procedure that will add shadow to a box.
This time we will not use the Screen Array, but we will read the character of
the screen with the GetCharXY function. The Procedure will make a shadow fx,
to the bottom right corner of the box that we will give the dimensions.

---[ CODE ]-------------------------------------------------------------------

Procedure BoxShadow(x,y,w,h,atr:byte) // Position, dimension and attribute
                    // for the shadow. To give the color we 
                    // can write something like this:
                    // 8 + 23 * 16, where 8 is dark gray for
                    // the foreground and 23 is light gray 
                    // for the background
var
  d: byte
begin
  for d:=x to x+w do begin    // We process the bottom line of the shadow
    gotoxy(d,y+h)       
    textcolor(atr)            // Change the color to our Shadow color
    write( GetCharXY(d,y+h))  // Read and write the same character but now
                              // it will have the shadow color.
  end
  For d:=y to y+h do begin    // We do the same for the right column of the
                              // shadow box.
    gotoxy(x+w,d)
    textcolor(atr)
    write( GetCharXY(x+w,d))
  end
end

// Again we can replace the gotoxy,textcolor,write combination with the 
// WriteXY, procedure.
------------------------------------------------------------------------------

To test this procedure display an ANSI file to the screen and execute the
BoxShadow procedure like this: BoxShadow(10,13,30,5,8+23*16) and see what 
happens. Nice ha?... :)

Now lets do something cooler... lets write a procedure that it will the change 
the colors of an ANSI file that we display on our screen. For example, if our
ANSI file, has a lot of Green, change that to Blue or Red... That way we build
a Colorize function like in Photoshop.

---[ CODE ]-------------------------------------------------------------------
Procedure ColorizeBlueScreen
var
  w,h,fg,bg,atr:byte
Begin
  for w:=1 to 80 do begin     // "Scan" the screen from left to right
    for h:=1 to 25 do begin   // and top to bottom
      atr:=getattrxy(w,h)     // Get the color of that position
      fg:=atr % 16            // Separate the foreground
      bg:=16 + (atr / 16)     // and background color
      if fg=2 then fg:=1      // if its green turn it to blue
      if fg=10 then fg:=9     // if its light green turn it to light blue
      if bg=18 then bg:=17    // if the background is green turn it to blue
      textcolor(fg + bg*16)   // Apply the new color
      gotoxy(w,h)             
      write(getcharxy(w,h))   // and write down the character with new color
    end
  end
End
------------------------------------------------------------------------------

When you execute this code, you will see that everything green turns to blue
without altering the image in other way... cool ha? :) With the same logic you
can make many other cool "Screen Fxs"... a very cool script from Gryphon, is
making "Screen Transitions". Find it and examine the code. You will find some
cool stuff. The name of the script is AnsiWipe and the package is awipe10.zip
I totally recommend this script, cause it has many screen fxs and you can 
study the code.


As an end to this tutor, lets save our screen to a file, so we can save the
new image, with the new colors, from our previous example. We will save the 
data, that are stored in the Screen Array and in a format that is supported by
Mystic BBS. So all colors will be saved as pipe colors. Also we will optimize
a little bit the code, so that we don't have to write the color for each 
character, but only when there is a change in the color.

---[ CODE ]-------------------------------------------------------------------
Procedure SaveFile(OutF:String)
Var Fp  : File            // Our File variable
Var X,Y : Byte
Var FG,BG : byte          // Current colors
Var pFG,pBG : byte        // Previous Character Color
Var C   : Char
Var S   : String
Begin
  fAssign(Fp,OutF,66)     // Assign the file to the variable
  fReWrite(Fp)            // Create the File
  pFG:=0                  // Initialize the previous colors 
  pBG:=0
  s:='|07|16'             // In this variable we will store each line of the 
                          // screen and when we reach the end of it, then we
                          // will write it to the file. Also we begin with a
                          // standard color of Gray Text on Black background
  For y:=1 To 25 Do Begin       // Scan the Array by line, from top to bottom
    For X:=1 To 79 Do Begin
      FG:=screen[X,Y].cl % 16       // Get the foreground color
      BG:=16+(screen[X,Y].cl / 16)  // Get the background color
      if pFG <> FG then s:=s+'|'+padlt(int2str(fg),2,'0') // Is there a change
      if pBG <> BG then s:=s+'|'+padlt(int2str(bg),2,'0') // to the color?
                                    // if yes... then we write the new color
                                    // remember, mystic needs two digits for
                                    // the color code. ex. 02 not 2, so we use
                                    // the PadLT function to add the leading
                                    // zero.
      C:=screen[X,Y].ch           // Get the current Character from the Array
      s:=s+chr(ord(C))            // Add it to the temp variable S
      pFG:=FG                     // Store the current colors to the previous
                                  // ones, to check the next character.
      pBG:=BG
    End
    fWriteln(Fp,s)                // Write the complete line and proceed to
                                  // the next line, until we have read all 25
                                  // lines of the screen array
    s:='';
  End
  fClose(Fp)
End
------------------------------------------------------------------------------

If you test the procedure then you will have a Mystic type ANSI file, saved
with the data, that you have grabed earlier.

Screen manipulation is very usefull, not only in Mystic but generally in 
programming. Now you can make your own Menus that will restore the background
and other color or re-placing fxs.

Below is a complete script just for example of all the info that we spoked in
this tutorial. To use it, you will have to change the name of the ANSI file
(snake.ans) to one of yours and place it in the Scripts folder as with the
script. 

If you downloaded this tutor in a package, then the ANSI file is included.

---[ CODE ]-------------------------------------------------------------------

// Two Arrays example
Var Grid    : Array [1..80,1..25] Of Char  // This will store the chars.
Var Attr    : Array [1..80,1..25] of Byte  // This will store the colors

// One Array Example
Type          // We create a Record type
  xChar = Record
    Ch : Char   // This will store one Char.
    Cl : word   // And this the color of it.
  end

Var
  Screen : Array[1..80,1..25] of xChar  // This Array now can hold the info
                                        // of the screen
                      
Procedure GrabScreen
Var X,Y  : Byte
Begin
   For X:=1 to 80 Do Begin
      For Y:=1 To 25 Do Begin
         Screen[X,Y].ch:=GetCharXY(X,Y)
         Screen[X,Y].cl:=GetAttrXY(X,Y)
      End
   End
End                     

Procedure Highlight(x,y,bgc:byte)  
Var
  fg:byte
Begin
  gotoxy(x,y)           
  fg := Screen[x,y].cl % 16   
  textcolor( fg + bgc * 16 )  
  write(screen[x,y].ch)       
end

Procedure BoxShadow(x,y,w,h:byte; atr:word)
var
  d: byte
begin
  for d:=x to x+w do begin    
    writexy(d,y+h,atr,screen[d,y+h].ch)
  end
  For d:=y to y+h do begin
    gotoxy(x+w,d)
    textcolor(atr)
    write( GetCharXY(x+w,d))
  end
end

procedure writescreen
var
  w,h,fg,bg:byte
begin
  for w:=1 to 80 do begin
    for h:=1 to 25 do begin
      writexy(w,h,screen[w,h].cl,screen[w,h].ch)
    end
  end
end

Procedure SaveFile(OutF:String)
Var Fp  : File
Var X,Y : Byte
Var FG,BG : byte
Var pFG,pBG : byte
Var C   : Char
Var S   : String
Begin
  fAssign(Fp,OutF,66)
  fReWrite(Fp)
  pFG:=0
  pBG:=0
  s:='|07|16'
  For y:=1 To 25 Do Begin   
    For X:=1 To 79 Do Begin
      FG:=screen[X,Y].cl % 16
      BG:=16+(screen[X,Y].cl / 16)
      if pFG <> FG then s:=s+'|'+padlt(int2str(fg),2,'0')
      if pBG <> BG then s:=s+'|'+padlt(int2str(bg),2,'0')
      C:=screen[X,Y].ch
      s:=s+chr(ord(C))
      pFG:=FG
      pBG:=BG
    End
    fWriteln(Fp,s)
    s:='';
  End
  fClose(Fp)
End

Procedure ColorizeBlueScreen
var
  w,h,fg,bg,atr:byte
Begin
  for w:=1 to 80 do begin    
    for h:=1 to 25 do begin  
      atr:=getattrxy(w,h)    
      fg:=atr % 16           
      bg:=16 + (atr / 16)   
      if fg=2 then fg:=1    
      if fg=10 then fg:=9   
      if bg=18 then bg:=17  
      textcolor(fg + bg*16) 
      gotoxy(w,h)             
      write(getcharxy(w,h)) 
    end
  end
End

var datapath:string
                      
begin
  DataPath := JustPath(ProgName);
  DispFile (datapath+'snake');
  grabscreen
  writexy(1,25,15,'Screen Grabbed to Array')
  pause
  clrscr
  writexy(1,25,15,'Now the screen is clear...')
  pause
  writescreen
  writexy(1,25,15,'...and now we restore the data from our array.')
  pause
  savefile(datapath+'bluesnake.ans')
  writexy(1,25,15,'Saved the screen to a file')
  pause
  ColorizeBlueScreen
  writexy(1,25,15,'Colorized the screen from green to blue.')
  pause
  clrscr
  dispfile(datapath+'bluesnake.ans')
  writexy(1,25,15,'And now we loaded the saved file...')
  pause
end

------------------------------------------------------------------------------

    ___ _          _       _                     
   /   (_)___  ___| | __ _(_)_ __ ___   ___ _ __ 
  / /\ / / __|/ __| |/ _` | | '_ ` _ \ / _ \ '__|
 / /_//| \__ \ (__| | (_| | | | | | | |  __/ |   
/___,' |_|___/\___|_|\__,_|_|_| |_| |_|\___|_|   
                                                
You are free to use this tutor as you need. Just give a mention to me (xqtr).
Credits goes to g00r00, the programmer behind Mystic BBS software and to Mystic
Guy, (Avon) of the Agency BBS, for their great job, keeping BBSing alive... 

Also, respect to Gryphon, whos a greate MPL programmer and his Scripts are
most useful and helpful to Mystic BBS.

Sorry for any typos... English is not my native language...                                            

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    ____  ____ _____              ___                 ____             __      
   / __ )/ __ ) ___/___  _____   /   |  ________     / __ )____ ______/ /__    
  / __  / __  \__ \/ _ \/ ___/  / /| | / ___/ _ \   / __  / __ `/ ___/ //_/    
 / /_/ / /_/ /__/ /  __(__  )  / ___ |/ /  /  __/  / /_/ / /_/ / /__/ ,< _ _ _ 
/_____/_____/____/\___/____/  /_/  |_/_/   \___/  /_____/\__,_/\___/_/|_(_|_|_)

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=   
