QB Express

Issue #19  ~  March 27, 2006

"A magazine by the QB community, for the QB community!"

In This Issue



From The Editor's Desk

Written by Pete

It's here! At long last! It's finally here!

I will now formally apologize for the lateness of this issue; I wanted to get it out by the end of February, I really did, but time comes at a premium these days, and things like... work... and sleep... unfortunately came first. At the end of January, I began working 11 - 14 hour days every day of the week, and I saw my free time evaporate to zero. (It seems to happen to me a lot... for some reason I like to put too much on my plate, and then a few bites in, I realize that my eyes were bigger than my stomach.)

But alas! You don't care about my excuses, you just want to get to the goods. And the goods come in no short supply this issue, let me tell you. We've got some of the best tutorials and articles ever features in QB Express this month, more stuff than you can shake a stick at (more than 300KB of text alone in this issue)...which almost makes up for the lack of an issue last month. And that's not to mention the fact that we're kicking off the Qlympics 2006 this month. It's a fantastic issue, if I do say so myself. Enjoy!

Submit To QB Express

You all know the drill. This magazine can't exist without people SUBMITTING articles, editorials, tutorials, reviews, news and feedback. This is not just a solo effort by me... it's a group effort by people throughout the QB community. If you have anything to submit, or have time to write something, DO IT!

If you want to write about something, but can't think of a topic, or need inspiration, check out the "Official QB Express Article Requests" thread! There have been quite a few articles requested -- and even if none of them strikes your fancy, we can help you come up with something that you would like to write about. If you're interested in getting your own monthly column or just want to write an article or two, by all means, do it! Anything that is submitted will be included!

I also want feedback and letters to the editor regarding this magazine. I want suggestions and critiques. What do you like? What don't you like? What could be done better? Let me know!

All submissions and feedback can be sent to pberg1@gmail.com. You can also PM me on the Pete's QB Site or QBasic News message forums. If QB Express is going to continue to be so good, YOU need to contribute!

-Pete



Letters

Letter From MystikShadows

Wow, I'm still sitting here, wondering how #18 got to be so big in so little time. And like you said, not only is it big, it's just about the best issue (quality wise) there has been. and all this, in 16 days. I tip my hat to everyone that made this issue possible. Like you said, they should all pat themselves on the back (I patted my back already ;-) ). I mean I'm in Aww in what we've done in the available time we had. Superbe work if I do say so myself. IF everyone would put the energy they did in those 16 constantly for the whole usual month, I wonder what kind of issue would pop up ;-).

I'd like to start with Mark's "About QBasic.com" if I may. Not to bring back that old conflict, Mark already knows some aren't happy with what he's done with QBasic.com. But I'm not one of those people. The first thing I hated the most was that seperation within the Qmunity. Not seeing people I used to see all the time really hit me. I know for sure that this wasn't the reason why v1ctor created FB in the first place. I'm not exactly sure what happened exactly in the past 7 months or so. But I do know this, what Mark did with QBasic.com is exactly what we (all of us there were the Qmunity) need. Stop isolating each other, I don't really care who did what when to cause this, but I think it's lasted long enough. so I think what Mark did was outstanding work towards accepting both QB and FB alike. if you want to keep on using qbasic, go right ahead, if you want to move to FB awesome, just respect each other's decision, simple as that no? I'd like to see more of this myself on more sites as well.

The awards were given to those who really deserved them this month I believe. We all know now how much work Adigun put into AFLIB2 and How much work he is still putting in it documenting the whole library. And I have to say that the screenshots on his website really speak for themselves a picture is worth a 1000 words for sure. I hope everyone went to check them out because, wow, with every new added feature AFLIB2 just kept on becoming completely awesome by all standards. And well FreeBasic.net is the fastest growing website/forum I've seen in a good while. Rightfully so as well. Questions get answered (unless they have to do with PCOPY in SCREEN 0 LOL) sorry, I had to say it hehe. Seriously though, they really do get answered and fast, and that's also part of what makes the freebasic forum as good as it is today. . So yes, they definitaly deserve this award, no two ways about that.

The tree tutorial from syn9 are very well explained, thorough, detailed with plenty of code, just how I like them. From his 1st 4 parts I've learned alot, as most people reading this might know, I'm not from a 3D background (atleast not as much as I might have liked to be), so I'm really enjoying what I'm learning from these tutorials and any other I've read so far. this was part 3 and 4 I can't wait to read part 5, 6, 7, and so on hehe. excellent work there syn9.

The Wallace Visual Editor Tutorial. Well for one thing, I didn't know about it hehe. So when I read his tutorial, I discovered but the tool and how to use it. How complete a surprise is that? Very clear tutorial too if I do say so myself. He really took us step by step paying attention to details and I think very much succeeded in making his tutorial crystal clear and easy to follow. Great work and now that I know it exists, I just might start using it hehe. Thanks for this contribution Wallace.

And that brings me to Moneo's TRUE/FALSE TESTING. One thing I learned in my programming life and career is that when the voice of wisdom and experience speaks, you better shut the hell up and listen. And Moneo is nothing less than exactly that, the voice of experience. I don't tell him that all the time and on every posts he makes, but when I see a post made from him, I really sit down and pay attention to what he has to say, to me, or to someone else. Like I told him in your forum, I just wish he would have written that tutorial 31 years ago when I started programming. It really is, by far, the best, cleanest, easiest to follow explanation of the real TRUE/FALSE testing I've ever seen in both my personal and professional career. An absolute "chef-d'oeuvre" (this means a real masterpiece). Awesome creation Moneo, I'm looking forward to reading, hopefully, many mo re tutorials from you.

In that same line of thought. we have rdc's "A Closer Look at Managing Complexity Program Size and Organization". No matter which way you slice and dice it, programming is ALL about managing complexity, not just about generating code and praying things work when you compile it. I really like the way rdc (really disfunctional coder hehe) explained the concept and theories in his article. Basically, grab control of the code right from the start and keep it all the way to the end of the coding phase. I can't wait to see the rest of his series. rdc, you've got a fan now.

And to finish this off, Eclipzer's "Pointers In FreeBasic". Those than know pointers probably know just how hard it is to explain no matter what the language. In most schools this is usually one of the toughest parts of programming because they teach variables then suddenly throw in this pointer notion where your variables aren't variables anymore, they are just memory addresses to where the real data resides. those that know pointers know what this difference is, but to those learning pointers for the first time, it's really a big step. And I'd like to say that Eclipzer's tutorial seemed to have convered that aspect of programming in a unique yet rather clear way. If some people tried to learn pointers but still didn't quite get it, read this tutorial, it just might be the light at the end of the tunnel you've been looking for. Very well written and explained. Great Work Eclipzer.

One thing I didn't thing when #17 came out was that 1. we'd have a QB Express to read un January and 2. that we'd have such a great quality material to read in such a short time. This are the types of situation where I LOVE being proven wrong. And boy was I ever proven wrong. I'll never underestimate the potential of the Qmunity ever again :-). And well, let's not forget the main man behind it all, Pete himself :-). Without contributions the magazine would be small, but without out, the magazine would not be. :-).

Heheh, it's ironic reading this letter now, which was focused primarily on the short 16-day turnaround time for #18, considering that this issue is over a month late. Oh well. I've discovered that when you go from working 0 hours a day to 12, you have much less time for your hobbies. :) Anyhow, great feedback on the issue as always. I always look forward to your letter after every issue!

-Pete


Letter From Seb McClouth

Hi Pete,

First and all, hows you? You've been doing like a-hell-of-a-jobS (mind the 'S') lately, how do you keep up? Doesn't it make ya crazy?

Me, I'm just keeping up at work. I found my drive (joy@work) back.

McClouth Software, as I have founded my software programming company (one man company that is, currently), has been working on a low profile lately.

Qbinux is currently on hold, whatever you call on-hold anyway. I've been doing some things around it. Since Novix (link here to novix.tk) is a mayor (let's say 99%) of Qbinux, I've been trying to understand Novix. Simple because the 50 attempts I had on building Qbinux gave the following problems:

Currently I have a working (okay 40% working) boot, which is able to detect some hardware onboard...

A fully functional login routine. A prompt which can only process the command 'shutdown'.

So not really much.

I'm gonna work some more on that thing.

I'm not gonna make a promise here thou... But before the end of March I'll be (damn, I did make one) giving out a tech-release.

Next QBExpress I'll be telling more about the QFS (Qbinux From Scratch) project.

Until then!

Too bad to hear about the delays and problems you're running into! I hope you got everything up and running...since it is now the end of March, after all (tech-release, heheh?). Anyway, thank you for the update!

-Pete


Letter From Michael Calkins

Hello, Pete Berg:

This is a response to, in QBE #18: "TRUE/FALSE TESTING" by Moneo

I was happy to see this article. I feel that these matters are vitally important to proper understanding of QBASIC. I feel Moneo did a fine job in writing it.

However, I feel that the wording of rule #3 was a fuzz confusing.

"RULE #3: However, when a boolean expression is used as a value, assigned to a variable or output in any way, QB will convert the result, after evaluation, to TRUE (-1) or FALSE (0)."

I think something like this would be more accurate:

"RULE #3: The results of relational opperations are always 0 (false), or -1 (true). Relational opperators are =, <, >, <=, >=, and <>>."

But anyway, good work, Moneo! These matters, bitwise logic, true/false, Boolean algebra, integer arithmatic, binary numbers, are extremely vital to QBASIC programming.

Regards,

Michael Calkins

I have to echo your sentiments; Moneo's article was fantastic. But I think your suggestion for Rule #3 is great and really helps clear it up quite a bit. Great work!

-Pete


Letter From DrV

I just want to let MystikShadows know that his MIDI article in QB Express #18 was the absolute best coverage of the details of MIDI files I've ever seen (and I've read quite a bit on the matter) - the sort of article I wish I would've had five years ago. :) And, of course, great work to everyone who contributed; it was an excellent issue, as always.

-- DrV (Daniel Verkamp)

I agree with you 100%. MystikShadows is a phenomenal writer -- thorough and easy to understand, and best of all, he's reliable and consistent. I too am a big fan of the MIDI series, and can say with complete confidence that it is the best MIDI tutorial ever seen in the Qmunity. Three cheers for MystikShadows!

-Pete


Letter From Oracle

Hello all,

First, a short introduction. My alias is "oracle" and I used to be in the QB scene a few years ago. I'm the webmaster of qbnz.com, and I also worked with TurboFX on Total Destruction 1 and with Neo on Total Destruction 2 (which never got released, but was still looking awesome - if Neo still has a copy I urge him to release it so maybe it could be finished one day!)

I'm writing in about the qbnz.com website. I know I said before that I would stick with it, but unfortunately sometimes life doesn't work with you on these things. I've been sidetracked by other things and qbnz has languished for a long time.

But I'm determined to not have qbnz.com die the same way that qbasic.com did (I see it's back now however - congrats to who ever pulled that off!). It's in the first google page when searching for qbasic, so it would be great if when people visited it they would find a still active site.

So I'm putting out a call for help: If you want to manage, code or design a qbasic/freebasic website, please get in touch with me! You'll have a relatively free reign to create new website, while I would handle the server side management and payment of any costs. The server has unlimited disk space, PHP and MySQL, so you're not constrained there.

So if you're interested in helping, or have any thoughts or queries, please get in contact with me on the QBN forums.

Oracle

Sounds great! This truly is a great opportunity for an up-and-coming QB/FB coder, and I hope you get a lot of responses. QBNZ is still one of the best QB sites around, and it's been tough seeing it languish for so long. Hopefully with a little help you can help restore it to its former glory!

-Pete


Have a letter for the editor? Send all your rants, raves, ideas, comments and questions to pberg1@gmail.com.


Express Poll

Every issue, QB Express holds a poll to see what QBers are thinking. The poll is located on the front page of Pete's QBasic Site, so that's where you go to vote. Make sure your voice is heard!

What is the best QBasic game developer of all time?

With the help of Lachie Dazdarian, I've decided to run a tournament to find out who the community thinks is the greatest QB game programmer / programming group of all time.

The two of us have nominated the 50 top QB game developers, who we feel deserve special recognition for their contributions to the QB gaming scene. From these 50 choices, YOU are going to vote to select the greatest QB game programmer. The winner will receive a special award at the Qlympics 2006, which will take place in QB Express over the next few months.

I've randomly split up the nominees into five divisions. You will get to vote for the top developer in each division, and then we will have a final vote between the five finalists.

So, without further ado, here are the nominees:

DIVISION 1
Aleksander Trojanowski (ATTE series)
Bulma Produktions (Suds Skins 1 & 2, Johnny Abbot's Sex Adventures 1&2)
CMC (DarkPhear)
Hamster Republic (Wandering Hamster, SpitWar, RPG, Cowbobs)
Hyper Anime (BAkuen SakuRu, Kunio Kun)
Pieslice Productions(MUX, TerraScape)
RelSoft & Adigun A. Polack (Frantic Journey)
Shattered Realm Productions (TWIGZ Engine, The Great Escape)
TopGun Software (Space Commando 1&2)
Typosoft(Ped xing's Quest, Sumo, SuperSumo 1&2)


DIVISION 2
Darkness Ethereal (Mattress Warrior, Secret Of Cooey series, In The Nocturne, Mysterious Song, Lianne in the Dark Crown, Legend of Lith II)
Eric Carr (SpinBall)
focus ZERO (Monkey Blast!)
J.B. (Sonic Xtreme)
Master Creating (Shadow Of Power, Diamond Fighter IV)
M \ K Productions (Pieces 1&2, Bob Saget Killer 2000, Fury)
Na_th_an (Jill The Goddess, Lala Prologue, Oytkator's Plans)
Pantera55 (Elysian Fields)
Pasco (Groov Buggies, France '98 World Cup Soccer)
Sasha Vukelich (Dynamic - The Colonization of Jupiter)


DIVISION 3
Piptol (Ghini Run, Squealer TNT, Kingdoms)
Future Software (BomberZone, Zelda Clone, Cobra, Space Invaders)
Jocke The Beast (Dark Woods 1&2, Mirkwood)
Lachie Dazdarian (Detective Academy, Ball Blazing Fantasy, Rocket Fuel Mayhem, Dark Quest, Another World Memory, Run 'Em Over, Pong Worz)
Mark Hall (ARC Legacy)
Oren Bartal (Super Stack, Ultimate Super Stack)
syn9 (ZeroG)
Stefan Hendriks (Arrakis)
Terry Cavanagh (The Hunt, Black Hole)
WisdomDude (Hack-Man 2&3, Cyber Chick)


DIVISION 4
BINARYmagic (Alien Terror, Anaconda)
BjM Software (DreamScape)
Danny Gump (Mystical Journey, Super Mario World Clone)
Jason Gould / The_Brain (Puz, Peanut Patrol 2)
JAWS V Soft (Mini RPG series)
Mike Snyder (Lunatix, Lexter 1&2)
Nekrophidius (Wrath Of Sona, Two Lords)
PHAT Kids(Kids of Karendow Chapter 1, PHAT Professional Burglar)
SV Reanimator (PromZone, Cyclone)
TMB (Percussor, Around The World, The Little Pixie 2, The Adventures)


DIVISION 5
Angelo Mottola (WetSpot 1&2)
Delta Code(Larry The Dinosaur 1&2, Unofficial Tournament)
Jace Masula (CODELINK, StarQuest)
Kevin Reems (Stick Fighters Brawl 1&2)
Michael Hoopman (Dark Ages)
Milo Sedlacek (MonoSpace)
Nick London / NutzBoy (Peanut Patrol)
SonicBlue Productions (SB's Bricks)
StarsDev (The Terror)
Tsugumo (Untitled, TheGame)

In order to vote, visit the front page of Pete's QB Site. (The poll can be found in the left column.)

Good luck to all the nominees -- and may the best developer win!

Results of Division 1

The top two developers move on to the final round of the competition!

DeveloperVotesPercentGraph
Hamster Republic911%
Pieslice Productions56%
Bulma Produktions34%
CMC00%
TopGun Software23%
Typosoft2430%
Shattered Realm Productions11%
Hyper Anime45%
Aleksander Trojanowski23%
RelSoft & Adigun A. Polack2937%
76 Total Votes

Typosoft and RelSoft / Adigun A. Polack move on! With 30% and 37% of the vote respectively, these two developers were the clear winners (and very deserving ones too, I might add)! I can't wait to see how they fare when we get down to the final ten...

But before that can happen, we still have four more preliminary divisions to go through! So go to Pete's QB Site and vote on round #2! (The poll is already up.)



News Briefs

News from all around the QB community, about the latest games, site updates, program releases and more!

Website News

The First Dedicated FreeBasic Games Site: FreeBasic.info

It's about time! After over a year of talk about the subject, someone has finally stepped up to make a website dedicated to collecting and redistributing FreeBasic games. That person is Cirux, and his website is FreeBasic.info.


While the main site has not yet launched, the FB games section is already well on its way at: games.freebasic.info. There are nearly thirty games at present, and more are added every day. Each game comes with a few screenshots and a brief description, and the main index page allows you to sort by Author and Title of the game. It's fairly barebones at the moment, but with a little more work, this will surely be one of the most useful FB sites on the web.

News Brief by Pete


Syn9's Hideout Gets Redesign; Zero G Ship Redesign

Syn9 has been pretty busy for the past few months. First of all, he gave his website, Syn9's Hideout, a complete redesign. Gone is the dark red/black look we've come to know, and in its place is a stylish gray and white look. It's an excellent design, definitely one of the best looks anywhere in the QB/FB community -- but would you expect anything less from Syn9?

All of the old content is there, plus some new stuff that has been added recently. Check out, for example, the screenshots of Zone War, an upcoming real-time-strategy game. It looks spectacular, and you will most certainly be hearing more about it from QB Express in the future.

Also, Syn9 has posted a new shot of the ship design for his upcoming racing game, Zero GTR. Needless to say, it looks fantastic:


I can't rave enough about the great work Syn9 is doing for the FB community, from his games to his tutorials, he really sets the bar high. Don't miss his website, at syn9.thingie.net!

News Brief by Pete


Project News

Cute Short Game Projects!

The last few months, redcrab has launched a new game programming "company" called Cute Short Game Projects, where he releases fantastic FreeBasic minigames. Since the last issue, he has released four extremely fun FB games: Mushroom Raindrop, Bouncing Stuntman, Hurry Chef and Sleepwalker, and they are all worth your time:


I highly encourage you to check out these games, available at the CSGP website!

News Brief by Pete


Updates From Lachie Dazdarian

Lachie Dazdarian, one of the busiest guys in the QB community has been quite busy in recent months. He sent me this list of news...check it out!

  • Mini Space Rogue is canceled:
    http://fbtk.net/phpBB2/viewtopic.php?t=1012 ~ msrogue_demo.zip

  • I've ported Another World Memory to FreeBASIC and advanced it:
    http://www.freebasic.net/forum/viewtopic.php?t=2805 ~ awmemfb.zip


  • I've released a custom font printing routine for GFXlib:
    http://www.freebasic.net/forum/viewtopic.php?t=2806 ~ customfont.zip

  • I've updated my scrolling engine (only a fix so it would compile correctly in FreeBASIC v.0.15 plus another small fix):
    http://fileanchor.com/18911-d ~ FBScrollEd1.zip

  • QBasic Games Directory News:
    I've finished going through all the QBasic games I've acquired so far so the QBasic Game Directory now features 441 games out of which 43 are demos(just a note that Wandering Hamster is still a demo so not all these 43 demos are unplayable). '\' in the links where changed to '/' and all entries where rechecked and descriptions IMPROVED. 40 games are pending. Those are games which I'm unable to run or they are mine(2-3). I've found a great collection of HTML to PHP to MySQL tutorials but since in the next few months my mission is FINISH COLLEGE OR DIE I'm sad to announce that this will have to wait. Sorry. But the good thing is that these games are archived and BACKED-UP on a CD(together with the MySQL database). No fear of my work disappearing into oblivion. I'm not sure if I should bother to release another database preview.

  • Cheers!

News Brief by Lachie Dazdarian / Pete


gMap, Tile-Based Map Generator

Date: Tuesday, February 14th, 2006

It seems that chaos and Josiah Tobin have been busy recently with the creation of gMap, a tile based map generator designed to create maps for RPG like games. Here is a screenshot of the gMap in action:

Some of you might recognize some of Josiah Tobin's awesome artwork in this screenshot. The goal is to take map elements from the right side of the screen and bring them in the left design area in order to build, tile by tile, a complete scene. Seems like they have created one fantastic looking piece of software that's sure to blow the mind of all RPG creators in the QMunity.

You can follow the development of gMap in the thread created on the href="http://www.freebasic.net/forum/viewtopic.php?t=2942">FreeBasic Forum to see more screenshots as well as user's comments.

News Brief by MystikShadows


AFLIB2 Status Report

Date: February 19th; this is slightly out-of-date; AFLIB2 Has been delayed, due to some computer problems.

I've asked Adigun A. Polack for an update on his AFLIB2 project (which by today needs no introduction). Although Adigun has gotten himself a bit busy (to say the least) with "The Winter 2006 FreeBASIC Ultimate Demo Compo" which you can read all about right here. He still took the time to let me know that AFLIB2 development is as I reported last month, done. Documentation still manage to progress some (he has now reached 100 pages of documentation and expects to reach over 200 pages by the time it is done).

Adigun also informed me that, as expected and as announced on the forums, AFLIB2 is still planned to be released early in 2006 (in March or April) so that's coming up quite fast.

Look for upcoming updates in next month's issue of QB Express.

News Brief by MystikShadows


Shattered Realms Productions Releases Go! Kart
So technically this is C++ news, not QB/FB, but I'm gonna post it anyway. As Greg of Shattered Realms said, "it is not a QBasic game, since I had to move on to C++, but since the majority of my website is dedicated to QBasic I was hoping you wouldn't mind."

Shattered Realm Productions is proud to announce that Go! Kart, a 1 year project written in C++, has finally been completed. Go! Kart is a two-player side-scrolling racing game which features fast-paced gameplay, 12 exciting levels, excellent graphics, music, and sounds. Go! Kart has also been released with an easy to use WYSIWYG level editor so that you can create your own levels. You can find out more information about Go! Kart from Shattered Realm Productions.com

News Brief by Greg of Shattered Realms Productions


FBIDE now at version 0.4.6

Date: Sunday, February 19th, 2006; may be out-of-date

Vongodric Just posted today the release of a new version of FBIDE. The version is now 0.4.6. and here is the list of features and corrections as stated on his website.

Finally after lot of work it's done and out in the wild. It caintains many bugfixes ( all that I am aware of so far ) and new features as well.

Now FBIde has integrated filebrowser that let's you easily open/run programs right from within the IDE enviroment. Old Sub/Function browser is gone in the form it existed and now is a part of source code explorer ( click F2 ) but still is there in a more powerful form- Now you can also search for types, unions and enums!

Here's the full changelog:

    * Added source structure browser.
    * Added built-in file browser
    * Added Ctrl+Tab and ctrl+shift+tab for switching between tabs
    * Fixed crash when Ctrl+0 is pressed
    * Fixed keyword save bug
    * Fixed rightmargin state saving
    * Fixed crash when closing FBIde while help is open
    * Fixed filename case and space bug in running / compiling
    * Fixed keeping track of folding
    * Fixed brace highlight error when highlighting is turned on/off
    * Fixed crash when program is running, but all tabs have been closed
    * Fixed highlighting bugs: '$ is shown now as preprocessor, @ is treated as operator. Varios smaller fixes
    * Fixed After failed compilation go to first error
    * Changed code area tab's panel buttons are now a bit better looking.
    * Changed active/inactive tabs have now same width
    * Changed Sub/function list now also lists enums, types and unions 

Quite alot of work has been done. And of course, more is definitaly on the way with all that was talked about for the upcoming 0.5.0 version. Be sure to keep yourself updated on this excellent IDE.

News Brief by MystikShadows


New Version of UnTank!

UnTank is currently (24 Feb) in version 0.18f - and things are definitely improving.

Here's a shortlist of some of the new features:

  • I've begun work on some interface details. The speedometer and booster meter (press Q to use) are small details for now, but I'm working on getting a full console in.
  • There's a partially implemented skybox put in - this needs to be optimised as well as improved in quality (both of which go hand-in-hand, so there'll be both a quality and performance increase).
  • Friction is now dependent on what type of terrain you're over - ice barely slows you down, while in sand you'll lose speed quickly.
  • Configuration files allow you to mess with the settings. In data/config.txt, you can change the resolution with line 1, and the frame rate cap with line 2 (0 to disable the cap).
  • There's walls around the arena with ads on them.

And some of the things you'll be seeing in upcoming versions:

  • Complete overhaul of the physics and graphics engines - better performance both in terms of FPS and in handling.
  • An improved console and some menus.
  • Finally, you'll be able to shoot stuff. This goes in hand with the console, since you'll need that to select weapons.
  • Independent controls for the tank, the turret, and the camera. Also with this will come new camera controls.
  • I'll also start to put a menu system in, and there's going to be some options to change which tank components you're using (as soon as the modelers get to work and send me some models).

Here's a link to the latest version (as of 24 February): untank0_18f-debug.zip

I may update it before QBXP comes out, so check the thread on FBTK for the latest updates.

News Brief by Xerol


Tons of Other FB Releases!

There have been a lot of FB game / demo releases lately...and you should check 'em out!

News Brief by Pete


Competition News

Rogue Compo Update

Date: February 19th

Some of your might wonder how come last month we didn't have a Rogue Compo Update. Well last month seemed to be a "we're all busy" month of some sort. I asked for my regular update but didn't get any answers so no update could be created. Something has happend this month however. So let's see exactly where the compo stands.

DopeD was the first to reply to this month's status report thread I started right here and here's what he had to say.

"Umm, well, seems like noone's answered, and I'm a bit late myself, but here goes. I've resumed progress after a lazy two weeks, and I've started actually turning this into a game - with a simple, unspectacular story. The engine needs a few tweaks and a couple of additions, but is more or less finished. I hope I can turn this into something playable..."

Hopefully soon enough, we'll all be able to see where this is going. Our next to answer was Dr_D who had a short reply that read as follows:

"I'm slowly starting to get back into it, but with Rick being so busy at work, I wouldn't expect to see much, if anything at all this month."

At least it's good to see this team is getting back at it. Even if we don't have much to report, it's good to see that quite possibly, next month will have a bit more to say.

Finally, Ryan was the last to answer at the time I wrote this update. Here is what he had to say:

"I'm busy playing DopeD's game still. It's great! Give it a try.

Other than that, I believe my server's how I like it. I just need to begin workin on the clients. ^_^"

This right there tells me that Ryan's halfway there on his project. So things are looking quite good on that side as well.

So let's see what next month brings us from this compo. I can only assume that January and February were just busy months for every one. See you at next month's Rogue Compo Update.

News Brief by MystikShadows


Tankz by iCEkID Wins First QBasic.com Contest!

Tankz by iCEkID has apparently won the first QBasic.com programming contest -- the one with a real cash prize generated by Google ads found on the QBasic.com homepage! The entries were as follows (as posted by Mark Wilhelm):

Enough, a 96kb Intro by Plasma is pretty cool. Nice music and a few graphics. It's surprising what you can fit in 96kb when you try, eh?
http://www.qbasic.com/wbb/thread.php?threadid=313&sid=

Detailed Scene by %00 is a almost-finished rendering of...umm...some cool sword from a Legend of Zelda game. Don't know which one though...
http://www.qbasic.com/wbb/thread.php?threadid=318&sid=

Modern Art Generator upped by TheMayor87 creates random still-life scenes. Cool to loop, sometimes generates desktop-quality stills.
http://www.qbasic.com/wbb/thread.php?threadid=370&sid=

Tankz by iCEkID is a PocketTanks clone, but a very good one, in QB no less, and smaller than 96kb!
http://www.qbasic.com/wbb/thread.php?threadid=363&sid=

TicTacToe by Phycowelder is a mouse-based TicTacToe game in QB. Source and compiled forms of this submission were accidentally uploaded in separate threads. No biggie.
http://www.qbasic.com/wbb/thread.php?threadid=373&sid= (Compiled Form)
http://www.qbasic.com/wbb/thread.php?threadid=372&sid= (Source)

Xbox 360 Matrix by yours truely was commissioned by one of my friends who didn't feel like doing his homework. His laziness was my gain.
http://www.qbasic.com/wbb/thread.php?threadid=371&sid=

The official voting thread opened on February 24th, and voting was set to last ten days. Voting has concluded, and Tankz came out on top with four votes (just one more than runners-up "Enough" and "Modern Art Generator". Although Mark Wilhelm has not officially announced a winner, the voting period is over, and iCEkID received the most votes...so congratulations to iCEkID!

News Brief by Pete




Qlympics 2006
Nominations Round

Written by Pete


It's finally here! Over the next few months, QB Express will be hosting the Qlympics 2006, where we will give recognition to all of the best work from the QuickBasic / FreeBasic community!

The Qlympics 2006 will be open to any FreeBasic or QuickBasic program released since January 1, 2003. We have not had a community-wide awards ceremony since the 2002 Gaming Gold Awards, and it is due time for QB / FB programmers to get some much-needed recognition.

But I'm sure you're dying to find out about the categories and the nomination process...so take a look!


The Categories

Gaming Awards


Non-Gaming Awards


Awards for Written Work


The Schedule

QB Express #19 (March) - Pre-Nomination Round - That's right now!

This month, I need help compiling a list of all eligible programs for the 2006 Qlympics. That means EVERY QB / FB program released since January of 2003. Each entry must be accompanied by a working download link, or a link to the official site.

The way I'll be doing this is through posts on message forums around the Qmunity -- one at Pete's QB Site, one at Qbasic News and one at FreeBasic.net. You are also able to submit nominations to me via email. Everyone will be welcome to submit nominations. I want the nomination list to be EXTENSIVE, and no eligible program should be left off.

Over the course of this next month, I will take submitted nominations and split them up into the categories they're eligible for, so that we have a master list of all nominated programs.

How do I nominate a program / person? Just tell me their name, the name of their program (if applicable), and a link where I can find the work. You can either post your nominations in an official thread, or email them to pberg1@gmail.com.



QB Express #20 (April) - Nomination Round

I will print the FULL list of nominees in the next issue of QB Express. There will be an accompanying voting form, where readers will be able to vote for their top three or five choices in every category. That means you! (Only one ballot per person -- this will be enforced!)

After a few weeks of voting, I'll tally the votes. The top three or five vote-getters will be the official nominees for each category, and will be up for the awards.



QB Express #21 (May) - Final Round

A panel of judges will choose the winners, based on the nominations chosen by the Qmunity. (This is to ensure objectivity, and that all programs are fully and thoroughly evaluated. We want to make sure that every program is given a fair chance.)



QB Express #22 (June) - Results

The final results will be released, and we'll hand out the award banners!

Rules


That's All, Folks

Tune in next month for the official nomination list!


Any questions or feedback you have can be directed to: pberg1@gmail.com


Gallery

Written by Stormy

Every issue QB Express features a preview and exciting new screenshots from an upcoming QB game. If you would like your game featured, send in some screenshots!

Mystic World

This month's Gallery article comes from Stormy, creator of the upcoming FB RPG called Mystic World. It's a traditional tile-based RPG like the kind that swept through the QB community like crazy back in the late '90s -- but in the past few years, they've become increasingly rare in favor of more action-based releases and graphics demos. Mystic World looks like it will be old school Qmunity RPG gameplay at its finest. Who could ask for more?

-Pete


Hi everyone,

I would like to present my upcoming role playing game called "Mystic World" for FreeBasic. Please note, that it's only a working title. I will figure out a better name in future ! :P

But before coming closer to my project I would like to speak some words about my motives I had before I started this game. In my history as a programmer I never made a real big game before. The few ones I made are just kinda small and hardly worth mentioning. Thus, I decided to create something big to prove myself that I can archieve something like that. In this time I was heavily inspired by those RPGs that had been written in QB. Exactly that athmosphere that was given such old-school RPGs I want to recreate, but just only in a fairly shape. So I began to work on Mystic World on my 486 with DOS, went on Windows by using Rellib and finally reached Linux as my developing OS to finish my game. And believe it or not, it was somehow hard to convert each code to the next system. ;)

I hope you didn't fell asleep, yet. No? Great, so let's come closer to the game itself. Currently I'm working on the whole background stuff I need to develop a full-working game as I want. Thus, I build a robust engine, which is very customizeable, heavily based on scripts with a scripting language created to DOS' times, fast paced and portable to some respect. It runs on Linux and Windows quite well without any problems. I also paid attention for older PCs by limiting game resolution to 640x480 and color depth to 16bit. It runs very fast on my PC (CPU: 1533MHz) with 380 FPS without using any libraries like SDL or Allegro for instance. As you can see, I thrust in the power of the gfxlib from lillo! ;) Well, after all by having reached such a good performance I tried to integrate some effects to ruin the performance - eww *g* I mean to let my game look even more mature and nicer.

If you have look to this feature-list, you might be convinced of the abilities of my engine:

With those effects you won't have something better as a unfinished walkaround-demo which we have often seen in the past. I also integrated a lot of things, which are essentially for the RPG itself. Thus, I managed already the whole stuff with heroes and its classes, weapons, armors, items and spells and of course the saving and loading process. All of those things do work fine for now and I'm working at the moment only on some minor improvements, that might be important in future.

The current game I'm working on by using my engine will belong to the fantasy-universe and will provide a nice detailed and well-balanced fantasy environment. This game won't only contain "hack'n'slay" parts, so it will have rather real quests, solving puzzles and such things. Of course the whole battle-thingy won't get a raw deal! ;) Currently I'm working on the spells in fight, which will be definitely an eye-candy for you. I also gathered some nice music of artists whose permission I already gained to use them in my game. In combination with appropiate sound effects you ears will be coddled as well.

OK, enough spoken. Let the screenshots tell you more:


This is our brave hero ! =)


Our hero in front of the shop in a nameless testing village...


On this screenshot you can see some light sources and mirror effects. And of course some NPCs, that are hanging around there...


That snow fall might be exaggerated. But it was for testing purpose only. :) It doesn't fit at all due to the missing winter tileset.


This is a shot of the current battlesystem. Our team seems to be in trouble with some devilish slime blobs... :) And this shot shows also, that our mage is about to use one of his healing spells.


This is the itemlist featured with icons to make the player finding the items faster as with ordinary text. You can also see, that I selected two items in order to switch their positions. Automatic sort is also included. And please note, that I was too lazy to translate every item in this list. :P


This menu is accessable via ESC and shows the party.


The stats of our mage. You see that there is a lot of space to put more stuff into this box... ;) I will enhance this later by the time.


