RPG Toot Part V: Dungeon Mapping - Exploring the Dungeon
There are many different ways to handle mapping a dungeon, which is basically keeping track, for the player, where the character has been so the player doesn't have to draw an actual map (many older games used to make the player map for themselves, but most players found this more tedious than fun). In the toot version, simply once an area is explored, it's uncovered and "visible". To do this, I had to make some changes to the work done before (remember, I haven't been planning this out, just going along).
Basic Changes:
~Eliminated Sprites: The main reason I did this was because of an annoying blue background I couldn't seem to get rid of. Frustrated with it, I started deleting code until I found out it was the sprites themselves causing the problem. So, no more sprites.
~Pasting Images: Instead of sprites, I am now pasting the images that were previously used for sprites. This seems to work fairly well except for one problem...
~Erasing Images: Unlike sprites, pasted images don't delete themselves once you are done with them (i.e. move them somewhere else). So, we have to be clever and erase the images once we move them (mainly the player and the monster).
That's the changes, and I've tried to keep up the rem statements. Here's what was added:
~A blank image to hide the map so the player can't see it.
~Small Wall and Floor images (gray and black, but could be more detailed like the tiles in Phaelax's code) that are used to draw over the blank image, revealing the map area (see the "Clear Light" function).
~Monster checks to see if it's in an unhidden area before drawing itself. Monster also undraws it's last move.
~Clear_Light() function draws the dungeon around the player. The overall (global) dungeon isn't drawn - it's just locally drawn around the player as the player moves along. We still have a map-image just in case we need it later (we can always delete it if we don't use it).
`First, we are going to set up our player character (pc) variables.
`We will use a User Defined Type because it makes this code easy to read.
`I use floats for the stats, even though they will only be integers, because
`we may want to do some math with flaots later on. I don't want DB to convert
`the results to an integer unless we tell it to.
`I also expect to return to this and add more or make changes, but this is a good start.
Type Character
Str as Float
Dex as Float
Con as Float
Int as Float
HP as Float
Gold as Float
Level as Float
Name as String
EndType
`Notice that we CAN use INT, a reserved word, as a User Defined Type.
`Declare our player as a global variable.
Global Player as Character
` This is an array that will hold our map level.
Dim Dungeon(30,30)
Global Startx as Integer
Global Starty as Integer
Global Exitx as Integer
global Exity as Integer
Global PlayerX as Integer
Global PlayerY as Integer
Global LastTime as DWord
Global MonsterX as Integer
Global MonsterY as Integer
Global MonsterTimer as DWord
Global WallGray as DWord
WallGray=Rgb(100,100,100)
`Now, create our character.
CLS
Set Cursor 0,0
Input "Enter your character's name:",Player.Name
Print
Print "Welcome ";Player.Name
Wait 1000
`Generate some stats.
Do
Cls
Set Cursor 0,0
Print Player.Name;", press [R] to re-roll stats, or [Y] to accept stats."
Print
`Randomize the stats. Picks a number from 2 to 10: rnd(4)=0-4. So 0-4 plus 0-4 equals 0-8.
`Plus 2 = 2 to 10. There is also more of a chance of 5&6 results than there are of 2 or 10.
Player.Str=rnd(4)+rnd(4)+2
Player.Dex=rnd(4)+rnd(4)+2
Player.Con=rnd(4)+rnd(4)+2
Player.Int=rnd(4)+rnd(4)+2
Player.HP=Player.str+player.con
Player.Gold=(Player.Dex+Player.Con)*2
`I based gold on Intelligence and dexterity since the smart and/or quick person (thief?)
`should have more. And to balance that Hit Points is based on Str and Con.
`Print the stats out.
Print "Strength: ";Player.Str
Print "Dexterity: ";Player.Dex
Print "Constitution: ";Player.Con
Print "Inteligence: ";Player.Int
Print "Health: ";Player.HP
Print "Gold: ";Player.Gold
`Let the player choose to keep the stats or re-roll.
Do
a$=Lower$(Inkey$())
If a$="r" or a$="y" Then Exit
Loop
if a$="y" Then Exit
Loop
Player.level=1
`This is added to create our player
Make_Player()
`We moved the Dungeon Construction code over to a function. This will make it easier to call from anywhere.
Randomize_Dungeon(Timer())
Draw_Dungeon()
playerx = startx
playery = starty
lastpx = playerx
lastpy = playery
Start_Monster()
LastTime=Timer()
Paste Image 1001,0,0
Do
`Move the player
playerx=playerx+rightkey()-leftkey(): `Left and right movement
playery=playery+downkey()-upkey(): `Up and down movement
`Check for a valid move and if enough time since last move has past.
if Dungeon(playerx,playery)<1 or (Timer()-LastTime)<200
playerx=lastpx
playery=lastpy
Endif
`Reset timer if player was moved. This keeps us from zooming across the dungeon.
IF lastpx<>playerx or lastpy<>playery
LastTime=Timer()
Endif
`And reposition the player.
Clear_Light(PLayerX,PLayerY,2)
Move_Monster()
Paste Image 1,(playerx-1)*15,(playery-1)*15: `Player
`Track our player's last position.
lastpx=playerx
lastpy=playery
Loop
Wait Key
End
`Draw Dungeon around the Player or monster
Function Clear_Light(dx,dy,r)
For x=dx-r To dx+r
For y=dy-r To dy+r
If x<31 and y<31
If Dungeon(x,y)=1
Paste Image 1002,(x-1)*15,(y-1)*15
Else
Paste Image 1003,(x-1)*15,(y-1)*15
Endif
If x=Startx and y=Starty
Ink Rgb(0,255,0),0
Box (x-1)*15,(y-1)*15,x*15,y*15
Endif
If x=Exitx and y=Exity
Ink Rgb(255,0,0),0
Box (x-1)*15,(y-1)*15,x*15,y*15
Endif
If x=MonsterX and y=MonsterY
Paste Image 2,(x-1)*15,(y-1)*15
Endif
If x=PlayerX and y=PLayerY
Paste Image 1,(x-1)*15,(y-1)*15
Endif
Endif
Next y
Next x
EndFunction
Function Randomize_Dungeon(seed)
Randomize seed
`Create a Dungeon Level
`Codes:
`-1 = Solid wall. These walls are permanent and should not get replaced.
` 0 = Wall. These walls are temporary and can be replaced with ether open area or solid walls.
` 1 = Open. These are open areas and also should not be replaced.
`First, clear out the dungeon.
For x = 0 to 30
For y = 0 to 30
Dungeon(x,y)=0
Next y
Next x
`First define our outside walls as solid.
For i = 1 to 30
Dungeon(i,1)=-1
Dungeon(i,30)=-1
Dungeon(1,i)=-1
Dungeon(30,i)=-1
Next i
`Select our starting point "our entrance"
startx=rnd(20)+5
starty=rnd(20)+5
Dungeon(startx,starty)=1
`Now do our random map from that point.
lastx=startx
lasty=starty
Set Cursor 0,0
Do
do
`the random statements pick a number from -1 to 1 (or 0 to 2 minus 1).
r=(rnd(1)*2)-1
c=0
if rnd(2)=1 then s=rnd(1) `this is used to keep a path going in the same direction.
if s=1
t=r:r=c:c=t `swap column and row
Endif
newx=lastx + r
newy=lasty + c
if Dungeon(newx,newy)=0 Then Exit
if rnd(40)=3 `This keeps us from trying new directions forever.
n=-1
Do
newx=rnd(28)+2
newy=rnd(28)+2
if Dungeon(newx,newy)=0 or (Dungeon(newx,newy)=1 and rnd(20)=1)
Dungeon(newx,newy)=0
exit
Endif
if rnd(100)<n `Again, when we run out of places, don't want to keep trying forever.
newx=0:newy=0 `setting these to 0 will tell us we're done.
Exit
Endif
inc n
Loop
Endif
if newx=0 and newy=0 then exit
if Dungeon(newx,newy)=0 Then exit
Loop
if newx=0 and newy=0 then exit
Dungeon(newx,newy)=1 ` set our new open space.
`this next part puts up some random solid walls around the last position.
For i =-1 to 1
If Dungeon(lastx+i,lasty)=0 and rnd(2)=0
Dungeon(lastx+i,lasty)=-1
Endif
Next i
For i =-1 to 1
If Dungeon(lastx,lasty+i)=0 and rnd(2)=0
Dungeon(lastx,lasty+i)=-1
Endif
Next i
`Draw_Dungeon()
lastx=newx
lasty=newy
Loop
Endfunction
Function Draw_Dungeon()
cls
Get Image 1002,0,0,15,15,1: ` A blank image for drawing clear space
Ink WallGray,0
Box 0,0,15,15
Get Image 1003,0,0,15,15,1: ` A blank image for drawing wall space
Ink Rgb(50,50,50),0
Box 0,0,30*15,30*15
Get Image 1001,0,0,30*15,30*15,1: ` A big blank square to cover our area.
Ink WallGray,0
For x= 1 to 30
For y = 1 to 30
If Dungeon(x,y)<1
Box (x-1)*15,(y-1)*15,x*15,y*15
Endif
Next y
Next x
`Draw our Entrance
Ink Rgb(0,255,0),0
Box (startx-1)*15,(starty-1)*15,startx*15,starty*15
`And last, make and draw our exit.
Do
exitx=Rnd(27)+2
exity=rnd(27)+2
If Dungeon(exitx,exity)=1
d=(exitx-startx)*(exitx-startx)+(exity-starty)*(exity-starty)
if d>8 Then exit
Endif
Loop
Ink Rgb(255,0,0),0
Box (exitx-1)*15,(exity-1)*15,exitx*15,exity*15
Get Image 1000,0,0,30*15,30*15,1:` A picture of our map (a treasure for later on).
Endfunction
Function Make_Player()
`What we do here is simply read in the data statements for each pixel, then capture the image.
`c is our color. If we read c, and it's 0, then c= 0. Otherwise, if c=1, it will convert to white: rgb(255,255,255)
Ink 0,0
Box 0,0,25,25
Local c as Dword
Restore Character_Pic
For y=0 to 14
for x=0 to 14
read c
c=c*Rgb(255,255,255) `"c" is either 0 or 1. Multiply by White: RGB(255,255,255) and either get 0, or white.
Dot x,y,c
Next x
Next y
Get Image 1,0,0,15,15,1
Endfunction
Function Make_Monster()
Ink 0,0
Box 0,0,25,25
Local c as Dword
`We will be making changes to this later on to select more than one monster type
Restore Spider_Pic
For y=0 to 14
for x=0 to 14
read c
c=c*Rgb(255,50,50): `"c" is either 0 or 1. Multiply by White RGB(255,255,255) and either get 0, or white.
Dot x,y,c
Next x
Next y
Get Image 2,0,0,15,15,1
`We will be adding in moster stats in the next update.
Endfunction
Function Start_Monster()
Make_Monster()
Do
x=rnd(27)+2
y=rnd(27)+2
if Dungeon(x,y)=1 Then Exit: `Looking for an open space
Loop
MonsterX=x
MonsterY=y
MonsterTimer=Timer()
Endfunction
Function Move_Monster()
If Timer()-MonsterTimer<1000 Then ExitFunction: `We only want the monster to move once a second (for now).
MonsterTimer=Timer(): `Reset our MonsterTimer
xdir=(PlayerX>MonsterX)-(PlayerX<MonsterX): `Add 1 if player is to the right, subtract 1 if the player it to the left.
ydir=(PlayerY>MonsterY)-(PlayerY<MonsterY): `Same as the x, but verticle.
`Now check for valid moves.. and move the monster
nx=MonsterX+xdir
ny=MonsterY+ydir
If Dungeon(nx,ny)=1
`Erase the previous monster position if the monster was not hidden.
If Point((MonsterX-1)*15+7,(MonsterY-1)*15+7)<>WallGray Then Paste Image 1002,(MonsterX-1)*15,(MonsterY-1)*15
MonsterX=nx
MonsterY=ny
`Draw the monster if the monster is not hidden.
If Point((nx-1)*15+7,(ny-1)*15+7)<>WallGray Then Paste Image 2,(nx-1)*15,(ny-1)*15
ExitFunction
Endif
nx=MonsterX+xdir
ny=MonsterY
If Dungeon(nx,ny)=1
If Point((MonsterX-1)*15+7,(MonsterY-1)*15+7)<>WallGray Then Paste Image 1002,(MonsterX-1)*15,(MonsterY-1)*15
MonsterX=nx
MonsterY=ny
If Point((nx-1)*15+7,(ny-1)*15+7)<>WallGray Then Paste Image 2,(nx-1)*15,(ny-1)*15
ExitFunction
Endif
nx=MonsterX
ny=MonsterY+ydir
If Dungeon(nx,ny)=1
If Point((MonsterX-1)*15+7,(MonsterY-1)*15+7)<>WallGray Then Paste Image 1002,(MonsterX-1)*15,(MonsterY-1)*15
MonsterX=nx
MonsterY=ny
If Point((nx-1)*15+7,(ny-1)*15+7)<>WallGray Then Paste Image 2,(nx-1)*15,(ny-1)*15
ExitFunction
Endif
If Point((MonsterX-1)*15+7,(MonsterY-1)*15+7)<>WallGray Then Paste Image 2,(MonsterX-1)*15,(Monster-1)*15
`If we get to here, there are no valid moves and the monster stays put.
Endfunction
Character_Pic:
data 0,1,0,0,0,0,0,1,1,0,0,0,0,0,0
data 0,1,0,0,0,0,1,1,1,1,0,0,0,0,0
data 0,0,1,0,0,0,1,1,1,1,0,0,0,0,0
data 0,0,1,0,0,0,0,1,1,0,0,0,0,0,0
data 0,0,0,1,0,0,1,1,1,1,1,0,0,0,0
data 0,0,0,1,0,1,1,1,0,0,1,1,1,0,0
data 0,0,0,0,1,1,0,1,0,1,1,1,1,0,0
data 0,0,0,0,1,0,1,1,1,0,1,1,0,0,0
data 0,0,0,0,0,0,1,1,1,0,1,1,0,0,0
data 0,0,0,0,0,0,1,1,1,1,1,0,0,0,0
data 0,0,0,0,0,0,1,1,0,1,1,0,0,0,0
data 0,0,0,0,0,0,1,1,0,1,1,0,0,0,0
data 0,0,0,0,0,0,1,1,0,1,1,0,0,0,0
data 0,0,0,0,0,1,1,1,0,1,1,1,0,0,0
data 0,0,0,0,0,1,1,1,0,1,1,1,0,0,0
Spider_Pic:
data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
data 0,0,1,0,0,1,0,0,0,1,0,0,1,0,0
data 0,1,0,1,0,1,0,0,0,1,0,1,0,1,0
data 0,1,0,1,0,0,1,1,1,0,0,1,0,1,0
data 0,1,0,0,1,1,1,1,1,1,1,0,0,1,0
data 1,0,0,0,1,1,0,1,0,1,1,0,0,0,1
data 1,0,1,0,1,1,1,1,1,1,1,0,1,0,1
data 1,0,1,0,0,1,1,1,1,1,0,0,1,0,1
data 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
data 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
data 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1
I think you can start to get the feel for the game by now. Your character explores the dungeon, watching out for lurking monsters. This is the primary catch. The next catch we will add in is the ability to fight the monster when we come across it. The game gets a bit scarier then. After that, we will add some treasure, some traps, and work on the stairs. If I have time, I'll throw in a "store" and maybe a few magic items.
I'm only working with one monster at a time in the tutorial. With a bit of work, several monsters can be added for more challenge.
"Droids don't rip your arms off when they lose." -H. Solo
REALITY II