hi, try this new menu program, changes are: text color switch, no interaction with title of the menu (previously, option 1 was activated by clicking on the menu title but it should not);no more resizing sprites, as the effect wasn't so good I think.
:
rem
remstart
Self-Contained Code
by Steve Vink
This example illustrates how TO make self-contained, reusable code.
It utilises Functions, Types, AND Arrays.
remend
`*******************************************************
` This include file is the main body of our program
` All of the hard work involcved in creating a menu is of no concern to us here.
` The menus.dba include file contains all of the necessary coding.
` All we need to know is what functions have been provided to expose
` the functionality
` REMEMBER, we are not interested in the variables, types and arrays
` that have been created to support the menu system.
`*******************************************************
` To make the code easier to read I will make my menu options into
` readable constants
#CONSTANT cMENU1OPT1 1
#CONSTANT cMENU1OPT2 2
#CONSTANT cMENU1OPT3 3
#CONSTANT cMENU2OPT1 4
#CONSTANT cMENU2OPT2 5
#CONSTANT cMENU2OPT3 6
#CONSTANT cMENU2OPT4 7
` Outside of the Menu system, we need to keep track of our 2 menu IDs.
` Because we never interrogate the menu system variables directly,
` we must make our own variables.
GLOBAL Menu1
GLOBAL Menu2
GLOBAL V_Premier_spriteE : V_Premier_spriteE=500
` Call the basic setup commands...
SYNC ON: SYNC RATE 60
SET DISPLAY MODE 1024,768,32,1
` **********************************************************
` Call the function that instigates our menu system.
` **********************************************************
CLS 0
InitMenu()
` **********************************************************
` Make our first menu. We simply provide a name, how many columns
` of options we would like, and which sprites we should allocate to the menu
` **********************************************************
Menu1 = MakeMenu("My First Menu",1,V_Premier_spriteE)
` Add some options, using the function provided.
makeoption("Click Me!", cMENU1OPT1)
makeoption("2 Column Menu", cMENU1OPT2)
makeoption("EXIT", cMENU1OPT3)
` **********************************************************
` Make a second menu, with 2 columns...510
` **********************************************************
Menu2 = MakeMenu("A 2 column menu",2,V_Premier_spriteE)
` Add some options, using the function provided.
makeoption("Click Me!", cMENU2OPT1)
makeoption("Back to Menu 1", cMENU2OPT2)
makeoption("FLASH!", cMENU2OPT3)
makeoption("EXIT", cMENU2OPT4)
` **********************************************************
` Use SetMenu() to make menu 1 the active menu, then show it...
` **********************************************************
setMenu(Menu1)
` **********************************************************
` This is all we need to do to process the menu!
` You will note we don't even SYNC, the menu system handles it all.
` **********************************************************
DO
opt = showMenu()
` Delete the last menu after returning...
clearMenu()
` process my options. Check the value returned,
` and take the appropriate action.
ProcessMyOptions(opt)
LOOP
FUNCTION processMyOptions(opt)
` **********************************************************
` This function simply looks at the option selected by the user
` and carries out the request.
` **********************************************************
SELECT opt
` ********** Menu 1...
CASE cMENU1OPT1
CLS
CENTER TEXT SCREEN WIDTH() / 2, SCREEN HEIGHT() / 2, "You Picked Option 1 on Menu 1"
SYNC
WAIT 3000
ENDCASE
CASE cMENU1OPT2
setMenu(Menu2)
ENDCASE
CASE cMENU1OPT3
END
ENDCASE
` ********** Menu 2...
CASE cMENU2OPT1
CLS
CENTER TEXT SCREEN WIDTH() / 2, SCREEN HEIGHT() / 2, "You Picked Option 1 on menu 2!"
SYNC
WAIT 3000
ENDCASE
CASE cMENU2OPT2
setMenu(Menu1)
ENDCASE
CASE cMENU2OPT3
FOR n = 1 TO 10
CLS RGB(RND(255), RND(255), RND(255))
SYNC
WAIT 100
NEXT n
ENDCASE
CASE cMENU2OPT4
END
ENDCASE
ENDSELECT
ENDFUNCTION
rem TODO: 1;REMPLACER FLECHE/CURSEUR PAR UN SPRITE 2;FIXER LA TAILLE DU TEXTE PUIS ADAPTER LA TAILLE DES SPRITES SELON LA RESOLUTION ECRAN 3;AJOUTER SONS, SABLIER,ANIMATIONS ARRIERE-PLAN
rem TODO : global V_Premier_spriteE : V_Premier_spriteE=500 ; déclarer cette variable et l'utiliser dans MakeMenu("My example Menu",1,V_Premier_spriteE)
remstart
Self-Contained Code
by Steve Vink
This example illustrates how TO make self-contained, reusable code.
It utilises Functions, Types, AND Arrays.
remend
`*******************************************************
` Supporting Data structures
` Our menu system requires various components to "live"
`*******************************************************
` Menu Type. This holds the information about each individual menu
TYPE tMenu
title$
Columns
Options
firstSprite
ENDTYPE
` Option Type. This holds the information about each individual option
TYPE tOption
MenuId
title$
value
x
y
ENDTYPE
` We also need some global variables to support the system.
TYPE tMenuVar
CurMenu
CurOption
Initialised
font$
fontSize
mouse
mousedown
ENDTYPE
GLOBAL gMenuVar AS tMenuVar
` These variables don't need to be declared here
` but they have been so that they are visible in the Debug window
GLOBAL txt$
GLOBAL x
GLOBAL y
GLOBAL NumRows
GLOBAL rows#
GLOBAL V_Animer_SpriteE
GLOBAL V_Animer_Sprite_OldE
`*******************************************************
` Function: InitMenu()
` This function initialises all of the required data,
` and brings our self-contained menu system to life
`*******************************************************
FUNCTION InitMenu()
` We will make life easier for the programmer implementing our menu system.
` If this function is not called, the code will fail.
` More often than not, no error will be generated.
` So every Make... function will call this one.
` If we have already initialised, we will quickly skip over this code.
IF gMenuVar.Initialised <> 1
` Make the Menu and Option arrays
` Because I use 0 as a special value in my programs, I add an empty element
` after creation. It also makes it easier to count!
DIM gMenu() AS tMenu
ADD TO QUEUE gMenu()
DIM gOption() AS tOption
ADD TO QUEUE gOption()
gMenuVar.curMenu = 0
gMenuVar.Initialised = 1
gMenuVar.Font$ = "Arial"
gMenuVar.FontSize = 24
rem gMenuVar.FontSize = SCREEN WIDTH()/33
rem gMenuVar.FontSize = SCREEN WIDTH()/48
rem SET TEXT TO BOLD
SET TEXT TO BOLDITALIC
gMenuVar.mouse = 999
gMenuVar.mouseDown = 0
ENDIF
ENDFUNCTION
`*******************************************************
` Function: MakeMenu()
` This function creates one menu
` We will return the menu number to the user for future reference.
` We will also assist the programmer by making this new menu
` the current menu
`*******************************************************
FUNCTION MakeMenu(title$, columns, firstSprite)
` Make sure the menu system has been initialised.
` As this is reusable code, it's essential to make it
` as robust as possible.
InitMenu()
` Add a new menu to our array
ADD TO QUEUE gMenu()
gMenu().title$ = title$
gMenu().columns = columns
gMenu().firstSprite = firstSprite
` Set the current menu
curMenu = ARRAY COUNT(gMenu())
SetMenu(curMenu)
` Return the new menu ID!
ENDFUNCTION curMenu
`*******************************************************
` Function: MakeOption()
` This function creates one option
` We will return the option id to the user for future reference.
` We will add the option to the current menu
`*******************************************************
FUNCTION MakeOption(title$, value)
` If the current menu is 0, we haven't actually made one yet!
` To make our code robust, and help the programmer,
` we will check this and make a menu if necessary
` Because MakeMenu() will ensure the system is initialised
` we won't be duplicating the effort here.
IF gMenuVar.curMenu = 0
mnu = MakeMenu("Menu 1", 1, 100)
ENDIF
` Add a new option to our array
ADD TO QUEUE gOption()
gOption().menuId = gMenuVar.curMenu
gOption().title$ = title$
gOption().value = value
` Update the menu option count...
INC gMenu(gMenuVar.curMenu).options, 1
` Set the current option
curOption = ARRAY COUNT(gOption())
SetOption(curOption)
` Return the new option ID!
ENDFUNCTION curOption
`*******************************************************
` Function: SetMenu()
` This function sets the id of the current menu
` At a glance, it does very little. But it's still important
` to write this code as a function
` 1. It is called from several places, including our own menu code
` 2. We need to hide the data from the programmer.
` 3. In the future this may become a more complex procedure
`
` The function returns the menu id. The user can interrogate this
` to ensure the request was successful.
` If an invalid menu reference is passed, the return value will differ.
`*******************************************************
FUNCTION setMenu(menu)
IF menu <= ARRAY COUNT(gMenu())
gMenuVar.curMenu = menu
ENDIF
ENDFUNCTION gMenuVar.curMenu
`*******************************************************
` Function: SetOption()
` This function sets the id of the current option
` At a glance, it does very little. But it's still important
` to write this code as a function
` 1. It is called from several places, including our own menu code
` 2. We need to hide the data from the programmer.
` 3. In the future this may become a more complex procedure
`
` The function returns the option id. The user can interrogate this
` to ensure the request was successful.
` If an invalid option reference is passed, the return value will differ.
`*******************************************************
FUNCTION setOption(option)
IF option <= ARRAY COUNT(gOption())
gMenuVar.curOption = option
ENDIF
ENDFUNCTION gMenuVar.curOption
`*******************************************************
` Function: showMenu()
` This function is the main driver of our self-contained code
` It will construct the menu, display it and receive the feedback
`*******************************************************
FUNCTION showMenu()
` Work out how we need to format the menu
` Based on number of options and number of columns
` We will do this every time, because it's possible
` that the menu layout has changed
CLS RGB(0,0,0)
` Set the font for the menu. Font is a System-wide parameter
SET TEXT SIZE gMenuVar.FontSize
SET TEXT FONT gMenuVar.Font$
`Create the Menu Title Image
makeTextImage(gMenu(gMenuVar.curMenu).title$, gMenu(gMenuVar.curMenu).firstSprite,0)
width = TEXT WIDTH(gMenu(gMenuVar.curMenu).title$) : height = TEXT HEIGHT(gMenu(gMenuVar.curMenu).title$)
SPRITE gMenu(gMenuVar.curMenu).firstSprite, (SCREEN WIDTH() / 2) - (width / 2), (gMenuVar.fontsize / 2),gMenu(gMenuVar.curMenu).firstSprite
rows# = (gMenu(gMenuVar.curMenu).options) / ((gMenu(gMenuVar.curMenu).columns) * 1.0)
Numrows = INT(rows#)
IF NumRows < rows# THEN INC NumRows
` Work out the centre position of each menu option...
OptionPosition = 0 : ` Use this to track our position in our options array
FOR n = 1 TO NumRows
y = (gMenuVar.fontsize * 2) + ((SCREEN HEIGHT() / (NumRows + 1)) * n)
FOR m = 1 TO gMenu(gMenuVar.curMenu).columns
x = SCREEN WIDTH() / (gMenu(gMenuVar.curMenu).columns + 1) * m
option = ((n-1) * (gMenu(gMenuVar.curMenu).columns)) + m
IF option <= gMenu(gMenuVar.curMenu).options
` Find next option for this menu...
WHILE gOption(OptionPosition).menuId <> gMenuVar.curMenu
INC OptionPosition, 1
ENDWHILE
` Now we have the next option, update x and y
gOption(OptionPosition).x = x
gOption(OptionPosition).y = y
` Now we need to make the text image...
MakeTextImage(gOption(OptionPosition).title$, gMenu(gMenuVar.curMenu).firstSprite + option,1)
` Make and position the sprite...
width = TEXT WIDTH(gOption(OptionPosition).title$) + 8 : height = TEXT HEIGHT(gOption(OptionPosition).title$) + 8
SPRITE gMenu(gMenuVar.curMenu).firstSprite + option, gOption(OptionPosition).x - (width / 2), gOption(OptionPosition).y - (height / 2),gMenu(gMenuVar.curMenu).firstSprite + option
ENDIF
INC OptionPosition, 1
NEXT m
NEXT n
makeBackground()
` Process the menu. Once we return from here, we simply need to pass the selected option back
` to the main program for the developer to react to.
value = processMenu()
ENDFUNCTION value
`*******************************************************
` Function: processMenu()
` Once the menu is on-screen, we have to respond to the feedback
` That all happens here...
`*******************************************************
FUNCTION processMenu()
` Firstly, make a hidden sprite. We use this to work out if our mouse is over an option...
mouse = gMenu(gMenuVar.curMenu).firstSprite
WHILE IMAGE EXIST(mouse) = 1
INC mouse
ENDWHILE
GET IMAGE gMenuVar.mouse,1,1,2,2
SET SPRITE gMenuVar.mouse,1,0
rem sprite gMenuVar.mouse, mousex(), mousey(), gMenuVar.mouse
V_Animer_Sprite_OldE = 0
V_Animer_SpriteE = 0
value = 0
SPRITE gMenuVar.mouse, MOUSEX(), MOUSEY(), gMenuVar.mouse
HIDE SPRITE gMenuVar.mouse
WHILE value = 0
PASTE IMAGE 1000,1,1
SPRITE gMenuVar.mouse, MOUSEX(), MOUSEY(), gMenuVar.mouse
` The following code is a safety check
` The user must release the mouse and press again
` between options
` Otherwise, you may process multiple requests.
IF MOUSECLICK() = 0 AND gMenuVar.mousedown = 1
gMenuVar.mousedown = 0
ENDIF
` If mouse clicked, check which option...
IF MOUSECLICK() = 1 AND gMenuVar.mousedown = 0
gMenuVar.mousedown = 1
hit = SPRITE COLLISION(gMenuVar.mouse, 0)
` If hit is greater than 0, we were over a sprite when we clicked...
IF hit > 0 AND hit <> V_Premier_spriteE
option = hit - gMenu(gMenuVar.curMenu).firstsprite
` Animer_le_sprite cliqué; JOUER UN SON.... >>>>
` STRETCH SPRITE Hit,100,125
SET SPRITE DIFFUSE V_Animer_SpriteE, 255, 255, 255
` PASTE SPRITE Hit, SPRITE X(Hit), SPRITE Y(Hit)
` SYNC : WAIT 10 : SYNC : WAIT 10 : SYNC
` find the corresponding array element for this option...
checked = 0
FOR n = 1 TO ARRAY COUNT(gOption())
IF gOption(n).MenuId = gMenuVar.curMenu THEN INC checked, 1
IF checked = option
value = gOption(n).value
EXIT
ENDIF
NEXT n
ENDIF : ` end checking which option clicked.
ENDIF : ` End processing collision occurred
` toucher Sprites avec souris;
V_Animer_SpriteE = SPRITE COLLISION(gMenuVar.mouse, 0)
` Animer sprite;
IF V_Animer_SpriteE > 0 AND V_Animer_SpriteE <> V_Premier_spriteE
` Animer_le_sprite touché; JOUER UN SON.... >>>>
IF V_Animer_SpriteE <> V_Animer_Sprite_OldE
` STRETCH SPRITE V_Animer_SpriteE,125,105
SET SPRITE DIFFUSE V_Animer_SpriteE, 255, 255, 0
V_Animer_Sprite_OldE = V_Animer_SpriteE
ENDIF
ENDIF
` Redimensionner Sprite précédemment touché mais qui ne l'est plus.
IF V_Animer_Sprite_OldE > 0 AND V_Animer_SpriteE <> V_Premier_spriteE
IF V_Animer_SpriteE <> V_Animer_Sprite_OldE
` STRETCH SPRITE V_Animer_Sprite_OldE,100,100
SET SPRITE DIFFUSE V_Animer_Sprite_OldE, 255, 255, 255
V_Animer_Sprite_OldE = 0
ENDIF
ENDIF
rem set cursor 0,0
rem print V_Animer_SpriteE
SYNC
ENDWHILE
DELETE SPRITE gMenuVar.mouse
DELETE IMAGE gMenuVar.mouse
ENDFUNCTION value
`*******************************************************
` Function: clearMenu()
` This function clears the current menu
` It will delete sprites and images, but
` leave the data in the arrays intact, for the next use.
`*******************************************************
FUNCTION clearMenu()
` Delete Menu Header...
DELETE SPRITE gMenu(gMenuVar.curMenu).firstSprite
DELETE IMAGE gMenu(gMenuVar.curMenu).firstSprite
FOR n = 1 TO gMenu(gMenuVar.curMenu).options
DELETE SPRITE gMenu(gMenuVar.curMenu).firstSprite + n
DELETE IMAGE gMenu(gMenuVar.curMenu).firstSprite + n
NEXT n
ENDFUNCTION
`*******************************************************
` Function: MakeTextImage()
` This function creates an image from a text string
` We will use it for our buttons and menu titles
`*******************************************************
FUNCTION MakeTextImage(txt$, img, border)
IF txt$ = "" THEN txt$ = "Error!"
width = TEXT WIDTH(txt$) + 8 : height = TEXT HEIGHT(txt$) + 8
` Get a free bitmap to make our image...
BREAK
bitmap = 1
WHILE BITMAP EXIST(bitmap) = 1
INC bitmap,1
ENDWHILE
CREATE BITMAP bitmap, width, height
IF border = 1
LINE 1,1,width,1
LINE 1,1,1,height
LINE width-1,height-1,width-1,1
LINE width-1,height-1,1,height-1
ENDIF
CENTER TEXT width / 2,4,txt$
GET IMAGE img,1,1, width, height,1
DELETE BITMAP bitmap
ENDFUNCTION
`*******************************************************
` Function: MakeBackground()
` This function creates a background image using memblock trickery
` As nice as it would be to cover the detail of this,
` we'll leave it for another day!
`*******************************************************
FUNCTION makeBackground()
IF IMAGE EXIST(1000) = 0
sw = SCREEN WIDTH()
sh = SCREEN HEIGHT()
` Make a memblock...
MAKE MEMBLOCK FROM BITMAP 1, 0
FOR n = 1 TO sh
hOffset = 12 + ((n-1) * 4 * sw)
FOR m = 1 TO sw
IF (n < 5 OR n > (sh - 5)) OR (m < 5 OR m > (sw - 5))
red = 0 : green = 255 : blue = 0
ELSE
red = 255 * n/(sh * 1.0) : green = 0: blue = 255 - (255 * n/(sh * 1.0))
ENDIF
WRITE MEMBLOCK BYTE 1, hOffset + (4 * (m-1)), blue
WRITE MEMBLOCK BYTE 1, hOffset + (4 * (m-1)) + 1, green
WRITE MEMBLOCK BYTE 1, hOffset + (4 * (m-1)) + 2, red
WRITE MEMBLOCK BYTE 1, hOffset + (4 * (m-1)) + 3, 255
NEXT m
NEXT n
MAKE IMAGE FROM MEMBLOCK 1000, 1
ENDIF
ENDFUNCTION
to understand the program, read this (an extract from newsletter-30-june-2005:
http://thegamecreators.com/data/newsletter/newsletter_issue_30.html#9 :
DBPro Tutorial - Self-Contained Code
Exams, beach towels laid out at dawn, and realising I left a piece of mouthwatering pizza in the box the night before. These are just 3 examples of things I hope I never encounter again in my life. And often, even though the fruits of your labour are out of this world, you are glad your code is complete and the development nightmare is over. Wouldn't it be fantastic if all of the effort could be simply dropped into your next project, and you reaped the rewards of your trials and tribulations again?
Over the past three months we have built up a small arsenal of useful techniques: Functions - creating isolated sections of code, used for repetitive tasks, or tasks that are best kept self-contained. Assets - keeping game assets, such as characters and vehicles, in good shape through the use of arrays. Scalability - the ability to shrink and grow our library of assets by using dynamic arrays. Variation - using the same Type to define variations of our assets. For example, all monsters have limbs, but each species may have a different number. Maintaining these in one Type allows generic code, or functions, to control each species. Blueprints - defining the basic building blocks of our program in easy-to-handle structures. Blueprints can also contain the "static" elements for reference; for example, the size of every Mini is identical and need not be recorded against each copy.
This month we are going to reinforce the concept of self-contained, reusable code, and add a layer of "protection" to our already overwhelmed senses. At the same time, we'll use a different example again to broaden our ability to apply all of these techniques to another important element of games - The Menu. Each command in Dark Basic is essentially self-contained, reusable code. When you make a cube, the command triggers a complex sequence of events that result in you seeing a cube by entering just one line of code. The cube command also accepts parameters in order to customise the command and the result. We can emulate this process. We can write functions with parameters, and trigger complex scenarios time and time again. These functions will access Variables, Types and Arrays, but if designed correctly we will never have to expose ourselves to the complicated data structures that support our functions. It is the supporting data that will start the development of our menu system. This is a simple text-based system, but it will be easily modified to create more elaborate menus. Here are the elements we need to create:
System: details of menu style, state of the menu system, and where the user is.
Menu: Menu Id, Title, Columns, Sprites
Option: Option Id, Menu Id, Text, Value
This data will be supported by System, Menu and Option Types. The Menu and Option types will form arrays, to allow a scalable solution to be implemented. We can create multiple menus, with multiple options. We only need one array of options, rather than one array per menu, because we can associate the option to the menu by the unique Menu Id. We cannot determine the programmer's requirements in each project, so we must enforce a design that allows scalability, even though it may create a little more work at this stage.
The next step is to create the functions that will support our Menu System:
InitMenu(): If we want to hide the data structures, we must ensure they are created by our Menu System. Dimensioning arrays only happens by executing the code statements, we cannot declare them globally and expect them to be created. An initislisation routine is also essential to create default variable values. Looking back at our racing code last month, we would also want to create our blueprints.
MakeMenu(): This function will make one menu. It will add it to a dynamic array of menus, and apply the required values. We will supply these values as parameters. In order to refer to the menu later, the programmer will need to know it's unique identifier. We will return this value from the function.
MakeOption(): This function will make one option. It will be associated with a menu, and once again we will customise it by passing the details in by parameters.
ShowMenu(): To actually display a menu, we will use a simple function. The programmer, by using this one function, can pass control over to our self-contained system. In return, he will receive a value which represents the option taken by the user.
Personally, I'm a lazy programmer. I tell people I'm efficient, but I admit I'm kidding myself. I'm going to add some more "helper" functions. You may recognise these techniques from the standard Dark Basic commands.
SetMenu(): This function will allow the user to set the current menu being processed. The Menu functions will also use the same function internally. For example, when a menu is created we will SetMenu() to make it the currently active menu. The net result is that we do not need to pass the Menu Id in any of our functions, the "System" is managing the process for us.
ClearMenu(): Rather than the programmer having to make many calls to delete options, and finally the menu, one function will handle this.
You can see all six of these functions, and the supporting data structures in the downloadable sample code. Much of the logic and menu handling code has not been covered in any detail in the tutorials, but this emphasises the point. At the end of this session, you will be able to create a simple text menu without actually having to understand the underlying code, or recall how the data relationships are formed.
We are now ready to implement our menu. Here are some examples of how we would acheive this in our programs:
Make a Menu - requires a title, number of columns on the screen, and the first sprite to allocate:
MyMenu = MakeMenu("My Main Menu", 2, 100)
Set the Active Menu - The Active Menu will receive newly created options, and be displayed when requested.
SetMenu(MyMenu)
Make an Option - requires a title, and the value to return when this option is selected. The option will be added to the Active Menu.
MyOption = MakeOption("Play Game", cPLAY)
Run the Menu - requires no parameters, as the system knows the current menu. It will return the option selected by the user.
Option = ShowMenu()
The sample code shows the self-contained menu system as an include to a larger project. It has 2 menus, one single column, and one 2-column menu. The options will allow navigation between menus, trigger other functions and allow us to exit the program. You can see how the menu system handles the creation, display and interaction, and the programmer handles the user's requests.
Another advantage of this approach is that more than one programmer can work on the same project. All that needs to be shared is the "Interface", which in our case is the functions and their parameters. This menu system is very basic, but will suffice for testing. I can now continue to develop a much more impressive menu system, and as long as the interface doesn't change, the include file can simply be replaced in order to implement the newer version. We have just stepped into the realms of project management, and a whole new world of possibilities.
Now please excuse me while I return to my sun lounger, finish my pizza, and get some revision done.
Until next time, happy Coding!
Steve Vink.
Note: This tutorial has been packaged up into a nice friendly installer that will load a full-length version of the tutorial, along with DBPro source code into your Start Menu (in The Game Creators program group). Each month this collection will build-up into a formidable free resource.
note: global variable
V_Premier_spriteE is used to define the id number of first sprite and also correspond with id number of the sprite "menu title" so simply use this variable like this: MakeMenu("My example Menu",1,
V_Premier_spriteE)
todo: adjust size of text with screen resolution while avoiding too big sprites (to limit the memory used by sprites): sprites height should be 8, 16, 32, 64 or 128 pixels in size and then stretched according to screen resolution
-to replace the cursor with a custom one, should be a sprite pasted at mouse x,y screen coordinates (I think about cross hairs, for an FPS)
-to add some nice sounds effects
pascontent.