Spellcaster presents:


TTTTTTTTTT HH      HH EEEEEEEEEE    MM      MM    AAAA     GGGGGGGGG
    TT     HH      HH EE            MMM    MMM   AA  AA   GG
    TT     HH      HH EE            MM M  M MM  AA    AA  GG  
    TT     HHHHHHHHHH EEEEEE        MM  MM  MM  AAAAAAAA  GG   
    TT     HH      HH EE            MM      MM  AA    AA  GG    GGGG
    TT     HH      HH EE            MM      MM  AA    AA  GG      GG
    TT     HH      HH EEEEEEEEEE    MM      MM  AA    AA   GGGGGGGG

                                                        Issue 8
                                                        26-4-96


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  Index:

        1. Introduction
          1.1. About the magazine
          1.2. About the author
          1.3. Distribution
          1.4. Contribuitions
          1.5. Hellos and greets
        2. Mailroom
        3. Coding sites
        4. Designing a text adventure - Part I
          4.1. Plot creation
          4.2. Plotline
          4.3. Map creation
          4.4. Getting started
        5. Our friend, the pointer - Part III
        6. How to make a cool starfield
        7. Sprites Part I - Introduction
          7.1. Generating images
          7.2. Killing an image
          7.3. Displaying an image
          7.4. Clipping
          7.5. Saving to the disk
          7.6. Loading from the disk
        8. Graphics Part VII - Scrolling Part II
          8.1. Tile Scrolling
          8.2. Parallax Scrolling
          8.3. Partial Scrolls
        9. Hints and tips
       10. Points of view
       11. The adventures of Spellcaster, part 8


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  1. Introduction

    1.1. About the magazine

    Welcome to number 8 of 'The Mag', brought to you, as usual, by Spellcaster,
  alias Diogo de Andrade. This is another big issue, so scream in joy ! :)
    I'm very happy !! This is the first issue to feature an article I didn't
  write !! That article is a little bit different from the usual articles. It's
  about were can you get info on programming and other stuff coding-related.
  It was writen by Scorpio (another portuguese dude !!), one of the people
  that most support 'The Mag'... He is also the person that stands all of my
  questions in the MOO I'm trying now... Thanks, man, for beeing there... ;)
    Also, a little add... I'm trying to change the logo on top, the one that
  says 'THE MAG', but my ANSI art sucks !! In fact, all my art sucks !!! I was
  born to code... :) So, if anyone out there wants to do a better logo for me,
  go ahead !!! NOTE- No strange codes, just plain ASCII chars... Not even
  ASCII-E characters, because I use (and sometimes write) this in a UNIX
  machine, and I don't know how to configure the simbols on the screen... :(

    This magazine is dedicated to all the programmers and would-be programmers
  out there, especially to those that can't access the Net easily to get
  valuable information, to those who wish to learn how to program anything,
  from demos to games, passing through utilities and all sort of thing your
  mind can think of, and to those that can't find the right information.

    When you read this magazine, I'll assume some things. First, I assume you
  have Borland's Turbo Pascal, version 6 and upwards (and TASM for the assembly
  tutorials). I'll also think you have a 80386 (or 386 for short; a 486 would
  be even better), a load of patience and a sense of humor. This last is almost
  essencial, because I don't receive any money for doing this, so I must have
  fun doing it. I will also take for certain you have the 9th grade (or
  equivelent). Finally, I will assume that you have the last issues of
  'The Mag', and that you have grasped the concepts I tried to transmit. If
  you don't have the issues, you can get them by mail, writing to one of the
  adresses shown below (Snail mail and Email).

    As I stated above, this magazine will be made especially for those who don't
  know where to get information, or want it all in the same place, and to those
  who want to learn how to program, so I'll try to build knowledge, building up
  your skills issue by issue. If you sometimes fail to grasp some concept, don't
  despair; try to work it out.
    That's what I did... Almost everything I know was learnt from painfull
  experience. If you re-re-re-read the article, and still can't understand it,
  just drop a line, by mail, or just plain forget it. Most of the things I 
  try to teach here aren't linked to each other (unless I say so), so if you
  don't understand something, skip it and go back to it some weeks later. It
  should be clearer for you then. Likewise, if you see any terms or words you 
  don't understand, follow the same measures as before.

    Ok, as I'm earing the Net gurus and other god-like creatures talking
  already, I'm just going to explain why I use Pascal.
  For starters, Pascal is a very good language, ideal for the beginner, like 
  BASIC (yech!), but it's powerfull enough to make top-notch programms.
  Also, I'll will be using assembly language in later issues, and Pascal makes
  it so EASY to use. 
  Finally, if you don't like my choice of language, you can stop whining. The
  teory behind each article is very simple, and common with any of the main
  languages (C, C++, Assembly - Yes, that's true... BASIC isn't a decent
  language).

    Just one last thing... The final part of the magazine is a little story
  made up by my distorted mind. It's just a little humor I like to write, and
  it hasn't got nothing to do with programming (well, it has a little), but, 
  as I said before, I just like to write it.

    1.2. About the author

    Ok, so I'm a little egocentric, but tell me... If you had the trouble of 
  writing hundreds of lines, wouldn't you like someone to know you, even by 
  name ?

    My name is Diogo de Andrade, alias Spellcaster, and I'm the creator, 
  editor and writer of this magazine. 
    I live in a small town called Setubal, just near Lisbon, the capital of
  Portugal... If you don't know where it is, get an encyclopedia, and look for
  Europe. Then, look for Spain. Next to it, there's Portugal, and Setubal is in
  the middle.

    I'm 18 years old, and I just made it in to the university (if you do want
  to know, I'm in the Technical Institute of Lisbon, Portugal), so I'm not 
  a God-Like creature, with dozens of years of practice (I only program by 
  eight years now, and I started in a Spectrum, progressing later to an Amiga.
  I only program in the PC for a year or so), with a mega-computer (I own a 
  386SX, 16 Mhz), that wear glasses with lens that look like the bottom of a 
  bottle (I use glasses, but only sometimes), that has his head bigger than a 
  pumpkin (I have a normal sized head) and with an IQ of over 220 (mine is 
  actually something like 180-190). I can program in C, C++, Pascal, Assembly
  and even BASIC (yech!).

    So, if I am a normal person, why do I spend time writing this ?
  Well, because I have the insane urge to write thousands of words every now
  and then, and while I'm at it, I may do something productive, like teaching
  someone. I may be young, but I know a lot about computers (how humble I am;
  I know, modesty isn't one of my qualities).

    Just one more thing, if you ever program anything, please send to me... I
  would love to see some work you got, maybe I could learn something with it.
  Also, give me a greet in your program/game/demo... I love seeing my name.

    1.3. Distribution

    I don't really know when can I do another issue, so, there isn't a fixed
  space of time between two issues. General rule, I will try to do one every two
  weeks, maybe more, probably less (Eheheheh). This is getting to an issue
  every month, so, I'll think I'll change the above text... :)
    'The Mag' is available by the following means:

    - Snail Mail : My address is below, in the Contributions seccion... Just
                   send me a disk and tell me what issues you want, and I
                   will send you them...

    - E-Mail : If you E-mail me and ask me for some issues, I will Email you
               back with the relevant issues attached.

    - BBS's : I don't know for sure what BBS's have or will have my magazine,
              but I will try to post it in the Skyship BBS.
              If you have a BBS and you want to receive 'The Mag', contact me.

                 Skyship BBS numbers: (351)+01-3158088
                                      (351)+01-3151435

    - Internet : You can access the Spellcaster page and take the issues out
                 of there in:

                 http://alfa.ist.utl.pt/~l42686

    - Anonymous ftp: I've put this issue of 'The Mag' on the ftp.cdrom.com
                     site... I don't know if they'll accept it there, because
                     that's a demo only site, and my mag doesn't cover only
                     demos, but anyways, try it out... It has lots of source
                     code of demos.

    1.4. Contributions

    I as I stated before, I'm not a God... I do make mistakes, and I don't 
  have (always) the best way of doing things. So, if you think you've spotted
  an error, or you have thought of a better way of doing things, let me know.
  I'll be happy to receive anything, even if it is just mail saying 'Keep it 
  up'. As all human beings, I need incentive.

    Also, if you do like to write, please do... Send in articles, they will be
  welcome, and you will have the chance to see your names up in lights.
    They can be about anything, for a review of a book or program that can
  help a programmer, to a point of view or a moan. I'm specially interested in
  articles explaining XMS, EMS, DMA and Soundblaster/GUS.

    If anyone out there has a question or wants to see an article about 
  something in particular, feel free to write... All letters will be answered,
  provided you give me your address.

    I'm also trying to start a new demo/game/utility group, and I need all sort 
  of people, from coders (sometimes, one isn't enough), musicians (I can 
  compose, but I'm a bit limited), graphics artists (I can't draw nothing) and
  spreaders... I mean, by a spreader, someone who spreads things, like this mag.
  If you have a BBS and you want it to include this magazine, feel free to
  write me... I don't have a modem, so I can only send you 'The Mag' by Email.

    You can also contact me personally, if you study on the IST (if you don't
  know what the IST is, you don't study there). I'm the freshman with the
  black hair and dark-brown eyes... Yes, the one that is trying to make people
  believe he can code !! :)

    My adress is:
                 Praceta Carlos Manito Torres, n§4/6§C
                 2900 Set£bal
                 Portugal

    Email: dgan@rnl.ist.utl
           l42686@alfa.ist.utl.pt

    And if you want to contact me on the lighter side, get into the Lost Eden
  talker... To do that telnet to:

                        Alfa.Ist.Utl.Pt  : Port 1414

    If that server is down, try the Cital talker, in

                        Zeus.Ci.Ua.PT    : Port 6969

    I'm almost always there in the afternoon... As you may have guessed already,
  my handle is Spellcaster (I wonder why...)...

    1.5. Hellos and greets

    I'll say hellos and thanks to all my friend, especially for those who put 
  up with my constant whining.
    Special greets go to Denthor from Asphyxia (for the excelent VGA trainers),
  Draeden from VLA (for assembly tutorials), Joaquim Elder Guerreiro, alias
  Dr.Shadow (Delta Team is still up), Joao Neves for sugestions, testing and
  BBS services, and all the demo groups out there.
    I will also send greets to everybody that responded to my mag... Thank
  you very much !
    Super Very Special thanks go to: Ricardo 'Scorpio' Oliveira...

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

  2. Mailroom

    Well, I've got another letter telling me I SUCK ! Just kidding... I got
  a letter of someone who noticed one error in a previous issue. It was a
  friend of mine called Nuno, and he studies in the same school as I do, and
  he is also trying to major in Computer Engeneering (hope he doesn't end like
  me and he makes through !)...
    Well, so what's the screw-up, you may ask... And I will answear... In issue
  four, in the graphics section, in the part about circles, I told you that
  Pascal uses reverse-angling, that is, that the trigonometrical circle goes
  in a clockwise direction... Well, this is WRONG !! Pascal goes in the
  standart (anti-clockwise) direction... The reason that made me believe so
  was that in real life, usually the y axis increases the more you go upwards
  and decreases going downwards... But in the computer sense, this goes
  reverse, and that is why I screwed up... Sorry but that ! :)
    Thanks, NSJ !

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

    Other letter I got was from a guy who told me that he got an Heap Overflow
  error, when he runned the same program lot's of times... I've checked the
  program and realized that it was my fault ! I forget to told you, when I
  talked about virtual screens, back in issue 5, that you should de-allocate
  the pointers to the virtual pages, because they are filling the memory.
  So, in the end of the program, when you don't need the virtual screens anymore
  you should call the CloseVirt procedure:

                      Procedure CloseVirt;
                      Var A:Byte;
                      Begin
                           For A:=1 To Npages Do
                           Begin
                                Freemem(Virt[A],64000);
                                VP[A]:=$A000;
                           End;
                      End;

    Sorry about that... :)

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

  3. Coding sites

    Ok, this is the article writen by Scorpio (as you will guess)... I,
  Spellcaster will make my coments between square brackets []...

    I am Ricardo Manuel Oliveira, aka Scorpio and I live in Portugal...
    [ I already told you that !! :) ]
    I have read all the issues of The Mag (like all of you :)) and I was
  very angry 'cos Spellcaster didn't know the URL of my page (Check 'THE
  MAG #7).. :)...
    He didn't know Denthor's one too, though... :)
    [ So, I'm no URL storing machine !! :) ]
    Now, more seriously:
    This article is a coding-resources list gathered by me... Why have I decided
  to share it with you all? 'Cos I'm happy right now... I have created a
  HTML page about my local soccer club (Vitoria Sport Clube, for portuguese
  people), and it appeared reviewed in a national magazine (CyberNet, for ...)
    So, I have felt that I have to share this joy with lots of people and
  I'm contributing to the growth of 'THE MAG'... Since I read it, I could see
  my name there, right ?
    [ Of course you can !! Write some more !! I like having less work to do ! ]

  DEMO-RELATED CODING PAGES:
  --------------------------

  http://www.dur.ac.uk/~d405ua/demoftps.html
  Extensive list of demo-related FTP servers (UK)

  http://www.cdrom.com/pub/demos/hornet/8086
  8086 Compo + Coding examples to the 8086

  http://196.6.101.37:80/grants/Asphyxya/Asphyxya.html
  Denthor / Asphyxia 's Homepage GREAT vga-coding Tutorials!!!

  http://alfa.ist.utl.pt/~l42686/
  Spellcaster's Homepage - 'THE MAG' (general (demo/vga)coding magazine)
  You're reading it
  [ My great page ! He forgot that 'The Mag' also haves stuff about game
    making and the Adventures of Spellcaster !! :) ]

  GENERAL-CODING PAGES:
  ---------------------

  http://www.cs.vu.nl/~jprins/tp.html
  Turbo Pascal Programmer's page
  If you're into Pascal, CHECK THIS ONE NOW!!!

  http://www.interlog.com/~jfanjoy/swag/swag.html
  SWAG Homepage - Pascal code for everybody (_LOTS_ OF CODE)

  http://www.fys.ruu.nl/~faber/amain.html
  Faber's Hotlist Assembly links

  http://www.cera2.com/assembly
  Assembly Language HotLists and Major Resources

  http://www.caiw.nl/~jdbruijn/cl.html
  Code Language Page, by Jack de Bruijn

  http://onyx.idbsu.edu/~jcofflan/
  Joe's Assembly Language Page, by Joe :-)

  http://www.netrunner.net/~irvinek/asm.htm
  Assembly Language Sources, by Kip P. Irvine

  Where can I find CODE?:
  -----------------------

  ftp://ftp.cdrom.com/pub/demos/code
  Democoding examples and more... a LOT more
  It's in USA, a bit _SLOW_ for european users during the afternoon...

  ftp://ftp.luth.se/pub/msdos/demos
  Mirror of cdrom
  _FAST_ for european users (_VERY_FAST_ indeed :)) (includes FTPMAIL!!!)

  ftp://ftp.co.iup.edu/code
  Mirrors cdrom's CODE dir.
  'Bit slow...

  ftp://x2ftp.oulu.fi/pub/msdos/programming
  This place is huge! It'll keep you busy for a LONG time.

  ftp://garbo.uwasa.fi/pub/
  Another BIG place... Happy searchin'

  Oh, yes... Check the alt.lang.* newsgroups
  [ And the following groups:
    comp.graphics.algorithms
    rec.games.programmer
    comp.programming
    alt.sys.ibm.pc.demos (I think this is it... Search for demos... :) ]

  At last, if you want more links try my bookmarks at
  http://wwwalu.ci.uminho.pt:8888/~si17899/bookmark.html
  To contact me, email me at: si17899@ci.uminho.pt
  [ Check it out !! :) ]

    Spellcaster, take the lead...

    Thanks Scorpio ! Feel free to write some more ! :)
  So, let's move on to the next article...

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

  4. Designing a text adventure - Part I

    This article is intended for the beginners who want to do games. I know
  that text adventures are definetly out, but the ideas behind a text adventure
  are very similar to those found in graphic adventures, so what I say here
  can be expanded to a graphic adventure...
    Note that these are my personal views on how a text adventure should be
  coded and designed... These articles will teach _EVERY_ step in the creation
  of a text adventure, from designing and plotline creation, to programming, to
  pointers on how to further expand the system...
    There can be some errors and irregularities in the code and explanations,
  because I'm writing this at the same time I'm coding the adventure, so I
  will sometimes make mistakes... If you spot one, or have any doubts, mail me !

    4.1. Plot creation

    This part of the article will give you some pointers on how you should start
  to build your text adventure...
    So, for me, one the more important parts of _ANY_ game is the plot... It's
  the first thing I do, because I believe that the other pieces of the game
  (playability and such) will come together, if you have a good story. In this
  stage you must try to be original... Because it's getting harder and harder
  to be original in the plot, you must be original in other things... For
  example, I do most of the story of the game with the idea of SUSPENSE ! I
  like to put the hero in the game with no idea of what he is supposed to
  accomplish... This is my idea... Other piece of originality is the atenction
  to detail... This is also very important, for it adds very much for the
  atmosphere of the game... So, if you follow these ideas, you can make a text
  adventure that is fun to play, even in these DOOM-clones filled times...
    Just for the record, I will state something about Doom... Cool graphics and
  gameplay, crappy story !! I admire the guys at ID software (technically
  speaking), but I would like to shoot their desing department ! :)
    Another important of the detail is to pay heed to the time/space in which
  the game occurs... If you are doing an adventure passed into outer-space, you
  shouldn't put an old Chevy in it... :)
    So, for our text adventure, I will conceive a little stupid story... So, let
  me think... Hum... This is hard... :)
    Ok, I got it! Let's imagine that the hero's uncle has just passed out (on
  the 25 April 1996... This is details, that make up the atmosphere), and he
  had sent you a letter (the letter is dated of the 23 April 1996... Again,
  detail) before he did so, saying that he has a treasure hidden in his
  mansion (that is in a high cliff, near the ocean), and if you can found it,
  it is yours... So, filled with lust for gold, the hero sets foot for the
  mansion. Crappy story, I know, but this one enables me to explain how to
  make a basic text adventure...
    Another thing I think it's very important in a game (of any kind) is the
  title... I don't know, but I spend a lot of time thinking of the name for a
  game... The name should give the user the will to explore the game further...
    So, one possible title for this game should could be 'The Mansion', but
  this doesn't call the atencion... So, you could name it 'Treasure Mansion',
  but this title is crappy, because the word 'treasure' has been used so many
  times in games and titles that has became vulgar... Other examples of words
  like that is 'Prince', 'Princess', 'Dragon'. Let's try another time... How
  about 'Evil Mansion'. It's getting better, but I think it isn't suitable for
  the game yet... We also have variations on the theme: 'Evil Home', 'Home of
  Evil', 'Evil Lair'... But still, altough they are cool, they remember a game
  where there is a house possessed by an evil sorcerer... And this is not the
  case (altough it could be)... The only problem (in this story I'm developing)
  in the house is that it has a couple of monsters/haunts... We could call it
  'Haunted House', or 'Haunted Mansion', but 'Haunted' is one of those words,
  similar to 'treasure'... So, we can be a bit more inventive, and add to the
  atmosphere. We could say that the name of the mansion is 'Fanglore', and call
  the game just that: 'Fanglore'... using this name, we've added to the
  atmosphere (because we added the detail of the name of the house), and we
  gave a name to the game that make the user not know what is he going to
  see... :) So, the process of creating the great text-adventure 'Fanglore' is
  underway... :)

    4.2. Plotline

    Next in the list (for me) is the plotline and map creation. These two steps
  should be made simultaneosly...
    The plotline is the course the game should follow normally...
    In Fanglore, the plotline is:

         Going to Fanglore -> Finding the treasure -> Escape the house

    A game can have several endings, and different soluttions for the game, and
  that increases the complexity of the plotline (and the overall interest of
  the game)... But the way of doing this is the same.
    Each part of the plotline is a different step of the game. Now, every
  step of the game has different objectives (though the game has only one
  objective). For example, the first step 'Going to Fanglore' has his own
  objective, that is, to enter the mansion. Also, every step of the game has
  a reason of existing... The basic reason is the completion of the game, but
  you must explain, that is, you must know what put the character(s) in that
  particular position. The first step of Fanglore exists because the taxi left
  us at the door of the mansion, that is closed for years, and you don't have
  the key. Also, very step of the game have a way to completing it's objective,
  and that also needs to be defined. Again, in the first step, the way of
  getting into Fanglore is to find a shovel and knock down the door... You
  also have to take in account possible problems and a way to overcome them...
  The problems in a text adventure are not only made of a difficult puzzle to
  resolve; monsters and other similar things also have it's effects... In the
  first part there aren't any problems, but in the second part, there is a
  room that is filled with gas, and you have to use a gas mask to go past that
  room, and to find the mask, you must go to the kitchen of the house and open
  the oven... That is were the mask is...
  So, scematically:

             - Going to Fanglore
               Objective ?
                 To get into Fanglore.
               Why ?
                 Because the cab left us at the door and we don't have the key.
               How ?
                  Get the shovel
                  Knock down door with it (the door opens)

    The other steps of the plotline:

             - Finding the treasure
               Objective ?
                 Find the treasure.
               Why ?
                 Because that is the point of the game.
               How ?
                 Go to kitchen
                 Open oven
                 Get gas mask
                 Wear mask
                 Go past the gas room
                 Find the library
                 Push bookshelf (you found a secret passage)
                 Go through passage
                 You found the vault with the treasure
               Other problems ?
                 Monsters in some rooms, that can kill you. You can kill them,
                 if you have the sword (that is in the main room)...

             - Escape the house
               Objective ?
                 Get out of Fanglore.
               Why ?
                 Because the passage has closed when you passed through it, and
                 you are stuck...
               How ?
                 Push a brick in the wall (reveals another secret passage)
                 Go through passage

    So, the plotline is complete... You should also create a map while you are
  doing the plotline...

    4.3. Map creation

    As said, the map should be made at the some time as the plotline. There
  isn't a standart way of doing the map... The ideia is this: In the map, you
  should setup were are going to be the exits of a room (the relantion between
  the various rooms) and you should also number all the rooms.
    The first draft for a map is shown in picture Map01.Pcx. It has six rooms:

    1. Kitchen                 2. Main Hall                  3. Gate
    4. Gas Room                5. Library                    6. Treasure Room

    For the plotline, you already know the importance of each of these rooms.
    The yellow line connecting rooms mean that there is an exit from one to
  another. If there isn't a line, there isn't an exit. The orange dotted line
  means a conditional exit, that is, an exit that is only open at certain times,
  or when you have made something (for example, the exit from room 3 to room 2
  only opens when you break down the door). An arrow on the end of a exitline
  means an unidirectional passage... For example, you can go east from room 5
  to room 6, but you can't go west back to room 5.

    But, this doesn't give much to explore, and almost all players like to
  search dozens of rooms... It is part of the detail... For example, in a
  mansion should have a dinning room, and a great garden... Well, these aren't
  present in the draft. And a mansion should also have bedrooms, and Fanglore
  as we designed doesn't have them.
    So, you make a more detailed map... This is in Map02.Pcx. It has 22 rooms.
  The rooms that are in green are the garden-rooms, and the others are the
  mansion-rooms. We've also added the dinning room also. But we didn't added
  the bedroom. Well, if you want, you can ommit certain details, but you have
  to add others. In the case of bedrooms, you can say that there is a stair in
  the Main Hall, that takes you to the upper floor, but the stair is blocked,
  so you can't go that way...
    So, the map is now complete.

    4.4. Getting started

    So, let's get started in the coding department... We first should get the
  room data. I've made a little program that enables you to type in the room
  data (with lame source code). Each room is a record that has the exits info,
  and the description of the room. So:

             Type RoomType=Record
                                 Desc:Array[1..10] of String[79];
                                 North,South,East,West:Byte;
                           End;

    The description of the room is an array of strings, with only 79 chars per
  string because the screen only has enough space for 80 lines, and you must
  take in acount the return character. You don't need to use the complete 79
  columns, neither the 10 lines of text. You just have to type in a line with
  a single '*' after the last line of text. If you want to use the 10 lines,
  the '*' is left out.
    As you have various rooms, you should have an array with the various rooms:

              Const NumberRooms=22;

              Var Rooms:Array[1..NumberRooms] of RoomType;

    The fields North, South, East and West are a number that indicates to
  what room you should go  if you go in that direction. A zero there indicates
  that there isn't an exit in that direction. For example, if Rooms[1].North=2
  then, if you go north in room 1 you will go to room 2.
    Fanglore will use a file called Room.Dat to store the room data.
    So, to end this issue's article in text adventure, here is the procedure
  to read the room data:

              Procedure ReadRoomData;
              Var F:Text;
                  A,B:Byte;
                  Flag:Boolean;
              Begin
                   { Prepares the text file for accessing }
                   Assign(F,'Room.Dat');
                   Reset(F);
                   { For every room in the game }
                   For A:=1 To NumberRooms Do
                   Begin
                        { Clear the room's description }
                        For B:=1 To 10 Do Rooms[A].Desc[B]:='';
                        { Read the description of the room }
                        Flag:=True;
                        B:=1;
                        While Flag Do
                        Begin
                             Readln(F,Rooms[A].Desc[B]);
                             If (B=10) Or (Rooms[A].Desc[B]='*') Then
                                Flag:=False;
                             Inc(B);
                        End;
                        { Read exit data }
                        Readln(F,Rooms[A].North);
                        Readln(F,Rooms[A].South);
                        Readln(F,Rooms[A].East);
                        Readln(F,Rooms[A].West);
                   End;
                   Close(F);
              End;

    The procedure is self-explainatory, with the exceptions of a few points.
    If you are wondering why do we clear the rooms description before we load,
  if there isn't anything there, this is the answear: Pascal, when it creates
  a variable, puts it anywhere in memory it can fit, but it can be something
  there, and Pascal doesn't erase it... It is good programming pratice to clear
  the comtent of a variable before using it (jee, I sound like one of my
  teachers). Second of all, I don't know if I already showed you what does the
  Inc keyword. The Inc keyword increases the variable given by one (if no other
  parameter is specified). So, Inc(B) is equal to B:=B+1, and Inc(B,2) is equal
  to B:=B+2...

    Well, the call to this procedure should be made in a procedure called
  something like Init, which sets up all variables and prepares the game to
  be played. So, procedure Init is (for the moment, for there are other stuff
  to initialize) like this:

              Procedure Init;
              Begin
                   ReadRoomData;
              End;

    And the Init procedure should be called from the main program. So, the
  main program should be like this, for the meanwhile:

              Begin
                   Init;
              End.

    This all is already typed in file FangLore.Pas...
    NOTE: Any file generated with the RoomGen program can be altered by hand,
          that is, you can load it to any ASCII editor and edit any errors
          and alterations you want to make... Don't forget to save as ASCII
          the altered file !
    So, in next issue, I will tell you how to build the phrase parser and some
  of the basic verbs... Until then, try to do it by yourself... :)

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

  5. Our friend, the pointer - Part III

    Well, this is the last of the articles on pointers... Nobody liked them, so
  I won't waste my time with them anymore... :(
    In this part I will discuss dynamic structures to the highest degree (I'm
  such a liar !! :) ).
    So, let's assume you want to do a database that stores the names and
  phonenumbers of all your friends (if you are like me, you have _LOTS_ of
  friends... :))) ). You could do it in the tradicional manner:

                  Type FriendRec=Record
                                       Name:String;
                                       Phone:String;
                                 End;

                  Var Friends:Array[1..100] Of FriendRec;

    But this would impose a limit of 100 friends, and you would spend your
  precious variable memory... Even if you increased the size of the array,
  you would have a limit of 128 friends, because you have 2 strings, that
  ocuppy 256 bytes each, and: 2*256*128=65536, that is the maximum memory you
  can allocate for variables... That is no good... You should be able to add
  phonenumbers 'till you run out of memory...
    You could use the method teached in number one, using static pointers, but
  that only enables to use 65536 bytes of memory, and that would only solve
  the problem of lack of variable memory.
    You can use a variant of method 2, that it is what I'm going to teach you.

    First, define this:

                       Type PFriend=^FriendRec;
                            FriendRec=Record
                                            Name:String;
                                            Phone:String;
                                            Next:PFriend;
                                      End;

                       Var Friends:PFriend;

    Ok, you did understand it, did you ?
    Think about this... What we are doing is to create a structure that points
  to a place in memory where a structure of type FriendRec is allocated. In
  the FriendRec structure it is defined another pointer that points to another
  structure of type FriendRec, and so forth... Look at the scheme below... In
  the beggining, variable Friends points to a random position in memory:

      Friends --> ?

    Then, you allocate the structure associated with variable Friends... To do
  so, you use the New command... So, you do:

                              New(Friends);

    The structure will change and become something like this:

      Friends --> Name
                  Phone
                  Next --> ?

    To access now the name and phonenumber of the first friend, you just do:

                              Friends^.Name:='Diogo Andrade';
                              Friends^.Phone:='555-1355';
                              Writeln(Friends^.Name);

    You should make always:   Friends^.Next:=NIL;  I will explain the reason
  why later.
    And if you want to create another structure, that is, another friend ?
  Yes, you do:
                              New(Friends^.Next);

    The structure would look something like this:

      Friends --> Name    --> Name
                  Phone   |   Phone
                  Next ---|   Next --> ?

    So, the fields would be accessed by:

                            Friends^.Next^.Name
                            Friends^.Next^.Phone
                            Friends^.Next^.Next

    But, if you increase the number of structures, it would become impossible
  to access them all... So, you use a little trick: you assign an extra variable
  to the first record, and then you go through all of them, like this:

               Procedure GoLast;
               Var Tmp:PFriends;
               Begin
                    Tmp:=Friends;
                    While Tmp^.Next<>NIL Do
                    Begin
                         Tmp:=Tmp^.Next;
                    End;
                    .....
                    { Tmp now points to the last structure }
                    .....
               End;

    Now do you understand what does the NIL value is needed for ? To know what
  is the last... :))
    I know this sounds complicated at first, but with pratice it will come to
  you as natural as 2+2=5... :)
    Just a few notes...
    1) Never loose the pointer to the first position in memory, or else you
       can't access that address... As the matter of fact, you shouldn't loose
       _ANY_ pointer, or else they will become lost pieces of memory you can
       no longer access...
    2) Don't forget to deallocate the pointers when you end the program, or
       else you will loose the memory on which they are allocated... They don't
       just go away like variables in a Pascal program... They need to be
       deallocated with the Dispose keyword...

    Well, I think this wraps it up... As I said earlier, this is the last of the
  pointer articles, so I'm going to start a new series in next issue... I don't
  know what it will be, but I think I'm going to make it about assembly...
  Stay tuned... Spellcaster out... :)

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

  6. How too make a cool starfield

    This article is about creating a starfield... Remember the starfield
  program I gave in the Graphics article, last issue ? Well, that program
  SUCKED big time ! :) As I don't like beeing regarded as a bad programmer
  (altough I am :) ), here is an article on about how to create a
  starfield... Well, starfields are funny... They are beutifull and exciting...
  And this text is sucking ! :)
    Now, really, the problem of the starfield in issue 7 is that it spent too
  much of the processor (it wasn't efficient, because it moved the entire
  screen, when it only needed to move a few pixels) and it was unreallistic,
  because we all know that stars that are further away from us move slower that
  stars close to us...
    So, let's get down to business... One of the reasons why the starfield of
  last issue's was so slow was that you scrolled _ALL_ the pixels of the screen,
  that is, you did 64000 moves !! That's ridiculous, if you think that the
  screen only had 1000 stars, that is, you could move all the stars using only
  1000 memory moves... That is 64 times faster !
    So, the ideia is this... You keep track of the coordinates of all the stars,
  and you move them wherever you want...
    Ok, so one star is a record:

               Type Star=Record
                               X,Y:Integer;
                               Color:Byte;
                         End;

    And lots of stars are an array:

               Var Stars:Array[1..1000] Of Star;

    Then, you must initialize the stars, the graphics and the palette... We
  will use the Mode13h unit, that was given the last issue:

               Procedure Init;
               Var A:Word;
               Begin
                    InitGraph;
                    { Sets up 16 grays... }
                    For A:=0 To 15 Do SetColor(A,A*4,A*4,A*4);
                    { Sets up stars }
                    For A:=1 To 1000 Do
                    Begin
                         Stars[A].X:=Random(320);
                         Stars[A].Y:=Random(200);
                         Stars[A].Color:=Random(15)+1;
                    End;
               End;

    Note: If you are wondering why Stars[A].Color:=Random(15)+1, know this:
          0 <= Random(x) < x ; but, in this example:
          0 <= Random(15) < 15 ; but a star shouldn't get color 0, so we add
          one to Random(15) and we get:
          1 <= Random(15)+1 < 16... So we get the colors we want... :)

    Having done this, let's move on to more theory... The ideia is this:

                      1) Clear Stars
                      2) Move Stars
                      3) Draw Stars
                      4) Go back to step 1

    So, how do you do this, you may ask... Well, like this...

    Step 1: You just draw the stars in their positions, but in color 0 (or
            whatever is the color of your background). Remember to only clear
            the stars before you move them, or else you don't clear them,
            you'll just draw them in color 0 elsewhere !

            Procedure ClearStars;
            Var A:Word;
            Begin
                 For A:=1 To 1000 Do PutPixel(Stars[A].X,Stars[A].Y,0,VGA);
            End;

    Step 2: You should give the ability to move the stars to every direction
            you may want... As this is a bidimensional starfield, you have
            2 incrementation variables. The ideia is to sum to the coordinates
            of the stars a value given to the procedure.

            Procedure MoveStars(Ix,Iy:Integer);
            Var A:Integer;
            Begin
                 For A:=1 To 1000 Do
                 Begin
                      Stars[A].X:=Stars[A].X+Ix;
                      Stars[A].Y:=Stars[A].Y+Iy;
                 End;
            End;

            Now, let's assume you want to move the stars left... You just
            do MoveStars(-1,0); Easy, isn't it ?
            Wrong ! If you do like this, you can get an error... For example,
            let's assume:
                         Stars[1].X:=0;
                         Ix:=-1;
            If you execute the MoveStars procedure, you will get an error,
            because Stars[1].X is a variable of type Word, that doesn't allow
            negative numbers!
            The best way to fix this is to make the X and Y fields of the
            Star's record variables of type Integer and check if their are out
            of bounds... If they are, you wrap it around the screen.
            Wrap around the screen is a process that means that is a star has
            coordinates (0,10) and it is scrolled left, a new star will appear
            at (319,10)... So, the new procedure is like this:

            Procedure MoveStars(Ix,Iy:Integer);
            Var A:Integer;
            Begin
                 For A:=1 To 1000 Do
                 Begin
                      Stars[A].X:=Stars[A].X+Ix;
                      Stars[A].Y:=Stars[A].Y+Iy;
                      If Stars[A].X<0 Then Stars[A].X:=319;
                      If Stars[A].X>319 Then Stars[A].X:=0;
                      If Stars[A].Y<0 Then Stars[A].Y:=199;
                      If Stars[A].Y>199 Then Stars[A].Y:=0;
                 End;
            End;

    Step 3: Well this should be simple enough... You just draw the stars ! :)

            Procedure DrawStars;
            Var A:Word;
            Begin
                 For A:=1 To 1000 Do
                   PutPixel(Stars[A].X,Stars[A].Y,Stars[A].Color,VGA);
            End;

    Well, this is easy enough, right ? The whole program is here:

               Program Starfield;

               Uses Mode13h;

               Type Star=Record
                               X,Y:Integer;
                               Color:Byte;
                         End;

               Var Stars:Array[1..1000] Of Star;
                   A:Integer;

               Procedure Init;
               Var A:Word;
               Begin
                    InitGraph;
                    { Sets up 16 grays... }
                    For A:=0 To 15 Do SetColor(A,A*4,A*4,A*4);
                    { Sets up stars }
                    For A:=1 To 1000 Do
                    Begin
                         Stars[A].X:=Random(320);
                         Stars[A].Y:=Random(200);
                         Stars[A].Color:=Random(15)+1;
                    End;
               End;

               Procedure ClearStars;
               Var A:Word;
               Begin
                    For A:=1 To 1000 Do PutPixel(Stars[A].X,Stars[A].Y,0,VGA);
               End;

               Procedure MoveStars(Ix,Iy:Integer);
               Var A:Integer;
               Begin
                    For A:=1 To 1000 Do
                    Begin
                         Stars[A].X:=Stars[A].X+Ix;
                         Stars[A].Y:=Stars[A].Y+Iy;
                         If Stars[A].X<0 Then Stars[A].X:=319;
                         If Stars[A].X>319 Then Stars[A].X:=0;
                         If Stars[A].Y<0 Then Stars[A].Y:=199;
                         If Stars[A].Y>199 Then Stars[A].Y:=0;
                    End;
               End;

               Procedure DrawStars;
               Var A:Word;
               Begin
                    For A:=1 To 1000 Do
                       PutPixel(Stars[A].X,Stars[A].Y,Stars[A].Color,VGA);
               End;

               Procedure MoveAround(Ix,Iy:Integer);
               Begin
                    ClearStars;
                    MoveStars(Ix,Iy);
                    DrawStars;
               End;

               Begin
                    Init;
                    For A:=1 To 100 Do MoveAround(-1,0);
                    For A:=1 To 100 Do MoveAround(-1,1);
                    For A:=1 To 100 Do MoveAround(0,1);
                    For A:=1 To 100 Do MoveAround(1,1);
                    For A:=1 To 100 Do MoveAround(1,0);
                    For A:=1 To 100 Do MoveAround(1,-1);
                    For A:=1 To 100 Do MoveAround(0,-1);
                    For A:=1 To 100 Do MoveAround(-1,-1);
                    Closegraph;
               End.

    So, if you run this, you realize that this FLICKER A LOT !! And that
  anoying ! Ok, to mend this, you should use virtual screens... The ideia is
  the same, differing only that you clear and draw the stars in the virtual
  screen and then you copy the virtual screen to the VGA screen... That will
  avoid the flicker... Program:

               Program VirtualStarfield;

               Uses Mode13h;

               Type Star=Record
                               X,Y:Integer;
                               Color:Byte;
                         End;

               Var Stars:Array[1..1000] Of Star;
                   A:Integer;

               Procedure Init;
               Var A:Word;
               Begin
                    InitGraph;
                    InitVirt;
                    Cls(0,VP[1]);
                    { Sets up 16 grays... }
                    For A:=0 To 15 Do SetColor(A,A*4,A*4,A*4);
                    { Sets up stars }
                    For A:=1 To 1000 Do
                    Begin
                         Stars[A].X:=Random(320);
                         Stars[A].Y:=Random(200);
                         Stars[A].Color:=Random(15)+1;
                    End;
               End;

               Procedure ClearStars;
               Var A:Word;
               Begin
                    For A:=1 To 1000 Do PutPixel(Stars[A].X,Stars[A].Y,0,VP[1]);
               End;

               Procedure MoveStars(Ix,Iy:Integer);
               Var A:Integer;
               Begin
                    For A:=1 To 1000 Do
                    Begin
                         Stars[A].X:=Stars[A].X+Ix;
                         Stars[A].Y:=Stars[A].Y+Iy;
                         If Stars[A].X<0 Then Stars[A].X:=319;
                         If Stars[A].X>319 Then Stars[A].X:=0;
                         If Stars[A].Y<0 Then Stars[A].Y:=199;
                         If Stars[A].Y>199 Then Stars[A].Y:=0;
                    End;
               End;

               Procedure DrawStars;
               Var A:Word;
               Begin
                    For A:=1 To 1000 Do
                       PutPixel(Stars[A].X,Stars[A].Y,Stars[A].Color,VP[1]);
               End;

               Procedure MoveAround(Ix,Iy:Integer);
               Begin
                    ClearStars;
                    MoveStars(Ix,Iy);
                    DrawStars;
                    WaitVbl;
                    CopyPage(VP[1],VGA);
               End;

               Begin
                    Init;
                    For A:=1 To 100 Do MoveAround(-1,0);
                    For A:=1 To 100 Do MoveAround(-1,1);
                    For A:=1 To 100 Do MoveAround(0,1);
                    For A:=1 To 100 Do MoveAround(1,1);
                    For A:=1 To 100 Do MoveAround(1,0);
                    For A:=1 To 100 Do MoveAround(1,-1);
                    For A:=1 To 100 Do MoveAround(0,-1);
                    For A:=1 To 100 Do MoveAround(-1,-1);
                    Closegraph;
                    CloseVirt;
               End.

    If you are smart, you already realized that this is slow... Again! Why ?
  Because you are moving 64000 bytes again, because of the virtual screen!
    So, what's the solution for the slowness ? Well, in my computer, there
  isn't !! But, in a good computer, you can try testing for the Vertical
  Retrace and clear and draw only when the electron beam is going up the
  screen... But, to clear and draw in a single retrace, you should avoid
  computing the movement of the stars, so you should use two arrays to store
  the before and after positions.

    Now, let's get to the real fun stuff... It was about time !!! This is
  getting to be the most boring article in 'The Mag'... As the matter of fact,
  this the most boring article of any mag!
    Ok, multi-speed scrolling! Well, stars that are at a greater distance from
  us tend to move slower relatively to us. So, you must compute the movement
  of the stars bearing in consideration the distance... But, because you don't
  have to add another variable that stores the distance, you can use the color
  variable, because a star that is farther from us is naturally less bright, so,
  the nearest the color is to 0, the slower it will move. The only thing that
  must be changed in the program is the procedure that computes the stars,
  the MoveStars procedure. It will became something like this:

               Procedure MoveStars(Ix,Iy:Integer);
               Var A:Integer;
               Begin
                    For A:=1 To 1000 Do
                    Begin
                         Stars[A].X:=Stars[A].X+Ix*(Stars[A].Color Div 4);
                         Stars[A].Y:=Stars[A].Y+Iy*(Stars[A].Color Div 4);
                         If Stars[A].X<0 Then Stars[A].X:=319;
                         If Stars[A].X>319 Then Stars[A].X:=0;
                         If Stars[A].Y<0 Then Stars[A].Y:=199;
                         If Stars[A].Y>199 Then Stars[A].Y:=0;
                    End;
               End;

    You add to the coordinate of the star a value computed taking in acount
  the distance, that is, the brightness of the star...
    This gives 4 different speeds (16 different brightnesses DIV 4)... You
  could improve to use real values, instead of integer, and then have a greater
  flexibility...
    So, get coding and improve this code, because, altough it is better
  than last issue's code it just plain _SUCKS_ ! :) Have fun... :)

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

  7. Sprites Part I - Introduction

    This is the first of a series of articles that teaches you how to create
  and use your vey own Sprite Engine... Sprites are a very important part in
  coding games and some demos... They are the vital part of games like R-Type,
  Street-Fighter and all games like that.
    This will be more like a image handling tutorial, but sprites are just
  that, images that move (or not) on the screen. In the end of this first part,
  you will be able to get an image from the screen, how to save and load images
  from disk, and to put an image in the screen.

    7.1. Generating images

    This will teach you how to get an image from the screen.
    First of all, we need to know were to store it... The first think to come to
  mind is an array, but that isn't suitable to store an image, because we want
  to store images of different sizes, and an array has a fixed sized... So we
  need a structure whose size can be defined at run-time, rather than at
  compile time... So, who do you gonna call ? POINTERS !!!
    So, get 'The Mag', issue 7, for a complete explanation on how this
  particular way of handling pointers will work.
    So, the ideia is this:

                  1) Allocate the required space for the image
                  2) Get the image

    Let's start with number one... So, to allocate the space, we use the GetMem
  keyword, but we need to know how much space we want to allocate. Let's assume
  you are trying to make the GetImage procedure:

             Procedure GetImage(x1,y1,x2,y2:Word;Var Img:Pointer;Where:Word);

    This would get the image from (x1,y1) to (x2,y2) of page Where and store it
  in Img...
    Well, to store a pixel, you need a byte, well, to store a rectangular
  piece of image, you need x*y pixels, where x and y are the horizontal and
  vertical sizes, respectivelly. But, we also need to store the x and y sizes
  of the image, because we will need them to draw the image (to interpret the
  data store in the pointer, because that data won't be organized in a
  rectangule, but it will be stored as a line of bytes) and we need them also
  to deallocate the image, because we must know the size of the pointer to
  deallocate him... So, as the x and y are variables of type word, we need 2
  bytes for each, so we need 4 bytes for both. So, let's make the procedure:

             Procedure GetImage(x1,y1,x2,y2:Word;Var Img:Pointer;Where:Word);
             Var Dx,Dy:Word;
                 A,B:Word;
                 Segm,Offs:Word;
             Begin
                  { Allocate memory }
                  Dx:=Abs(x2-x1)+1;
                  Dy:=Abs(y2-y1)+1;
                  GetMem(Img,Dx*Dy+4);
                  { Gets the segment and offset of pointer, to access the }
                  { data indexed...                                       }
                  Segm:=Seg(Img^);
                  Offs:=Ofs(Img^);
                  { Stores x and y sizes of image }
                  Move(Dx,Mem[Segm:Offs],2);
                  Move(Dy,Mem[Segm:Offs+2],2);
                  { Store the image }
                  Offs:=Offs+4;
                  For A:=y1 to y2 Do
                    For B:=x1 to x2 Do
                    Begin
                         Mem[Segm:Offs]:=GetPixel(B,A,Where);
                         Inc(Offs);
                    End;
             End;

    Abs is a function that gives the absolute value of a number:

             Abs(5)  = 5
             Abs(-5) = 5

    GetPixel is a function defined in the Mode13h unit (given with this issue
  of 'The Mag'). It takes three parameters (x,y and Where), and it gives out
  the color number of the pixel at (x,y) of page Where.
    So, we have the procedure that gets an image and store it... As in every
  pointer, we need to deallocate it at some moment in time (even at the end
  of the program). So, before I forget, how to...

    7.2. Killing an image

    This is very simple... You just analise the x and y size of an image and
  deallocate the space given to the pointer.
    Code:

             Procedure KillImage(Var Img:Pointer);
             Var Dx,Dy:Word;
                 Segm,Offs:Word;
             Begin
                  { Get X and Y size of image }
                  Segm:=Seg(Img^);
                  Offs:=Ofs(Img^);
                  Move(Mem[Segm:Offs],Dx,2);
                  Move(Mem[Segm:Offs+2],Dy,2);
                  { Clear pointer }
                  FreeMem(Img,Dx*Dy+4);
             End;

    Easy, isn't it ?
    Let's move on... It is no use getting an image if you can't show it...

    7.3. Displaying an image

    Well, to display an image, you must go to the pointer and dump the data
  onto the screen... That's easy enough to do, and there are some ways to speed
  up that process.
    Let's image a 4x4 image, stored in thr Img pointer, and we want to place
  that image at some coordinates, (x,y).

  Img -> 04 00 04 00 FF FA A0 01 02 03 04 05 06 00 C0 30 20 10 EE 02
         ----- ----- ----------- ----------- ----------- -----------
         |     |              |           |           |           |
         |     |              |           |           |--------|  |
         |     |              |           |                    |  |
         |     |              |-----------|----- FF FA A0 01   |  |
         |     |                          |----- 02 03 04 05   |  |
         |     |                                 06 00 C0 30 __|  |
         |     |                                 21 10 EE 02 -----|
         |     Y Size
         |
         X size

    Did you understand the above scheme ? We must translate a linear batch
  of bytes into a rectangular one... So:

             Procedure PutImage(X,Y:Integer;Var Img:Pointer;Where:Word);
             Var Dx,Dy:Word;
                 A,B:Word;
                 Segm,Offs:Word;
             Begin
                  { Get X and Y size of image }
                  Segm:=Seg(Img^);
                  Offs:=Ofs(Img^);
                  Move(Mem[Segm:Offs],Dx,2);
                  Move(Mem[Segm:Offs+2],Dy,2);
                  { Draw the picture }
                  Offs:=Offs+4;
                  For A:=Y To Y+Dy-1 Do
                    For B:=X To X+Dx-1 Do
                    Begin
                         PutPixel(B,A,Mem[Segm:Offs],Where);
                         Inc(Offs);
                    End;
             End;

    Yupi !! We have a PutImage procedure... But let's improve it... If you
  look closely at the scheme, you know that in you can transfer a line of the
  image at one, using the move command... So replace:

                  For A:=Y To Y+Dy-1 Do
                    For B:=X To X+Dx-1 Do
                    Begin
                         PutPixel(B,A,Mem[Segm:Offs],Where);
                         Inc(Offs);
                    End;

    With:

                  For A:=Y To Y+Dy-1 Do
                  Begin
                       Move(Mem[Segm:Offs],Mem[Where:A*320+X],Dx);
                       Offs:=Offs+Dx;
                  End;

    There it is... A fast (Pascal thinking) routine to put images on the
  screen...
    Try to put an image in coordinates in a way the image goes off the screen...
  As you may see, the image wraps around the screen in the X direction. Well,
  this won't do for most games, so we must introduce:

    7.4. Clipping

    Well, the ideia behind clipping is quite simple... You verify if the
  coordinates of a certain pixel are out of the screen, and if they are, you
  simply don't place it... This is obviosly slower than the normal PutImage,
  and you can't put the entire line all at once in the screen, but sometimes
  it's more usefull... So, you define the variables:

                           MinX=0; MaxX=319;
                           MinY=0; MaxY=199;

    This is the drawing window... Making this definitions you gain flexibility,
  because you gain the ability to clip the images to a part of the screen, not
  only to all of the screen.
    So, adapting the first PutImage procedure:

             Procedure PutImage_C(X,Y:Integer;Var Img:Pointer;Where:Word);
             Var Dx,Dy:Word;
                 A,B:Word;
                 Segm,Offs:Word;
             Begin
                  { Get X and Y size of image }
                  Segm:=Seg(Img^);
                  Offs:=Ofs(Img^);
                  Move(Mem[Segm:Offs],Dx,2);
                  Move(Mem[Segm:Offs+2],Dy,2);
                  { Draw the picture }
                  Offs:=Offs+4;
                  A:=Y;
                  While (A<=Y+DY-1) And (A<MaxY) Do
                  Begin
                       B:=X;
                       While (B<=X+DX-1) And (B<MaxX) Do
                       Begin
                            If (X>=MinX) And (Y>=MinY) Then
                              PutPixel(B,A,Mem[Segm:Offs],Where);
                            Inc(Offs);
                            Inc(B);
                       End;
                       Inc(A);
                  End;
             End;

    We use a While cicle instead of a For cicle, because if you think a bit,
  notice that if an X value is larger than the maximum X value permited, then
  all the X values of that line are also out of bounds, so no use in testing
  them... And the same for the Y values, so the use the while cicles to test
  it. So, inside the loops, we only need to test if the coordinates are bigger
  than the minimum values...
    You should choose carefully which one of the routines (with or without
  clipping) to use in your program, because the routine without clipping is
  a lot faster and is most usefull if certain cases...

    7.5. Saving to the disk

    Sometimes, you can't generate all the images you want in your programs,
  so you must load them from disk. But, to load to the disk, you have to save
  it first...
    To do so, you have to choose a format. I usually use a quite simple one:
  four bytes with the x and y sizes, and then the bitmap information... This
  way you can save and load directly from and to the pointer.
    So, let's make the procedure:

             Procedure SaveImage(Var F:File;Img:Pointer);
             Var Dx,Dy:Word;
                 Segm,Offs:Word;
             Begin
                  { Get X and Y size of image }
                  Segm:=Seg(Img^);
                  Offs:=Ofs(Img^);
                  Move(Mem[Segm:Offs],Dx,2);
                  Move(Mem[Segm:Offs+2],Dy,2);
                  { Save the data }
                  BlockWrite(F,Img^,Dx*Dy+4);
             End;

    We assume the parameter F (of type file) is already an opened file for
  writing. Why don't we open the file to write and close it within the
  procedure ?
    Well, because you may want to save more than one picture in a file, and
  if you did so, you only would be able to save one image.
    Now, let's pass on to:

    7.6. Loading from the disk

    I'll don't even bother explaining the loading procedure... It is _SO_
  simple !! You read the size, and you get the memory for the pointer... Then,
  you just shoot the data to the pointer! Simple, isn't it ?

             Procedure LoadImage(Var F:File;Var Img:Pointer);
             Var Dx,Dy:Word;
                 Segm,Offs:Word;
             Begin
                  { Get X and Y size of image }
                  BlockRead(F,Dx,2);
                  BlockRead(F,Dy,2);
                  { Get the memory }
                  GetMem(Img,Dx*Dy+4);
                  { Store X and Y sizes }
                  Segm:=Seg(Img^);
                  Offs:=Ofs(Img^);
                  Move(Dx,Mem[Segm:Offs],2);
                  Move(Dy,Mem[Segm:Offs+2],2);
                  { Store the image }
                  Offs:=Offs+4;
                  BlockWrite(F,Mem[Segm:Offs],Dx*Dy);
             End;

    So, here ends the first part of the sprites tutorial... Hope you enjoyed.
  Just a note: I've put all the procedures given in this issue in an unit
  called Images. That unit will be further expanded in the next issues, so
  make sure you update it...

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

  8. Graphics Part VI - Scrolling Part II

    As you may have gussed already, this is the continuation of last issue's
  graphics seccion...
    In this issue, we will delve into other forms of scrolling, more complex
  ones... Not more complicated, just more complex.

    8.1. Tile scrolling

    You know the Ultima games ? (of course you know them ! Everybody knows
  them !!)...
    Well, they use this type of scrolling... What's the ideia ? Think about
  this: imagine you wanted to do a game and you wanted to scroll around in a
  playfield made up of 10 full screens... Well, you know a screen uses 64000
  bytes, so 10 screens would use 640000 bytes ! That almost all base memory !
  And a game with 10 screens isn't that big !! One of my old games had a
  playfield with almost 500 screens !!!
    But, if you think for a while you will come to the conclusion that in so
  many screens some things are going to be repeated... Imagine that the
  playfield is a grassland, with some trees... Well, that's were the concept
  of tilling comes to action...
    You must have some sort of a map and some graphics... The map will tell
  which graphics go to which places... So, like a puzzle, you build the image
  of small tiles.
    I'll only explain this method in a simple way, because I'm intending to do
  a whole article on them soon, because there is so much to know about them...
    So, let's put this into practice... You already know how to read and display
  images, so you must only to use following procedure to display a certain
  part of the map:

             Procedure DispGeo(X,Y,DX,DY,Where:Word);
             Var A,B:Word;
             Begin
                  For A:=0 To DX-1 Do
                    For B:=0 To DY-1 Do
                      PutImage(A*16,B*16,GeoImages[GeoMap[X+A,Y+B]]^,Where);
             End;

    Looks confusing ? It isn't... I'll explain... This procedure displays in
  coordinates (0,0) of the page choosen by the Where parameter the zone of the
  map that goes from (X,Y) to (X+DX,Y+DY). Don't confuse the x and y
  coordinates of the screen (or virtual page) with the x and y coordinates
  of the map.
    The map should be stored in the GeoMap array (this can be loaded or
  generated by the program), and the images (the tiles) should be loaded to
  the GeoImages array of pointers... This can be done with the following
  procedure:

             Procedure LoadGeoImages(Filename:String);
             Var F:File;
                 Index:Word;
             Begin
                  Assign(F,Filename);
                  Reset(F,1);
                  Index:=1;
                  While Not Eof(F) Do
                  Begin
                       LoadImage(F,GeoImages[Index]);
                       Inc(Index);
                  End;
             End;

    This loads all images available in a file to an array called GeoImages.
    In the example I'm making, the tiles are 16x16 pixels... That is the
  reason of the multiplication for 16 in the DispGeo procedure.
    An example of a procedure that generates a GeoMap is:

             Procedure GenGeoMap;
             Var A,B:Word;
             Begin
                  For A:=0 To GeoMapSizeX Do
                    For B:=0 To GeoMapSizeY Do
                      GeoMap[A,B]:=Random(5)+1;
             End;

    This fills the GeoMap with random tiles, ranging from 1 to 5.
    Now, to scroll around this map, you just have to call the DispGeo procedure
  with different X and Y values (don't alter the Dx and Dy values, or else
  you'll get funny results on the screen).
    There is a test program that uses the above procedure with this mag...
  It is called ScrlGeo.Pas... It uses the file ScrlGeo.Img that stores the
  images of the trees.
    So, the basics to get you going is taught... Use this knowledge wisely,
  my child... :)

    7.2. Parallax scrolling

    Some computers (like the Amiga) have built-in circuits that handle parallax
  scrolling, but in the PC, you have to code yourself your parallax engine...
  But, what is parallax scrolling... Well, parallax scrolling is similar to
  what I have done with the starfield effect in this issue (see article 6).
    Objects that are further away from us appear to move slower, so you must
  scroll them at different times... What I'm going to teach is a combination
  of two scrolls... For example, check the Prllx01.Pas program... This scrolls
  a piece of land, with some water on the sides... It uses the theory of
  scrolling of the last issue.
    Now, check the Prllx02.Pas program... It is similar in most respects,
  differing only in the fact that it scrolls clouds...
    Now, let's combine the two together !
    As we want to make parallax scrolling, you must make sure that the clouds
  move faster than the land, so let's do it like this: while the land scrolls
  one pixel, the clouds scroll two pixels ! Then, for every frame, we just have
  to combine the two scrolls... To do so, we draw the land and then we draw
  the clouds, making sure that only the white parts of the cloud are drawn,
  not the black parts. To do so, we have to check each pixel of the image to
  put on top... So, we have to modify the CopyPage procedure... Here is the
  new one (the T stands for Transparency):

                Procedure CopyPage_T(From,Too:Word);
                Var Offs:Word;
                Begin
                     For Offs:=0 To 63999 Do
                       If Mem[From:Offs]<>0 Then Mem[Too:Offs]:=Mem[From:Offs];
                End;

    Then, you just alter the procedure that calls the scrolls, in a way that
  it scrolls two times the clouds before scrolling once the land... And voil ,
  you have parallax scrolling... It's all in the Prllx03.Pas file... This is
  _VERY_SLOW_, but it is only to demonstrate... In future issues I will teach
  you how to do this in assembler and that will _REALLY_ speed up that thing...

    7.3. Partial scrolls

    This final part talks about partial scrolls... That's an easy concept to
  grasp... To partially scroll anything, independent from the direction, the
  ideia is to move one line (or part of a line) at a time... I'll give you the
  procedures that do this, and you dissect them as you will:

             Procedure ScrollUp(x1,y1,x2,y2,Where:Word);
             Var A:Word;
             Begin
                  For A:=y1 To y2 Do
                    Move(Mem[Where:A*320+x1],Mem[Where:(A-1)*320+x1],x2-x1);
             End;

             Procedure ScrollDown(x1,y1,x2,y2,Where:Word);
             Var A:Word;
             Begin
                  For A:=y2 DownTo y1 Do
                    Move(Mem[Where:A*320+x1],Mem[Where:(A+1)*320+x1],x2-x1);
             End;

             Procedure ScrollLeft(x1,y1,x2,y2,Where:Word);
             Var A:Word;
             Begin
                  For A:=y1 To y1 Do
                    Move(Mem[Where:A*320+x1],Mem[Where:A*320+x1-1],x2-x1);
             End;

             Procedure ScrollRigth(x1,y1,x2,y2,Where:Word);
             Var A:Word;
             Begin
                  For A:=y1 To y1 Do
                    Move(Mem[Where:A*320+x1],Mem[Where:A*320+x1+1],x2-x1);
             End;

    I think this is fairly simple to understand... The ScrollLeft and
  ScrollRight procedures haven't changed much, but in full screen scrolls we
  could move the entire screen up and down with just one intruction, but with
  partial scrolls we must move it line by line...
    Well, this is the end of the scrolling saga... I think I've covered almost
  everything, except tile-scrolling, that will be discussed in detail a future
  issue...

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

  9. Hints and Tips

                *   - Begginners tip
                **  - Medium tip
                *** - Advanced tip


    - Logic operations on ilogic operands (**)

      Well, I'm loosing my brain thinking about something to put here... I can't
      remember nothing...
      Ok, I remember something... Did you knew you could do logical operation
      such as AND, OR and NOT with operands of any kind ?
      You may ask, how does this work ???? Well, like this... Imagine the
      internal (binary) representation of the number 41:

                        41 = 00101001 ;  Then you ANDit with number 1:
                         1 = 00000001 ;  The result is:
                             00000001 ;  He makes the logic corresponding with

      the logic tables I have given you... Similiarly:

      OR:                 NOT:

      00101001        NOT 00101001 =
   OR 00000010            11010110
    = 00101010

      This is quite usefull sometimes, because logic operations are much faster
      than aritmetic options... There are quite nifty tricks to do with this,
      but I'll leave that for other issue... :)

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-

 10. Points of View

    Ooff !! I'm tired... Almost 80000 bytes this one !! That's 15000 more than
  the second larger issue...
    Well, what can I say in this issue's point of view ?
    I really don't know... I'll satrt by telling what I'm going to put in the
  next issue... I'll continue the Sprites series (talk about transparency,
  animation and movement), the Graphics Series (I'll talk about text in mode
  13h)... I'll also do part II of the 'Building a text adventure' tutorial
  (talking about the parser and basic commands). I'll also start a tutorial
  on 'How to make a program 100% in assembler'... And I'm thinking of making
  another article on effects coding... I think that is going to be another
  _LARGE_ issue ! :)
    I don't know when is issue 9 be ready, because I'm going into projects
  time at the university (couple of works in assembly, one in Modula 2 and
  another in C)... And also, I must study a bit (if I want to pass the
  semester alive :) Also, I'm working on a game (won't tell you anything about
  it !! It's a secret), I'm making a talker and a MUD, and also a demo (all
  I'm going to say about this one here is the name: Into The Shadows !! Neat,
  isn't it ?). I don't know were I'll get the time to do this all, but I'm
  going to try...
    Well, this points of view are sucking big time !! As all this issue...
    Ok, I'll leave you with a question ?
    Am I beginning to be more serious in 'The Mag' ? Am I growing old and
  less funnier ? Hope not... :) But I feel I'm not as a comediant as I used to
  be... Maybe I've run out of jokes... Or maybe I'm just tired (after all,
  80000 bytes !!!)... So, to give you some humor, here's a couple of jokes:

        How many Microsoft programmers are needed to screw a lightbulb ?
        None !! They just change the standart ! :))))

        'Who the hell in General Failure and why is he reading my disk ???'