My rather functional paced editor where you can design an entire map (apart from the scripts).


This is one example the message box. In this dialog box I can determine the direction of this NPC. See this snippet to see how easy it is to do such an effect:

[NPC]
 ClearOptions
 AddOption RIGHT "Go right!"
 AddOption LEFT "Go left!"
 AddOption UP "Go up!"
 AddOption DOWN "Go down!"
 AddOption 99 "Do nothing."
 ChoiceBox "" "Where do you want me to go ?"

 IF Decision != 99
  NPC.Move PickedNPC Decision
 ENDIF
 IF Decision = 99
  NPC.Movement PickedNPC UnMoveAble
  MSGBAR "You send player {PickedNPC} into the direction {Decision}."
 ENDIF

Showing you this snippet, I can also introduce you in the structure of this script language which is obviously similiar to BASIC.

[NPC] and [/NPC] ... include a SUB. [NPC] is everytime called when you talk to a NPC. The variable "PickedNPC" gets the value of the respective NPC you want to talk. You can also set own SUBs and call them."IF" is similar threaten like in BASIC. You can even combine more conditions via the logical operators AND and OR. Relational operators like =, <, > and <> are possible, too. Instead of using <> you can also take != if it's familiar to you. "IF" can also be overlayed.

Most of the commands should be easy to understand. There is simply one command that requires its parameters in order to work properly, as we know it every programming language. Some parameters are optional. See for example the first parameter of "ChoiceBox", that allows to determine a own picture in the format BMP or TGA. The window and the text will adapt to this stretching picture automatically.

Every string, that is used in message boxes or in the message bar can contain variables by the use of {name of variable} and small icons via "[number of the icon]".

Well, that was everything from my side. I hope you enjoyed this free tour to have a look into the current state of my game. If you are interested in supporting me, you will be absolutely welcome. Especially I would have a need for graphic artists, composers or story tellers. Especially since the moment I realized that my english skills aren't that good to write an entire story, I would really appreciate an helping hand there... =)

Just contact me via ICQ 131070118 or via email Paul.Grunewald[you may put here an @ in]gmail.com

- Stormy (Paul Grunewald)



Bungy The Worm - Game Review

A review by Stéphane Richard (Mystikshadows)

INTRODUCTION:

Not too long ago (back in November 2005), Lurah created his own version of what is known today as a snake like game. This type of game is defined as. A game where you are a snake, slithering your way through a playing field going after the food and other goodies that appear on the playing field. Typically you're not allowed to go over yourself or certain other "evil" elements of the playing or the game is over. This concept, still today makes for an addictive combination.

As you probably know by now, I will begin this review by talking a bit about the game and finish with the gaem review itself. So then, Let's begin this review of Bungy The Worm.

ABOUT BUNGY THE WORM:

What's to say about Bungy The Worm? Well, for one thing, you can read what others thought of Bungy by looking at this thread on the FreeBasic.net forum. As you can see it got some pretty rave reviews and comments when it came out and rightfully so I might add. Bungy is the most recent adaptation of a classic gaming concept. And well with the additions of some good music and decent sound effects, it makes it one of the more unique adaptations as well.

I have 2 screenshots to show you here. This first one is the main logo page with the game's menu options.


This second screenshot is the main game screen. this particular screenshot shows the evil things I talked about earler (in red) and the water (in blue) where you're also not supposed to go into.



And now, for the review itself. As always, I will review the game based on playability, overall entertainment, replay value and technical feature. So far I believe these evaluation criteria are complete enough to give a good idea of a game's strong points so i'm sticking with them. Let's get started shall we?

PLAYABILITY: Score (4/5)

The game offers a very simple principle. That principle is to stay alive basically. To accomplish that you need to stay away from anything shown in red and the water. But since it is a worm, you do need to feed it. So the P and the $ you really do have to get them and you have to remember that you can't go on top of yourself. You play like any classic snake like games by using the arrow keys. and as you eat things up and slither all arround the playing field, you get longer (which adds to the challenge) and you get shorted when you eat something (which helps keep the game real. In the respects of what a true snake like game is, I would say that Bungy The Worm is a nice addition to the snake game legacy. As such I gave it a score of 4 out of 5.

OVERALL ENTERTAINEMENT: Score (3/5)

When you reach higher levels, you get to eat more stuff, avoid more bad things and fight a little more for your life. The mysic is interesting and I believe fits the game quite well. It's not too hard to get yourself into a situation where there is only one possible exit and you have to find it fast enough or you will die. Basically, there's all sorts of little things that just keeps you wanting to play like this. The only drawback (if you can call it that) is that the game has no purpose, no ultimate goal and I tend to wonder how a snake like game, with a purpose like this, would feel like. But by all classic defintions of a snake like game, there isn't really any drawbacks. For that, I gave the Overall Entertainment a score of 3 out of 5.

REPLAY VALUE: Score (4/5)

When you start playing Bungy, and you play it for a little while, atleast that's what happened to me when playing it, you tend to just want to "do better next time" just for that, in a simple game like a snake game, is already a great "replay" value. But add to that the interesting soundtrack and the other factors that come into play and you have yourself a good combination. I think it warrents it's 4 out of 5 because of all the little details that he put into bungy that make it a unique snake like game.

TECHNICAL FEATURES: Score (4/5)

Some of you might think that a game like Bungy The Worm doesn't have much technical merit. After all, it's a snake (or in this case a worm) that goes all over a square playing area. Well for one thing, it's got sound effects and a music sound track. Which isn't bad at all for a Snake like game. The game is very playable and since you can't hit yourself, the wall, the water and the red things on the screen it has atleast a decent collision detection to say the least. I think Bungy The Worm is the most technically advanced ASCII snake like game. So there's definitaly some merit to that. As I mentionned on QBasicNews, A game review should be done based on the type of game you're reviewing. In that context, 4 out of 5 is definitaly more than warranted. It deserves every single point.

THE FINAL VERDICT: 15/20 (75%)

Overall, if you look around the web and compare Bungy The Worm to other existing snake like games, I'm sure you'll have to agree with me that Bungy really takes the cake. By far one of the best implementation I've seen in a long long time of this simple game playing concept. With a touch of sophistication with the sound effects and sound track. A great piece of work. I hear that Bungy 2.0 may be in development or soon will be. I can't wait to see what that version will have to offer us.

If you haven't tried Bungy The Worm yet, all you have to do to get it is take a look here: Bungy The Worm, and get yourself a copy. Then you can see for yourself how good Lurah's version of the classic snake game is.

MystikShadows
Stéphane Richard
srichard@adaworld.com

Visit the official site, or download Bungy directly: Bungy.zip


Can't We All Just Get Along?
Working As A Team On Programming Projects

Written by Imortis Inglorian

I think at one time or another, we will all want to work on a team project. It seems like a good idea at first, but when you get there you quickly find that it's not as easy as you thought it would be. Well, guess what! I have a handful of tips and trick to help you not kill your partners! And if you do kill them I may write another article on how to hide the body/evidence from the local law enforcement. Let's hope that I don't have to write that one, okay?


Getting Started

When starting any programming project it's always a good idea to have a plan before you start programming. This is more true on a group project, and here is why:

"I want to do this, so I'll just go ahead and do it. I'll tell [insert partner's name here] later. He/she'll be so happy!

Yeah, right. This almost never works. Nine times out of ten, this will completely ruin the things that the partner had in mind or they just won't like it. Everyone gets mad; no one wants to give up his or her idea. Having a good plan before you get started makes this much less of a problem.

An extension of this is to make the planning process as democratic as possible. Get the members of your team to agree on the plan, and you can almost entirely remove this problem. I want to stress the word almost. You can never completely get rid of this problem, but you can come close.


Dividing Up the Work Load

I will be the first to tell you that I suck at graphics programming. If you are in the same boat, you know it. So should your team mates. Everyone is good at something, but no one is good at everything. If you suck at [X], then let your team mates know. There is a good chance that someone else does not suck at [x] but instead they suck at [y] which is exactly what you are good at. Okay, so maybe it won't always be that easy, but I think you get the point. By splitting up the jobs based on a "who is best qualified" basis, things will get done a whole lot smoother and faster.


To the Bat-Complier, Robin!

This might seem like a "no duh" comment, but you also have to decide what you are going to program in. Partner A might like C++, Partner B might like Pascal, and Partner C might like COBOL (heaven forbid). You all have to decide what language you plan to use for the project.

Once that is decided, you can all start programming to your heart's content, but keep in mind that other people will be trying to work with your code. Do your best to make it readable to others. How many times have you looked at a piece of source code on the net that looks like this?

x = x+12
y = y-12
x2 = x
y2 = y
print x+x2+y+y2
'etc...

This looks horrible. You can tell what it's supposed to do, kinda... But you have no idea what x, x2, y, and y2 are for. In this small example, it's not so bad, but on a big project, you will need to document your code a bit better. The best way is to use a variable naming convention that makes its purpose and type known. Like so:

intFirstXCoord

We can dissect this variable name to see its purpose. int means that the variable is an Integer. FirstXCoord tells you the purpose of the variable. It is the first x coordinate. This kind of documentation is highly effective, but can be a pain in the butt. You should also use lots of comment lines to make sure that the over all intent of the program is easy to see as well.


It's Not Whether You Win or Loose, It's How You Play the Game

Regardless of whether or not your project is a success, have fun with it and learn new things. That's the whole point of a group project, right? Don't let it become work, because work is an oppressive construct made by the MAN to keep us down. You should avoid "jobs" at all cost, so try not to let your team project become one. If you stop having fun, you loose the will to write good solid code and instead just want to "get it done". If your project doesn't work, well then modify it. Try to find a way to salvage the work that you have done already. If you can make something of it, then it was not a failure.


Yeah... I should go now...

Well, I guess this is it... *sniffle* You guys are growing up so fast... You need to go out on your own and use what I have taught you... *sniffle* Go on! Get outta here! Don't you understand! I don't want you any more! Go! *sniffle, sniffle*


Download a copy of this tutorial: TeamProg.rtf


FreeBASIC - Restrospect, Review and Prognostic

Written by Stéphane Richard (Mystikshadows)

INTRODUCTION:

Here we are, in March of 2006, FreeBasic has been in development officially for over a year. And I thought it might be fun to basically, review the FreeBASIC situation in terms of it's past (retrospect), it's present (Review) and it's future (Prognostic). After all, a year (or more) of coding warrents some form of status report no? So I have decided to give my own personal view of FreeBASIC, past, present and future. For those of you who haven't seen FreeBasic yet, you'll get a chance to get acquainted with a bit of it's history. See where and when it all began, how far it went in just a year you'll get a glimpse of all it's possibilities. And hopefully I'll get the curious even more curious to get their copies of FreeBasic and give it a serious try. More than that (especially in the prognostic section I would say), what I really want to do is open up a communications channel about where freebasic seems to be going versus where FreeBasic should be going and see how we can meet somewhere in the middle. Don't know what I mean? Not to worry, when you read the prognostic section (and part of the Review section) it should become clearer.

Also, as I've been following the development of FreeBASIC since it's beginning. I've been noticing some evolutionary turns that I think are worth mentionning. Some are great, but I believe some should be taken rather seriously and taken into consideration. Now although these represent my points of views mainly. I think that they are general enough to be taken as important points in terms of the BASIC definitions. You'll see what I mean when I get to that part. Basically, as far as BASIC is concerned it seems that somehow, what basic should be is either being redefined or ignored (forgotten) either because of how busy the FB team currently is or because maybe they just haven't thought about it. We'll talk about that later. Right now, let start this article with a retrospect of what happened in the last year and some since FB was first announced.

FREEBASIC THE BEGINNING: (THE RETROSPECT)

I don't know about you. But I remember that Wednesday, October 27th of 2004. I was going through the forums as I usually do, and when I got to QBasicNews forum, I noticed this post entitled freeBASIC (a 32-bit QB-syntax compatible compiler) preview ... I read that title and my first thought was "huh?" somebody finally decided to do this? So I clicked on that title and read what that post said. Now as far as I'm concerned, I've already seen more than one attempt at making a 32bit QB (DOS or Windows) and unfortunately for me, these attempts have all failed. So all I could say when I saw this thread was "I hope this one makes it far enough". And I continued doing whatever it was I was doing back then.

After a few months of doing my usual routine and checking this thread to see what was happening with FreeBASIC I said to myself, well, they're still working on it, already a good sign. then on a fateful December 2nd 2004 post, I could read the title FreeBASIC 0.01 Beta Released. This is when I personally started getting interested even more in FreeBASIC (basically I didn't want to get my hopes up until a first release was out because of my past experience with other 32bit QB projects I've seen. In the months that followed these announcements I've seen features get added, bugs corrected, and updates that just kept on appearing at quite an astonishing rate I might add. The Qmunity, since the beginning has been amazing at supporting v1ctor in his FreeBASIC endeavour and most definitaly were at least partly responsible for FreeBASIC's growing popularity and growing reliabity. People were telling v1ctor what they thought FreeBASIC should have, should be compatible with and v1ctor, whenever feasible, would go ahead and implement the new features to give QB coders what they wanted in FreeBASIC.

When you look at all the work that happened since that first release, you can see that none of this has really stopped since that first release and to me, that mean one thing, FreeBASIC was making it's mark quite fast. When version 0.07b of FreeBASIC came out, I decided to take a crack at porting some of my own code in there to see how it would compile and what the resulting exe would do. I was pleasantly surprised to find that In my case, I only had to convert keyboard codes to CHR$(255)+CHR$(KeyValue) instead of QB's CHR$(0)+CHR$(KeyValue) to see my program automagically start to behave as expected. This was a rather simple but long program (the contact management program you've seen in my File Manipulation Tutorial Series - Random Access Files). Since I saw how fast I could get this to work in FreeBASIC, that's when I got addicted like the rest of us and started using FreeBASIC for most of my recent projects. So great, I knew FreeBASIC would prove more than useful to me, but I knew, even back then that the Qmunity memebers weren't all application builders like me. Most were game makers and I was curious to know how FreeBASIC answered their needs. It didn't take too long before I noticed people were spitting out some graphics and game concepts that literally blew my mind away (now you know what happened to me ;-) ). No two ways about it in my mind anymore, FreeBASIC was the tool of choice for the Qmunity and beyond from then on out.

