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

Mini Space Rogue - Source Code Elementary Instructions

by Lachie D.(January, 2006)

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

If you are expecting deep-through instructions you are
on the wrong place. This is just elementary stuff so
you wouldn't be COMPLETELY lost. The code is somewhat 
spaghetti and not very "economic" since this was meant 
to be a "quick" project. Sorry about that. Just be aware 
that I'm AWARE of all the bad things in the code.

The graphics files:
-------------------

SROGUE0.PUT - SROGUE5.PUT - Various sprites used in
the game from icons, NPCs, etc. The are scattered all
around the PUT files without much logic.

SROGT(levnum).PUT - Ship level "levnum" non-light 
                    reflecting tiles
SROGT(levnum)1.PUT - Ship level "levnum" light
                     reflecting tiles(real walls)

levnum if a number from 1-6(did I have to say that?).

Fonts are stored in FNT files. Level maps are stored
in MAP files(DATA directory).

PUT and FNT files are PP256 compatible graphic files.


Main game variables and subs:
-----------------------------

Mode variable flags if the player is in non-battle mode
(Mode = 1) or in battle mode(Mode = 2).

Turn variable flags if it's the player's turn(Turn = 1;
the player has control over the interface) or NPCs'
turn(Turn = 2; player hasn't got control over the
interface - cursor turns into red X).

BattleTurn flags whose turn it is during battle
(BattleTurn = 1 - it's player's turn).

WorkingMode flags which option(operate with, examine, etc.)
is active and which sub to execute according to it.

ArrowStatus and IconStatus flag on which icon the mouse
cursor is in order to draw a square around it and which
WorkingMode to initiate it the player clicks.

The main loop is placed in the "Main" sub where the code 
for player's movement is placed and where main "drawing"
sub is called(Engine.DrawScreen). Subs Engine.DrawCorpses, 
Engine.DrawLockers, Engine.DrawPlayer and Engine.DrawNPC
draw corpses, lockers, player and NPCs on the screen. Main
interface sub is Engine.DrawInterface where the entire
interface is drawn and the place where the code that manages 
submenus is placed(which sub to load according to Mode and 
WorkingMode variables).

The following piece of code shows how this is done:

if Mode = 1 then
if WorkingMode = 1 then Engine.DrawStatus
if WorkingMode = 2 then Engine.DrawWepInv
if WorkingMode = 3 then Engine.DrawWepInv
if WorkingMode = 4 then Engine.DrawEquipInv
if WorkingMode = 5 then Engine.DrawItemsInv
if WorkingMode = 6 then ExamineLocation
if WorkingMode = 7 then OperateWith
if WorkingMode = 9 then Engine.DrawSaveGame
if WorkingMode = 13 then Elevator

....


About NPCs
----------

NPCs are managed with Engine.DrawNPC sub where the program
loops through ALL the NPCs to DRAW them but MOVES/MANAGES 
them ONE BY ONE. The current NPC that is moved is managed 
with currentNPC variable. If the currently active NPC 
shouldn't be moved or the movement of the NPC is finished we 
add 1 to currentNPC variable with:
currentNPC = currentNPC + 1(changes the active NPC!). 

If the currently active NPC ends up on the player the battle 
is initiated.

During battle the program manages NPC(Location, currentNPC) - 
the currently ACTIVE NPC. Very good system, if I may say so.


About loading lockers and doors from the MAP files
--------------------------------------------------

Each time we load a map file we load lockers and doors
(with every new game) or just count their number(when
we load a game or change the ship level). 

For lockers we check in the MAP file for a tile number 
156(image of a locker) and then flag the proper position 
of a locker.

For doors we check for a tile number 296(vertical door - button
tile) or 311(horizontal door - button tile) and then flag
the proper door position and mode(opened or not).

So you need do input lockers and doors in the MAP files. 
Doors are not being draw since they are drawn with tiles. 
When you open/close a door the program simply changes the 
door tile(when you open a door we change the door tile of
that specific door to 1 -> clear floor).


The map array and lighting:
---------------------------

To enable lighting I created 5 versions of each tile in
the tiles PUT files, each version representing one level
of illumination. Each tile is sorted from the most
lighted one to the darkest one. You will notice with
non-light reflecting tiles(in the PUT files) that the first 
version and the second version are the same(graphically). 
That's because initially I wanted to have 5 levels of 
illumination but ended up using 4 which left the first 
versions(them most lighted) unused. Later this tile 
position became crucial with light reflecting tiles(read on). 
Non-reflecting tiles means that they are not reflecting 
light(light goes through them or covers them). No all 
colliding tiles block the light thus the "non-reflecting 
tiles" expression.