-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x

 11. The adventures of Spellcaster, the rebel programmer of year 2018.

  Episode 8 - Suicide

    The upgrading of our jetcar was going well... We were trying to hook up a
  Durallium block to the front of it, and to do so we needed to increase the
  power of the engine.
  - Has anyone ever told you that you are insane ? - Deathripper asked me.
  - Yep... - I answered, without taking my eyes of the drone that was lifting
    the heavy block. - Am I cool or what ?
  - No, you are insane... You are a suicidal maniac...
  - Well, suicide is quite fun, if you look at it with a nice perspective - I
    replied, with a grim look in my face and a dark tone of voice.
  - As the matter of fact, I'm the crazy one, going after this psicho's plans
    like this... - Karl muttered to himself, kicking a bolt and looking at his
    sister, that was working on the engine of the craft.
  - Well, this "psycho", as you called me can't code jack-shit, - I said,
    looking at Kristie with hatred in his eyes, remembering what she said about
    him about a week ago. - but I was the one that made this thing possible...
  - Oh, let's all kneel at the feet of the Great Lord Spellcaster, Master of
    Assembly Coding and Ruler of Gourad Shading !! - Excalibur said, ironically.
  - Shut the hell up, Kristie... - I shouted, my voice echoing in the cold
    closed room. - You shouldn't talk of what you don't know... - I said, with
    a cold emontionless voice.
  - Why do you stand up to this piece of shit, Karl ??!!! - She asked, waving
    her arms frantically.
  - Well... He's got a point... All we ever do is say that he can't code,
    altough the only thing we used so far to destroy Comptel is his virus...
    We are beeing unfair...
  - You are ?! - I asked, surprised. - Yes, of course you are... - I continued,
    trying to hide the surprise in my voice.
  - WHAT ?! - Excalibur shouted. - ARE YOU PAYING HEED TO A GUY THAT LOOKS LIKE
    HE HAS COME FROM THE VISUAL BASIC KINDERGARTEN ?!!! - Her voice then
    changed to a more friendly tone, yet filled with anger. - Well, in that
    case I'm OUT of your stupid and suicidal plan...
  She got up from beneath the jetcar and she opened the door of the garage.
  The sunlight hurted my eyes, and I look at her perfect body's shadow, thinking
  of the reasons that make me fight with her, when I knew, deep in my heart
  that I loved her. She swang her head, making her black hair fly in the wind,
  and she look directly at me... I could be wrong, but in her eyes there was
  much more than just hatred... I just didn't knew what it was... Then she
  turned over again and she left the garage.
  - Well, Gundsen, it looks like we don't have a pilot for the jetcar... - I
    said in a sad voice.
  - Don't worry... She'll be back... She always does... And after all, her hate
    for William Gates is so deep in her that she can forget any other hatred.
  I stood there in silent for awhile. Then I broke the silence with a question:
  - Why does she hate Gates so much ?
  - I know why... But only she can tell you...
  The garage fell in silence again... I looked at Gundsen, and his eyes were
  staring at me, as he could read my thoughs. He said, smilling:
  - No, I don't think she hates you at all...
  And saying this, he also leaved the garage and I was left alone with my
  thoughs, wondering if my feelings for Kristie were indeed so deep and visible
  as it seemed...


                                             See you in the next issue
                                            Diogo "SpellCaster" Andrade