It seemed that everything was going FreeBASIC's way, releases were still being released quite fast, many bugs were getting corrected almost as they appeared, the freebasic website and it's forum were growing well (just look at the number of members on the freebasic.net forum, that speaks for itself. FreeBASIC was getting the recognition all the hard work deserved to get and things have been great for FreeBASIC since that time. More and more library support was added for things like fmod, Open AL, SQLite, SDL and others. And who here doesn't recognize that these libraries are "required" to help with the success of FreeBASIC. I know I do, even if I dont use alot of them right now. So of course, I more than welcome the fact that they are supported and supported quite well and efficiently I might add.

FREEBASIC TODAY: (THE REVIEW)

FreeBASIC today is quite the programming tool If you've never stopped to look at what FreeBASIC is today, allow me to do just that right here. FreeBASIC originally started out being "the closest QuickBASIC compatible compiler" this of course never meant 100% compatibility, it mean a syntax as close to that of QuickBASIC's own syntax as a 32 bit compiler could logically allow. This most definitaly understated that any 16 bit specific syntax would automatically not be implemented. This is quite understandable. Today however, FreeBASIC became much more than that. As a language, it evolved already into an almost complete language filled with many datatypes, statements and functions combined with a compiler that really seems to do the job right as far as turning code into an efficient executable. Take a look at this set of features (taken from the freebasic website itself):

And, just after you read these features, that's not all, FreeBASIC also boasts a complete set of support for quite an impressive list of libraries and engines. Most of which today, are standards in their own rights. Don't believe me? take a look at this list of support libraries and engines (again taken directly from their website):

And there's still more to come the lists just keep on growing. FreeBASIC's popularity is also spreading beyond it's original audience. People from all over the OpenSource programming industry are starting to pay attention to FreeBASIC and rightfully so. It doesn't take a rocket scientist to see where FreeBASIC is going and to realize that this might be something that's been waited on for a long while now, and it's finally happening. So yes, FreeBASIC is becoming quite the phenomena. More and more people today are giving FreeBASIC a try and simply don't seem to regret doing so at all. FreeBASIC is still gaining momentum and well, I'm incredibly happy to see how fast and well things are going for FreeBASIC and the FreeBASIC team. I'm very happy for them, their work really shows when you download FreeBASIC and see what it's all about. And they deserve every bit of credit they've received (and more).

Now, most of you by now, after reading all my praise about FreeBASIC might be wondering why I mentioned I had concerns or that not everything was perfect. I mean, just look at how much good I have to say about FreeBASIC. Hard to imagine I might have something different to say about it (well aside my everlasting PCOPY in SCREEN 0 mode). When I watch how far FreeBASIC has gone since it's beginning, nothing I have said so far is wrong. FreeBASIC is really growing like I said, the FB Team is working hard on the project and the results of their hard work is really shining through in what FreeBASIC is today. So yes, everything I said is true. My concern is on a different level.

FREEBASIC TODAY: (THE TWIST)

if you've been on the FreeBASIC forum in the past say 9 months or so, fairly continuously. You will easily notice that 1. Bugs reported get fixed quite quickly and 2. New features, when they make sense get added quite quickly as well. And that's good, that's part of the strength that FreeBASIC offers. My concern is, at what cost are these features (language or library related) being added to the arsenal of FreeBASIC tools? For the past 9 months (longer than that but especially the past 9 months) the FB team have been busy giving users the feature they ask for, and fixing bugs. That's all good, but there's something happening with FreeBASIC in the midst of all this hardwork and devotion to the users.

You see, as many of your know, I'm not a game programmer (well until recently that is) and I'm certainly not an OpenGL or SDL user. In other words, few of the libraries interest me aside the SQLite library and ncurses (which seems to be gone now). I am an application developer, a tools and utilities developer, and recently a text game developer. As such I don't benefit from all these libraries (but like I said, they really should be available, that was one of the many smartest moves made by v1ctor and the FB team). But I do benefit from the FreeBASIC language itself. I benefit from the fact that you can include Assembly directly in the code in an intelligent way. I love the simplicity of the BASIC syntax. And that is my main concern when I look at what is going on. And since I can't be overwhelmed by all these libraries that everyone uses, I can take the time to sit back and see the language itself. At how FreeBASIC implements and supports BASIC in all it's definition. and well, this is where I wonder a bit.

I guess everyone has his or her own view of what FreeBASIC should be as a language. Some people program in FreeBASIC using what they've come to adopt as their standard, but how standard is their standard? Basically, one thing I've noticed over the past 15 months or so is that the FB team is busy giving the users what they want (atleast the majority of them). As such well just look at the features i described and you'll see how well that work has been going. The list of features is growing at a very fast rate and that's great. When I mentionned before, at what cost are these new features made available to FreeBASIC users, I meant, how does these features and libraries affect the way we code in FreeBASIC. my opinion, based on observations of the progress of FreeBASIC, is that FreeBASIC is losing its essence in some way. It's sacrificing language features for the sake of giving features that the users want. As a language, support for the libraries that are there is good to have, no two ways about it, however, there is a BASIC way to allow support for these libraries and there is the C++ way to give that support. And so far, probably because it's faster, the C++ way seems to have been chosen. For a BASIC language and of course in my own personal opinion, it's not the way these libraries should be made available because it opens a door that with time, will get harder and harder to close as the workload to close it will be proportional to the number of libraries support to change. Right now, there's a good number of libraries that need a more BASIC way of doing things. Now, is it more work, yes it is. is it worth the extra time? i like to believe so, for the sake of BASIC and what it is "supposed to" means.

FreeBASIC to me, has two responsibilities. The first of which is to be as compatible to QuickBASIC as possible. Since it was the original goal after all as stated on that first post on QBasicNews and to me, this means that if a function or statement that exist in QuickbASIC, special efforts should be made to implement it (at least try to implement it first). I've read on some posts people asking for the PSET statement if it existed, if it was available in the DOS port of FreeBASIC only to read as a reply something like: "if you want PSET in DOS use Allegro!". Now, this maybe true, Allegro is an impressive library and I'm sure it has something that can act as the equivalent to PSET somewhere. However, there is a big difference between using PSET and learning the complete Allegro library to find where it's version of PSET is and how it is implemented and used. If the goal of FreeBASIC is to be as QuickBASIC compatible as it can be. What would it be to code PSET to call the Allegro function it needs, instead of telling QB (or FreeBASIC users) to learn Allegro functions and syntax to do what a standard (and very common) BASIC command does? Do you see where I'm getting at? I'm not saying it's not more work for the FB team to do this with the PSET command, of course it is, I realize that, however, to me, making the PSET (or any other graphic commands) call their Allegro counter parts (If there's no other way to do them) would be the "REAL" BASIC approach to implementing the FreeBASIC. in other words, some work should be done, especially in these situations where a language element isn't directly implementable for some reason, to give them something they can work with that looks and feels as close to BASIC as possible. This will of course encourage more users to give things a shot if they know there's something close to what they want AND it will also considerably reduce the learning curve for new comers to FreeBASIC which may not have any OpenGL or SDL background at all to work on.

BASIC should be simple to learn no matter what feature is added to it. To me, that's what the bottom line should be and efforts should be made to support that bottom line. there's a couple of of places in the language (regardless of the os it's implemented on) that can be more BASIC like. The most obvious of those of course of in the support of all these C++ libraries that is offered. right now, if i look at a FreeBASIC sample program that uses OpenGL, or any other of these libraries, chances are, if I add a ; at the end of each line it will compile in any C++ compiler that can use OpenGL. This is know as a thin binding to a library. Any BASIC coders, including those that coded these programs will tell you that this code looks like C++ code. Is there any way around this? Yes, there's two ways to approach these. The first suggestion is a thick binding rather than a thin binding to the same libraries. for some of the libraries (maybe for most of them) it may be simpler to do a thick when OOP is implemented but still, i'm saying it now so that hopefully it will be remembered when the time comes. A thick binding offers a different means of accessing the low level functions that exist in the library. One that can be made as safe to use as people expect from a BASIC language (this is where I think that we're drifting off) BASIC, as a language should offer a means of doing whatever we want in a safe way.

The other way is to add to the parser engine itself. hence, if we want to add support for a library, perhaps a quick set of instructions (that look and feel like BASIC) would definitaly be easier to learn than learning all the C++ functions that make up the library or a set of generic instructions designed to wrap the library with a set of statements that give a clear access to the core functionality would be closer to what BASIC programmers expect to see in a BASIC language such as simple syntax, easy to understand constructs, and other features that make BASIC this fun and easy to use and learn Learning Allegro to me does not constitute a BASIC learning experience. However, learning statements that look and feel like basic (PSet, Line, Circle and whatever, that have a BASIC taste to them is the BASIC way to do it. As a BASIC user, i don't care if these BASIC like commands use Allegro or OpenGL or whatever else to do it's task doesn't matter. Obviously some users don't mind that since there's already so many OpenGL projects in the making, however, putting myself in the comming BASIC programmer eager to learn FreeBASIC, i think what i'm saying here more than makes sense. Don't you? Of course, I'm not suggesting we dump the standard library syntaxes, any sooner than you "dump" standard commands when you create a sub or function. But an evolving compiler means you have an opportunity to give BASIC ease and power on top of library functions. by default you can call allegro using allegro syntax. But if PSET (for example.) needs allegro to be implemented, why not make it so the compiler understands PSET?

FREEBASIC TOMORROW: (THE PROGNOSTIC)

A prognostic, for those that might not know isn't a 100% exact science, it's a plausible conclusion and prediction based on current data and facts. Now, if we look at how things are today in the FreeBASIC world, where they are heading can be one of two directions. Now, because of how good FreeBASIC is, I think both directions would offer a future to FreeBASIC. However, not both futures would coincide with the fact that FreeBASIC is/should be a BASIC dialect first. The conclusions are somewhat extremist too. So perhaps we should look at these as "directions to work towards" rather than look at them as "directions to reach", it would be a more realistic direction then in my opinion. let's review them.

The first direction (or goal) is that FreeBASIC continues to develop as it is now. As it stands today, FreeBASIC is very much recognizable as a language, maybe even as something close to BASIC. Add to that more and more C++ libraries that will be supported (as users voice themselves and tell them what libraries they want to see in FreeBASIC). As these new libraries get supported (because we all know how well, quick and efficiently these libraries get added to the arsenal of libraries that is already supported by FreeBASIC) so yes, FreeBASIC will support one of the biggest and most complete set of libraries ever thought of in a programming language. After the libraries will probably come support for libraries written in other languages I assume. For example, there are some highly advanced scientific and statistical libraries that have been implemented in languages like FORTRAN and others which simply do not exist in any other languages. some of those libraries are designed to allow compatibility between languages like FORtRAN and other of the domain specific languages which would add to the list of styles one can program in using FreeBASIC. Add say 3 of the major libraries created in 3 of the major other languages to the mix and suddenly FreeBASIC is starting to look like any other language but BASIC itself. FreeBASIC becomes Free_____ >- insert language here since in how kinda feels like C++, Fortran, maybe php and python, who knows, all depends on the libraries and the languages they were made in. In this direction, FreeBASIC becomes popular to the users of the libraries that are supported, no matter what language these users usually use to code with the libraries. hence, the language, FreeBASIC, becomes a 2nd priority to the fact that it MUST support the library. I'm not saying that it shouldn't support these. I'm certainly not saying that it shouldn't support what it already does support either, these are all excellent additions. but just read the next paragraph to understand my point of view completely.

This is my own personal favorite direction. But I think I can also say that this is the direction a BASIC language should at least be strongly considered while it's being developed. This direction doesn't mean that the libraries and features mentioned in the previous paragraph cannot exist. Quite the contrary. What this direction does mean though is that the Language takes precedence over the libraries it supports. Like I said previously, yes, this does mean more work for the FB team (or any other contributor that might want to take a hand at it). instead of making FreeBASIC use a library on the library's own territory, it could, with a little coding, give the users a FreeBASIC means of accessing the library. To this, yes, we can use the two methods i mentionned earlier, thick bindings and/or language extensions (which I don't believe would be that much more work since it would just add a few statements designed to give a better, more BASIC like means of using the functions that are in the libraries. Again here, putting myself in the feet of the BASIC programmers and QB programmers alike to whom All currently implemented libraries might be interesting to them but not at all costs. If they see an example of using OpenGL that isn't just a bunch of function calls like it is today. As BASIC developers they might be more tempted to cross the line into OpenGL if the syntax atleast seems to be made to be understood by BASIC programmers. BASIC should be simple, it's not the first time I say it in this document. But BASIC should be AS SIMPLE as it can get. Time and effort should be spent to make sure BASIC is as simple as BASIC should be.

As you can see, these two conclusions are very plausible. There's no doubt in my mind that one of these will happen. The first one is already well on it's way to happening too. As i said, some might think why change what seems to be working fine and helping FreeBASIC be as popular as it is right now? Well, the question, the issue, isn't really about where FreeBASIC is today (although as i mentionned, even today, in my opinion, it could use a little taste of BASIC. The concern is about where FreeBASIC is heading, what kind of future does v1ctor want for FreeBASIC, what his goal was versus what FreeBASIC is today. Does he want a good basic, or a language that supports everything there is, like I said, both directions have a future, but of course, not the same future. Alot of time has been spent in the development of FreeBASIC giving it's users what they want. Maybe it's time to sit down and see what we can do to give v1ctor what he wants out of the project. He may be satisfied with what he has, maybe not, i don't know. But think if this as one good reason to stop and write this article. For victor, for all the users, for all the new users there will be in the near and far away future.

IN CONCLUSION:

And there you go. Again I really want to stress here, I don't want to start a war or anything, of course not, but I do want to see what the possibilities are, what we have at our disposal to help FreeBASIC be as BASIC like as it can be. This is of course both for newcomers, current users, and people from other BASIC dialects that would like to code in FreeBASIC, the more BASIC like we can keep it, the better FreeBASIC will look to all 3 categories of users. I'm hoping that some form of discussion results from this. i really feel FreeBASIC as a project could really benefit from all I've mentionned and i want to know what other people think about these.

Again, it's no question to me that FreeBASIC is becoming quite a powerful tool, a tool that can be used and should be used for it's intended purposes, a tool that is growing fast in popularity still today and rightfully so, my question is, is it becoming a powerful BASIC or a powerful tool? That, only time will tell, but I for one most definitaly hope it does. Because it's what got me interested in the whole FreeBASIC project in the first place. What about you? this isn't pointing fingers to anyone, it's nothing close to that. But I fear that if FreeBASIC continues in its current direction it may turn into some hybrid so caught up into features that neither BASIC or C/C++ programmers will ultimately want to use. By that I mean, if BASIC programmers see that FreeBASIC is to C++ like to be worth the change, they might not want to. likewise, if C++ programmers look at FreeBASIC and see it looks and feels like C++, they might not bother changing either since it won't save them time, not even at the prototyping level of development. like all FreeBASIC fans here, i want FreeBASIC to work, to continue growing in popularity, to be spread all around the internet like a virus that can't be stopped but i do want it to be BASIC. Until then, happy coding and let's see what we can make out of all this.


MystikShadows
Stéphane Richard
srichard@adaworld.com



QBasic Tetris Games

Written by Lachie Dazdarian (December 2005)

Introduction

At the first glance this article probably didn't catch your interest. Mainly because Tetris is one of the most(if not the most) cloned or remade games in the entire history of computer games which made its gameplay too common and therefore boring. One just can't escape Tetris. You will stumble on some clone weather you want it or not. As a part of a floppy disk coping program(ah, the good old Amiga 500), some installation program(to make the installation less boring, I've seen it) or it came in a pack with some other game you were interested in. Even if you don't like the concept of Tetris you can't deny the influence it had on the computer game industry and individual game designers. Not to mention all the people who refused to play computer games before discovering Tetris.

Other thing that might have pulled you off is the fact that only SonicBlue's Bricks and perhaps Netris of all the QBasic Tetris games made any impact on the community. And those two games are far from the top 10 QBasic games. So you might think that an article of this kind can't be nothing more than a harmless way to fill the magazine with some content. I'm here to prove you wrong. Or not. You could be one of those actually interested in the topic of this article and are now very annoyed with this tedious introduction.

If you don't know what Tetris is I feel sorry for you. I can try to explain the gameplay but since there is around 20 links for various Tetris games in this article just download one and see it for yourself.


The Original Tetris

If you are wondering when the original Tetris was released and how it looks this section will answer all your questions.

Tetris was invented by a 30-years-old Russian researcher Alexey Pajitnov who at that time was working at the computer center of the USSR Academy Of Scientists in Moscow. The original programmer was 18-years-old Vadim Gerasimov, a student studying computer science at the Moscow University. The original commercial Tetris was a result of the joint efforts of the Moscow University computer center and Andromeda Software from London and released by Spectrum HoloByte in 1987. All this is said in the very game so don't think I made some extensive research. We all know how Tetris inspired so many game designer and resulted in countless number of clones and remakes, some new probably being made as you are reading this. What remained through all these years are the default Tetris pieces, so perfectly constructed with 4 blocks per piece. All the new pieces that were invented never seemed so lucid as the original ones or added any depth to the original gameplay.

The original Tetris is not a bad game at all. I encountered that game for the first time on Amiga 500 and it didn't really appeal to me. Maybe it was the Russian letters on the title screen. I don't remember anymore. I have the IBM-PC version only for few months now. It supports CGA and EGA graphics mode and looks rather nice in EGA mode(better graphics than in many clones). The game plays fine but maybe too fast too soon. I cannot be sure what's the game's optimum speed and if the program is able to optimize the game speed according to computer configuration. The game allows you to pick the starting level and the number of randomly filled rows in the bottom of the playfield. When a certain amount of horizontal rows is cleared the game speeds up and the background changes. The controls are rather uncommon compared with Tetris games that came after it. You play the game with the numeric keypad. 4 and 2 moves the falling piece left or right, 5 rotates it, 2 drops it and 8, which is very inconvenient, skips the level. These controls might drove away an inpatient player confused with the level skipping. What also doesn't help this game today is the fact it requires DOSBox to run on nowadays PCs. A charming point can be the communist note in the game like high scores table being entitled "Top Comrades" and the USSR references on some background images. Definitely a collectors item.


Download the original Tetris here: origtetris.zip (109 KB)


QBasic Tetris Games

QBasic Tetris games turned out to be quite a discovery in quantity as well as in originality. Definitely more than I expected to see. I've found 18 QBasic Tetris games total and few Tetris-like games. I'm pretty sure I didn't miss any game that should have been included. The number of QBasic Tetris games was a surprise to me since they are not that easy to code. Well, not easy as it's to code a Sokoban game and there isn't that many QBasic Sokoban games made(to my knowledge). This article will feature a small review of every included game accompanied with a score. The scoring system is devised especially for this article and it shouldn't be used as a reference with my other scoring systems. The games are scored with emphasis on how good and original Tetris games they are. Scores range from 1-5 and I also used tenths of a number. Since the games are listed from the lowest to the highest scores this allows me to list them more "precisely".

All uncompiled games in the list come with QBASIC.EXE and a batch file that runs the game. All the games were tested in Windows XP and they run fine without any need for shortcuts or DOS emulators. Few games require VDMSound for music or sound effects to work(those games run fine without VMDSound but silent) and I will mention in descriptions which games require that.

One game, Shayne Co's Tetris, was excluded from the article since it came with a fatal bug(the pieces don't drop by themselves). If that game worked it would get a score around 2.9 so you are not missing anything.


Ludatris

A truly bizarre game labeled by the very designer as a game not for sane people. If computer games were considered to be artworks Ludatris would be a respected piece of avant-garde. The game looks and plays very strange. Despite the highly original and hypnotic gameplay Ludatris is an extremely difficult game to play and enjoy. Wierd features like the drop key not dropping your piece but freezing it in the middle of the playfield or the random playfield movement in horizontal direction. I would render Ludatris as unplayable but I can't be 100% sure. Whatever the case is I'll stick to what Josh said - not for sane people. Ludatris doesn't feature any music or sound effects.

Developer: Josh Striblinge
Year of release: 2001
Screenshot: ludatris.gif
Download: ludatris.zip (39 KB)
Compiled: Yes
Final score: 2.1


Tetris

You might not agree with me scoring Jorden Chamid's Tetris so low but I just can't and won't fabricate my objective judgment. Jorden had an awful sense for game design(I'm basing this on his Tetris games). Both his Tetris games fail in one major way - they are so annoyingly slow-paced making them close to unplayable. Almost two seconds for a piece to drop one position and on level 2 the increase of speed is ignorable. The very screenshots might give an impression of a solid Tetris game with simple but clear graphics. I'll also mention few WAV sound effects(when you clear a row, end game, etc.) and MIDI background music(annoying). There is a certain sloppiness in the menu design but nothing serious. Still, all the positive aspects of this game become irrelevant with such a poor choice of game speed. In one sentence, playing this game feels more like a punishment than anything else.

Developer: Future Software(Jorden Chamid)
Year of release: 1997
Screenshot: tetris.gif
Download: tetris.zip (469 KB)
Compiled: Yes
Final score: 2.4


MHTris

Basing your judgment only on the screenshot you might wonder why such a low score for such a pretty looking game. Well, MHTris is one of those games which serves no other purpose but to exist and fill the designers portfolio. It looks nice(hi-res 640*480 graphics), features some silly story and flamboyant design but nothing more. MHTris' gameplay fails to contain the most important element of a Tetris game - the increase of game speed when certain amount of rows is cleared. It also doesn't feature any sort of high scores table(not even an empty one). If you are enough skilled you can play one round forever and the game will never become more challenging and exciting. It will play with the same speed constantly. This game seems like a waste of time to me. MHTris doesn't feature any music or sound effects.

Developer: MicroHuf
Year of release: 1998
Screenshot: MHTris.gif
Download: MHTris.zip (153 KB)
Compiled: Yes
Final score: 2.6


Tentris

A quite boring and uninspired Tetris game but it plays properly. Tentris is slightly too slow for my taste but not slow as Jorden Chamid's Tetris games. Tentris features some very high scores to beat. Some of the most challenging scores I've encountered doing this article.

Developer: Folker Fritz
Year of release: 2003
Screenshot: tentris.gif
Download: tentris.zip (48 KB)
Compiled: Yes
Final score: 2.8


QTetris

An interesting ASCII Tetris game featuring some strange rules which I didn't like that much. The game comes with few bugs but at least there are some high scores to beat. QTetris also features an incomplete two players mode where one player's pieces move horizontally while the other player's move vertically. Interesting but like I said, that mode is not finished and very buggish(read: unplayable). One very cool feature in this game is the piece set editor where you can create your own pieces. The game comes with few already made piece sets. I wouldn't recommend this game to people looking for a quick Tetris game. Only for curious people as well as those fixated with originality.

Developer: Belly Laugh Software
Year of release: 1997
Screenshot: qtetris.gif
Download: qtetris.zip (237 KB)
Compiled: No
Final score: 2.9


Tetris 2 - The New Blood

A very cool Teris clone introducing a new element in the gameplay - killing. While having to fill horizontal lines with blocks you can also kill the small stooges that keep falling in the playfield by slamming the Tetris pieces on them. The game features some nasty gore effects and a cool bonus related to them(blood dripping from the playfield). While the game feels very entertaining in the beginning you will soon become aware of the flaws in the gameplay. The playfield is too wide for a Tetris game so it's a bit tedious too clear a row. Also, the game doesn't speed up with time. Itís interesting to say that this is a pure QBasic 1.1 program not relying on any external graphics files and despite that the game looks very good(the movement of the stooges and the blood effects). Not something youíll play over and over but worth being checked.

A note: tetris2nb.zip includes two BAS files. TET_NEW.BAS is a slightly altered source code where I tried to slowdown the game. Have in mind that it still might run too fast on YOUR PC.

Developer: Christian Dever
Year of release: 1998
Screenshot: tetris2nb.gif
Download: tetris2nb.zip (203 KB)
Compiled: No
Final score: 2.9


Tetris 2.0

This sequel to Jorden's first Tetris game does feature several improvements but the critical flaw from the first Tetris is not fixed. Tetris 2.0 features better graphics and nicely designed menus though the graphical design of the blocks is not something I would score high(shaded with bright edges). Tetris 2.0 also features a cool 2 players simultaneous mode as well as MIDI background music and few WAV sound effects(nothing fancy but it's there). While I can appreciate the improvements and solid look of the game nothing of this can hinder the annoyingly slow gameplay.

Developer: Future Software(Jorden Chamid)
Year of release: 1998
Screenshot: tetris20.gif
Download: tetris20.zip (434 KB)
Compiled: Yes
Final score: 2.9


Stevetris

Another unimpressive Tetris clone with below average graphics and ok gameplay. This Tetris game features a bit more complex scoring but the default high scores are very easy to beat. Another flaw is that the key pressing is slightly too sensitive. Stevetris also features few sound card(non-PC Speaker) sound effects which you can hear if you run the game with VMDSound(simple but very relaxing sound effects). Running the game without VDMSound might cause certain slowdowns when you clear a row. There is a funny story behind the reasons why Steve made this game. If curious check the game out.

Developer: Steve Rosenthal
Year of release: 2002
Screenshot: stevetris.gif
Download: stevetris.zip (56 KB)
Compiled: Yes
Final score: 3


QuadBLOX

A solid Tetris game interesting because of its huge Tetris pieces and a very small playfield. I guess this adds to challenge value. No special features. Quite challenging scores to beat. Only PC Speaker sound effects but they are surprisingly pleasant.

Developer: Derek Andrews
Year of release: 2000
Screenshot: QuadBLOX.gif
Download: QuadBLOX.zip (61 KB)
Compiled: Yes
Final score: 3


Blocks

A rather odd Tetris game featuring very poor presentation and poor graphical design. It features 3 game modes, two with 4 blocks per piece(standard Tetris pieces and difficult pieces) and one with 3 blocks per piece. Blocks also allows you to change the width of the playfield, to turn on/off the bombs and to modify few other less interesting parameters. The very playfield is uncommonly high(deep). The problem of Blocks is the already mentioned poor presentation and the stupid high score system which remembers only one score(despite of different game modes and parameters that influence on the difficulty). The gameplay is ok(level-based; color of pieces changes as the levels progress). No sound effects or music.

Developer: Hamster Republic Productions
Year of release: Unknown
Screenshot: Blocks.gif
Download: Blocks.zip (45 KB)
Compiled: Yes
Final score: 3


TF Tetris

An average Tetris clone featuring MIDI background music(yuck) and one Sound Blaster sound effect. The graphics are mediocre mostly due the poor choice of colors. Mouse controlled interface. Ok gameplay. Empty high scores table. It's interesting that TF Tetris, while featuring Hall Of Fame(10 best scores), also features Hall Of Shame(10 worst scores). The question is where you will find people to fill both of them. TF Tetris plays in different colors if you run it in DOSBox but it's nothing that changes my low opinion about the graphical design.

Developer: TF Software
Year of release: 1998
Screenshot: tftetris.gif
Download: tftetris.zip (198 KB)
Compiled: Yes
Final score: 3.1


Future Blocks

An interesting Tetris game featuring huge amount of options and an AI which allows you to play against the computer(not simultaneously; higher score wins in set number of pieces). It features hi-res(640*480) but quite drab in-game graphics. Menus, on the other hand, are very cool(huge font). The problem with controllable parameters in this game is that you can change them above the playable values(for example, playfield going off the screen limits) or even to negative values(can screw up the game but not crash it). Also, I don't know what half of them does(I couldn't decipher their effect) and the game doesn't come with instructions. Other flaws are the lack of high scores table, game not speeding up with time(I didn't notice it after 40 cleared rows nor a speed variable in the source code) and the lack of sound effects and music. So I'm quite split when giving a final score to this game. It has some neat options and modes none of the other QBasic Tetris games have(like playing against the computer) while in the same time it fails to feature some crucial elements of the Tetris gameplay. If you are into games with many controllable parameters but without character get Future Blocks.

Developer: Michael Fogleman
Year of release: 2000
Screenshot: futureblocks.gif
Download: futureblocks.zip (52 KB)
Compiled: Yes
Final score: 3.1


QBasic Tetris

A nice Tetris game with not so colorful graphics(single color Tetris pieces) but well designed overall(very specific for this designer). QBasic Tetris features nice menus, instructions and a lot of options. In QBasic Tetris you can choose the starting speed of the game, complexity of the falling shapes and few more interesting parameters. I should mention that the controls donít include the down key(used when you want to speed up the falling of a piece) but only the drop key which can be a bit frustrating. If you choose to download QBasic Tetris don't expect nice graphics but a very well executed Tetris game. Empty high scores tables. The game also features PC Speaker sound effects but I doubt you'll play this game with sound effects turned on.

Developer: Dominik Kaspar
Year of release: 1998
Screenshot: QBTetris.gif
Download: QBTetris.zip (68 KB)
Compiled: Yes
Final score: 3.3


Didris

Didris is a quite original Tetris game featuring various extra elements in the classic Tetris gameplay. In Didris, similar to Tetris 2 - The New Blood, small stooges keep falling in the playfield(one by one) who you need to kill as well as prevent this helicopter to reach them. The classic objective is still there Ė clearing horizontal lines. Another feature is acid rain which you can use after certain amount of pieces is dropped and which eats away parts of the dropped pieces. Sometimes a bomb appears instead of a Tetris piece which destroys pieces around the contact zone. Very colorful design and all done without external files(pure QBasic). What lowers my overall impression is the movement of the stooge and the helicopter which is not pixel based and the lack of blood effect when you kill a stooge(funny how I consider this a flaw). Didris only features PC Speaker music(yuck) which you can, luckily, turn off. I don't understand why someone would take the trouble to compose PC Speaker music. I must be honest and say that Didris didn't get me hooked despite all the extra features. That might not be the case with you so I urge you to try out the game yourself.

Developer: Dietmar Moritz
Year of release: 1998
Screenshot: didris.gif
Download: didris.zip (91 KB)
Compiled: Yes
Final score: 3.3


QB MATRIX

Some of you might wonder why I'm scoring QB MATRIX better than, for example, QuadBLOX or QBasic Tetris but this is just a result of small details. QB MATRIX is better grapically designed than the previously mentioned games featuring really pleasent colors and nice looking Tetris pieces. Also, QB MATRIX features a very neat interface where you can keep a quite detailed track of your score. As for options QB MATRIX allows you to choose the starting level, amount of filled lines in the playfield on the very beginning and to turn on/off the slam mode. QB MATRIX only features PC Speaker sound effects and they are poor(you can turn them on/off). High scores table is included but it comes with ridicoulously easy deafult scores to beat.

Developer: Tim Truman
Year of release: 1996
Screenshot: qbmatrix.gif
Download: qbmatrix.zip (208 KB)
Compiled: No
Final score: 3.4


Textris

Textris could easily be scored with 3 by someone else. This higher score is a result of my personal(might not be very rational) likeness of the game. Textris is a simple Tetris game with the classic gameplay and average graphics. It only features poor PC Speaker sound effects. So what I like so much about this game? The simple and effective design. Textris doesn't feature any menus or a title screen. Everything is on a single screen, even the high scores table(the default table is empty). This makes Textris one of the best "quick" Tetris games in the QBasic world where you don't have to think about game parameters and similar stuff. Just one double click and you are playing a Tetris game. The constantly present high scores table is another plus. Textris has a multiplayer mode but since I don't own any kind of local or Internet connection I was unable to test it(it asks for a com port number). Textris also features mono color particle effects in the background which you can change from effects like fireworks to deep space.

Developer: Crystal Vision
Year of release: 1999
Screenshot: textris.gif
Download: textris.zip (59 KB)
Compiled: Yes
Final score: 3.4


Netris

