Thanks Slayer!
Here's some more information about Statics. Specifically the Static Map.
That static map, as mentioned before, is a rough copy of the terrain map. For example; where there are roads on the Advanced Terrain, there should also be roads on the Statics Map. The colors used on the Statics Map are determined by the Statics script file. Here is our file:
*** Static Objects
***
*** Format:
***
**** File Name;Red;Green;Blue;X Base;Y Base;Z Base;X Add;x Add;y Addz;Percentage;Cull;Fog;GrassFlag
***
***
*** Details:
***
**** File Name (Text): Relative File Location and Name of the Static Object
***
**** Red, Green, Blue (Integers): RGB from the Color Map assigned to Static Object
***
**** X Base, Y Base, Z Base (Integers): Base Scale for Static Object
**** X Add, Y Add, Z Add (Integers): Random add values for Static Object Scale
***** i.e. X Base=60 and X Add=40, the Static object will be scaled 60+Rnd(40) or 60% to 100%
***
**** Percentage (Integer): Percent chance of Static Object being used.
***
***
*** Grass1
WorldMediaStaticsGrass4.x;0;255;0;45;38;45;20;30;20;40;0;1;1
*** Grass2
WorldMediaStaticsBush1.x;0;255;0;65;50;65;20;20;20;30;0;1;1
*** Grass3
WorldMediaStaticsGrass2.x;0;255;0;45;38;45;20;30;20;30;0;1;1
*** Grass4
WorldMediaStaticsGrass1.x;0;255;0;45;38;45;20;30;20;30;10;1;1
*** Grass5
WorldMediaStaticsRoad2.x;0;255;0;45;35;45;20;30;20;10;1;1;0
*** Purple Flowers
WorldMediaStaticsFlowers1.x;255;255;0;45;35;45;20;20;20;50;0;1;1
*** Purple Flowers
WorldMediaStaticsFlowers1.x;255;255;0;45;35;45;20;20;20;50;0;1;1
*** Yellow Flowers
WorldMediaStaticsFlowers2.x;255;128;128;60;45;60;20;20;20;50;0;1;1
*** Yellow Flowers
WorldMediaStaticsFlowers4.x;255;128;128;60;45;60;20;20;20;50;0;1;1
*** Road1
WorldMediaStaticsRoad1.x;128;128;0;60;50;60;40;10;30;80;1;0
*** Road2
WorldMediaStaticsRoad2.x;128;128;0;60;50;60;40;10;30;30;1;0
*** Trees1
WorldMediaStaticsTree1.x;200;200;0;50;50;50;10;10;10;100;0;1
*** Forest Trees1
***WorldMediaStaticsForest Grove1.x;10;80;0;100;100;100;5;5;5;100;1;1
*** ShadowGrass1
WorldMediaStaticsShadowGrass1.x;0;50;0;50;35;50;20;30;20;60;1;1
*** ShadowGrass2
WorldMediaStaticsShadowBush1.x;0;50;0;50;35;50;20;30;20;100;1;1
*** ShadowRoad1
WorldMediaStaticsShadowRoad1.x;50;50;0;60;50;60;40;10;30;100;1;0
*** Herbivore Grass1
WorldMediaStaticsGrass4.x;25;150;0;45;35;45;20;30;20;40;1;1
*** Herbivore Grass2
WorldMediaStaticsGrass2.x;25;150;0;45;35;45;20;30;20;30;1;1
*** Herbivore Grass3
WorldMediaStaticsGrass1.x;25;150;0;45;35;45;20;30;20;30;1;1
*** Herbivore Grass4
WorldMediaStaticsRoad2.x;25;150;0;45;35;45;20;30;20;10;1;1
***LightRay
*WorldMediaStaticsLightRay.x;254;254;254;50;250;50;10;10;10;100;0;0
*** The last object is always water and the color coding must be set to 1,1,1
*** Water
WorldMediaStaticsWater5.x;1;1;1;100;110;100;10;0;10;100;0;0
WorldMediaStaticsWater5.x;1;1;1;100;110;100;10;0;10;100;0;0
As you can see, the format starts with the file name, then Red, Green, and Blue (I will briefly cover the rest of the items later). These RGB values are the values to paint on the Statics Map in order to have the appropriate static objects appear in that location on the terrain. As you can see, for the Grass Statics, the color RGB(0,255,0) is used. So, everywhere a green pixel on the Static Map is read, the code places a Grass Static Object.
You might be wondering how the pixels are read from the respective memblocks. This was a simple matter of writing a few functions to convert x/y image coordinates to a Memblock position. First, the first 3 DWords (12 bytes) of an image Memblock are the Width, Height, and Color Depth of the image. The next DWord is the x,y = 0,0 location on the image. If an Image is 64x64x32 pixels, the first 64 DWords (after the first 12) in the Memblock is are all the x coordinates along the y=0 row (x,y = 0->63,0). The next 64 DWords are the x coordinates along the y-1 row (x,y = 0->63,1). And so on. Here are the functions used to convert a pixel location into a Memblock location:
Function Get_Pixel_Pos(x,y,MemblockID)
p as dword
p=0
if Memblock Exist(MemblockID)=0 Then Exitfunction p
w=Memblock_Width(MemblockID)
h=Memblock_Height(MemblockID)
d=Memblock_Depth(MemblockID)
p=y*(w*(d/8))+x*(d/8)
max as dword
max = h*w*(d/8)
if p>Get Memblock Size(MemblockID)-1
p=Get Memblock Size(MemblockID)-1
endif
p=p+12
if p<12 then p=12
Endfunction p
Function Memblock_Width(MemblockID)
If Memblock Exist(MemblockID)=0 then Exitfunction 0
If Get Memblock Size(MemblockID)<12 then exitfunction 0
w=Memblock Dword(MemblockID,0)
Endfunction w
Function Memblock_Height(MemblockID)
If Memblock Exist(MemblockID)=0 then Exitfunction 0
If Get Memblock Size(MemblockID)<12 then exitfunction 0
h=Memblock Dword(MemblockID,4)
Endfunction h
Function Memblock_Depth(MemblockID)
If Memblock Exist(MemblockID)=0 then Exitfunction 0
If Get Memblock Size(MemblockID)<12 then exitfunction 0
d=Memblock Dword(MemblockID,8)
Endfunction d
The result of the Get_Pixel_Pos() function is the Memblock byte that starts that pixel's DWord. So, to get the color of the pixel at a specific location the code would look like this:
p=Get_Pixel_Pos(x,y,1)
color as DWord
color=Memblock Dword(1,p)
The returned color from Memblock 1 (our Statics Map image) is compared to the loaded Statics database (stored in an array), which returns the type of static that belongs at the location x,y. Assuming no static has been previously assigned to the x,y location (which is stored in Memblock 2), the Unused_Static() function returns a Static Object to use.
Function Unused_Static(StaticType)
Flag=0
For i = 0 to MaxStatic
Inc LastStaticChk(StaticType)
If LastStaticChk(StaticType)>MaxStatic Then LastStaticChk(StaticType)=0
If Object Position Y(Static(StaticType,LastStaticChk(StaticType)))<-9000 or Object In Screen(Static(StaticType,LastStaticChk(StaticType)))=0
Flag=1
Exit
Endif
Next i
i=LastStaticChk(StaticType)*flag
Endfunction i
The returned value is then stored in Memblock 2 at the location p (which was previously calculated), and the Static Object is placed on the terrain and angled appropriately.
The Statics Map Memblock can also be linked to play sound effects related to the terrain being walked on. For Grass, play a walking in grass sound. For roads, play a walking on rocks sound. For sand, swamp, and so forth, appropriate sound effects can also be played. The speed of the character can also be modified for terrain as well using the Statics Map. As you can see, the Statics Map, while not critical to an RPG, is very difficult to pass up.
The rest of the Statics script helps define some obvious aspects of the Statics, such as size ("add" is how much to randomly add onto the base size), Cull and Fog flags, Grass Flag (which is a flag used to scroll the static texture to simulate wind), and Percentage. The Percentage value is a chance of using any given Static Object type. If the chance fails, the code moves on to the next type and checks that type against a chance value. So it is important to set the last Static Type for a particular RGB value at 100, so a blank terrain space will not show up (unless you want it to).
{I would like to acknowledge Tinkergirl's original work on the Statics Map. She called it something else back when she first introduced the concept, but the basic idea has not really changed very much).
2D Buttons
Any RPG needs 2D buttons. And, while the term "2D Buttons" may seem a bit redundant (as in, are there any other type?), there are 3D buttons, which will be covered later.
The basic concept behind how this project does buttons is by using sprites and sprite collision. Take a small 1x1 pixel sprite, hide it, and position it every cycle with the mouse pointer. Check that sprite for a collision against other sprites, and return the value. This little snippet gives a good idea of this concept.
Sync On:Sync Rate 60
`Make a 64x64 pixel box.
Box 0,0,64,64,RGB(255,0,0),RGB(255,0,0),RGB(255,0,0),RGB(255,0,0)
`Get our small 1x1 sprite image as image 1.
Get Image 1,0,0,1,1,1
`Get our test 64x64 sprite image as image 2.
Get Image 2,0,0,64,64,1
`Make and hide the pointer-sprite.
Sprite 1,0,0,1
Hide Sprite 1
`Place our test button.
Sprite 2,50,50,2
Do
Cls
`Move the pointer sprite to the mouse location.
Sprite 1,MouseX(),MouseY(),1
`Print the sprite collision information.
Set Cursor 0,0
Print Sprite Collision(1,0)
Sync
Loop
All that is left to do is check for a mouse button being pressed, and we are in business. The 2D Button code is a bit more complex. Most if it is data management. The buttons can be scripted (ideally they should be). There are also some features that can be applied to buttons, which are normally available for sprites. For example, there is an animated button property, which will switch between two frames on the same image, which indicates the mouse is over that button.
In addition to scripting and features, this button code also retains stored values that can be assigned to buttons. So now two values can be returned from our buttons. The first value is the array position of the button in the database (which is basically the order the button was created in). This value may change if buttons are deleted. The second value is one that is assigned to the button. This value does not change. This can be useful for ensuring that a fixed value is returned despite deleting buttons. Or the return value can be usefull for categorizing buttons. However the assigned value is used, it is a must-have feature.
Here is our 2D Buttons source code:
Rem *** Include File: Buttons.dba ***
Rem Created: 4/15/2006 11:27:03 AM
Rem Included in Project: C:Program FilesDark Basic SoftwareDark Basic ProfessionalProjectsTURTLE OMMORPGGameSourceommorpg.dbpro
Remstart
Button Commands:
Initialize Buttons: Use this command first to set up the Button functionality
return integer=Make_Button(filename, x position, y position, return value) : This command can be used to make a button.
filename is the file location & name of the button image file.
x/y position is x/y location on the screen to place the button.
return value is the value to store with the button. The Get Button command will return this assigned value.
HighLightFlag is set to one if the image is a double-width image with a normal & highlight button image
This function returns the button's ID as an integer. The ID is not needed, but may be usefull.
Delete_Button(Button_ID): Using the button's ID, you can delete a button using this command.
Clear_Buttons(): Clears out all button's and stored values. Buttons are no longer initialized.
return integer=Get_Button: Returns the stored value for the button the mouse is currently over.
return integer=Get_Button_ID: Returns the button ID for the button the mouse is currently over.
return integer=Free_Button(): Internal use function to find the next free button.
Remend
`++++++++++++++++Demo+++++++++++++
Remstart
Sync On: Sync Rate 60
#Constant Main_Menu 27
#Constant Save_Menu 31
#Constant Load_Menu 42
Initialize_Buttons
Make_Button("Button1.png",100,10,Main_Menu)
Make_Button("Button2.png",100,110,Save_Menu)
Make_Button("Button3.png",100,210,Load_Menu)
Do
i=Get_Button_ID
b=Get_Button()
Set Cursor 0,0
Print "Button ";i;"=";b
Sync
Loop
End
Remend
`++++++++++++++Functions+++++++++++++
`Initializes the button functions for use.
#Constant Initialize_Buttons Initialize_Buttons()
Function Initialize_Buttons()
Get Image 4999,0,0,1,1,1
Sprite 4999,-10,-10,4999
Hide Sprite 4999
Dim Buttons(0) as Integer
Endfunction
`Reads buttons in from a file (using the Parser functionality (not included)
`The text file format is: FileName(relative path);X Pos;Y Pos;Return Value;Animation Flag
`To use this functionality, simply create a text file for the desired buttons using the above format.
`Call the Parse_Buttons() function to read the file in and create the desired buttons.
Function Parse_Buttons(Path as String,File as String)
If Parse_File(Path+File,1)=0 Then ExitFunction 0
For i = 0 to parser_max_lines(0)
Make_Button(path+PFile(i,0),Val(PFile(i,1)),Val(PFile(i,2)),Val(PFile(i,3)),Val(PFile(i,4)))
Next i
Endfunction 1
`Makes the actual buttons. Can be called directly, or is used in conjunction with the Parse_Buttons() function.
`Arguments are: File Name for the button image,X Pos,Y Pos, Return Value, Animation Flag.
` Note: Animated buttons are created as two images side by side. The image is divided in half. The first
` half is displayed in the button's rest state, and the second image is displayed in the button's
` ready or active state (when the mouse pointer is over the button).
`The return value is the buttonID which can be used for other functions, but is not required.
Function Make_Button(File as String,xpos as float, ypos as float,RVal as Integer,HLFlag)
b=Free_Button()
if b-5000>Array Count(Buttons(0))
Dim Buttons(b-5000) as Integer
Endif
If HLFlag>0
Create Animated Sprite b,File,2,1,b
Sprite b,xpos,ypos,b
Set Sprite Frame b,0
Buttons(b-5000)=-RVal
Else
Safe_Load_Image(File,b,1)
Sprite b,XPos,YPos,b
Buttons(b-5000)=RVal
Endif
b=b-5000
Endfunction b
`Returns the stored button value.
#Constant Get_Button Get_Button()
Function Get_Button()
For b=0 to Array Count(Buttons(0))
if Buttons(b)<0
Set Sprite Frame b+5000,1
Endif
Next b
Sprite 4999,MouseX(),MouseY(),4999
b=Sprite Collision(4999,0)
if b=0 then exitfunction 0
r=0
if b>4999 and b< 5500 and sprite visible(b)=1
b=b-5000
if b<=Array Count(Buttons(0))
r=Abs(Buttons(b))
if Buttons(b)<0
Set Sprite Frame b+5000,2
Endif
Endif
Else
b=-1
Endif
Endfunction r
`Returns the button's ID. The ID is different from the stored value, but can be used to reference the Sprite and/or image.
#Constant Get_Button_ID Get_Button_ID()
Function Get_Button_ID()
Sprite 4999,MouseX(),MouseY(),4999
b=Sprite Collision(4999,0)
if b=0 then exitfunction -1
if b>4999 and b< 5100 and sprite visible(b)=1
b=b-5000
Else
b=-1
Endif
Endfunction b
`Returns a 1 if the specific button is active (has the mouse over it), otherwise returns a 0.
Function Check_Button(BtnID)
If Sprite Exist(BtnID+5000)=0 Then ExitFunction 0
Sprite 4999,MouseX(),MouseY(),4999
b=Sprite Collision(4999,BtnID+5000)
if b<>0 and sprite visible(BtnID+5000)=1
b=1
Else
b=0
Endif
Endfunction b
`Pastes the given button
Function Paste_Button(BtnID)
If Sprite Exist(BtnID+5000)
Paste Sprite BtnID+5000,Sprite X(BtnID+5000),Sprite Y(BtnID+5000)
Endif
Endfunction
`Pastes all the current buttons in their current states.
Function Paste_Buttons()
For i =5000 to 5999
If Sprite Exist(i)
Paste Sprite i,Sprite X(i),Sprite Y(i)
Endif
Next i
Endfunction
`Use this function to reposition a button.
Function Position_Button(BtnID,x,y)
If Sprite Exist(BtnID+5000)=1
Sprite Abs(BtnID+5000),x,y,Abs(BtnID+5000)
Endif
Endfunction
`Use this function to hide a button. The button is still in place but will be ignored.
Function Hide_Button(BtnID)
If Sprite Exist(BtnID+5000)=1
Hide Sprite BtnID+5000
Endif
Endfunction
`Unhides a button.
Function Show_Button(BtnID)
If Sprite Exist(BtnID+5000)=1
Show Sprite BtnID+5000
Endif
Endfunction
`Sets the button's alpha blending.
Function Set_Button_Alpha(BtnID,alpha)
If Sprite Exist(BtnID+5000)=1
Set Sprite Alpha BtnID+5000,alpha
Endif
Endfunction
`Set's the button's priority.
Function Set_Button_Priority(BtnID,Prty)
If Sprite Exist(BtnID)=1
Set Sprite Priority BtnID+5000,prty
Endif
Endfunction
`Set's the button's width
Function Button_Width(BtnID)
If Sprite Exist(BtnID+5000)=1
w=Sprite Width(BtnID+5000)
Endif
Endfunction w
`Set's the button's height.
Function Button_Height(BtnID)
If Sprite Exist(BtnID+5000)=1
h=Sprite Height(BtnID+5000)
Endif
Endfunction w
`Delete's the specified button
Function Delete_Button(BID)
BID=BID+5000
If Sprite Exist(BID) Then Delete Sprite BID
If Image Exist(BID) Then Delete Sprite BID
Endfunction
`Delete all the buttons and clear the memory. Must re-initialize buttons after using this function to use buttons again.
#Constant Clear_Buttons Clear_Buttons()
Function Clear_Buttons()
For i = 4999 to 5099
If Sprite Exist(i) Then Delete Sprite i
If Image Exist(i) Then Delete Image i
Next i
UnDim Buttons(0)
Dim Buttons(0) as Integer
Endfunction
`Returns a free sprite in the range of 5000-5099
Function Free_Button()
flag=0
For i = 5000 to 5499
If Image Exist(i)=0 and Sprite Exist(i)=0
flag=1
Exit
Endif
Next i
i=i*flag
Endfunction i
And here is our Main Menu, which uses the Button functionality above.
Rem *** Include File: menu_main.dba ***
Rem Created: 21/01/2006 20:02:25
`This will call our menu, and depending on the results, modify an .ini file
`and give the options to quit, or start the game.
function load_menuimages()
Initialize_Buttons
`Load in the Main Menu.txt parser file.
mediapath$ = "MenusMediaMain Menu"
Parse_File(mediapath$+"Main Menu.txt",1)
`Background Image
load image mediapath$+PFile(0,0),1
`Music
Dim Theme_Music(0)
Theme_Music(0)=Free_Music()
Load Music PFile(2,0),Theme_Music(0)
`Buttons
Parse_Buttons(mediapath$,PFile(1,0))
endfunction
function main_menu()
Sync On:Sync Rate 30:Autocam Off
set display mode 1024,768,32
Color Backdrop 0
load_menuimages()
Loop Music Theme_Music(0)
Local local_alpha as float
Do
if local_alpha<254 then inc local_alpha,.5
sprite 1,0,0,1
show sprite 1
Size Sprite 1,Screen Width(),Screen Height()
Set Sprite Alpha 1,local_alpha
`repaste background
paste sprite 1,0,0
Hide sprite 1
Option= Get_Button()
Option = Option * (MouseClick()=1)
Select Option
Case 1
New_Game()
Endcase
Case 2
`Load_Game()
Endcase
Case 3
`Init_Options()
Endcase
Case 4
New_Character()
Endcase
Case 5
`Init_Credits()
Endcase
Case 6
Exit_Game()
Endcase
EndSelect
Sync
Loop
endfunction
Function New_Game()
Sync Rate 0
Delete Sprite 1
Delete Image 1
Stop Music Theme_Music(0)
Clear_Buttons
Run_Intro()
Load Image "HudMediaLoading-Laced.png",99,1
Sprite 99,0,0,99
Sync
Init_Game()
Delete Sprite 99
Delete Image 99
Sync
Sync
Main_Loop()
Endfunction
Function Load_Game()
Delete Sprite 1
Stop Music Theme_Music(0)
`Clear_Buttons
`Game_Loader()
Endfunction
Function Init_Options()
Delete Sprite 1
Stop Music Theme_Music
`Clear_Buttons
`Options()
EndFunction
Function New_Character()
Delete Sprite 1
Delete Image 1
Stop Music Theme_Music(0)
Clear_Buttons
Character_Creation()
EndFunction
Function Init_Credits
Delete Sprite 1
Stop Music Theme_Music(0)
`Clear_Buttons
`Options()
EndFunction
Function Exit_Game()
Exit Prompt "Thank you for playing.","Goodbye"
End
Endfunction
You can also download the attached file with source code, media and scripts.
Open MMORPG: It's your game!