
==============================================================================
   
       the          _ __                           _               
   _________  _____(_) /____     ___  ____  ____ _(_)___  ___      
  / ___/ __ \/ ___/ / __/ _ \   / _ \/ __ \/ __ `/ / __ \/ _ \     
 (__  ) /_/ / /  / / /_/  __/  /  __/ / / / /_/ / / / / /  __/ _ _ 
/____/ .___/_/  /_/\__/\___/   \___/_/ /_/\__, /_/_/ /_/\___(_|_|_)
    /_/                                  /____/ 
    
==============================================================================
  Another Droid BBS                                               XQTR // 2020
  29/11/2020                                                     GPL3 Licensed
==============================================================================

  sometimes to make something new, you have first, to make the right tools,
  to help you create it. so, while i was creating something else, i ended
  adopting this method, to save and display ansi graphics. it's not entirely
  new, as if i recall, something similar has been used in the past.
  
  so, the idea is to be able, to display small (or even) big blocks of ansi
  (or even ascii) graphics and save them all together in one file. this one
  file, acts like the "database" for the graphics, in your project. you can
  include from entire screens of ansi graphics (80x25 or even bigger) or
  just small blocks ex: 8x8, 10x4, 80x3 etc. the size doesn't matter.
  
  this way, you can use this sprite engine, to display headers, banners,
  background images or even ansi fonts, like TheDraw does. it's very simple,
  as an idea and even simpler in making it and using it, but in the end, it
  gives much flexibility to the programmer/modder to build awesome stuff.
  
  the engine is based in the file format it needs to store the graphics and
  of course, the code to load and display them. lets begin with the file 
  format. this is part of the complete example below:
  
  !author=xqtr
  !date=29/11/2020
  !license=GPL3
  !title=sprites engine demo
  #80x25, 132x60, whatever resolution, but if it's for adjustable write
  #adjustable
  !resolution=
  #ansi/ascii
  !mode=ansi
  !comment=no comment ;)
  !contact=xqtr@gmx.com
  #name;width;height
  @top_left_corner;9;7
  [1;47m         
  [1;47mÛÜ Ü     
  [1;47mÛ²ÛÛßÜßß 
  [1;47m²ß       
  [1;47mß        
  [1;47mß        
  [1;47m         
  
  the file uses fields. it's similar to an INI file, so it's easy to use and
  understand. the "rules" are:
  
  //: each line beginning with a hast tag # are comments and ignored. this
      way you can describe things, put titles, comments etc.
  
  //: empty lines are also ignored
  
  //: lines beginning with an exclamation mark ! are header fields, to 
      describe the whole file. you can add the author of the file, a contact
      email/weblink, comment, title, the resolution you designed the file
      for etc. you don't have to include all of them, just the ones you want.
      the complete set of fields are:
      
      !author=
      !date=
      !license=
      !title=
      #80x25, 132x60, whatever resolution, but if it's for adjustable write
      #adjustable
      !resolution=
      #ansi/ascii
      !mode=
      !comment=
      !contact=
  
  //: lines beginning with an at symbol @ are special and declare the start
      of an ansi block. consider them as the header for the graphic block.
      the format for this type of line is:
                            
                            @name;width;height
      
      it uses the format of a CSV file, for simplicity. the name field can be
      anything, just don't use spaces, instead use dashes or undescopes. the
      width and height are necessary, to display the graphics correctly. the
      height and width, must be the value of the actual graphic, not the 
      length of the ansi string! 
      
      you can use ansi escape codes or MCI codes for the graphics. you don't 
      add them to the width of the graphic. so if the line looks like:
      
                                |10hello |14world 
                                
      the actuall length of the graphic would be 11 and not 17! same for the
      ansi escape sequences.
  
  //: each line below a header block line (@) contains the actual graphics 
      for the block. empty lines are not ignored in this case! if the height
      of a block is 5 and is something like this:
      
      @example;4;5
      ####
      ####
      
      ####
      ####
      
      the empty line will be included and displayed, when you display the 
      graphic. also the hash symbol is not ignored. all characters, mci codes,
      ansi escape sequences are included in the graphic, if there are in this
      area.

==============================================================================      

  below are two functions in Python/MPY to load the file and use the graphics.
  the function "loadsprites" loads all inforamation stored in the file, to 
  a dictionary, that you can use with the other function below or in any other
  way you want.
  
  loading a file is simple as this:  
                  
                  sprites = loadsprites('sprites.dat')
                  
  
  to display a sprite we use the writesprite function:
  
  writesprite(x,y,name)
    x     : the x position to display the graphic
    y     : the y position to display the graphic
    name  : the name of the graphic block as written in the field line @
            describing the graphic block / sprite.
            
  to display a graphic from the file you do it like this:
  
                writesprite(10,10,'bottom_left_corner')
                
                
  use the example to understand the usage. it's not hard. just remember, that
  you can use it to display entire screens of ansi graphics, so perhaps you
  can store all the ansis a mod uses in one file, instead of multiple or just
  small blocks of graphics (titles, banners, headers) that you can use 
  everywhere in the screen.
  
  although the basic function to use is the writesprite, i also included some
  other function, that show the real power of this idea/engine and how can be
  used in a more advanced way.
  
  the "alignsprite" and "repeatsprite" functions, give you the possibility to
  display the sprites, based on relative coordinates thus can be used in some
  type of adjustable themes, for various resolutions.
  
  run the example in mystic bbs and resize your terminal app, to see how it
  adjusts the graphics based on the resolution of the terminal.
      
==============================================================================
  mystic MPY example
==============================================================================
  
from mystic_bbs import *
import os

width,height = termsize()

def cleararea(x1,y1,x2,y2,bg):
  for i in range(y2-y1):
    gotoxy(x1,y1+i)
    write(bg*(x2-x1+1))

def writexy(x,y,a,s):
  gotoxy(x,y)
  textcolor(a)
  write(s)    

def loadsprites(filename):
  if not os.path.isfile(filename): return
  sf = open(filename,'r')
  res = dict()
  while True:
    line = sf.readline()
    l = line.upper()
    if not line: break
    if line.startswith('#'):
      continue
    elif line == '':
      continue
    elif l.startswith('!AUTHOR'):
      res['author']=line.split('=')[1].strip()
    elif l.startswith('!DATE'):
      res['date']=line.split('=')[1].strip()
    elif l.startswith('!LICENSE'):
      res['license']=line.split('=')[1].strip()
    elif l.startswith('!TITLE'):
      res['title']=line.split('=')[1].strip()
    elif l.startswith('!RESOLUTION'):
      res['resolution']=line.split('=')[1].strip()
    elif l.startswith('!MODE'):
      res['mode']=line.split('=')[1].strip()
    elif l.startswith('!COMMENT'):
      res['comment']=line.split('=')[1].strip()
    elif l.startswith('!CONTACT'):
      res['contact']=line.split('=')[1].strip()
    elif line.startswith('@'):
      line = line[1:]
      name,w,h = line.split(';')
      w=int(w)
      h=int(h)
      name = name.replace('"','')
      item = dict()
      item['width']=w
      item['height']=h
      item['data'] = list()
      for d in range(h):
        l = sf.readline()
        item['data'].append(l)
      res[name]=item
  return res
  
def writesprite(x,y,name):
  for yi in range(sprites[name]['height']):
    gotoxy(x,y+yi)
    write(sprites[name]['data'][yi])
    
#align a sprite to corners of screen
#align values are: TOP_LEFT,TOP_RIGHT,BOTTOM_LEFT,BOTTOM_RIGHT,CENTER
#LEFT_VERT_CENTER,RIGHT_VERT_CENTER
def alignsprite(name,align='TOP_LEFT',x1=1,y1=1,x2=width,y2=height):
  global sprites,height,width
  w = sprites[name]['width']
  h = sprites[name]['height']
  align=align.upper()
  if align=='TOP_LEFT':
    writesprite(x1,y1,name)
  elif align=='BOTTOM_LEFT':
    writesprite(x1,y2-h+1,name)
  elif align=='TOP_RIGHT':
    writesprite(x2-w+1,y1,name)
  elif align=='BOTTOM_RIGHT':
    writesprite(x2-w+1,y2-h+1,name)
  elif align=='CENTER':
    writesprite(1+x1+(x2-x1) // 2 - w // 2,1+y1+(y2-y1) // 2 - h // 2, name)
  elif align=='LEFT_VERT_CENTER':
    writesprite(x1,1+y1+(y2-y1) // 2 - h // 2,name)
  elif align=='RIGHT_VERT_CENTER':
    writesprite(x2-w+1,1+y1+(y2-y1) // 2 - h // 2 ,name)
  elif align=='TOP_HOR_CENTER':
    writesprite(1+x1+(x2-x1) // 2 - w // 2,y1,name)
  elif align=='BOTTOM_HOR_CENTER':
    writesprite(1+x1+(x2-x1) // 2 - w // 2,y2-h+1,name)
  else:
    writesprite(x1,y1,name)
    
def repeatsprite_hor(name,startx,starty,endx):
  global sprites,height,width
  w = sprites[name]['width']
  h = sprites[name]['height']
  dx = 0
  while dx+startx<endx:
    writesprite(startx+dx,starty,name)
    dx+=w

def repeatsprite_ver(name,startx,starty,endy):
  global sprites,height,width
  w = sprites[name]['width']
  h = sprites[name]['height']
  dy = 0
  while dy+starty<endy:
    writesprite(startx,starty+dy,name)
    dy+=h
    
def draw_box(x1,y1,x2,y2,title,cl):
  if cl.upper() == 'BLUE':
    textcolor(31)
    tcl=31
  elif cl.upper()=='GREEN':
    textcolor(15+2*16)
    tcl=15+2*16
  cl=cl+'_'
  cleararea(x1,y1,x2,y2,' ')
  tw = x2-x1-sprites[cl+'box_top_left']['width']-sprites[cl+'box_top_right']['width']
  #alignsprite('box_left_column','LEFT_VERT_CENTER',x1,y1,x2,y2)
  #alignsprite('box_right_column','right_VERT_CENTER',x1,y1,x2,y2)
  repeatsprite_hor(cl+'box_bottom',x1,y2,x2)
  repeatsprite_ver(cl+'box_left_column',x1,y1,y2)
  repeatsprite_ver(cl+'box_right_column',x2,y1,y2)
  alignsprite(cl+'box_bottom_right','bottom_right',x1,y1,x2,y2)
  alignsprite(cl+'box_bottom_left','bottom_left',x1,y1,x2,y2)
  alignsprite(cl+'box_top_left','top_left',x1,y1,x2,y2)
  alignsprite(cl+'box_top_right','top_right',x1,y1,x2,y2)
  writexy(x1+sprites[cl+'box_top_left']['width']+1,y1,tcl,title.center(tw,' '))
    
sprites = dict()

sprites = loadsprites(getcfg()['script']+'sprites.dat')

write('|15|23|CL')
alignsprite('top_left_corner','top_left')
alignsprite('top_right_corner','top_right')
alignsprite('left_column','LEFT_VERT_CENTER')
alignsprite('right_column','right_VERT_CENTER')
repeatsprite_hor('bottom_line',6,height-1,width-18)
alignsprite('bottom_right_corner','bottom_right',1,1,width,height-1)
alignsprite('bottom_left_corner','bottom_left',1,1,width,height-1)

draw_box(5,5,20,20,'TiTLe','blue')

draw_box(width-40,3,width-10,20,'TiTLe','green')

gotoxy(1,10)
write('|PN')
    
    
=============================================================================
//file: sprites.dat
=============================================================================
!author=xqtr
!date=29/11/2020
!license=GPL3
!title=sprites engine demo
#80x25, 132x60, whatever resolution, but if it's for adjustable write
#adjustable
!resolution=
#ansi/ascii
!mode=ansi
!comment=no comment ;)
!contact=xqtr@gmx.com

#name;width;height
@top_left_corner;9;7
[1;47m         
[1;47mÛÜ Ü     
[1;47mÛ²ÛÛßÜßß 
[1;47m²ß       
[1;47mß        
[1;47mß        
[1;47m         
@top_right_corner;9;7
[1;47m         
[1;47m     Ü ÜÛ
[1;47m ßßÜßÛÛ²Û
[1;47m       ß²
[1;47m        ß
[1;47m        ß
[1;47m         
@bottom_left_corner;10;5
[1;47mÜ         
[1;47mÜ         
[1;47m²         
[1;47mÛÜ        
[1;47mß²ÛÛÜßÜÜ  
@bottom_right_corner;9;5
[1;47m        Ü
[1;47m        Ü
[1;47m        ²
[1;47m       ÜÛ
[1;47m ÜÜßÜÛÛ²ß
@bottom_line;21;1
[1;47m   ßÜ°Ü ßÜÜÜßÜ²ÜÜßÜ  
@left_column;1;11
[1;47m°
[1;47m±
[1;47m²
[1;47mÛ
[1;47mÛ
[1;47mÝ
[1;47mÛ
[1;47m²
[1;47m±
[1;47m°
[1;47m 
@right_column;1;11
[1;47m°
[1;47m±
[1;47m²
[1;47mÛ
[1;47mÛ
[1;47mÞ
[1;47mÛ
[1;47m²
[1;47m±
[1;47m°
[1;47m 
@blue_box_top_left;5;4
[0;44m°±ßßÝ
[0;44m²    
[0;44m²    
[0;44mÝ    
@blue_box_top_right;5;4
[0;44mÞßß±°
[0;44m    ²
[0;44m    ß
[0;44m    Û
@blue_box_bottom_left;6;3
[0;44mß     
[0;44m²     
[0;44m°±ÜÜÜÜ
@blue_box_bottom_right;6;3
[0;44m     Þ
[0;44m     ²
[0;44mÜ ÜÜ±°
@blue_box_left_column;1;1
[0;44mÛ
@blue_box_right_column;1;1
[0;44mÛ
@blue_box_bottom;1;1
[0;44mÜ
@green_box_top_left;5;4
[0;42m°±ßßÝ
[0;42m²    
[0;42m²    
[0;42mÝ    
@green_box_top_right;5;4
[0;42mÞßß±°
[0;42m    ²
[0;42m    ß
[0;42m    Û
@green_box_bottom_left;6;3
[0;42mß     
[0;42m²     
[0;42m°±ÜÜÜÜ
@green_box_bottom_right;6;3
[0;42m     Þ
[0;42m     ²
[0;42mÜ ÜÜ±°
@green_box_left_column;1;1
[0;42mÛ
@green_box_right_column;1;1
[0;42mÛ
@green_box_bottom;1;1
[0;42mÜ


==============================================================================

         _____         _   _              ____          _   _ 
        |  _  |___ ___| |_| |_ ___ ___   |    \ ___ ___|_|_| |        8888
        |     |   | . |  _|   | -_|  _|  |  |  |  _| . | | . |     8 888888 8
        |__|__|_|_|___|_| |_|_|___|_|    |____/|_| |___|_|___|     8888888888
                                                                   8888888888
                DoNt Be aNoTHeR DrOiD fOR tHe SySteM               88 8888 88
                                                                   8888888888
 /: HaM RaDiO   /: ANSi ARt!     /: MySTiC MoDS   /: DooRS         '88||||88'
 /: NeWS        /: WeATheR       /: FiLEs         /: SPooKNet       ''8888"'
 /: GaMeS       /: TeXtFiLeS     /: PrEPardNeSS   /: FsxNet            88
 /: TuTors      /: bOOkS/PdFs    /: SuRVaViLiSM   /:            8 8 88888888888
                                                              888 8888][][][888
   TeLNeT : andr01d.zapto.org:9999 / ssh: 8888                  8 888888##88888
   SySoP  : xqtr                   eMAiL: xqtr@gmx.com          8 8888.####.888
   DoNaTe : https://paypal.me/xqtr                              8 8888##88##888