I'll be honest and say I didn't like Netris much on the first try and originally typed in a much smaller score. The biggest reason for such impression was the psychedelic(I'm not sure how else to describe it) menu design and boring in-game backgrounds. But after a more throughout examination of the game I realized it deserves a higher score. Netris features the classic Tetris gameplay(no extra features) and the game overall is well executed. The graphics are above average(with the best looking Tetris pieces of all the QBasic Tetris games) while the sound effects are good(Sound Blaster compatible). There is also the atmospheric(but maybe not the most suitable) background music which you can hear in Windows only if you run the game with VDMSound. Netris supports IPX protocol multiplayer mode but I was unable to test it. Despite my change of original opinion to better I still feel that Netris could use a better design with a more coherent style. Netris is originally a VBDOS game but the package comes with a stripped QBasic source code. So I'm not sure how much Netris belongs to this article.

Developer: Vertical Horizons
Year of release: 1998
Screenshot: Netris.gif
Download: Netris.zip (95 KB)
Compiled: Yes
Final score: 3.8


SonicBlue's Bricks

SB's Bricks features the best graphics and graphical design of all the QBasic Tetris games. I only have one negative thing to say about the graphics and that's the awkward look of the Tetris pieces. They are without textures but feature bright and dark edges which makes them look wrong when stacked up in piles. SB's Bricks features MIDI background music but the setup file for them is horrible. You need to set the path to a specific MIDI file and set its length! Bwhat?! Stupid. Anyway, the MIDI tracks are poor as usual. While featuring very good graphics and the classic gameplay this game also offers two extra groups of pieces you can turn on/off. Funky pieces increase the game difficulty because they are hard to connect with other pieces while special pieces have some special "abilities" like ability to destroy dropped pieces or adding single block pieces onto the dropped pieces. Very original features and they add a new flavor to the classic Tetris gameplay. SB's Bricks comes with a high scores table filled with challenging scores and a save game option. I think you expected for SB's Bricks to get the highes score.

A note: I'm not sure about the year of release with this game. It seems too early to me but the only available information about the release date is the files' last date of modification.

Developer: Eric Schneider
Year of release: 1995
Screenshot: SBBricks.gif
Download: SBBricks.zip (532 KB)
Compiled: Yes
Final score: 4.1


QBasic Tetris-like Games

There are few QBasic Tetris-like games I've stumbled upon which are worth being mentioned. Some even have the word "Tetris" in the title while featuring very little of the classic Tetris gameplay. Most of them are quite interesting. I've scored these games on the same was as QBasic Tetris games with scores 1-5 but of course not with the same criteria. I've put most weight on the gameplay while scoring the following games.


Tetris 3

The designer of Tetris 3(the correct title is Tetris^3) describes this game as any other Tetris game but with one more dimension. He couldn't be more wrong. Tetris 3 has nothing of the original Tetris but the title. We could barely connect Tetris 3 gameplay with Columns. In Tetris 3 single block pieces fall into a mock-3D playfield and your goal is to connect only two of the same color(4 colors in the game). While featuring flashy but average graphics, Sound Blaster music and sound effects the gameplay is, to say at least, very poor. It's very easy to keep the playfield with no more than 1-2 blocks in it and the only thing that stops you in this is the increasing speed which makes the game impossible to play after certain amount of time. Also, there is no any sort of score in this game so you can't compete against another result(not even yours you would memorize or write down). It's just a game with a completely missed gameplay.

Developer: Vral
Year of release: 2000
Screenshot: tetris3.gif
Download: tetris3.zip (540 KB)
Compiled: Yes
Final score: 2.4


Chinese Square II

I never played the original Chinese Square so playing this game felt very odd in the beginning. Chinese Square plays similar to Tetris. Squares of various sizes fall one by one in the playfield and you need to place them on the lowest possible position. Thatís because when a falling pieces touches a previously dropped piece it turns everything under itself into a colliding zone. You need to avoid for a corner of a falling piece to touch a high and narrow colliding zone and try to place it where it will create the least amount of new colliding zone. You also have few bombs you can use to destroy the colliding zone. The gameplay is good but it lacks challenge since the default high scores are too easy to beat. The graphics are monochrome and not so good while the music is played through PC Speaker. If you are into obscure gameplays download this game.

Developer: Cheng-ning Pu (Jack)
Year of release: 1997
Screenshot: ChineseSquare2.gif
Download: ChineseSquare2.zip (62 KB)
Compiled: Yes
Final score: 3


Bricko The Challenge! ver.0.7

A rather nice hi-res(640*480) puzzle game where you slide blocks of various sizes so that as many of them fills the screen. In the very start the playfield is empty and the best thing to do then is to wait for the falling blocks to fill the screen. Very nice Sound Blaster sound effects. Very good graphics but not much of it. Bricko supports multiplayer mode via IPX connection but I was unable to test it. A high scores table is present but it's empty. The game has that "time waster" value with very relaxing gameplay. Worth being checked.

Developer: Davey Taylor
Year of release: 1999
Screenshot: bricko.gif
Download: bricko.zip (92 KB)
Compiled: Yes
Final score: 3.2


Tetris 2

Yet another game named Tetris 2 confusing us even more. Despite the unoriginal title this is a very interesting ASCII Tetris-like game. Its gameplay is more similar to Columns than to Tetris. While some pieces that fall in the playfield are the classic Tetris ones some are not. In Tetris 2 you need to connect 3 blocks of the same color horizontally or vertically(not diagonally) and the falling pieces are compiled of differently colored blocks(3 kinds of color total). There are two game modes, continuous and puzzle mode. In each mode you goal is to clear(destroy) the blocks that have two blinking squares in them. Together with blocks with blinking squares there are the regular blocks(without squares in them) and blocks with non-blinking squares in them. The falling pieces are always constructed of regular blocks. If you, for example, destroy a green block with blinking squares(connect it with two or more green blocks) all the other green blocks in the playfield with squares in them will be destroyed too. Each level in continuous mode starts with certain amount of randomly placed blocks in the playfield(usually the ones with squares and their amount depends on the level) and always with 3 blocks with blinking squares(each of every color). It's always better that you "target" the blinking blocks and try to destroy them as soon as possible. You can choose any level from 1 to 20 in continuous mode and if you finish level 20 the game continues. I don't know how many levels there is. If you lose a game the game completely ends. The high scores table for continuous mode is filled with challenging scores to beat. In puzzle mode you have to destroy all the blocks in the playfield with mere 1-3(depends on the level) falling pieces so you need to think carefully how to use them. Thus the name of the mode. You can chose to start in any level from 1 to 20 and there is 40 puzzle levels total. If you lose a game in puzzle mode you can continue unlimited number of times and there is no score in it. The challenge is in solving the level. Both these modes feature excellent gameplay and are highly entertaining to play.

The design of the game is very good despite the ASCII(text) graphics featuring nice menus, fadeouts and screen transitions. Tetris 2 doesn't feature any music or sound effects. As far as I'm concerned this game is a wonderful QBasic discovery and I recommend it to just about everybody, especially QBasic game collectors. Another excellent game for a remake. Trust me on this.

Note #1: To switch this game to full screen mode press ALT+ENTER after you run it.

Note #2: To rotate the falling piece use (,) and (-) keys(the key right from (M) key and the key left from right shift)

Note #3: The designer displays some copyright nonsense before the title screen with stuff about Tetris 2 being a registered trademark of Nintendo so it's possible that this game is a remake and all the praise I'm giving it regarding its originality might be wrong. I don't know. That's why people, when releasing a game, write some darn documentation!

Developer: Timothy Peters
Year of release: 2005 (I was surprised too)
Screenshot: tetris2.gif
Download: tetris2.zip (214 KB)
Compiled: No
Final score: 3.6


What to pick?

Some kind of summary of all these games is needed. First, you have to download SB's Bricks because it belongs to QBasic history. If you are looking for a "simple" Tetris game get Textris, QBasic Tetris or QB Matrix. Tetris 2 - The New Blood or Didris if you like original stuff, Ludatris if you like obscure games and definitely Tetris 2 because of the addictive gameplay.


Non-QBasic Tetris Games

I must say I didn't do much research on non-QBasic Tetris games but there are few games that should be mentioned.

Two Tetris games were released again by Spectrum HoloByte few years after the original.

Super Tetris was released in 1991 and it featured much improved VGA graphics and Sound Blaster music and sound effects. Super Tetris offered various game modes(single player, cooperative, ...) and the game objective was changed. In Super Tetris your goal in each level is to clear all the already present blocks on the screen. You also have bombs that help you in this. The gameplay is quite good and, by my humble opinion, better than the classic one. The only flaw I see in the game is the poor graphical design which you can notice right away by looking at the screenshots. Background images are excellent and they feature circus themes. Sound Blaster routines in this game are not very compatible with Windows and even VDMSound doesn't help. I recommend you turn the sound and music off or play the game in DOSBox.

Tetris Classic(screenshot) was released a year after and it featured the classic Tetris gameplay(the levels change as you clear certain amount of rows) and a similar number of options and modes to Super Tetris. The graphics were equally excellent and Sound Blaster music and sound effects were implemented.

Both of these games are mandatory for anyone wanting to create a respectable remake.

As for newer Tetris games I only know of Alizarin Tetris(screenshot) released in 2001 by Kiri Wagstaff and Westley Weimer as freeware. It's an excellent Tetris game featuring a respectable amount of options though I would prefer more flexibility in game modes. They are mostly restricted and based on "clearing" a level in a certain amount of time. You can play solo or against an opponent(computer or human). Alizarin Tetris only features sound effects but there is quite few sound sets available. Still, almost all of them are unsuitable for a Tetris game. Despite some of the flaws in the graphical design you should check this game if you are planning to create a 24 or 32 bit graphics Tetris game because of the wonderfully drawn Tetris pieces.

You can get all these non-QBasic Tetris games and few more at http://www.the-underdogs.org (just type in "tetris" in the site search engine).


Final Words

Wow! This is by far the biggest article I ever wrote and I doubt I'll ever write a bigger one. 31 KB of pure text. It really took more time than I expected it to take. I'm not sure I would start writing it if I knew it would be such a big cookie to swallow. But now when it's done I'm pleased with it. Sorry if the article felt monotonous but it's hard to review 17 Tetris games in one article and sound new all the time.

I hope at least one person will enjoy in some game from the article or maybe even get inspired by it and start working on the first FreeBASIC Tetris game.

Until the next article, stay among the living.


Visit my articles website: http://kentauri.digitablackie.com/articles


Multi Processing Core for QB

Written by Nick Verlinden

What is this Ďcoreí?

If you didnít read the first article, I will now clarify your mind. This core you are reading about is made in QB for running Ďsimultaneousí subs. By simultaneous I donít really mean at the same time. It just runs an essential part of the sub, then switches to the main core checking weather thereís something new the core must do, like activate an application. Due to the limits of QB, you will have to learn how it works in order to programming it. For an example, you cannot program a sub with normal variables. Why you ask? Well itís because the core exits the sub to call the system sub, when it switches back to where it was, the variables are erased. Thatís why I defined some standard global variables for strings and integers. You can always add more types.


How Multi Processing is this thing?

Like I said, It doesnít actually run subs simultaneous, it just switches between subs constantly. Not to be seen by the naked eye, because the core works pretty fast, fast enough to do some amazing stuff while still displaying an updated clock or something else.

A little visual perspective of how a good MPQ works:



Why would someone write such a thing?

I started this project out of curiosity if I could do it and if it where really possible. First I made a clock sub witch ran simultaneous with another (canít remember what), then I said, holy smoke this works! Then I started to rewrite the whole thing with a more adaptive core, one that could run any sub simultaneous, not only the clock. The first really releases where a little buggy, but later on it became more stable. Until recently, I felt that the project was near to finished as a demonstration. Thinking logically and designing plans to make this, I completely wrote a simple system in 3 months. The result is what you see now.


What are the plans?

The plan was to make a library out of it. After thinking about it how this would be possible and how I was going to do that, I came to the conclusion itís not going happen. It isnít possible with my system. Why you ask? Well for starters you have to make an interface to drive the core. The core itself you handles the subs. Something has to drive the core. The interface adds a process to the core, the interface will also tell the core to end a process. In my demoís case the interface was the script.appshelf sub. And the CMD sub was the shell, the users interface to input commands. The interface holds all the information of the present subs. If you want, you could make an interface with a scripting language, that can load files as subs. Off coarse that will slow it down a bit. But you see the possibilities of making an interface is limited as far as QB is. In my demo you could run a maximum of 255 processes due to the shared variable space needed. Try to declare some more, you will probably get an out of memory error.


So what may the project bring?

Since Iím not going to make a library out of it, Instead I will guide you trough the design process using an example. Iím still working on the example, so you might see it in a time not too far from now. Iím planning on making a source merger with maybe an IDE for the core functions. The IDE will most likely not happen, I maybe know something about logical routines, but I donít have clue to make a proper text editor with scroll function marking functions in red like in a good IDE (if anyone is interested to work with me on that, please contact me on my e-mail. But for now Iím working on a good solution but it may take some time. But in the mean time, I will start working on a guide, on how to build a good interface for the core. Maybe even with a simple graphical interface, who knows. Until then, stay tuned! This is far from finished. Who knows we will eventually end up with a DOS GUI with multiprocessing possibilities.


Support the project, have questions, suggestions?

You may always contact me at this e-mail address: Digital-x@zatlap.be. Donít hesitate to correct my problems. I look forward to it.


Download a copy of this article: Multi_Processing_Core_for_QB.doc


TITLE

Organized by Pete


Send your entries to pberg1@gmail.com!


'apostrophe

Written by mennonite

 'apostrophe:
 news from the qbasic forum



[2006 feb 17 issue 2]

  • feature: from the editor
  • freebasic bots in the qbasic chatroom
  • op-ed: no discussion of the "fb thing" in issue 2
  • they're round, you know: kristopher windsor on the forum
  • iki
  • matthost.be
  • feature: anything else? (renamed from "other announcements")

  • = from the editor =

    welcome to the second issue of 'apostrophe - the first issue seems to have went over better than i expected; from now on it will be hosted in qbe one day ahead of when it is posted on my website, so the ' page on my website will technically only be an archive of past issues.

    i'm surprised to have another issue out so soon, in my experience i'm not used to watching the forum and having so much to report. maybe i just wasn't in that frame of mind; although i still believe we're much more active than we used to be, as far as actual projects go. (it's gotta be enough for me to fill a couple pages, reporting on what i see when no one else here submits stuff to qb express.)

    anyway, if you're reading this, you probably came for news, so here it is, in brief:

    1. matthew is offering web space.
    signup takes only half a minute, and it's worth it for the pm feature alone- that i designed and matthew implemented and scripted flawlessly, i might add... the pm feature can be integrated into your websites (on, and/or outside of matthost) so that people can leave you a message from your site (for those of you that wouldn't write your own script for that, anyway. matthew really did a great job.)

    2. 'apostrophe has moved!
    i'm tired of angelfire abusing my visitors, (ads are understandable, ads i'm okay with; what angelfire does is just spam and intrusion...) and i'm not sure what i'm going to do with the site, but i have already moved my best stuff to http://peltkore.net/~mennonite now and if nothing else, i can keep each of the issues of 'apostrophe here (in addition to all my other site content, that i'm in the process of cleaning up and transferring.)

    3. mark said nice things-
    i still don't personally like the way mark is doing things in general, and i'm sure this will come up again, but until then- i liked his letter/article in qbe #18. i feel compelled to add that when you're really effed off about something someone is doing, it's -so annoying- when that person is reasonable, polite, and patient. thank god no one's perfect.

    seriously tho, this is what some of the people on the forum are thinking, but, don't blame the forum. this is hardly something we all agree on (as if there is anything we all agree on other than liking qb.) so there, fair enough. besides, i've kind of given up on things getting better on that front.

    4. kristopher, code or die gold medalist:
    our new forum regular likes circles. a lot. so in the past few weeks he's posted maybe a dozen programs he's done with them, some as intros to trig. i used to love doing stuff like that (although pity, i never got much into the math.)

    5. if i do say so myself...
    in the previous issue, i told you about my qbasic tutorial. to me, it wasn't just a tutorial; it's something i've been wanting to put together for many years... and of all the qb projects i've ever worked on, it's probably the one i'm most pleased with.

    now there is an fb project i'm even more pleased with. some people still think i'm actually against fb somehow, it's not so, but that's not what this issue is about. iki is my new favorite project- (of course, i will still finish the tutorial-) the coolest thing i probably ever worked on. i'll be sure to tell you more about it in this issue.

    and there is a little surprise i have almost nothing to do with at the end of this issue- i dunno how many people are going to skip to the very end, but... if you don't it will be worth the wait. good stuff in this issue...

    so- even though i don't believe it, here is issue 2- i hope someone reads it and gets something out of it, just as i always manage to get something out of qbe. cheers.

    - mennonite

    = freebasic bots in the qbasic chatroom =

    we have bots in our chatroom, now- two of them. llipse wrote the first one, with a mix of java and fb, and eventually worked the rest of the java out and developed it into a pure fb-program.

    he then put it up for matthew to download, and matthew made his from that version.

    at first i found them irritating, they just spammed the room- but they've grown on me and i've taken a liking to the cheery way they do exactly what we expect them to do. amongst other features like keeping track of url's, matthew's bot- "azile", says "hi guest: mennonite!" when i say "hi bot" :)

    "azile" of course is eliza backwards, named after the famous ai-simulation program used by ancient computer geeks to vent their frustrations before the advent of internet (er, bbs?) pornography. my favorite version is still the one from xyzzy productions :) the gif screen was a nice touch, although it shouldn't have made as much of an impression on me as it did. (but it did.)

    llipse's bot, which is simply called "nBot" but also logs in as "n54messenger" sometimes, has the extremely useful feature of notifying us of a new post in the forum and subforums while we chat. i'm very pleased with that one.

    anyway, whatever annoyances they caused in the beginning, the bots have become a welcome addition to the room, and have made themselves useful. considering they are only basic programs, they add just a little more character to the room than you'd expect them to. they also provide an excellent prototype for other people interested in perpetrating such silliness, and if i recall correctly, llipse hopes to update them to include other engines for other kinds of rooms. fun.
    [menn]

    = no discussion of the "fb thing" in issue 2 =

    whoops-

    ok really, nevermind that right now.

    = they're round, you know: kristopher windsor on the forum =

    i think i planned an article here, but the most relevant thing is the screenshots, so i'll just put some up. the images are actually pretty self explanatory, and you can click them to get the source code, if you like. i really did love doing random stuff like this when i was younger- sometimes i wish people would go back on occasion to just some pure fun instead of "producing" all the time. not everything has to be project oriented, especially in basic- the only language that really doesn't necessitate a project to be worth using (i think...) kris has clearly been having a lot of fun.


    oh, and here's a preview of kris' latest project, which he's managed to continue updating despite a constant barrage of suggestions :)

    it is so cool to have someone working on stuff like this.

    welcome to the forum, kris...
    [menn]

    = iki =

    earlier in january, i had the immense pleasure of using a program that i wrote. it's been many years, but i still love this about basic.

    so i sat there, looking at the screen at a message like this one:

    
     iki text utility also is a shell. if you do not like iki then you all can go to 
    
     welcome to iki! 
    
    

    that was nice. 'so,' you ask, 'wtf is an iki?'

    screenshot

    it all started with matthew and newkid, who were adding a wiki to a website and adding a wikicity about quickbasic, respectively.

    this made me think, for the first time in a while, that there should be some kind of "offline wiki" that (at least in this case) combined essentially plain text with hyperlinking and quick, easy editing.

    i kept trying to figure out the best way to make it so it could launch batch files, and it came to me that i could make it so it ran them from a "launch" folder, to keep things clean and simple.

    now iki has such a shell feature, so i can even use it instead of a desktop. in version 0.3, most internet shortcuts will work, although for now they are restricted to the length from one end of the screen to the other. for longer links, and links with unaccepted characters, note that ANY website can be linked to via a batch in the launch folder. usually, however, it is quicker and easier to just make a link like this: [[http://peltkore.net/~mennonite]]

    (and here is as good a place as any to mention that iki is not, nor intended to be a text browser for html. there will be a few- tags that are loosely based on wiki tags, but iki is not meant as an alternative to wiki either. as the website says, wiki is online, iki is offline- they have different purposes.)

    when i realized the latest version was closer to working in xp because of the dos compatibility, i went ahead and added some code using the method earthborn introduced for knowing if a program is running in 9x or 2k/xp. it helped.

    i also created a demo of the latest version that starts to tell a "choose your own adventure" type story about a princess that thinks living in a castle is boring. i hoped this would interest chandler, since he is always writing silly but really interesting stories on the forum - if i know anyone that could write a full-length story with lots of options to choose from, it's him.

    screenshot

    it was llipse that discovered that you could make iki run better in 2k/xp if you compiled it in fbwin instead of fbdos, unfortunately it won't work as a shell or launch url's this way, due to a bug in fbwin. if you SHELL to anything, the getmouse function will stop returning coordinates until you reload iki. we couldn't find a way to code around this, but we didn't even think to play with setmouse, at the time. if anyone knows a way around this bug, please add a note to the suggestions page at the webwiki, here: http://quickbasic.wikicities.com/wiki/Suggestions_For_Iki ...the binary that comes with the iki source is pre-compiled in fbdos, which does not have this problem with SHELL and GETMOUSE.

    (...obviously, "webwiki" would be a bit redundant under most circumstances, but i call it that to make sure no one confuses wiki pages and iki pages. i figure a little clarity shouldn't hurt anything.)

    because iki is gpl, you can make iki into a special custom shell, or even a gui version or something- and you can release your own iki-derived projects online. (one of the features of 0.4 will be cleaner, better organized sourcecode ;) that is, if you want to... i am hoping that iki will fork into many semi-related projects, some of which i may write myself.

    meanwhile, mystikshadows is pushing me to add features :) i'll see if i can- hopefully i can do a bit more with it, but already i find it's incredibly useful- and maybe, maybe more people will too.

    that would be pretty cool.

    you can download iki at this page on my new peltkore site, or leave suggestions and feature requests at the webwiki, if you like.
    [menn]

    = matthost.be =

    by matthew

    Hello again! I am offering free web space at http://matthost.be/.

    Here's how it started:

    I found out that .be domains were free until January 31, 2006... I took advantage of that offer and registered a ton of domain names.... and one was matthost.be. So I set up a web hosting account and delegated matthost.be to it. Then I got to work on the script. The script took me a few days to get to the point where people could register and edit text files (no uploads).

    I have modified the script to allow uploads and file renaming and deletion. Then I placed google adsense ads on the side of every page on the site (except for the pages hosted at matthost.be) to help raise more money for the qbasic.com fundraiser. (the one I mentioned in the previous issue)


    Plans for the future:

    I plan to get a better web hosting plan for it...

    What happens when the .be domain expires? I think I will likely move it to an easy-to-remember second-level domain, or hopefully I'll be able to renew it.


    Features:

    • You get 2MB on instant activation(you can request more, if needed).
    • There are no ads shown on any web site hosted by matthost.be.
    • You upload/rename/delete/edit files using a very simple online control panel that I wrote myself in PERL.
    • Quoting mennonite, "signup takes literally half a minute."
    • It isn't exclusively for BASIC sites.
    • Unmetered bandwidth
    • Weekly backups
    • Web-based upload form / text-editor
    • Web-based email (optional)
    • Integrated PM feature
    • CGI guestbook, plans for a larger cgi collection including "htmlhell"


    Limitations:

    • No support of custom server-side scripts (for security reasons,) but you can paste code in to use the cgi collection.
    • Each file must be 500k or smaller
    • No FTP
    • Bugs I don't know about


    Well, I hope you try it out.

    Bye for now!
    [matt]

    = anything else? =

    yes, i promised a surprise here, this is what it is not: next issue i will be trying out a different style of reporting on qbasic forum news: if everyone hates it i will go back to the way i did this second issue.

    and here is what the surprise is:

    qbasic.
    web browser.
    gpl.

    some of you are going to take this more seriously than others... i would have used freebasic, personally (you think i wanna tackle a BROWSER without flat memory access?) but there are a few reasons i've never attempted a project like this myself:

    1. too much of a perfectionist to get started. this does in a lot of ideas.

    2. i like SHELLing to wget, and that hadn't been promoted as a method yet. it's EASY, and some of us don't understand socket programming... although llipse does and matthew uses llipse's ws library in his freebasic projects.
    3. didn't know anyone else that was interested, actually- besides me.

    so as you already probably noticed, kristopher has blazed this trail for us. he created qbrowser, which we already found a vulnerability in (links are SHELLed, so you have to filter > and | and < out of potential malicious link url's) and fixed (that's not too hard to patch, really, i've done it before) and showed us that regardless of our perfectionism and fancy new toys, SOMEONE is going to write a browser in qbasic.

    bravo, kris!

    of course there is only so much you can do with a project like that, until it is gpl. matthewr2_1 began work on his own browser shortly after qbrowser was released, and he's doing a fine job with it it so far. of course, like most text browsers (lynx comes to mind) it's not as if anyone is aiming for full standards compliance. that doesn't bother me at all, i think if <p> and <a> and the closing tags for both can be handled, it will be good enough for me. the unsupported tags should simply be ignored - that is how html originally worked before all this standards nonsense.

    (to me, html is such a free language that - while i try to code html in a simple way that is unlikely to fail badly in ANY browser ... although things do sometimes go amiss - strictly standardized html would be like an ANSI version of English. i mean, what are you going to DO with that? i think it's just a move towards the propriatary, actually. but as i often do, i hope i'm wrong about this one... still, look at it this way: is lynx standards compliant? no... is it a good browser? yes! i don't much care for the w3c, and i question their real motives. i know that sounds terribly paranoid, but that doesn't make me less suspicious.)

    that thankfully aside again, matthew has released his code under the gpl as he said he would, so now we can have some fun. there is a wiki at this address: http://www.mbrowser.da.ru/wiki so people can talk about mbrowser and make changes in the code.

    personally i am too lazy to bother with this method, and the gpl allows me to work on my own fork of the project. "mnbrowse" will be on my peltkore website, and as i tweak mbrowser this way or that way to do what i want (like make sure it can handle <p> and <a> properly-) matthew can fold that code back into the main version if he wants to.

    the great thing about this project is its potential: it is as unlimited as qbasic is (yeah, i know qbasic has its limitations, but we can do ALL that it can do.) and after all, there are still a number of projects to stretch what's left of qb (no thanks to ntvdm) to its very limits, like qbinux, etc.

    (maybe matthew should talk to seb mcclouth about including a fork of it in qbinux. sadly i am one of the self-deprived-underprivileged few that have never had the sense to take a moment to download and try out qbinux, so i don't know if it has a browser yet or not. it's not like i haven't meant to.)

    anyway, this should have been done a long time ago. well, now it has. bravo, kris - bravo, matthew.

    matthew's official mbrowser website:
    http://www.mbrowser.da.ru


    and there is the news from the qbasic forum. i hope it was good for you, too.

    until the next edition, happy coding :)


    'apostrophe copyright (c) 2005, 2006 mennonite; images and submissions property of their respective owners.
    following issue 2, you can always find 'apostrophe in qb express first, before you find it here.


    Monthly Awards

    Written by Pete

    Every month, QB Express hands out two awards to recognize QB or FB coders and websites that have done exceptional work in the last month. They are not awarded for work done in the past, only for work that has been released since the last issue of the magazine. We will bring you these awards on a monthly basis to help give credit where credit is due.

    Site of the Month

    FreeBasic.info
    http://games.freebasic.info

    Webmaster: cirux


    Several months ago, I remember having several discussions on various message forums around the Qmunity about how there should be a website dedicated to hosting (and reviewing) FreeBasic games. There have been several halfhearted attempts at this (QB45.com, the "new" V Planet), and Adigun A. Polack's FreeBasic GFX Demo Central plays host to many fantastic games -- but not all of them, since its focus is on graphics demos and not on conventional games. I myself had even vowed to start an FB games website, but like many, real life caught up to me and I never got to it.

    So, understandably, when cirix launched his new website, FreeBasic.info with a focus on collecting and hosting all of the finished FB games in one unified site, I was overjoyed. Although the site is brand new and extremely barebones at the moment, I can see where this site is going -- and it's going to be great. At the moment, requesting people to help him find any games that he may have missed. If he keeps the site going and continues to add to it (which it looks like he will do), this site could end up being the hub of the FB game programming scene.

    For his fantastic work in collecting and organizing the FB game development community, cirix is the QB Express webmaster of the month!


    Programmer of the Month

    Redcrab
    http://csgp.suret.net/

    One month ago, I had never even heard of Redcrab. Four weeks later, after four phenomenal game releases under his belt, Redcrab seems like a seasoned veteran in the FB community.

    Redcrab describes his games as "Cute Short Game Projects", and since the end of February, he's unveiled Sleepwalker, Hurry Chef, Bouncing Stuntman and Mushroom Raindrop. These fantastic minigames are reminiscent of the ones kids used to play on Nintendo Game And Watch or Tiger Electronics handhelds. Sure they're simple, but they're also endlessly addictive and extremely fun. Whether you're trying to keep a half dozen stuntmen aloft by moving a single giant trampoline back and forth, or becoming a chef and preventing syrup leaks and hungry mice in a busy kitchen, it's the kind of quirky fun that is just plain irresistable.


    You can find all four of Redcrab's games at Cute Short Game Project, and judging by his productivity in the past month, I wouldn't be the least bit surprised if you see another couple Cute Short Games from RedCrab in the next month.

    For releasing so many awesome games in February and March, Redcrab is the QB Express programmer of the month!


    Have a suggestion for Programmer / Site of the Month? Email me!


    Comics

    By Rattrapmax6

    Rattrapmax6 is back this month with another QBasic Horse Humor comic, plus Tunginobi debuts his brand new "Stickblob" series!





    QBasic Horse Humor





    stickblob comics


    stickblob comics


    stickblob comics




    Tree Tutorial: Parts 5 and 6

    Written by Syn9

    Part 5 'Branch Skinning'

    I was originally going to have including saving of the mesh information into a data file to load directly into an opengl scene, but I decided to instead just include the generation code into the opengl program so that you could generate several random trees on the fly if you wanted. Also, everyone has a different preferred method for saving information, so I'll leave that to the individual.

    The purpose of this section will be to give our tree mesh a skin by applying the mesh coordinates to a texture as well as finding the normals for the polies for lighting.

    Included in this next file is an opengl resource set that I've developed and have been using with many of my test programs. Most of the Routines are not necessary for our program, but here are a few of importance:

    sub cross2 (x1!, y1!, z1!, x2!, y2!, z2!, x3!, y3!, z3!, nx!, ny!, nz!) - This sub takes in 3 3d coordinates and produces a normal vector that is found by taking the cross product of the 3d vectors

    sub deinit () - Shuts down the program

    sub load_texture[Alpha] (texFil$, tex as gluint) - These 2 routines are used for loading our textures into the scene. The 2nd parameter tex as gluint is a gluint that serves as a handle to an openGL texture.

    sub selectRes () - The select resolution screen that pops up in the beginning. Ths doesn't really require glfw, but that just happened to be how I wrote it before finding out this can be done with SDL alone.

    Here is a link to the basecode for our openGL tree generator: glbase.zip

    The Skin

    This is essentially very easy. We take the original display code that figured out all the polygon points and before drawing them we perform a cross product to get the lighting normal, and we assign texture coordinates to each point.

    Here is a blown up view of what we're going to do:



    For each of the quads that make up the branch we'll map each point to a specific u/v coordinate on the texture. As you can see from the image we start with the upper left corner and proceed to the upper right in a counter clock wise fashion. For the Triangles that make up the end points we use u/v coordinates (0,0)->(.5,1)->(1,0) this way the point is at the bottom center. Since it is a triangle, we only need three u/v coordinates.

    At this same time we perform our cross product by passing the first 3 coordinates for the quad and all 3 coordinates for the triangle. We only need the first 3 coordinates for the quad since it is a flat plane... relatively, heh, there is a bit of twisting, but for the most part its very close. If you wanted to be really picky you could perform a cross product for each individual vertex and average them together to make a nice round branch, but that is beyond the scope >)

    The First Code Changes: Porting to OpenGL

    The first part of the tree code for generation has been lumped into the function generateTree (). There are really no alterations to is. We just need to dim shared nBranches at the beginning for our drawing routine. Instead of using pnti(4,1) we'll be using pntf(4,2) to store our 3d coordinates for drawing. This is due to the fact that we're no longer using getXY since opengl is doing all the projection work for us. Also, we need to include leafscale! so that our drawing routine can keep track of how large to scale the leaf planes.

    I've also added the pixel grid to the bottom of the tree as it was in the original generator.

    Here is our new drawing routine. This takes our old line drawing and converts it to use for opengl. It is essentially exactly the same. It just skips getXY and uses glVertex3f to define our line points.

    sub drawTree ()
        glBegin GL_LINES
        glColor3f .5, .25, 0
        for branch = 0 to nBranches - 1
            nNodes = branches(branch).nNodes
            for node = 0 to nNodes - 1
                for p = 0 to MAX_SEGS - 1
                    n = p + 1
                    if n = MAX_SEGS then n = 0
                    pntf(0,0) = branches(branch).nodes(node).plane(n).x
                    pntf(0,1) = branches(branch).nodes(node).plane(n).y
                    pntf(0,2) = branches(branch).nodes(node).plane(n).z
                    n = p
                    pntf(3,0) = branches(branch).nodes(node).plane(n).x
                    pntf(3,1) = branches(branch).nodes(node).plane(n).y
                    pntf(3,2) = branches(branch).nodes(node).plane(n).z
                    
                    nn = node - 1
                    if nn > -1 then
                        n = p + 1
                        if n = MAX_SEGS then n = 0
                        pntf(1,0) = branches(branch).nodes(nn).plane(n).x
                        pntf(1,1) = branches(branch).nodes(nn).plane(n).y
                        pntf(1,2) = branches(branch).nodes(nn).plane(n).z
                        n = p
                        pntf(2,0) = branches(branch).nodes(nn).plane(n).x
                        pntf(2,1) = branches(branch).nodes(nn).plane(n).y
                        pntf(2,2) = branches(branch).nodes(nn).plane(n).z
                        refs = 4
                    else
                        pntf(1,0) = branches(branch).root.x
                        pntf(1,1) = branches(branch).root.y
                        pntf(1,2) = branches(branch).root.z
                        refs = 3
                    end if
                    
                    if refs = 3 then
                        'triangle
                        glVertex3f pntf(0,0), pntf(0,1), pntf(0,2)
                        glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
                        
                        glVertex3f pntf(0,0), pntf(0,1), pntf(0,2)
                        glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
                        
                        glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
                        glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
                    else
                        'quad
                        glVertex3f pntf(0,0), pntf(0,1), pntf(0,2)
                        glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
                        
                        glVertex3f pntf(2,0), pntf(2,1), pntf(2,2)
                        glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
                        
                        glVertex3f pntf(2,0), pntf(2,1), pntf(2,2)
                        glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
                        
                        glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
                        glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)                   
                    end if
                next
            next
        next
        glEnd
        
    end sub
    



    The leaf planes are left out to aid in describing the skinning process.

    Loading the Textures

    Our tree will use 2 textures. One for the leaf plane and one for the bark. They will require 2 slightly different loading routines due to the fact that the leaf plane has an alpha channel so you can see through the leaves.

    This requires the following code around the init call:

    DIM SHARED barkTex as gluint, leafTex as gluint
    
    init  'not new
    
    load_texture "bark.png", barkTex
    load_textureAlpha "leaves.png", leafTex
    

    Bark:
    Leaves:


    The actual loading of the textures is beyond the scope, but if you would like to learn more please visit: NeHe Lesson 06: Texture Mapping

    Alterations to the drawing

    Instead of using lines, we'll now use Quads and Triangles. And each time we'll use glTexCoord2f to map a u/v coordinate to each point. Remember when drawing polygons to have them follow the correct point order (CCW or CW) depending on your implementation so that they are not backfacing and possibly culled.

    Part of our addition to this routine will be having it draw in 2 passes. Once for the quads and once for the triangles. This is so that we don't have to call glBegin for each polygon, only twice. The Bold code below is new.

    sub drawTree ()
        
        glBindTexture GL_TEXTURE_2D, barkTex
        
        glColor3f 1, 1, 1
        for pass = 0 to 1
            if pass = 0 then glBegin GL_TRIANGLES
            if pass = 1 then glBegin GL_QUADS
                
            for branch = 0 to nBranches - 1
                nNodes = branches(branch).nNodes
                for node = 0 to nNodes - 1
                    for p = 0 to MAX_SEGS - 1
                        n = p + 1
                        if n = MAX_SEGS then n = 0
                        pntf(0,0) = branches(branch).nodes(node).plane(n).x
                        pntf(0,1) = branches(branch).nodes(node).plane(n).y
                        pntf(0,2) = branches(branch).nodes(node).plane(n).z
                        n = p
                        pntf(3,0) = branches(branch).nodes(node).plane(n).x
                        pntf(3,1) = branches(branch).nodes(node).plane(n).y
                        pntf(3,2) = branches(branch).nodes(node).plane(n).z
                        
                        nn = node - 1
                        if nn > -1 then
                            n = p + 1 
                            if n = MAX_SEGS then n = 0
                            pntf(1,0) = branches(branch).nodes(nn).plane(n).x
                            pntf(1,1) = branches(branch).nodes(nn).plane(n).y
                            pntf(1,2) = branches(branch).nodes(nn).plane(n).z
                            n = p
                            pntf(2,0) = branches(branch).nodes(nn).plane(n).x
                            pntf(2,1) = branches(branch).nodes(nn).plane(n).y
                            pntf(2,2) = branches(branch).nodes(nn).plane(n).z
                            refs = 4
                        else
                            pntf(1,0) = branches(branch).root.x
                            pntf(1,1) = branches(branch).root.y
                            pntf(1,2) = branches(branch).root.z
                            refs = 3
                        end if
                        
                        if refs = 3 and pass = 0 then
                            'triangle
                            glTexCoord2f 0, 0
                            glVertex3f pntf(0,0), pntf(0,1), pntf(0,2)
                            glTexCoord2f .5, 1
                            glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
                            glTexCoord2f 1, 0
                            glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
                        elseif refs = 4 and pass = 1 then
                            'quad
                            glTexCoord2f 0, 0
                            glVertex3f pntf(0,0), pntf(0, 1), pntf(0,2)
                            glTexCoord2f 0, 1
                            glVertex3f pntf(1,0), pntf(1, 1), pntf(1,2)
                            glTexCoord2f 1, 1
                            glVertex3f pntf(2,0), pntf(2, 1), pntf(2,2)
                            glTexCoord2f 1, 0
                            glVertex3f pntf(3,0), pntf(3, 1), pntf(3,2)
                            
                        end if
                    next
                next
            next
            
            glEnd
        next
        
    end sub
    

    First we Bind our texture to GL_TEXTURE_2D so that it shows on our polygons. Next we have our 2 pass system. At the end you see how we changed the drawing commands to include the texture coordinates.

    Lighting

    To have this tree lit, we need to generate a surface normal for each polygon. This can be performed by simply calling cross2 (...) before our drawing takes place. Then including glNormal3f nx!, ny!, nz! before the drawing of each poly

    Add this code before if refs = 3 and pass = 0 then

    cross2 pntf(0, 0), pntf(0, 1), pntf(0, 2), pntf(1, 0), pntf(1, 1), pntf(1, 2), pntf(3, 0), pntf(3, 1), pntf(3, 2), nx!, ny!, nz!
    glNormal3f nx!, ny!, nz! 
    

    Before:


    After:

    That's it! Our tree is skinned! Lastly we'll move on to finishing up the openGL scene by adding the transparent leaf planes and making a small random forest!

    Part 6 'Final OpenGL Scene'

    First we will be including in the leaf drawing code from the first generator. Upon writing this part of the tutorial i realized that the X coordinates for the planes in pnt() are backwards. This was causing them to be culled.

    This code will be added directly to the end of drawTree ()

    The code additions are in Bold

        'draw the leaf planes:
        pnt(0,0) = .5
        pnt(0,1) = -.5
        pnt(1,0) = .5
        pnt(1,1) = .5
        pnt(2,0) = -.5
        pnt(2,1) = .5
        pnt(3,0) = -.5
        pnt(3,1) = -.5
        
        glDisable GL_LIGHTING
        glBindTexture GL_TEXTURE_2D, leafTex
        glColor4f 1, 1, 1, .8
        for leaf = 0 to MAX_LEAVES
            glPushMatrix
            glTranslatef leaves(leaf).x, leaves(leaf).y, leaves(leaf).z
            glRotatef camrz, 0, 0, -1
            glRotatef camry, 0, 1, 0
            glRotatef camrx, -1, 0, 0
        
            
            for p = 0 to 3
                pntf(p, 0) = pnt(p,0) * leaves(leaf).size * leafscale!
                pntf(p, 1) = pnt(p,1) * leaves(leaf).size * leafscale!
                pntf(p, 2) = 0
            next
            
            glBegin GL_QUADS
                glTexCoord2f 0, 0
                glVertex3f pntf(0,0), pntf(0, 1), pntf(0,2)
                glTexCoord2f 0, 1
                glVertex3f pntf(1,0), pntf(1, 1), pntf(1,2)
                glTexCoord2f 1, 1
                glVertex3f pntf(2,0), pntf(2, 1), pntf(2,2)
                glTexCoord2f 1, 0
                glVertex3f pntf(3,0), pntf(3, 1), pntf(3,2)
            glEnd
            glPopMatrix
            
        next
        glEnable GL_LIGHTING
    

    After the fixes to pnt() we disable the lighting so that our leaf planes are constantly lit. Next we set the color of the plane to bright white with a slight transparency so that the full color of the leaf texture shows up, but they are slightly see though.

    	glPushMatrix
            glTranslatef leaves(leaf).x, leaves(leaf).y, leaves(leaf).z
            glRotatef camrz, 0, 0, -1
            glRotatef camry, 0, 1, 0
            glRotatef camrx, -1, 0, 0
    

    This code essentially is the same thing we did with our matricies in the first generator. We translate our drawing point to the location of the center of the tree plane. Then Rotate in the opposite direction and order of our camera rotations at the begining so that our plane is facing the camera.

            for p = 0 to 3
                pntf(p, 0) = pnt(p,0) * leaves(leaf).size * leafscale!
                pntf(p, 1) = pnt(p,1) * leaves(leaf).size * leafscale!
                pntf(p, 2) = 0
            next
    

    This block of code has changed considerably to use pntf() and to take out the matrix multiplication since it is no longer necessary. We then use these pntf() coordinates to draw our texture.

    Some very necessary parts of code that make this effect with the leaves possible can be found toward the bottom of init ()

    glBlendFunc GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA glAlphaFunc GL_GREATER, .5 glEnable GL_BLEND glEnable GL_ALPHA_TEST

    glBlendFunc GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA - Sets up our blending function to blend any alpha color that has an alpha value with the color in the background.

    glAlphaFunc GL_GREATER, .5 - This routine will cause any alpha value in our leaf texture that is > .5 to be drawn. This way the engine allows you to draw through parts of the texture that have an alpha value of 0. This effect is called BillBoarding and is very important. To see what happens when you don't do this comment out glEnable GL_ALPHA_TEST

    The last 2 lines simply turn on Alpha Blending and the Alpha Test routine

    Some small changes that I've made to the tree generator settings to get this tree are:

        MAX_LEAVES = 10 'give the impression of a much fuller tree
        sag! = 20       'make the branches sag more
        leafscale! = 4  'make the leaf planes larger
    

    Here is the current state of the code: openGLTree.bas This requires the above download for the rest of the base files. Note: Hit ENTER to generate a new tree just like in the original.

    The Forest

    For this effect we'll create about 10 base trees and randomly place then around the environment. Trees are notorious for consuming resources so we'll use a system called Display Lists to 'precompile' our tree models into memory on the video card so that they draw tremendously faster.

    For a more in-depth explanation of display lists check out: NeHe Lesson 12: Display Lists

    Each display list will have a handle like the textures. Since we have to change our leaf plane coordinates and angles every fame to reflect the current camera properties we cannot precompile them, so we'll need to keep track of each leaf layout for each tree display list. You'll see this change in the dimensioning of leaves().

    Also, There is now a drawLeaves () sub so that they can be drawn independently of the tree branches.

    Additions in Bold

    CONST MAX_TREES = 50, MAX_TREE_DL = 10
    DIM SHARED branches(MAX_BRANCHES) AS branchType, leaves(MAX_TREE_DL, MAX_LEAVES) AS leafType, nBranches
    DIM SHARED rotSpd!, scaleSpd!, leafscale!
    
    DIM SHARED barkTex as gluint, leafTex as gluint
    DIM SHARED barkTex2 as gluint
    
    type treeDataType
        x   as single        'location of the tree
        z   as single
        xAngle  as single    'angle of the tree
        yAngle  as single
        displayList as gluint  'the display list the tree uses
    end type
    
    DIM SHARED treeDL(MAX_TREE_DL - 1) as gluint, treeData(MAX_TREES - 1) as treeDataType
    

    Now instead of calling generateTree, we call generateForest:

    sub generateForest ()
        
        for tree = 0 to MAX_TREE_DL - 1
            generateTree tree
            treeDL(tree) = glGenLists(1)
            glNewList treeDL(tree), GL_COMPILE
                drawTree
            glEndList
        next
    

    Loop through each tree display list, generating the tree, creating the display list, then compiling it.

        for tree = 0 to MAX_TREES - 1
            maxdist! = 60
            maxXAngle! = 20
            angle! = rnd * 360
            treeData(tree).x = (rnd * maxdist! - (maxdist! / 2)) * cos(rad * angle!)
            treeData(tree).z = (rnd * maxdist! - (maxdist! / 2)) * sin(rad * angle!)
            treeData(tree).displayList = int(rnd * MAX_TREE_DL)
            treeData(tree).xAngle = rnd * maxXAngle! - (maxXAngle! / 2)
            treeData(tree).yAngle = rnd * 360
        next
    
    end sub
    

    Loop through each forest tree positioning it randomly in a circular area, assigning a random display list, and changing the angle of the tree.

    The last change to the code is in drawview () where we would normally call drawTree to draw a single tree. Instead we loop through each forest tree, change the drawing location to the tree location, rotate the tree, then call the display list. Afterwards we draw the leaf planes specific to that display list and return our matrix back to normal:

        for tree = 0 to MAX_TREES - 1
            glPushMatrix
                treeList = treeData(tree).displayList
                glTranslatef treeData(tree).x, 0, treeData(tree).z
                glRotatef treeData(tree).xAngle, 1, 0, 0
                glRotatef treeData(tree).yAngle, 0, 1, 0
                glCallList treeDL(treeList)
                drawLeaves tree
            glPopMatrix
        next
    

    You might have noticed that we'll need to add these new rotations to the counter rotations of the leaf planes to make them continue to face forward. Here are those changes to drawLeaves (tree):

    sub drawLeaves (tree)
    	treeList = treeData(tree).displayList
    
    	[...]
    
    	glPushMatrix
            glTranslatef leaves(treeList, leaf).x, leaves(treeList, leaf).y, leaves(treeList, leaf).z
            glRotatef treeData(tree).yAngle, 0, -1, 0
            glRotatef treeData(tree).xAngle, -1, 0, 0
                
            glRotatef camrz, 0, 0, -1
            glRotatef camry, 0, 1, 0
            glRotatef camrx, -1, 0, 0
    
    	for p = 0 to 3
                pntf(p, 0) = pnt(p,0) * leaves(treeList, leaf).size * leafscale!
                pntf(p, 1) = pnt(p,1) * leaves(treeList, leaf).size * leafscale!
                pntf(p, 2) = 0
            next
            
    

    Notice the changes to the leaves() array to take into consideration the display list dependant leaf set.

    Some small changes were necessary to make this forest view. rotSpd! = .5 * fpsr and scaleSpd! = .5 * fpsr were added to main (). These speed up the rotation and scaling speed as well as make them frame rate dependant so the scene rotates at a constant speed at different frame rates. Also, zback = 100 instead of 50 to facilitate seeing all the trees.

    Front View:


    Top View:


    Closing Remarks

    That's it for the Tree Tutorial. I hope my explanations were clear and that you've found this technique helpful. If you have any questions, drop me a line at syn9_at_dev9.net. Please keep in mind that there are undoubtedly more ways to make trees and optimize these routines. So I implore you to seek out other methods and find the one that fits your code best.


    Here are all final project files: treeTutorial1-6_Files.zip


    A FreeBasic Memory Leak Detector

    Written by Daniel Verkamp (DrV)

    INTRODUCTION

    FreeBasic, as you might already know, includes a C-like memory management system, which includes full-fledged pointers, as well as routines to allocate blocks of memory dynamically at runtime. While this leads to great flexibility for the programmer, it also creates the possibility to "shoot yourself in the foot", a la Stroustrup. If you choose to use pointers and dynamic memory allocation, FreeBasic will not protect you from yourself. Using pointers, you can easily access memory that you have not allocated for your use; the compiler cannot check for out-of-bounds accesses because it cannot know the limits of a particular pointer. However, you can allocate a block of memory and forget to deallocate it, or you can try to free the same block of memory twice; this sort of problem is more easily detected. In fact, this article describes the use and implementation of just this sort of tool - a memory leak detector for FreeBasic.


    USAGE

    The FreeBasic Memory Leak Detector (FBMLD) is very simple to use; just include the header fbmld.bi in each source file of your program:

    #include "fbmld.bi"
    

    Then use FreeBasic's built-in memory management routines Allocate, CAllocate, Reallocate, and Deallocate as you would normally.

    When you run your program, FBMLD will print out diagnostic messages starting with (FBMLD) to inform you of memory management errors in your code.

    As an example, here is a (fabricated) example program with some memory management problems:

    Option Explicit
    
    #include "fbmld.bi"
    
    Dim George As Any Ptr, John As Any Ptr, Paul As Any Ptr, Ringo As Any Ptr
    
    Print "FreeBasic Memory Leak Detector test"
    
    George = CAllocate(123)
    
    John = Allocate(456)
    
    George = ReAllocate(George, 789)
    
    Deallocate(John)
    
    Paul = Reallocate(0, 5309)
    
    Deallocate(George)
    Deallocate(George)
    
    ' Note that this is a "double free" - we are 
    ' attempting to deallocate George, which has
    ' already been deallocated.
    '
    ' This will be caught and reported immediately.
    
    Ringo = Allocate(867)
    
    ' We have not deallocated Paul or Ringo, so
    ' they will be reported as allocated but 
    ' not deallocated.
    
    Print "fbmld test - End"

    When the second Deallocate(George) is executed, FBMLD will print:

    (FBMLD) test.bas(20): Deallocate on unallocated memory (&H322630)

    George has been deallocated already, so trying to execute it again is not acceptable. In this case, the problem is simple to solve - just remove the second Deallocate(George) line. Some other cases might require more thought, such as when a particular block of memory could be deallocated in more than one place in the code.

    When the program ends, two errors relating to memory that has been allocated but not deallocated will be printed:

    (FBMLD) test.bas(28): 867 bytes allocated here was not deallocated (&H3265F8)
    (FBMLD) test.bas(17): 5309 bytes allocated here was not deallocated (&H323FE0)

    These bugs can be fixed by adding Deallocate(Paul) and Deallocate(Ringo) to the end of the program.

    Note that the messages will not necessarily contain the exact same memory addresses as given here; the address is printed as a debugging aid only and should not be assumed to be constant.


    IMPLEMENTATION

    FBMLD is written in FreeBasic and contained in a single header file, requiring no extra libraries or external resources beyond the standard C library, which is always linked (FreeBasic's runtime library depends on it). The standard memory management functions are removed from the namespace with the Option NoKeyword statement. Then these keywords were redefined with #define to call replacement functions, using the predefined __FILE__ and __LINE__ preprocessor defines to track where each function was called. These replacement functions use the standard C library functions malloc(), calloc(), realloc(), and free(). Each function is declared Private to allow the use of FBMLD in projects with multiple source code files.

    A global list is maintained of all allocated memory blocks, and when any memory-management function is called, the list is manipulated accordingly. If allocated memory is freed correctly, it is removed from the list. If any elements remain in the list at the end of the program, they are reported as being allocated but not deallocated.

    For thread safety, the built-in mutex routines are used. If you do not wish your program to be linked to the multithreaded library, just define FBMLD_NO_MULTITHREADING before including the header:

    #define FBMLD_NO_MULTITHREADING
    #include "fbmld.bi"

    Normally, FBMLD will print all of its messages with the standard Print statement; if you would like it to output the messages somewhere else (a file, for example), you can modify fbmld_print accordingly. Just keep in mind that the report at program termination might happen after the FreeBasic runtime's termination code has already been executed (file handles closed and so forth).


    CONCLUSION

    FBMLD was written by Daniel Verkamp. You can contact him at i_am_drv@users.sourceforge.net. You can download the fbmld.bi header here.


    Download a copy of this tutorial: FB_Memory_Leak_Detector_DrV.html


    Text Parser Tutorial, Part Two:
    How to use the Text Whaty-What From Issue #16

    Written by Imortis Inglorian

    Guess what, everybody! I'm back! Aren't you happy? Well? Don't be so quiet! Alright fine, so your not over joyed to see me, but what the heck... I'll finish this article anyway.

    Now if you remember correctly, my last article was written on how to program a simple text parser in either QB or FB. This article is going to build on that, so if you haven't read the first on, I suggest to head on over to issue #16 and check it out.


    Let's get down to bid-ness!

    Bid-ness is programming, and Bid-ness is good, am I right? Anyway, I'll start by giving the code that was show in the last tutorial.

    Declare Function Parse(ToParse as String)
    
    Dim Shared Parsed(100) as String
    
    Function Parse(ToParse as String)
    	Dim CurrentPosition as Integer
    	Dim CurrentCharacter as String
    	Dim WordCount as Integer
    	Dim WordSize as Integer
    	For CurrentPosition = 1 to Len(ToParse)
    		CurrentCharacter = MID$(ToParse,CurrentPosition,1)
    		Select Case CurrentCharacter
    			Case " "
    				If Not WordSize = 0 Then
    					Parsed(WordCount + 1) = MID$(ToParse, CurrentPosition - WordSize, WordSize)
    				End If
    				WordCount = WordCount + 1    
    				WordSize = 0
    			Case CHR$(34), "'", ","
    				If Not WordSize = 0 Then
    					Parsed(WordCount + 1) = MID$(ToParse, CurrentPosition - WordSize, WordSize)
    				End If
    				WordSize = 1
    				Parsed(WordCount + 1) = MID$(ToParse, CurrentPosition - WordSize + 1, WordSize)
    				WordCount = WordCount + 1    
    				WordSize = 0
    			Case Else
    				WordSize = WordSize + 1
    		End Select
    		If CurrentPosition = Len(ToParse) Then
    			WordSize = WordSize - 1
    			Parsed(WordCount + 1) = MID$(ToParse, CurrentPosition - WordSize, WordSize + 1)
    		End If
    	Next CurrentPosition
    	Parsed(0) = MKI$(WordCount)
    End Function

    Ooh... It's pretty... What is it? It is a text parser. It's whole purpose is to take some text and break it up into pieces to make it eiser to work with.


    "What kind of text, oh writer, might be made easier to work with?"

    Well, anything really... Not specific enough? Fine how about this? Ever heard of a scripting language? If not, there are some great tutorials on the net that talk about them. A scripting language is a great way to make your game engine flexable and reusable. If you wrote a game and made it so that all events in the game were hard coded into the engine, it would make that code extremely difficult to use in a future project. By using a scripting language, you make it far more easy to use again. Observe...

    #Include "parser.bas"
    Dim CommandString as String
    Dim I as Integer
    
    'open a text file containing your commands.
    Open "commandfile.txt" For Input as #1
    Do
    
        'Reads you commands from the file one line at a time
        Input #1, CommandString
        Parse(CommandString)
    
        'This Select is for all the commands you want to make plus one
        'to tell you you misspelled one of your commands some where.
        Select Case Lcase(Parsed(1))
            Case "print"
                For I = 2 to CVI(Parsed(0))+1
                    print Parsed(I) + " ";
                next I
                print
            Case else
    		'This lets you know that you messed up somewhere
                Print Parsed(I) + " is an Invalid command!"
        End Select
    Loop Until EOF(1)
    close #1
    sleep

    This is an extremely simple example, but it can be tweeked to do what ever you what. This works best for files that don't have comma seperated lists. If that's what you're using it's better to just read them into seperate varibles like normal. But when working with a plain text file, this works wonders. That's not all it can do, either.


    It slices! It dices!

    Okay, so maybe it's no good for julian fries, but it does have a number of uses. I originally wrote this parsing routine for use with an AI Chatter bot. For those of you who don't know, a chatter bot is just a program that tries to talk to the user. I would love to beable to post working code for this example, but my own project is unfinished. The sky is pretty much the limit when it come to this routine.

    You could use it for reading some other programs file type. (assuming you know the file structure)
    You could use it for a scripting engine, as seen above.
    You could use it to disect user input as show in the first tutorial.
    You could use it to take over the work... Okay maybe not that one.

    The main thing to remember is that the parser routine is not set in stone. You can change it to fit any needs you might have by adding or modifying the Case statements. You can change the symbols and characters that the parser is looking for to extend it to do what ever you wish.


    Conclusion

    I hope this has been informative. I can't think of anything else to put with it... So... Get outta here... Scat... Don't make me get the hose...


    Download a copy of this tutorial: ParseTuTwo.zip


    A Closer Look at Managing Complexity:
    Data Representation and Manipulation

    Written by Rick Clark

    A Closer Look at Managing Complexity Data Representation and Manipulation
    "On two occasions I have been asked, 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?' I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question."

    - Charles Babbage

    I love the above quote, because it aptly describes the major source of errors in a program, namely data errors. In the previous article on complexity, I discussed the problems associated with large programs. In this article, I will discuss the negative effects of complexity as it relates to data within a program.

    First though, let me give you real world example where data complexity was a big problem in a commercial application. A certain company had a program that had been in development for several months and the program still did not run correctly, even after three different programmers had worked on the program. The company called me to take a look at the program and see if I could get it up and running.

    One glance at the source code and I could tell where the program was failing. Nearly every variable in the program was a global variable, and the variables were being updated willy-nilly throughout the code, creating data errors and program failure. It took me a month to clean up the global variables and organize the data structures and code in such a way that it could be debugged successfully. It only took a few days to fix the problem areas and get the program running after the code cleanup.

    The other programmers had tried to debug the program without data and code organization, but couldnít because of the complexity introduced by the global variables. By reducing the number of global variables, the complexity was also reduced, allowing me to debug the problem routines and apply the fixes. Overall, the program wasnít overly complex; it had been made that way through mismanaged data.


    Data Complexity

    The problems of data complexity do not arise from the type and amount of data we are working with in our program. The number 1 is no more complex than the number 100 and the string ďMary had a little lambĒ is no more complex than ďJack and Jill ran up the hill.Ē A database containing a million rows of data is no more complex than a database containing one row of data. The problems of data complexity arise when we misrepresent our data in a program and when we mishandle our manipulation of that data.

    To illustrate the first problem, data representation, suppose the user must enter a number between 1 and 9. Letís say that we use an integer for the input. What happens if the user enters an A? The program will fail. Is it the fault of the user? No, it is our fault because we misrepresented the range of possible values that could be input into our program.

    To illustrate the second problem, mishandling data, we use a string instead of an integer to collect the above input, and then convert that string to a number. The user again enters an A, and when converted to a number results in 0. When we try to use this number, the program will fail, because 0 is outside our range of acceptable numbers. Again, the problem is ours because we failed to check for the 0 case when we tried to use the data.

    These examples are trivial, but the problems are the same in any size program. Both of these errors occur when we donít fully understand a programís data space.


    Data and Input Spaces

    You can define data as the ďspaceĒ in which a program operates. Any data inside the boundaries of the space is valid data and any data outside the boundaries is invalid data. The overall data space of a program is defined by the problem that the program is designed to solve. Within this overall data space, are subset data spaces such as those in our input example. We can think of the overall data space of the program as a large box, and inside the box are smaller boxes and boxes within boxes that define the various types of data that we must handle in a program. Each data space has a set of boundaries, and we must remain within those boundaries if the program is to run successfully.

    Letís keep the box analogy in our mind for the moment. A box is used to store something. We put stuff into the box and we take stuff out of the box. If we put a bowling ball in a box containing our nice set of beer steins, the beer steins will be a nice pile of glass at the end of the day. To correctly pack our box, we need to know both what the box is supposed to hold, and what are the possible items that could find their way into the box. We have to make sure we label the boxes correctly, and then watch the packers pack the box to make sure no bowling balls get packed with our beer steins.

    For data within a program, we create a variable that is appropriate to the data we want to manipulate. We label the box an integer. However, we donít want the full range of an integer in the variable, we only want the numbers 1 through 9, which defines the data space of the variable. A box simply wastes space unless it is holding something, so we must put something into our variable to make is useful. Here is where we may have a problem. What are the possible values that could find their way into the variable box? What is the input space of the variable? If we use the integer variable for keyboard input are we going to get a beer stein or bowling ball? The numbers we want 1 through 9 or A through I?

    Like the data space, the input space has a set of defining boundaries. For keyboard input, the number and type of keys on the keyboard define the input space. For a text file, it is all the possible values that a text file can hold, both printable and non-printable characters, with the added complexity of formatting rules that need to be taken into account. For a database, it is the layout of a table or the results of a SQL query. Often times we can strictly define the input space, many times we canít. A text file may be formatted in a particular way that we expect to see, but we canít be sure that the user hasnít edited the file and changed the format. How we represent the data in our program depends on the input space to that variable.

    We could convert the keyboard input to its equivalent ascii code and store that number in our integer variable, but now we have data that is outside the data space we have defined for our variable. The numbers we are looking for, 1 through 9, lie within the input space of the keyboard, it is just a matter of mapping the input space to the data space of our variable, which means adding additional code to our program. The amount of code we need to facilitate this mapping process determines the order of magnitude of the data.


    First Order Data

    First order data has a one to one relationship between data space and input space. We can use the minimum amount of code and we know that the input data is within the data space of the variable. Most data generated within a program, is first order data.


    Second Order Data

    Second order data requires some conversion since the input space is a sub- or super-set of our data space. However, since the two data sets are related, the conversion is minor, consisting of a single state transition from one form to another. By state transition, I mean that a string is converted into a number, or a number into a string, an integer is cast to a real or a real is cast to an integer. Second order data is common when a program must collect keyboard input, parse a text file and in some database systems where you pass a buffer to the record manager and it returns the data in the buffer.

    Second order data is more complex, since the possibility exists that the input may be outside of our data space. Our keyboard input example above is a second order data problem. We can use a string to capture the input, but we must test the value to be sure it falls within our range of 1 to 9.

    Second order data is more complex because we must use intermediate values to map the input space to the data space. Suppose in our keyboard example we choose to use a string to collect the input. This string must be converted to a number, and that number must be checked to make sure it lies within our data space of 1 to 9. Only when the input data falls within our defined data space do we store the number into our integer variable.


    Third Order Data

    Third order data is the most troublesome, since there is little to no commonalty between the input space and the data space, and a large effort must be employed to map the two spaces. Converting a jpeg to a bitmap is a third order data problem.

    Third order data also has the potential to incur data loss. A 24-bit image may look quite good, but when converted to a 16-bit image, the result isnít always desirable, due to the loss of information incurred in the conversion process. Third order data must be analyzed carefully to see if the result is worth the increased complexity of our program and the potential loss of data.


    Defensive Programming

    Since second and third order data requires mapping the input space to the data space, we must employ defensive programming techniques to ensure that our program doesnít fail on unexpected input. An example of this is a Null value in a database. In many programming languages, trying to assign a Null to a control or string will result in an error. Our input routine must take into account a possible Null value and handle the situation accordingly, either by checking for a Null value, or converting the field to the appropriate value using the languageís conversion functions.

    In many cases we canít predict what the unexpected input may be, and in these cases, we must utilize the languageís error trapping constructs to prevent program failure. By trapping unexpected data from the input space, the program can choose it will continue, and do so in a controlled and graceful manner. Program crashes are not only ugly and indicate sloppy or lazy programming, but can leave the computer in an unstable state that could affect other processes. If the language doesnít have any error trapping available, then it may be time to evaluate a language that does if the program needs to execute without failure.


    Pitfalls of First Order Data

    It may seem that first order data is problem free, but in fact a whole host of problems can arise from misrepresenting first order data within a program. The two main areas of misrepresentation are scope and data type. Overuse of global variables within a program is a scope problem and cause unwanted side effects and errors. Data type problems usually involve using the wrong data type or an unnecessarily complicated data type. Using a byte when an integer may be needed, or using a pointer value when a simple variable would work equally well are examples of misrepresenting the data type.

    Scope refers to the accessibility of a variable within a program. Most structured programming languages have at least two levels of scope, global and local. A variable that has global scope can be accessed anywhere within a program. A variable that has local scope is only accessible within the block where it has been defined. The general rule of thumb is to use the lowest level of scope possible for a variable to minimize errors. Unfortunately, there is no single definition of scope across all programming languages. Each language implements scope according to the needs of the language, and there can exist various levels between global and local scope. For example, an object may contain Public variables that can be accessed outside the object, and Private variables that are hidden within the object and inaccessible.

    Learning the scope rules of the language should go hand-in-hand with learning the languageís lexicon in order to properly represent data within the program. Scope errors are some of the most subtle and hard-to-find errors that can occur, but by using scope properly, these errors can be kept to a minimum, and when they occur, can be localized to a small block of code.

    Data type errors generally arise from simply not understanding the data types available in a programming language. Does an integer represent a number from -32,768 to 32,767, or -2,147,483,648 to 2,147,483,647? Knowing the available types, and the ranges of those types, will eliminate most data type errors.

    However, there is a data type error problem that I like to refer to as the gee-whiz data type. This problem arises from using a complicated data type when a simple data type can be used to get the same result. For example, I have seen complicated, compound pointer types that were used simply because the programmer discovered how to use pointers and thought that the gee-whiz factor of using pointers elevated his or her stature as a programmer. In reality, all it did was to complicate the program, make it harder to debug and opened a door to potentially crippling errors. It is an axiom of programming that simple is better and using the simplest data type possible reduces the complexity of a program and reduces the chance of complexity related errors.


    Good Data, Better Program

    Programs are created to solve a particular problem, and that problem is defined by its data space. A good program solves the problem at hand by correctly describing and manipulating the data space of the problem. Good data doesnít necessarily mean a bug-free and workable solution, but good data does mean that we have adequately described the problem and that it one less thing we need to worry about as we build our program solutions.


    Download a copy of this article: DataComplexity.rtf


    How To Program A Game With FreeBASIC - Lesson #1

    Written by Lachie D.(January, 2006)

    Introduction

    The objective of this series of lessons is to help newbies who know very little of BASIC to learn the basics of programming in FreeBASIC necessary to create any computer game. Some elementary BASIC knowledge would help a lot though I believe that people who don't know BASIC at all should comprehend these lessons too. But applying them might be a problem for those people. I'm using here the word(well it's an acronym) "BASIC" and not FreeBASIC because if you know basics of QuickBASIC, Visual BASIC or any other variant of BASIC these lessons should be very easy to understand.

    I'm starting this series because I feel that tutorials of this kind were always something our community was missing. Even before FreeBASIC. I've corresponded during my programming lifetime with few programming newbies and they all had almost identical problems when trying to program a game. So I think I'm able to detect what beginners need quite well and on what way the stuff needs to be explained to them. I also remember my beginnings and the problems I had with using separated routines that were never meant to be combined and used to create a game. The breaking point for me was the moment when I discovered RelLib(a QuickBASIC graphics library by R.E.Lope) and the scrolling engine that was created with it. That scrolling engine motivated me to explore its mechanics and expand on it(with some help from R.E.Lope). In one single moment I acquired the ability to program most of the stuff(necessary to complete a game) by myself. It's like driving a bike. The moment when you really learn it lasts one second.

    So that's my goal with this series. To learn you enough so you would be self-sufficient in 90% of cases. And the best way to learn new things is to see them applied. Many tutorials fail in that by being too generic. You will always need help from more expert programmers but the point is that you don't need it on every step. Have in mind that this depends on the type of game you are developing and the graphics library you are using.

    The example programs and mini-games we'll create will be coded in GFXlib(a FreeBASIC built-in graphics library). Red Jumpy Ball, ArKade(you might need to look it up in the News Section) and Poxie were coded in it and I think those games are good references. But don't worry. Switching from one graphics library to another is easy when you know how to code in at least one. Also, a really interesting(especially to me) FreeBASIC graphics library named AFLIB2 is currently being developed so I might recode all the examples from this tutorial in it and make them public.

    I'm not sure how much time I will have to complete all the lessons so I'll try to make each one standalone and not dependant on the lessons that follow it.

    This tutorial will not deal with raycasting engines(3D programming) or something advance like that. If you want that but are a beginner you NEED the following lessons FIRST.

    Since we are going to code in FreeBASIC you need to get FreeBASIC first(if you don't have it yet) from http://www.freebasic.net(the examples were compiled with version 0.15b) and FBIDE(the best FreeBASIC graphical user interface at this point) from http://fbide.sourceforge.net.

    Example #1: A simple program - The circle moves!

    We'll start with some elementary stuff. The first program we'll code will not feature external graphics because loading graphics from external files(usually BMP images) is always a dirty business and will confuse you at this point. Trust me on this. Be patient.

    The program we'll create will allow you to move a circle around the screen. A very simple program but through making it we'll learn important facts and a lot of elementary statements and methods necessary to create any game with GFXlib.

    As we are using GFXlib you need to be aware of gfxlib.txt file(GFXlib's documentation) placed in the /FreeBASIC/docs directory. That's our Bible and very useful with these lessons since I will not explain every parameter of every statement used in the example programs(most likely).

    Open a new program in FBIDE. First thing we'll do is set the graphic mode. What's setting a graphic mode? Choosing the program's graphic resolution and color depth in bits(8bit, 16bit, ...). For example, 8bit color depth is the standard 256 colors mode(8 bits per pixel). The graphic mode is set with the SCREEN statement like this:

    SCREEN 13,8,2,0

    13 means 320*200 graphic resolution, 8 means 8bit graphics, 2 means two work pages and 0 means window mode(input 1 for full screen mode). Minimum of 2 work pages is recommended for any graphics dependant program. These things will become clearer a little bit later. For more details about the SCREEN statement refer to GFXlib's documentation(a more "advanced" version of the SCREEN statement is SCREENRES).

    The next thing we'll do is set a loop that plays until the user pushes the letter Q on the keyboard. Loops are the foundation of any program, not just a computer game. Coding a program on a way it would stop every now and then and wait for the user to type something in is a BAD and WRONG way to program anything you want for other people to use. We'll use loops as places where the program waits for the user to do something(clicks with mouse or pushes a key) and where the program executes some routine according to user's action. It will also be used as a place where objects not controlled by the player(enemies) are managed/moved. Loops are a must have.

    If you are aware of all these things you can skip to the end of this section and download the completed example(with comments). If there is something in it you don't understand then get back here.

    We can set a loop on more ways(with WHILE:WEND statements, using the GOTO statement - Noooo!) but the best way is to use DO...LOOP. This type of loop simply repeats a block of statements in it until the condition is met. You set the condition(s) after LOOP with UNTIL. Check the following code:

    SCREEN 13,8,2,0 ' Sets the graphic mode
    DO
    ' We'll put our statemens here later
    LOOP UNTIL INKEY$ = "Q" or INKEY$ = "q"
    

    If you compile this code and run it you'll get a small black empty 320*200 window which you can turn off by pushing the letter Q(you might need to hold it). The program simply loops until you press "Q or "q". I used both upper and lower case "Q" symbol in case Caps Lock is turned on on your keyboard. INKEY$ is a statement that returns the last key pushed on the keyboard. I will explain later why it shouldn't be used with games and what's a better substitute.

    To draw a circle we'll use the CIRCLE statement(refer to GFXlib's documentation). Check the following code:

    SCREEN 13,8,2,0 ' Sets the graphic mode
    
    DO
    
    CIRCLE (150, 90), 10, 15 
    
    LOOP UNTIL INKEY$ = "Q" or INKEY$ = "q"
    

    The last code draws a small circle on coordinates 150, 90 with a radius of 10 and color 15(plain white) in a loop which you can check if you compile the code. So how to move that circle? We need to connect it's coordinates with VARIABLES. For this we'll use two variables named circlex and circley. Check the following code:

    SCREEN 13,8,2,0 ' Sets the graphic mode
    
    circlex = 150 ' Initial circle position
    circley = 90
    
    DO
    
    CIRCLE (circlex, circlex), 10, 15 
    
    LOOP UNTIL INKEY$ = "Q" or INKEY$ = "q"
    

    This makes no change in the result of our program but it's a step to what we want to accomplish. You can change the amounts to which circlex and circley equal to change the circle's initial position but that's not what we really want. In order to move the circle we need to connect circlex and circley variables with keyboard statements.

    Now I will introduce a new statement instead of INKEY$ which can detect multiple keypresses and is much more responsive(perfect response) than INKEY$. The flaw of INKEY$, as well as being very non-responsive(which you probably were able to detect when trying to shut down the previously compiled examples), is that it can detect only one keypress at any given moment which renders it completely unusable in games.

    The substitute we'll use is MULTIKEY(a GFXlib statement) which features only one parameter and that's the DOS scancode of the key you want to query. You might be lost now. DOS scancode is nothing but a code referred by the computer to a certain keyboard key. If you check Appendix A of the GFXlib's documentation you will see what each code stands for. For example, MULTIKEY(&h1C) queries if you pushed ENTER. To avoid for your code to be hard to understand and read(both to you and others) FreeBASIC allows you to replace these scancodes with "easy to read" constants like it's explained in Appendix A. To avoid the need to copy and paste these constants declarations into your source code you can attach fbgfx.bi module onto your source code which has all these declarations typed in. What's a .bi file? Well, it can be any kind of module you would attach to your source code and which can feature various subroutines(if you don't know what a subroutine is we'll get on that later) and declarations used in your main module. You don't really need to include fbgfx.bi module in this very example since coping and pasting these declarations is an equally right way but you'll have to learn how to include .bi files into your programs eventually. So why not learn it now? The statement you need to add in the code we are working on is just one line as it follows:

    #include "fbgfx.bi" 

    It's best to put that line somewhere on the beginning of your program(before or after the sub declarations). You don't need to set a path to fbgfx.bi since it's placed in /FreeBASIC/inc directory. You only need to set a path to a .bi file if it's not in that directory or not in the directory where the source code is.

    Now the fun starts.

    We will add a new variable named circlespeed which flags(sets) how many pixels the circle will move in one cycle(loop). The movement will be done with arrows key. Every time the user pushes a certain arrow key we will tell the program to change either circlex or circley(depends on the pushed key) by the amount of circlespeed. Check the following code:

    #include "fbgfx.bi" 
    
    SCREEN 13,8,2,0 ' Sets the graphic mode
    
    circlex = 150   ' Initial circle position
    circley = 90
    circlespeed = 1 ' Circle's speed => 1 pixel per loop
    
    DO
    
    CIRCLE (circlex, circley), 10, 15
    
    ' According to pushed key we change the circle's coordinates.
    IF MULTIKEY(SC_RIGHT) THEN circlex = circlex + circlespeed
    IF MULTIKEY(SC_LEFT) THEN circlex = circlex - circlespeed
    IF MULTIKEY(SC_DOWN) THEN circley = circley + circlespeed
    IF MULTIKEY(SC_UP) THEN circley = circley - circlespeed
    
    LOOP UNTIL MULTIKEY(SC_Q) OR MULTIKEY(SC_ESCAPE)
    

    As you see we also changed the condition after UNTIL since we are using MULTIKEY now. Now you can exit the program by pressing ESCAPE too(I added one more condition).

    If you compile the last version of the code two things we don't want to happen will happen. The program will run so fast you won't even notice the movement of the circle and the circle will "smear" the screen(the circles drawn on different coordinates in previous cycles will remain on the screen). To avoid smearing you need to have CLS statement(clears the screen) in the loop so that in every new cycle the old circle from the previous cycle is erased before the new is drawn.

    To reduce the speed of the program the quickest fix is the SCREENSYNC statement(place it as the last statement inside the loop(for now)). What it does? It, as it's said in GFXlib's documentation, waits for the screen vertical blank to occur, then returns control to the caller program. Err...this probably confused you and I must admit I don't understand the definition either. What I know is that a similar statement with the same function prevents flickering in QuickBASIC programs. On nowadays PCs it doesn't make much sense since they are very fast. On the other hand it's very useful to FreeBASIC game designers that use GFXlib because it reduces the speed of every program on almost every PC and platform to 85 FPS. It's the best way to ensure(with GFXlib) that your program will run on every PC with almost the same speed. ArKade, Red Jumpy Ball and Poxie used it. If you are some FPS freak and want your game to run with 300+ FPS then you need to find a reliable, millisecond delay routine yourself. I still didn't though some solutions have been suggested to me(later on that).

    Copy and paste the following code and compile it:

    #include "fbgfx.bi" 
    
    SCREEN 13,8,2,0 ' Sets the graphic mode
    
    circlex = 150   ' Initial circle position
    circley = 90
    circlespeed = 1 ' Circle's speed => 1 pixel per loop
    
    DO
    
    CLS
    CIRCLE (circlex, circley), 10, 15
    
    ' According to pushed key we change the circle's coordinates.
    IF MULTIKEY(SC_RIGHT) THEN circlex = circlex + circlespeed
    IF MULTIKEY(SC_LEFT) THEN circlex = circlex - circlespeed
    IF MULTIKEY(SC_DOWN) THEN circley = circley + circlespeed
    IF MULTIKEY(SC_UP) THEN circley = circley - circlespeed
    
    SCREENSYNC
    
    LOOP UNTIL MULTIKEY(SC_Q) OR MULTIKEY(SC_ESCAPE)
    

    Viola! Our circle is moving and "slow enough".

    The last version of the code does not represent the desirable way of coding but I had to simplify the code in order to make this lesson easy to understand. What we need to do next is declare the variables on the way they should be declared in any serious program and show why we are having two work pages and what we can do with them.

    Now we'll declare the variables used in this example on the "most desirable" way. The first problem with the variables used in the example is that they are not readable in other subroutines and since every serious program is structured with many subroutines this fact is not acceptable. Variables are declared(dimensioned) on this way:

    DIM variable_name [AS type_of_variable] 
    

    The data inside [] is optional and the brackets are not used. Types of variables available in FreeBASIC are BYTE, SHORT, INTEGER, STRING, SINGLE, DOUBLE and few others but I don't find details about them important on this level. What you need to know now is that you should declare variables or arrays AS INTEGER when they hold graphics data(memory buffers holding the graphics) or when they represent data which doesn't need decimal precision(number of lives, points, etc.). Variables that need decimal precision are declared AS SINGLE. Those are usually variables used in games which rely on physics formulae like arcade car driving games or jump 'n run games(gravity effect). Simply, the difference between the speed of two pixels per cycle and the speed of one pixel per cycle is most often too large and in those limits you can't emulate things like fluid movement on the most satisfactory way. Also, behind DIM you should put SHARED which makes that the specific variable readable in the entire program(all subroutines). Don't use SHARED only with variables declared inside subroutines(I do it very rarely). If you are going to declare some variable inside a subroutine I advise you to replace DIM with REDIM(every DIM takes a piece of memory so this prevents some rare but possible cases of unpredictable memory consumption). Strings are used to hold text data.
    Like YourName$ = "Dodo". You can omit $ only if you declare YourName AS STRING first.

    So we will declare our variables with:

    DIM SHARED circlex AS SINGLE
    DIM SHARED circley AS SINGLE
    DIM SHARED circlespeed AS SINGLE
    

    You need to place these variable declarations on the beginning of your program after the subroutine declarations. We declared them all AS SINGLE since that gives us more flexibility in speed/movement control(not needed in this very example but most likely will be in your game). They also don't need to be SHARED in this example since we are not using subroutines but you should grow a habit of declaring variables like that.

    While this is a perfectly proper way of declaring variables it's not the most convenient way in larger projects where we have huge amount of variables usually associated to several objects(an object can be the player, enemy or anything that is defined with MORE THAN ONE variable).

    So first we'll define a user defined data type with the statement TYPE that can contain more variables/arrays(stay with me). We'll name this user data type "ObjectType". The code:

    TYPE ObjectType
         x AS SINGLE
         y AS SINGLE
         speed AS SINGLE
    END TYPE
    

    After this we declare our circle as an object:

    DIM SHARED CircleM AS ObjectType
    ' We can't declare this variable with "Circle"
    ' since then FB can't differ it from 
    ' the statement CIRCLE thus "CircleM".
    

    How is this method beneficial? It allows us to manage the program variables on a more efficient and cleaner way. Instead of(in this example) having to declare each circle's characteristic(it's position, speed, etc.) separately we'll simply use a type:def that includes all these variables and associate a variable or an array to it(in this case that's CircleM). So now the circle's x position is flagged with CircleM.X, circle's y position with CircleM.Y and circle's speed with CircleM.speed. I hope you see now why this is better. One user defined type can be connected with more variables or arrays. In this example you can add another object with something like 'DIM SHARED EnemyCircle(8) AS ObjectType' which would allow us to manage 8 "evil" circles with a specific set of routines(an AI of some sort) using the variables in the 'ObjectType' type:def(x, y, speed) and these circles could "attack" the user's circle on some way. In the next lesson all this will become more clear. Have in mind that not ALL variables need to be declared using a type:def. This is only for "objects" in your game that are defined(characterized) with more variables(like a hero determined by health, money, score, strength, etc.).

    After the change the final version of the code looks like this:

    #include "fbgfx.bi" 
    
    ' Our user defined type.
    TYPE ObjectType
         x AS SINGLE
         y AS SINGLE
         speed AS SINGLE
    END TYPE
    
    DIM SHARED CircleM AS ObjectType
    ' We can't declare this variable with "Circle"
    ' since then FB can't differ it from 
    ' the statement CIRCLE thus "CircleM".
    
    SCREEN 13,8,2,0 ' Sets the graphic mode
    SETMOUSE 0,0,0 ' Hides the mouse cursor
    
    CircleM.x = 150   ' Initial circle's position
    CircleM.y = 90
    CircleM.speed = 1 ' Circle's speed => 1 pixel per loop
    
    DO
    
    CLS
    CIRCLE (CircleM.x, CircleM.y), 10, 15
    
    ' According to pushed key we change the circle's coordinates.
    IF MULTIKEY(SC_RIGHT) THEN CircleM.x = CircleM.x + CircleM.speed
    IF MULTIKEY(SC_LEFT) THEN CircleM.x = CircleM.x - CircleM.speed
    IF MULTIKEY(SC_DOWN) THEN CircleM.y = CircleM.y + CircleM.speed
    IF MULTIKEY(SC_UP) THEN CircleM.y = CircleM.y - CircleM.speed
    
    SCREENSYNC
    
    'SCREENLOCK
    SLEEP 2         ' Prevents 100% CPU usage
    'SCREENUNLOCK
    
    LOOP UNTIL MULTIKEY(SC_Q) OR MULTIKEY(SC_ESCAPE)
    
    

    You will notice I added two more statements in the code. The SETMOUSE statement positions the system mouse cursor(first two parameters) and shows or hides it(third parameter; 0 hides it). You should input this statement with these parameters in every program AFTER the SCREEN statement(IMPORTANT!) by default because even if your program is going to feature a mouse controllable interface you will most likely draw your own cursor. Trust me on this. Uh, I using that line way too often.

    The SLEEP statement waits the amount of milliseconds you set and we are having it in our loop to prevent 100% CPU usage. You see, if you don't use that statement any kind of FreeBASIC program with a loop(even the simplest one) will hold up all the computer cycles and make all the other Windows tasks you might be running to crawl. It also makes difficult for you to operate with other tasks while that kind of FreeBASIC program is running. Err...this is not a huge problem and most programmers that have released FreeBASIC games so far did not bother to fix it. Probably the best solution is to leave the usage of the SLEEP statements in the game loops as an option(in a setup menu; later on that). SCREENLOCK and SCREENUNLOCK statements lock the current work page and I REMed them because they are not necessary. Why I inputted them is because while developing Poxie I noticed on places where full screen images were scrolled a lot of trembling and the only way I discovered that prevents this is to use these statements before and after SLEEP or simply to remove SLEEP. Anyway, this is just a hint to you if you in the middle of development of some project notice that kind of problem.

    Download the completed example with extra comments inside the source: move_circle.zip

    Phew, we are done with the first example. Some of you might think I went into too many details but I feel all this dance was needed to make the next examples and lessons a more enjoyable adventure. God, I suck at writing.

    Nevertheless, this example is far from what we want, right? So the next chapter will learn you how to load graphics from external files among other things. Are you excited as I am? :P

    Example #2: A warrior running around a green field

    In the next example we will be apllying all the knowledge from the first example so don't expect for this example to go into every statement again. I will explain every new statement and just brush off the old ones.

    In this section we'll start to code our mini-game which won't be completed in this lesson. In this lesson we'll just create a program where a warrior runs around a green field(single screen).

    First I'll show you what graphics we'll be using. We are going to work in 8bit color depth mode so the images that we are going to use need to be saved in that mode(256 colors mode). For warrior sprites I'll use the sprites of the main character from my first game Dark Quest.

    As you see this image features 12 sprites of our warrior each 20*20 pixels large. Two for each direction(walk animation) and one sprite for each direction when the warrior is swinging with his sword. Sword swinging wonít be implemented in the first lesson but will become necessary later.

    Second image is the background image which you can check/download if you click here(320*200 pixels large, 8bit BMP image).

    Download both images and place them where you will place the source or just download the completed example at the end of this section.

    On the beginning of our program we should include fbgfx.bi, same as in the first example, and then set the same graphic mode. The code:

    #include "fbgfx.bi" 
    
    SCREEN 13,8,2,0 ' Sets the graphic mode
    SETMOUSE 0,0,0  ' Hides the mouse cursor
    

    Now we will declare two arrays that will hold our graphics(one for the sprites and one for the background). Think of these arrays as memory buffers where the graphics are stored.

    First array we'll name "background1" and declare it with the following line:

    DIM SHARED background1(64004) AS INTEGER
    

    Why the number 64004? Because each byte will hold the information about one pixel of the background image(what's the color of that pixel). 320*200 = 64000 which means 64000 pixels. The extra 4 is needed because INTEGER variable itself is 4 bytes long.

    The next array we'll declare will hold the graphics of the 12 warrior sprites. We will dimension this array with two elements(dimensions), first representing the number of the sprite saved in the array and second used to store the graphics(each byte holding the information about one pixel) of that sprite(check GFXlib's documentation -> GET statement -> Differences from QB section). I won't go into details why this method is better than using only one dimension. Just have in mind that managing large numbers of sprites or tiles with a single dimension array is much more difficult and ďclumsyĒ especially if the sprites or tiles saved in that array are of different sizes. We'll name this array "WarriorSprite" and declare it with the following line:

    DIM SHARED WarriorSprite(12, 404) AS INTEGER
    

    Why the number 404? Because each sprite is 20*20 pixels large. 20*20 = 400 and we add 4(lenght of every INTEGER variable).

    Both these lines should be put in the code before the SCREEN statement. That's the way you'll write every program. Subroutine declarations, then variable declarations, then extra subroutine declarations if needed and then the real code. The beginning of our program should now look like this:

    #include "fbgfx.bi" 
    
    DIM SHARED background1(64004) AS INTEGER ' An array that will hold the
                                             ' background image
    DIM SHARED WarriorSprite(12, 404) AS INTEGER ' An array that will hold
                                                 ' the warior sprites.
    
    SCREEN 13,8,2,0 ' Sets the graphic mode
    SETMOUSE 0,0,0  ' Hides the mouse cursor
    

    After the screen resolution, color depth and number of work pages are set we will hide our work page before loading graphics onto it since we don't want for the user to see all of the program's graphics every time he or she starts our program. To accomplish that we'll use the SCREENSET statement. What it does? It sets the work page(first parameter) and the visible page(second parameter). In our case we will set page 1 as the work page and page 0 as the visible page. After using 'SCREENSET 1, 0' every time we draw or load something on the screen it will be loaded/drawn on the work page and won't be visible to the user until we use the statement SCREENCOPY. This allows us to load graphics onto the screen we don't want for the user to see and delete it before using SCREENCOPY which copies the content of the work page to the visible. This page flipping is also useful in loops with "graphics demanding" programs to avoid flicker or some other unwanted occurrence.

    BMP(bitmap) files are loaded in GFXlib with the statement BLOAD. BLOAD can also load BSAVEd images(images saved with the BSAVE statement). BMP images can be loaded directly into an array or onto the screen. When an image is loaded with BLOAD image's associated palette will be set as program's current palette. In 8bit mode all your graphics should be in the same palette. In 16bit and higher color depth modes you don't have to think about palettes. Check GFXlib's documentation for more details on these statements. I have no available information/routines for loading different image formats.

    First image we'll load is the background image and we'll load it onto the screen first and then store(capture) that data into an array with the GET statement. I prefer that way over loading images directly into arrays since it's more cleaner and flexible. Also, the second way can lead to errors with multidimensional arrays. The background image is loaded and stored into memory with the following code:

    SCREENSET 1, 0
    
    BLOAD "BACKGRND.bmp", 0
    GET (0,0)-(319,199), background1(0)
    

    BACKGRND.bmp is the name of the image we are loading. If it's placed in some subdirectory(not where the compiled program is) you need to set a path to it. If, for example, it's placed in the subdirectory "Graphics" you need to replace "BACKGRND.bmp" with "Graphics/BACKGRND.bmp". DON'T USE HARD PATHS in you programs like "C:/FreeBASIC/myprograms/Bobo/BACKGRND.bmp" because that's one of the most stupidest things you can do. It forces the user to extract your program in a specific directory and have death wishes about you. Parameter 0 in the BLOAD statement means we want to load the image onto the screen. Instead of 0 you can put an adress(memory pointer) where to load the image(into an array; refer to GFXlib's documentation). The GET statement in the previous code captures the graphics on the screen from the coordinates (0,0) to (319,199) and stores it into the "background1" array counting from position(byte) 0(input 0 by default).

    The second image we'll load is the one with the warrior sprites after which we'll store them into the "WarriorSprite" array. There is 12 sprites and each one is 20*20 pixels large. The code that loads the second image as stores the sprites is as follows:

    BLOAD "SPRITES.bmp", 0
    GET (0,0)-(19,19),  WarriorSprite(1, 0)
    GET (24,0)-(43,19), WarriorSprite(2, 0)
    GET (48,0)-(67,19), WarriorSprite(3, 0)
    GET (72,0)-(91,19), WarriorSprite(4, 0)
    GET (96,0)-(115,19), WarriorSprite(5, 0)
    GET (120,0)-(139,19), WarriorSprite(6, 0)
    GET (144,0)-(163,19), WarriorSprite(7, 0)
    GET (168,0)-(187,19), WarriorSprite(8, 0)
    GET (192,0)-(211,19), WarriorSprite(9, 0)
    GET (216,0)-(235,19), WarriorSprite(10, 0)
    GET (240,0)-(259,19), WarriorSprite(11, 0)
    GET (264,0)-(283,19), WarriorSprite(12, 0)
    

    Boy, all that code to store mere 12 sprites! Each GET goes for one sprite and you see how we stored each one on a different position in the array(first element in the "WarriorSprite" array). Now, what if your game features hundreds of sprites and tiles? What to do then? Well, you can apply a form of automation in sprites/tiles capturing if you align(compile) sprites or tiles sequentially in the BMP image. Place them one after another with 0 or more pixels of space between them. You probably noticed a certain order in coordinates used in the twelve GET statements from the last code. That's because these 12 sprites are nicely arranged in the image in one line(from left to right) and with 4 pixels between every sprite. This allows us to load all of them with a single GET statement and a FOR loop. Like this(this replaces the last piece of code):

    FOR imagepos = 1 TO 12
    GET (0+(imagepos-1)*24,0)-(19+(imagepos-1)*24,19),  WarriorSprite(imagepos, 0)
    NEXT imagepos
    

    If you don't understand how FOR loops work Iíll explain it. A FOR loop simply executes the statement(s) between FOR and NEXT until the variable specified with FOR reaches the number behind TO. In our code in the first cycle(loop) variable imagepos equals 1 and the coordinates in the GET statement are(try to calculate them manually) (0,0) and (19,19). When imagepos equals 12(last cycle) the coordinates are (264,0) and (283,19). So this FOR loop simply "goes through" all the sprites and stores them on the appropriate positions inside the "WarriorSprite" array. Oh, the wonders of FOR loops. Anyway, try to apply this knowledge when construction ways of loading large numbers of tiles and/or sprites from BMP images.

    The sprites are now saved on these positions:

    WarriorSprite(1, 0) - warrior moving down image #1
    WarriorSprite(2, 0) - warrior moving down image #2
    WarriorSprite(3, 0) - warrior moving up image #1
    WarriorSprite(4, 0) - warrior moving up image #2
    WarriorSprite(5, 0) - warrior moving left image #1
    WarriorSprite(6, 0) - warrior moving left image #2
    WarriorSprite(7, 0) - warrior moving right image #1
    WarriorSprite(8, 0) - warrior moving right image #2
    WarriorSprite(9, 0) - warrior swinging up
    WarriorSprite(10, 0) - warrior swinging down
    WarriorSprite(11, 0) - warrior swinging left
    WarriorSprite(12, 0) - warrior swinging right

    The entire code so far should look like this:

    #include "fbgfx.bi" 
    
    DIM SHARED background1(64004) AS INTEGER ' An array that will hold the
                                             ' background image
    DIM SHARED WarriorSprite(12, 404) AS INTEGER ' An array that will hold
                                                 ' the warior sprites.
    
    SCREEN 13,8,2,0 ' Sets the graphic mode
    SETMOUSE 0,0,0  ' Hides the mouse cursor
    
    ' Let's hide the work page since we are
    ' going to load program graphics directly
    ' on the screen.
    SCREENSET 1, 0
    
    ' Load the background image and store
    ' it in an array.
    BLOAD "BACKGRND.bmp", 0
    GET (0,0)-(319,199), background1(0)
    
    CLS ' Clear our screen since we
        ' are loading a new image(not
        ' neccesary but wise).
    
    ' Load the sprites onto the screen and store them 
    ' into an array.
    BLOAD "SPRITES.bmp", 0
    FOR imagepos = 1 TO 12
    GET (0+(imagepos-1)*24,0)-(19+(imagepos-1)*24,19),  WarriorSprite(imagepos, 0)
    NEXT imagepos
    

    We are finally done with loading graphics. Now we will declare additional variables needed in this example. I'll define a data type like in the previous example but it will contain more variables. The following code should be placed before the image array declarations.

    TYPE ObjectType
    X          AS SINGLE
    Y          AS SINGLE
    Speed      AS SINGLE
    Frame      AS INTEGER
    Direction  AS INTEGER
    Move       AS INTEGER
    Attack     AS INTEGER
    Alive      AS INTEGER
    END TYPE
    

    The object that will be used to control the warrior is declared with:

    DIM SHARED Player AS ObjectType
    

    Frame variable will be used to flag the sprite that needs to be displayed(according to warrior's direction, if he is moving or not, etc.). Direction will be used to flag the warrior's direction, Move if he is moving or not, Attack if he is attacking or not(so we could flag the proper sprite) and Alive if he is alive or not(not used in this example but most often necessary).

    Let's set a loop on the way it's done in the previous example but also add the SCREENCOPY statement in it since we have previously set the work and the visible page(with 'SCREENSET 1, 0').

    DO
    
    CLS 
    
    ' Our "graphics" statement go in here.
    
    SCREENCOPY
    SCREENSYNC 
    SLEEP 2    
    
    LOOP UNTIL MULTIKEY(SC_Q) OR MULTIKEY(SC_ESCAPE)
    

    We should now add a statement in the loop that will draw our warrior. To paste graphics onto the screen with GFXlib we use the PUT statement. It's very simple and works something like this:

    PUT (x coordinate, y coordinate), array, Mode
    

    Where "array" is the name of the array where our image/sprite/tile is stored. Mode represents several methods of pasting graphics but at this point you should be aware of two. PSET pastes all the image pixels while TRANS skips pixels that are of background color(transparent background effect). In 16bit and higher color depth modes transparent color is the color RGB 255,0,255(pink; 255 should be the highest value in the palette editor you are using but some drawing programs feature a different number as a maximum color value so have that in mind) while in 8bit mode it's the color 0(first in the palette). Our sprites must be pasted with TRANS or they would be pasted with a black square around them(consisted of color 0). By using MULTIKEY statements according to pushed arrow key we'll change warrior's position and flag the proper direction(with a number). According to direction we will paste the proper sprite. Here we go(entire code):

    #include "fbgfx.bi" 
    
    ' Useful constants(makes your code easier to read and write).
    const FALSE = 0
    const TRUE = 1
    
    DIM SHARED background1(64004) AS INTEGER ' An array that will hold the
                                             ' background image
    DIM SHARED WarriorSprite(12, 404) AS INTEGER ' An array that will hold
                                                 ' the warior sprites.
    
    SCREEN 13,8,2,0 ' Sets the graphic mode
    SETMOUSE 0,0,0  ' Hides the mouse cursor
    
    ' Let's hide the work page since we are
    ' going to load program graphics directly
    ' on the screen.
    SCREENSET 1, 0
    
    ' Load the background image and store
    ' it in an array.
    BLOAD "BACKGRND.bmp", 0
    GET (0,0)-(319,199), background1(0)
    
    CLS ' Clear our screen since we
        ' are loading a new image(not
        ' neccesary but wise).
    
    ' Load the sprites onto the screen and store them 
    ' into an array.
    BLOAD "SPRITES.bmp", 0
    FOR imagepos = 1 TO 12
    GET (0+(imagepos-1)*24,0)-(19+(imagepos-1)*24,19),  WarriorSprite(imagepos, 0)
    NEXT imagepos
    
    TYPE ObjectType
    X          AS SINGLE
    Y          AS SINGLE
    Speed      AS SINGLE
    Frame      AS INTEGER
    Direction  AS INTEGER
    Move       AS INTEGER
    Attack     AS INTEGER
    Alive      AS INTEGER
    END TYPE
    
    DIM SHARED Player AS ObjectType
    
    
    ' Warrior's(player's) initial
    ' position, speed(constant)
    ' and direction(1 = right)
    Player.X = 150
    Player.Y = 90
    Player.Speed = 1
    Player.Direction = 1
    
    DO
    
    ' Player.Direction = 1 -> warrior moving right
    ' Player.Direction = 2 -> warrior moving left
    ' Player.Direction = 3 -> warrior moving down
    ' Player.Direction = 4 -> warrior moving up
    
    Player.Move = FALSE ' By deafult player is not
                        ' moving.
    
    ' According to pushed key move the
    ' player and flag the proper direction.
    IF MULTIKEY(SC_RIGHT) THEN 
    Player.X = Player.X + Player.Speed
    Player.Direction = 1
    Player.Move = TRUE
    END IF
    IF MULTIKEY(SC_LEFT) THEN 
    Player.X = Player.X - Player.Speed
    Player.Direction = 2
    Player.Move = TRUE
    END IF
    IF MULTIKEY(SC_DOWN) THEN 
    Player.Y = Player.Y + Player.Speed
    Player.Direction = 3
    Player.Move = TRUE
    END IF
    IF MULTIKEY(SC_UP) THEN 
    Player.Y = Player.Y - Player.Speed
    Player.Direction = 4
    Player.Move = TRUE
    END IF
    
    ' According to player's direction flag the 
    ' proper sprite(check in the tutorial on which
    ' position each sprite is stored).
    IF Player.Direction = 1 THEN Player.Frame = 7
    IF Player.Direction = 2 THEN Player.Frame = 5
    IF Player.Direction = 3 THEN Player.Frame = 1
    IF Player.Direction = 4 THEN Player.Frame = 3
    
    CLS ' Clear the screen(REM this as get smearing). 
    
    ' Paste the warrior on Player.X and Player.Y coordinates, 
    ' using sprite number Player.Frame and skip background color.
    PUT (Player.X, Player.Y), WarriorSprite(Player.Frame, 0), TRANS
    
    SCREENCOPY
    SCREENSYNC 
    SLEEP 2    
    
    LOOP UNTIL MULTIKEY(SC_Q) OR MULTIKEY(SC_ESCAPE) 
    
    

    Take a note of the two constants which I added in the code and which allow us to make our code easier to write and read. Instead of dealing with 0 and 1 as conditions with variables than can only be true or false we say that FALSE in our code will mean 0 and TRUE will mean 1. If you compile this code you'll be able to move the warrior around a black screen(we'll add the background later) but his legs won't move. How to enable "walk" animation? Well, it's very easy. You'll see how the Move variable comes in handy now. You should input this code right after CLS:

    Frame1 = (Frame1 MOD 2) + 1
    IF Player.Move = FALSE OR Frame1 = 0 THEN Frame1 = 1
    

    The line 'Frame1 = (Frame1 MOD 2) + 1' is a substitute for:

    Frame1 = Frame1 + 1
    IF Frame1 > 2 THEN Frame1 = 1
    

    You don't have to think on what way the line 'Frame1 = (Frame1 MOD 2) + 1' functions. All you need to know is that is changes variable Frame1 by 1 in each cycle from number 1 to the number specified after MOD. If you want for Frame1 to loop from 50 to 66 you need to input 'Frame1 = (Frame1 MOD 16) + 50' but that's not we need. We need a variable that toggles from 1 to 2 in each cycle to enable walk animation. To make this work we need to change the 4 lines of code preceeding CLS(where according to player's direction the proper sprite is flaged) to this:

    IF Player.Direction = 1 THEN Player.Frame = 6 + Frame1
    IF Player.Direction = 2 THEN Player.Frame = 4 + Frame1
    IF Player.Direction = 3 THEN Player.Frame = 0 + Frame1
    IF Player.Direction = 4 THEN Player.Frame = 2 + Frame1
    

    So when Frame1 equals 1 and the player is moving right(Player.Direction = 1) Player.Frame equals 7 while when Frame1 equals 2 Player.Frame is 8. Check the lesson on the place where I specified on which position each sprite is stored in the "WarriorSprite" array. You'll see that ďmoving rightĒ sprites are stored on positions 7 and 8. Not why we need that condition where the Player.Move variable is used? When the player is not moving Frame1 needs to be 1 or 2 IN EVERY CYCLE(no sprite rotation). Second condition(IF Frame1 = 0) is there to prevent errors(when the loop starts Frame1 might equal 0 and the program might load a sprite out of bounds or something; I highly advise this sort of precaution measure).

    If you compile the code again with these changes you'll notice that warrior's legs are moving too fast. How to set the speed of sprite rotation? You need another variable like Frame1(we'll named it Frame2) that will grow to a higher number and connect it with Frame1 so that Frame1 doesn't change in every cycle but only when Frame2 equals a certain number. Check the following code:

    Frame2 = (Frame2 MOD 16) + 1
    IF Frame2 = 10 THEN Frame1 = (Frame1 MOD 2) + 1
    

    Now Frame1 will change(from 1 to 2 or vice versa) every time Frame2 = 10 and Frame2 will equal 10 every 16 cycles(it grows from 1 to 16 by 1 in every cycle and then drops to 1). We reduced the speed of rotation of the Frame1 variable! Change 16 to some other number to get a different speed of sprite rotation. Using several Frame variables in your code, some connected and some not, will become necessary in larger projects where you will have many objects represented with sprites that need to rotate with different speeds(monsters which walk with different paces, the speed of explosion animations, etc.). With ďwalkingĒ objects you need to synchronize the speed of that object with sprite rotation(the best you can) or your ďwalkingĒ object(player, monster, etc.) might seem like itís sliding or running in place.

    One of the last things we'll do in the second example is add a line that pastes the background which is as follows at should be placed before the PUT statement that pastes the warrior:

    PUT (0, 0), background1(0), PSET
    

    You can also remove CLS now since in every new cycle of the loop the background is pasted over the entire screen erasing all the graphics from the previous cycle.

    Last 4 conditions we'll add in the code are there to prevent the warrior to walk off the screen. Simple conditions but every new line of code should increase your programming experience which you need a lot at this point.

    IF Player.X < 0 THEN 
    Player.Move = FALSE
    Player.X = 0
    END IF
    IF Player.X > 300 THEN 
    Player.Move = FALSE
    Player.X = 300
    END IF
    IF Player.Y < 0 THEN 
    Player.Move = FALSE
    Player.Y = 0
    END IF
    IF Player.Y > 180 THEN 
    Player.Move = FALSE
    Player.Y = 180
    END IF
    

    You should be able now to understand this code. Player.Move is changed to FALSE so that the warrior doesn't seem like he is trying to push the edge of the screen. Try to REM these lines('Player.Move = FALSE') and see it yourself.

    The FINAL version of the code(for this lesson) looks like this(yippee!):

    #include "fbgfx.bi" 
    
    ' Useful constants(makes your code easier to read and write).
    const FALSE = 0
    const TRUE = 1
    
    DIM SHARED background1(64004) AS INTEGER ' An array that will hold the
                                             ' background image
    DIM SHARED WarriorSprite(12, 404) AS INTEGER ' An array that will hold
                                                 ' the warior sprites.
    
    SCREEN 13,8,2,0 ' Sets the graphic mode
    SETMOUSE 0,0,0  ' Hides the mouse cursor
    
    ' Let's hide the work page since we are
    ' going to load program graphics directly
    ' on the screen.
    SCREENSET 1, 0
    
    ' Load the background image and store
    ' it in an array.
    BLOAD "BACKGRND.bmp", 0
    GET (0,0)-(319,199), background1(0)
    
    CLS ' Clear our screen since we
        ' are loading a new image(not
        ' neccesary but wise).
    
    ' Load the sprites onto the screen and store them 
    ' into an array.
    BLOAD "SPRITES.bmp", 0
    FOR imagepos = 1 TO 12
    GET (0+(imagepos-1)*24,0)-(19+(imagepos-1)*24,19),  WarriorSprite(imagepos, 0)
    NEXT imagepos
    
    TYPE ObjectType
    X          AS SINGLE
    Y          AS SINGLE
    Speed      AS SINGLE
    Frame      AS INTEGER
    Direction  AS INTEGER
    Move       AS INTEGER
    Attack     AS INTEGER
    Alive      AS INTEGER
    END TYPE
    
    DIM SHARED Player AS ObjectType
    
    
    ' Warrior's(player's) initial
    ' position, speed(constant)
    ' and direction(1 = right)
    Player.X = 150
    Player.Y = 90
    Player.Speed = 1
    Player.Direction = 1
    
    DO
    
    ' Player.Direction = 1 -> warrior moving right
    ' Player.Direction = 2 -> warrior moving left
    ' Player.Direction = 3 -> warrior moving down
    ' Player.Direction = 4 -> warrior moving up
    
    Player.Move = FALSE ' By deafult player is not
                        ' moving.
    
    ' According to pushed key move the
    ' player and flag the proper direction.
    IF MULTIKEY(SC_RIGHT) THEN 
    Player.X = Player.X + Player.Speed
    Player.Direction = 1
    Player.Move = TRUE
    END IF
    IF MULTIKEY(SC_LEFT) THEN 
    Player.X = Player.X - Player.Speed
    Player.Direction = 2
    Player.Move = TRUE
    END IF
    IF MULTIKEY(SC_DOWN) THEN 
    Player.Y = Player.Y + Player.Speed
    Player.Direction = 3
    Player.Move = TRUE
    END IF
    IF MULTIKEY(SC_UP) THEN 
    Player.Y = Player.Y - Player.Speed
    Player.Direction = 4
    Player.Move = TRUE
    END IF
    
    ' The following 4 conditions prevent
    ' the warrior to walk off the screen.
    IF Player.X < 0 THEN 
    Player.Move = FALSE
    Player.X = 0
    END IF
    IF Player.X > 300 THEN 
    Player.Move = FALSE
    Player.X = 300
    END IF
    IF Player.Y < 0 THEN 
    Player.Move = FALSE
    Player.Y = 0
    END IF
    IF Player.Y > 180 THEN 
    Player.Move = FALSE
    Player.Y = 180
    END IF
    
    ' According to player's direction flag the 
    ' proper sprite(check in the article on which
    ' position each sprite is stored).
    IF Player.Direction = 1 THEN Player.Frame = 6 + Frame1
    IF Player.Direction = 2 THEN Player.Frame = 4 + Frame1
    IF Player.Direction = 3 THEN Player.Frame = 0 + Frame1
    IF Player.Direction = 4 THEN Player.Frame = 2 + Frame1
    
    ' Frame1 changes from 1 to 2 or vice versa every
    ' 16 cycles(set with Frame2 variable).
    Frame2 = (Frame2 MOD 16) + 1
    IF Frame2 = 10 THEN Frame1 = (Frame1 MOD 2) + 1
    IF Player.Move = FALSE OR Frame1 = 0 THEN Frame1 = 1
    
    ' Pastes the background.
    PUT (0, 0), background1(0), PSET
    
    ' Pastes the warrior on Player.X and Player.Y coordinates, 
    ' using sprite number Player.Frame and skip background color.
    PUT (Player.X, Player.Y), WarriorSprite(Player.Frame, 0), TRANS
    
    SCREENCOPY
    SCREENSYNC 
    SLEEP 2    
    
    LOOP UNTIL MULTIKEY(SC_Q) OR MULTIKEY(SC_ESCAPE) 
    
    

    Compile it and enjoy.

    Download the completed example with the graphics files and extra comments inside the source: move_warrior.zip

    Extra stuff: 16bit color depth and example #2

    If you want to convert example #2 into 16bit or higher color depth mode you need to convert the two images used in that example to 24bit color depth mode(24 BPP). The best tool for this task is IrfanView which is something anyone who owns a PC must have. With sprites you need to replace the background color 0 with color RGB 255,0,255 like it's done on this image(click on it):

    Other changes you need to do is change the screen mode to 14 since modes 13 and lower don't support color depths higher than 8bit. The code:

    SCREEN 14,16,2,0 
    

    Since screen mode 14 features a 320*240 pixels large screen we should edit the background image(enlarge it) and dimension the arrays so they can hold the new 24bit graphics. For the background image the new dimension is 320*240*4 + 4 = 307204 (refer to GFXlib's documentation -> GET statement). Also, we should change the screen edge coordinates which prevent the warrior to walk off the screen. Download the entire code with all the changes and new graphics files(16bit version of example #2): move_warrior16bit.zip

    If you set the program's color depth to 24bit or 32bit PUT translucency won't work properly. I don't have a lot experience with 16bit and higher color depth graphics so don't ask me to explain this occurrence. I DON'T KNOW! Just set the color depth to 16bit and work with 24bit images. If you have other requirements ask for help in the community forums.

    Homework

    Change the resolution of the 8bit version of example #2 to 640*400, change the screen edges to fit this resolution and don't modify the background image(you are not allowed to enlarge it).

    Download homework solution: mw_homework.zip

    I simply changed the screen mode to 17(640*400), pasted the background image 4 times on different positions so it would cover the entire screen and changed the screen edges.

    Final words

    What we learned(the most important things):

    That's all for lesson #1. It was a big one, eh? After finishing "QBasic Tetris Games" I thought I would never write a bigger article. Wasn't I deluded?

    I think the first lesson had to be this big. One month is too much for some short lesson to make sense.

    I'm not sure when I'll be able to compile the next lesson(these things really take a lot of time to write) but I plan to put a lot of stuff in it. Stuff like subroutine declarations and usage, how to manage NPCs(sheep in our mini-game) and create a simple AI, how to implement particle layers(an REinvention of my own :P), how to use mouse routines(I'll implement a main menu to show this), how to load variables from files and more.

    With hopes I'll be able to whip up the next issue in our lifetime, happy coding!


    Visit my reviews/articles site: kentauri.digitalblackie.com/articles/


    Download a copy of this tutorial: HTML, TXT, ZIP


    MIDI Programming - A Complete Study
    Part 2 - Data Structures

    Written by Stéphane Richard (Mystikshadows)

    INTRODUCTION:

    Welcome to the second part of the MIDI Programming Series. As I mentionned in the first part, we'll see one way of loading up a MIDI file in memory (in structures) so we can manipulate them at will. Although when we send this MIDI information to the MIDI ports will have to take the different pieces of information of each MIDI event and put them back into a series of bytes, we'll see here how useful it can be to treat these informations as independant fields. Now since we won't be playing the song per se, I'll be implementing the functions so that the give back the information in a printed textual fashion so you can see we actually did read the MIDI file.

    There's alot of coding information up ahead in this part of the series, we'll see exactly what information we can actually get from a MIDI file's header and track information for one thing. Likewise, we'll see exactly what needs to be done with each type of MIDI events. Some of these Events require 2 parameters to be read while others require only one parameter. So, how do we deal with all these different types of MIDI related information? We'll see that right here, right now. So fasten your safety belts as we implement a complete MIDI file loader and information extractor utility to give us everything that can be extracted from the MIDI file.

    OPENING AND LOADING A MIDI FILE:

    When you open a MIDI, it's not enough to open it and load it into, say a string variable, and think you're ready to play it. At least not at the level we'll be doing it here. You need something that can isolate the tracks and the isolate the midi events themselves. But first, let's declare some constants that will need when reading the midi file. Help make the code clearer in the process.

    ' ---------------------- ' Constant Definitions ' ---------------------- ' ------------------- ' MIDI File Formats ' ------------------- CONST SingleTrack = 0 CONST MultipleTracksSync = 1 CONST MultipleTracksAsync = 2 ' ---------------------------------- ' MIDI Status Constant Definitions ' ---------------------------------- CONST NoteOff = &H80 CONST NoteOn = &H90 CONST PolyKeypress = &HA0 CONST ControllerChange = &HB0 CONST ProgramChange = &HC0 CONST ChannelPressure = &HD0 CONST PitchBend = &HE0 CONST SysExMessage = &HF0 ' ----------------------------------- ' MIDI Controller Numbers Constants ' ----------------------------------- CONST MolulationWheel = 1 CONST BreathController = 2 CONST FootController = 4 CONST PortamentoTime = 5 CONST MainVolume = 7 CONST Balance = 8 CONST Pan = 10 CONST ExpressController = 11 CONST DamperPedal = 64 CONST Portamento = 65 CONST Sostenuto = 66 CONST SoftPedal = 67 CONST Hold2 = 69 CONST ExternalFXDepth = 91 CONST TremeloDepth = 92 CONST ChorusDepth = 93 CONST DetuneDepth = 94 CONST PhaserDepth = 95 CONST DataIncrement = 96 CONST DataDecrement = 97

    Now when we read a MIDI event, we'll need somekind of structure to hold the information that is found in the MIDI event itself. As I explained in the first part of the series, a regular MIDI. To this effect, take a look at the following two user defined types:

    TYPE TimeStructure Bar AS INTEGER Beat AS INTEGER Tick AS INTEGER END TYPE TYPE MIDIEventStructure EventTime AS TimeStructure EventDeltaTime AS BYTE StatusByte AS BYTE ChannelByte AS BYTE DataByteOne AS BYTE DataByteTwo AS BYTE END TYPE

    Now we need another structure to encompass the MIDIEventStructure into an array. we'll need one array per track. For the sake of simplicity of this example, I won't use a dynamic array in each track so we won't be needing pointers and linked list but typically that is what you would use in a case like this since you don't know how long a song (or each of it's composing tracks) are. Here's the TrackStructure:

    TYPE TrackStructure TrackNumber AS INTEGER TrackEventCount AS INTEGER MIDIEvents(131072) AS MIDIEventStructure ' 128 Kb / Track END TYPE

    These three structures is what we're going to use to keep the song in memory. The TimeStructure holds the standard beat:bar:tick information. The MIDIEventStructure holds the EventTime (a TimeStructure sub type) and the Event information. The EventDeltaTime tells the system to send the MIDI event right now but not play the event until DeltaTime has been reached. In MIDI it's a good technique to use to make sure the data that is to be played is already sent through the MIDI port and ready to play when the time is right. The StatusByte is the actual MIDI Event Type (refer to the first part of the series for a list of these MIDI Event Types). The ChannelByte tells which MIDI channel the MIDI event is to be sent through. DataByteOne and DataByteTwo form the parameters to the current MIDI event itself.

    Because the StatusByte and the ChannelByte are essentially the MSB (Most Significant Byte) and LSB (Least Significant Byte) of a one byte value we will need the following 4 support functions in order to get the MSB and LSB from the byte and store them in the StatusByte and ChannelByte fields of our MIDIEventStructure. The last function allows to get the length of a variable length MIDI message (such as SysEx).

    ' ========================================= ' This function returns the LSB of a byte ' Example: LowNibbble(&HD5) = &H5 ' ========================================= FUNCTION LowNibble(ByteValue AS BYTE) AS BYTE LowNibble = ByteValue AND &H0F END FUNCTION ' ========================================= ' This function returns the LSB of a byte ' Example: HighNibble(&HD5) = &HD0 ' ========================================= FUNCTION HighNibble(ByteValue AS BYTE) AS BYTE HighNibble = ByteValue AND &HF0 END FUNCTION ' ========================================= ' This function calculates the length of ' a variable length MIDI message. ' ========================================= FUNCTION GetVariableLength( MidiHandle AS INTEGER, _ Position AS INTEGER) AS INTEGER DIM Value AS INTEGER DIM Character AS BYTE Get #ch, Pos, C: Pos = Pos + 1 Value = C If (Value And &H80) <> 0 Then Value = Value And &H7F Do Value = Value * 128 Get #ch, Pos, C: Pos = Pos + 1 C = C And &H7F Value = Value + C Loop While (C And &H80) <> 0 End If readVarLen = Value End Function ' =============================================== ' Takes a byte (0-255) and returns a HEX string ' =============================================== FUNCTION HexByte(WorkByte AS BYTE) AS STRING DIM WorkText AS STRING WorkText = Hex(WorkByte) If Len(WorkText) = 1 THEN WorkText = "0" + WorkText HexByte = WorkText END FUNCTION ' ================================================ ' If NoteNumber is valid note, Creates Note Name ' and it's octave and returns that in a string ' ================================================ FUNCTION IsNote(ByVal NoteNumber As Long) As String DIM Octave AS INTEGER DIM WorkNote AS INTEGER DIM Note AS STRING Octave = NoteNumber \ 12 WorkNote = NoteNumber Mod 12 SELECT CASE WorkNote + 1 CASE 1 Note = "C" CASE 2 Note = "C#" CASE 3 Note = "D" CASE 4 Note = "D#" CASE 5 Note = "E" CASE 6 Note = "F" CASE 7 Note = "F#" CASE 8 Note = "G" CASE 9 Note = "G#" CASE 10 Note = "A" CASE 11 Note = "A#" CASE 12 Note = "B" END SELECT Note = Note + TRIM$(STR$(Octave)) IsNote = Note END FUNCTION

    With all these tools, we're ready to start the loading process. First thing will do is get some variables ready to read information from the file. We'll open that file in binary mode since we'll be reading bytes from it, binary is the only way to open the file in this case. Note that This is my version of some code I grabbed off the Internet, I altered it considerably for the sake of clarity of code and I commented it to help you better understand. I can say however, that if I would have created it totally it would have probably looked like this so I saw fit to just alter this code.

    ' ---------------------------- ' Work Variable Declarations ' ---------------------------- DIM MIDIString AS STRING DIM SongTracks() AS TrackStructure ' Tracks of a MIDI file

    Now that we have our variables declared, we can go ahead and open the MIDI file in question. I have include the song 1492 a piece from the Christopher Columbus movie for our needs. Now we will be implementing 2 subroutines here. The first is the MIDIFileFormatProperties() function and the other is the Actual ReadMIDIFile Function itself. Since ReadMIDIFile makes use of the ReadMIDIFileFormatProperties function we'll implement that last one first.

    ' ======================================================= ' NAME: ReadMIDIFileFormatProperties() ' PARAMETERS: MidiHandle AS INTEGER ' Position AS INTEGER ' EndOfTrack AS INTEGER ' ASSUMES: MidiHandle links to already Opened File ' RETURNS: A String containing MIDI File Properties ' CALLED FROM: The ReadMIDIFile() Function ' ------------------------------------------------------- ' DESCRIPTION: This function takes the pass MIDI file ' handle and current position in the file ' and gets information from the file that ' it adds to a string. When it is done, ' it will return that string to the ' calling function (ReadMIDIFile in this ' case). ' ======================================================= FUNCTION MIDIFileFormatProperties( BYREF MidiHandle AS INTEGER, _ BYVAL Position AS INTEGER, _ BYREF EndOfTrack AS INTEGER ) AS STRING ' ---------------- ' Work Variables ' ---------------- DIM Counter AS LONG DIM Bytes AS LONG DIM DataByte AS BYTE DIM DataByte2 AS BYTE DIM DataByte3 AS BYTE DIM DataByte4 AS BYTE DIM DataByte5 AS BYTE DIM WorkString AS STRING DIM WorkItem AS STRING * 13 ' ----------------------------------------- ' We Get the next byte from the MIDI file ' ----------------------------------------- GET #MidiHandle, Position, DataByte2 Position = Position + 1 ' ------------------------------------------------------ ' IF the byte read is 0 we can get the sequence number ' ------------------------------------------------------ IF DataByte2 = 0 THEN GET #MidiHandle, Position, DataByte3 Position = Position + 1 IF DataByte3 = 0 Then WorkString = WorkString + "seqnr/posfile" ELSE GET #MidiHandle, Position, DataByte4 Position = Position + 1 GET #MidiHandle, Position, DataByte5 Position = Position + 1 WorkString = WorkString + "Sequence Number " + _ CStr(B5 * 256 + B4) END IF ' ----------------------------------------------------- ' If the byte read is between 1 and 7 inclusively, we ' can get some more specific information. ' ----------------------------------------------------- ELSEIF DataByte2 >= 1 AND DataByte2 <= 7 THEN SELECT CASE DataByte2 CASE 1 WorkItem = "text" + " - " CASE 2 WorkItem = "copyright" + " - " CASE 3 WorkItem = "seq/tr. name" + " - " CASE 4 WorkItem = "instrument" + " - " CASE 5 WorkItem = "lyric" + " - " CASE 6 WorkItem = "marker" + " - " CASE 7 WorkItem = "cue point" + " - " END SELECT WorkString = WorkString & WorkItem Bytes = GetVariableLength(MidiHandle, Position) FOR Counter = 1 To Bytes Get #MidiHandle, Position, DataByte Position = Position + 1 WorkString = Workstring + CHR$(DataByte) NEXT Counter ' ------------------------------------------------------ ' IF the byte read is &H20 we can get the MIDI Channel ' ------------------------------------------------------ ELSEIF DataByte2 = &H20 Then WorkString = WorkString + "midi channel " Get #MidiHandle, Position, DataByte3 Position = Position + 1 Get #MidiHandle, Position, DataByte4 Position = Position + 1 If DataByte3 <> 0 Then WorkString = WorkString + "Length Error" WorkString = WorkString + HexByte(DataByte4) ' ---------------------------------------------------------- ' IF the byte read is &H21 we can get the MIDI Port Number ' ---------------------------------------------------------- ELSEIF DataByte2 = &H21 Then WorkString = WorkString + "midi port " Get #MidiHandle, Position, DataByte3 Position = Position + 1 Get #MidiHandle, Position, DataByte4 Position = Position + 1 If DataByte3 <> 0 THEN WorkString = WorkString + "Length Error" WorkString = WorkString + HexByte(DataByte4) ' ------------------------------------------------------------- ' A byte value of &H21 indicates the End of the current track ' ------------------------------------------------------------- ElseIf DataByte2 = &H2F Then WorkString = WorkString + "end of track " Get #MidiHandle, Position, DataByte3 Position = Position + 1 EndOfTrack = True ' ----------------------------------------------------------- ' A byte value of &H51 Means we're reading the song's tempo ' ----------------------------------------------------------- ELSEIF DataByte2 = &H51 THEN WorkString = WorkString + "tempo " GET #MidiHandle, Position, DataByte3 Position = Position + 1 Bytes = DataByte3 If Bytes <> 3 Then WorkString = WorkString + "Length Error" GET #MidiHandle, Position, DataByte3 Position = Position + 1 GET #MidiHandle, Position, DataByte4 Position = Position + 1 GET #MidiHandle, Position, DataByte5 Position = Position + 1 WorkString = WorkString + _ CStr(CLng(60000000 / CLng(CLng(DataByte3) * _ 256 * 256 + CLng(DataByte4) * 256 + _ CLng(DataByte5)))) + " BPM" ' ----------------------------------------------------- ' A byte value of &H54 Gets SMPTE Offsets Information ' ----------------------------------------------------- ELSEIF DataByte2 = &H54 THEN WorkString = WorkString + "SMPTE Offs " GET #MidiHandle, Position, DataByte3 Position = Position + 1 Bytes = DataByte3 IF Bytes <> 5 THEN WorkString = WorkString + "Length Error" FOR Counter = 1 TO Bytes Get #MidiHandle, Position, DataByte Position = Position + 1 WorkString = WorkString + HexByte(DataByte) NEXT Counter ' ---------------------------------------------------------- ' A byte value of &H58 Indicates the song's time signature ' ---------------------------------------------------------- ELSEIF DataByte2 = &H58 THEN WorkString = WorkString + "time sign " GET #MidiHandle, Position, DataByte3 Position = Position + 1 Bytes = DataByte3 IF Bytes <> 4 THEN WorkString = WorkString + "Length Error" GET #MidiHandle, Position, DataByte4 Position = Position + 1 GEt #MidiHandle, Position, DataByte5 Position = Position + 1 WorkString = WorkString + CStr(DataByte4) + "/" + _ CStr(2 ^ DataByte5) + " - " GET #MidiHandle, Position, DataByte4 Position = Position + 1 WorkString = WorkString + DataByte4 + " clocks/metr.click - " GET #MidiHandle, Position, DataByte5 Position = Position + 1 WorkString = WorkString + DataByte5 + " 32nd/quarter " ' --------------------------------------------------------- ' A byte value of &H59 Indicates the song's key signature ' --------------------------------------------------------- ELSEIF DataByte2 = &H59 THEN WorkString = WorkString + "key sign " GET #MidiHandle, Position, DataByte3 Position = Position + 1 Bytes = DataByte3 IF Bytes <> 2 THEN WorkString = WorkString + "Length Error" FOR Counter = 1 TO Bytes GET #MidiHandle, Position, DataByte Pos = Pos + 1 WorkString = WorkString + HexByte(DataByte) & " " NEXT Counter ' ----------------------------------------------------------- ' A byte value of &H7F Indicates a variable length property ' ----------------------------------------------------------- ELSEIF DataByte2 = &H7F Then Bytes = GetVariableLength(MidiHandle, Position) WorkString = WorkString + "propr.- len " + CStr(Bytes) Position = Position + Bytes END IF ' --------------------------------------------------------------- ' Finally we return the formulated string to the calling module ' --------------------------------------------------------------- MIDIFileFormatProperties = WorkString END FUNCTION

    Quite a function wouldn't you agree? This particular function will be executed in the Track Loop of the ReadMIDIFile() function. Hence it will be able to get this information, if available, for all tracks of a song. Now comes the core function itself. Let us now take a look at the ReadMIDIFile Function itself.

    ' ======================================================= ' NAME: ReadMIDIFile() ' PARAMETERS: File AS STRING ' ASSUMES: File is a valid MIDI file name ' RETURNS: A String containing MIDI File Properties ' CALLED FROM: The main part of the program. ' ------------------------------------------------------- ' DESCRIPTION: this function reads and displays the ' contents of a MIDI file. ' ======================================================= Function ReadMIDIFile(File AS STRING) AS STRING ' ---------------- ' Work Variables ' ---------------- DIM FileHandle AS INTEGER DIM Counter AS INTEGER DIM EventCounter AS INTEGER DIM WorkString AS STRING DIM DeltaTimeString AS STRING * 7 DIM StatusLabel AS STRING DIM SectionLabel AS STRING * 4 DIM EventLabel AS STRING DIM FormatType AS INTEGER DIM NumberOfTracks AS INTEGER DIM Track AS INTEGER DIM Resolution AS INTEGER DIM BeatsPerMinute AS INTEGER DIM NumberOfBytes AS INTEGER DIM ByteRead AS INTEGER DIM StringBytes AS INTEGER DIM Status AS BYTE DIM Position AS INTEGER DIM PreviousPosition AS INTEGER DIM TempPosition AS INTEGER DIM MessageLength AS INTEGER DIM DataByte AS BYTE DIM DataByte1 AS BYTE DIM DataByte2 AS BYTE DIM DataByte3 AS BYTE DIM DataByte4 AS BYTE DIM DataByte5 AS BYTE DIM DeltaTime AS INTEGER DIM CurrentTrack AS INTEGER DIM EndOfTrack AS INTEGER WorkString = WorkString & UCASE$(File)) + CHR$(13) ' ----------------------------- ' First we open the MIDI File ' ----------------------------- FileHandle = FREEFILE OPEN File FOR BINARY AS #FileHandle ' --------------------------- ' We read the first 4 bytes ' --------------------------- GET #FileHandle, 1, SectionLabel IF SectionLabel <> "MThd" THEN WorkString = "Invalid MIDI File Header" GOTO ReadMidiFileEND END IF ' -------------------------- ' We read the next 4 bytes ' -------------------------- GET #FileHandle, 5, DataByte1 GET #FileHandle, 6, DataByte2 GET #FileHandle, 7, DataByte3 GET #FileHandle, 8, DataByte4 IF NOT (DataByte1 = 0 AND DataByte2 = 0 AND DataByte3 = 0 AND DataByte4 = 6) THEN WorkString = WorkString + "Midi Header length is wrong." GOTO ReadMidiFileEND END IF ' ------------------------------------- ' Read the File Format and display it ' ------------------------------------- Get #FileHandle, 9, DataByte1 Get #FileHandle, 10, DataByte2 FormatType = DataByte1 * 256 + DataByte2 WorkString = WorkString & "Format type = " + CStr(FormatType) SELECT CASE FormatType CASE 0 WorkString = WorkString + " - single track any channel" + CHR$(13) CASE 1 WorkString = WorkString + " - multi tracks sep channels" + CHR$(13) CASE 2 WorkString = WorkString + " - multi patterns-songs" + CHR$(13) CASE ELSE WorkString = WorkString + " - Invalid MIDI File Format" GoTo ReadMidiFileEND END SELECT ' -------------------------------------------- ' Read the Number Of Tracks and Display Them ' -------------------------------------------- Get #FileHandle, 11, DataByte1 Get #FileHandle, 12, DataByte2 NumberOfTracks = DataByte1 * 256 + DataByte2 WorkString = WorkString + "Number of Tracks = " & CStr(NumberOfTracks) + CHR$(13) If FormatType = 0 AND NumberOfTracks > 1 THEN WorkString = WorkString + "Invalid Number of tracks for file format" GOTO ReadMidiFileEND END IF ' --------------------------------------------- ' We REDIM the Tracks Array to NumberOfTracks ' --------------------------------------------- REDIM SongTracks(1 To NumberOfTracks) AS TrackStructure ' -------------------------------------------- ' Read the Resolution in PPQN And Display it ' -------------------------------------------- Get #FileHandle, 13, DataByte1 Get #FileHandle, 14, DataByte2 Resolution = DataByte1 * 256 + DataByte2 WorkString = WorkString + "Resolution = " + CStr(Resolution) + " PPQN" + CHR$(13) ' ------------------------------------------------------- ' Offset Position Number to 15 and start loading tracks ' ------------------------------------------------------- Position = 15 FOR Track = 1 TO NumberOfTracks EndOfTrack = 0 SongTracks(Track).TrackNumber = Track GET #FileHandle, Position, SectionLabel IF SectionLabel <> "MTrk" THEN WorkString = WorkString + "Invalid Track Header Section" GOTO ReadMidiFileEND END IF GET #FileHandle, , DataByte1 GET #FileHandle, , DataByte2 GET #FileHandle, , DataByte3 GET #FileHandle, , DataByte4 NumberOfBytes = CLng(CLng(DataByte1) * 256 ^ 3 + _ CLng(DataByte2) * 256 ^ 2 + _ CLng(DataByte3) * 256 + CLng(DataByte4)) WorkString = WorkString + CHR$(13) + "Track " + CStr(Track) + _ " length = " + CStr(NumberBytes) + CHR$(13) Position = Position + 8 PreviousPosition = Position Status = 0 ' ---------------------------------------------- ' This WHILE Loop gets the data from the track ' ---------------------------------------------- WHILE Position - PreviousPosition < NumberOfBytes GET #FileHandle, Position, DataByte1 If DataByte1 = &HFF Then Position = Position + 1 Status = B1 EventLabel = MIDIFileFormatProperties(FileHandle, Position, EndOfTrack) Stat = " " & HexByte(Status) & " " ELSE DeltaTime = GetVariableLength(FileHandle, Position) DeltaTimeString = CStr(DeltaTime) GET #FileHandle, Position, DataByte1 If (DataByte1 And &H80) = &H80 Then Status = DataByte1 StatusLabel = " " & HexByte(Status) & " " Position = Position + 1 Else StatusLabel = "r" & HexByte(Status) & " " End If SongTracks(Track).MIDIEvents(EventCounter).DeltaTime = DeltaTime SongTracks(Track).MIDIEvents(EventCounter).StatusByte = HighNibble(Status) SongTracks(Track).MIDIEvents(EventCounter).ChannelByte = LowNibble(Status) ' --------------------------------------- ' This Select Case loads the parameters ' based on the MIDI Event Type. ' --------------------------------------- SELECT CASE HighNibble(Status) Case NoteOff Get #FileHandle, Position, DataByte2 Position = Position + 1 Get #FileHandle, Position, DataByte3 Position = Position + 1 EventLabel = "Note off.... " + isNote(DataByte2) + "-" + CStr(DataByte3) Case NoteOn Get #FileHandle, Position, DataByte2 Position = Position + 1 Get #FileHandle, Position, DataByte3 Position = Position + 1 EventLabel = "Note on..... " + isNote(DataByte2) + "-" + CStr(DataByte3) CASE PolyKeypress Get #FileHandle, Position, DataByte2 Position = Position + 1 Get #FileHandle, Position, DataByte3 Position = Position + 1 EventLabel = "Poly Keypress... " + CStr(DataByte2) + "-" + CStr(DataByte3) Case ControllerChange Get #FileHandle, Position, DataByte2 Position = Position + 1 Get #FileHandle, Position, DataByte3 Position = Position + 1 EventLabel = "Ctl Change.. " + HexByte(DataByte2) + " " + HexByte(DataByte3) Case ProgramChange Get #FileHandle, Position, DataByte2 Position = Position + 1 EventLabel = "Prg Change.. " + HexByte(B2) Case ChannelPressure Get #FileHandle, Position, DataByte2 Position = Position + 1 EventLabel = "Chan Press.. " + HexByte(B2) Case PitchBend Get #FileHandle, Position, DataByte2 Position = Position + 1 Get #FileHandle, Position, DataByte3 Position = Position + 1 EventLabel = "Pitch bend.. " + HexByte(DataByte2) + " " + HexByte(DataByte3) Case SysExMessage Select Case Status Case &HFE Case &HFF EventLabel = MIDIFileFormatProperties(FileHandle, Position, EndOfTrack) Case &HF0 TempPosition = Position MessageLength = GetVariableLength(FileHandle, Position) EventLabel = "SysEx - len: " & CStr(MessageLength) Position = Position + MessageLength Case &HF7 Case Else End Select End Select ' --------------------------------------------------- ' We Populate our Structure with It's Proper Values ' --------------------------------------------------- SongTracks(Track).MIDIEvents(EventCounter).DataByteOne = DataByte2 SongTracks(Track).MIDIEvents(EventCounter).DataByteTwo = DataByte3 End If SongTracks(Track).TrackEventCount = SongTracks(Track).TrackEventCount + 1 IF EventLabel <> "" THEN WorkString = WorkString & deltaT & Stat & reg + CHR$(13) EventLabel = "" END IF EventCounter = EventCounter + 1 WEND NEXT Track ' -------------------------------------------------------- ' Close The File Handle and return The Formulated String ' -------------------------------------------------------- ReadMidiFileEND: CLOSE #FileHandle readMidiFile = WorkString END FUNCTION

    There you have it This last function does the actual opening of the MIDI file and drives the loading of the MIDI events into our Structure. Although these are long functions. I think it's fairly clear to see what's going in here. Note that the EventLabel that gets populated in each of the Event Types is there for informational purposes only. When we actually do something with the MIDI File (in the next part of the series) the parts of this code that create a string for display will be taken off.

    The main part of the program would simply need to call the ReadMIDIFile with the file 1492.mid as it's parameter for the whole system to load the file into our SongTracks MIDIEvents array of structures. Such as:

    ' ------------------------------------------------------------ ' Call The Read MIDI File to load the song in memory ' and report some information about the song and it's tracks ' ------------------------------------------------------------ MIDIString = ReadMIDIFile("1492.mid")

    IN CONCLUSION:

    And there you have it, as promised, we now have a system that can effectively load a file into a structure that can be manipulated as you would any other array. The basic reason why SysEx information is treated so differently is simply because it is not a fixed piece of information. Anything can happen in SysEx (anything that is allowed by the MIDI gear itself that is). The basic idea is to load up the SysEx information in a string and send that information right to the MIDI port and MIDI channel you want. The MIDI Gear will receive this information and if it can (if one piece of equipment connected to the MIDI network can process the SysEx, it will). Other than that, it's a bunch of parameters to standard MIDI event types.

    In the next part of the series, we'll implement the playback engine itself. This is where timing will take it's importance as we will have a program that will load the MIDI file and actually play it in realtime. With that part, all of what you've learned in the past 2 parts of the series will really fit together as you'll see how everything is connected together to form a complete MIDI player. OF course, if you have questions or comments, you know how to reach me. So by all means please do so so we can make sure everyone is up to par on what I just did in this part of the series. It will be important to understand what's happening here before we go to the next part of the series. Until then, Happy MIDI programming.
    MystikShadows
    Stéphane Richard
    mystikshadows@gmail.com


    Download a copy of this tutorial: midifilespart2.html


    Final Word

    Well, that's all for the nineteenth issue of QB Express! Many thanks go out to all of our contributors this month, and to you, our loyal readers, for hanging in there while you waited for me to finally get this issue out.

    Submissions for the April issue of QB Express (yes, it will come out in April) are due by April 15th. As always, send them to pberg1@gmail.com.

    Until next time...END.

    -Pete


    Copyright © Pete Berg and contributors, 2006. All rights reserverd.
    This design is based loosely on St.ndard Bea.er by Altherac.

    QB Top 50 - The best QBasic and QuickBasic Sites