Map array is dimensioned with 3 elements. The first flags
the location(ship level), the second flags the X tile position 
while the third position flags the Y tile position. Like this:
Map(Location, Xtile, Ytile).TypeName

There are 3 variables in the Map's type-def. 
Map(Location, Xtile, Ytile).BaseL - flags the tile number
Map(Location, Xtile, Ytile).LightStat - flags the specific 
                                        tile's light status
Map(Location, Xtile, Ytile).FogStat - flags if the tile is
                                      revealed(fogged or not)

Location is an important variable used to flag on which ship
level the player is and according to it the program manages
the proper map, NPCs, lockers, doors and corpses.

In the Engine.DrawMap when the program loops through the 
map(not entire map but this is not important) we pass 
tile numbers to a certain variable(tilep) with this 
line(simplified code):

tilep = Map(Location, X, Y).BaseL
tilep = tilep + 4 

As you see I add 4 tile positions to the current tile. Why?
Because I want to make the tiles to be darkest by default
(the last version of each tile). Example: tile number loaded
from the map is 1. We add 4 to 1 and get 5. Number 5 is the 
darkest version of the tile number 1(in the MAP file).

Light status of all tiles is 0(not lighted) by default.

Map(Location, X, Y).LightStat = 0

The lines that follow in Engine.DrawMap sub is just a bunch 
of dirty code that blocks the light to pass through the 
light reflecting tiles(tiles above position 170 which 
are placed in SROGT(levelnum)1.PUT files). When I'm 
"illuminating" tiles according to player's positing I'm 
using a sub named "AddLight". All that this sub does is 
illuminates a tile if it's not being illuminated by a stronger 
light(it's because the player is not the only source of 
light in the game). AddLight sub simply changes the LightStat 
of a specific tile according to the passed illumination(it
can be from 1 - weakest illumination to 3 - strongest
illumination). To make all this illumination work I 
need the following line:

tilep = tilep - Map(Location, X, Y).LightStat

The entire magic is in that line. If, for example, the
tile is illuminated by 3(LightStat of that tile is 3)
and the tilep = 5 we'll get tilep = 2(the most
lighted version of a tile since tile number 1 is not
used and equals(graphically) tile number 2). Same thing 
goes for tiles 6, 11, 16, 21, 26 and so on. The numbers 
in the MAP files must represent the first versions of 
tiles(1, 6, 11, ...) or you'll mess up the lighting(the 
map maker takes care of that).

It's important to say that graphic images of "wall" tiles 
are stored in the "Tiles2" array while the regular 
tiles(non-light reflecting ones) are placed in the "Tiles" 
array. When tilep is above 170 the program displays a tile 
from "Tiles2" array(PUT statement) while when tilep is under 
171 a tile from "Tiles" array. I'm using this method because 
I can't fit all the tiles in one PUT file(PP256 64 KB limit). 
When I display a tile from "Tiles2" array I need to reduce 
tilep by 170 since tiles are counted from 1 in the PUT 
files. This might be confusing but never mind. It's not 
important for lighting that much.

Walls(tiles above position 170) are lighted by a special
sub(UnFogWalls) which lights only the walls. I should mention
that all illuminated versions of wall tiles are the
same(graphically) to make the walls more distinctive from 
the floor. The first version of each tile that is unused with 
non-light reflecting tiles came very handy when I wanted
to enable "the opposite side of a wall" effect. I made all 
the wall tiles that are under the player, are lighted and 
are in horizontal direction(you can see their face) not 
to light entirely(only the top of them). Otherwise they 
would be lighted entirely and that wouldn't be realistic(the 
light would seem like it passes through a wall and lights 
the opposite face of it). I simply made the first 
versions(on positions 171, 176, 181, ...) of all the wall 
tiles that are facing the screen with one side to light only 
in the top and other versions left the same. Then I told the 
computer to only use the first version of any lighted wall 
tile when that tile is under the player(AddLight sub). This 
would be more difficult to do if the wall tiles had more than 
one level of illumination. This system, in theory, can provide 
more complex solutions like "half-lighting" of vertical walls 
but this wouldn't be that easy to execute.


****END OF DOCUMENT
