Custom Cursors


(Xterm1n8or) #1

Hi ppl :smiley:

Wanted to try something easy, only i’m stuck. Guess it’s only easy if you know how to do it :roll:
Anyway, I tried out a few different cursor icons but now i want to keep them all and be able to select one when i want. Ive added this to my Outpost menu:

	SUBWINDOW( 6, 32, (SUBWINDOW_WIDTH), 28, "Cursor" )
	MULTI( 8, 48, (SUBWINDOW_WIDTH)-4, 10, "Cursor Icon:", .2, 8, "cursor", cvarFloatList { "Default" 0 "Luger" 1 "Colt" 2 "Knife" 3 }, "Selects alternate cursor icons" )

The bit where it’s told which file to use is in global.menu:

cursor		"ui/assets/cursor_luger"

The default cursor is 3_cursor3. This is where i currently make changes so i don’t have to keep renaming the cursor images.

I’m a bit unsure as to where to go from here. I’m guessing i need some sort of array like:

cursor[0]		"ui/assets/3_cursor3"
cursor[1]		"ui/assets/cursor_luger"
cursor[2]		"ui/assets/cursor_colt"
cursor[3]		"ui/assets/cursor_knife"

Was wondering if anybody had any ideas as to how to go about this?

Many thanks :smiley:


(Elite) #2

You’re on a good start, just keep heading in the direction you seem like you are going. You will need that array.

Something like this maybe will help

In cg_local.h place these statements anywhere u like:

//custom cursors
#define OP_CURSOR_DEFAULT 0
#define OP_CURSOR_KNIFE 1
#define OP_CURSOR_LUGER 2
#define OP_CURSOR_COLT 3
#define OP_CURSOR_MAX 4

You cna use an enum instead of the above defines as well, just chose this way for now cause it was quick.

In cg_local.h search for:

qhandle_t		cursorIcon;

and change it to this:

qhandle_t		cursorIcon[OP_CURSOR_MAX];

In cg_main.c search for this:

cgs.media.cursorIcon =				trap_R_RegisterShaderNoMip( "ui/assets/3_cursor3" );

and change it to this:

cgs.media.cursorIcon[OP_CURSOR_DEFAULT] =				trap_R_RegisterShaderNoMip( "ui/assets/3_cursor3" );
cgs.media.cursorIcon[OP_CURSOR_KNIFE] =				trap_R_RegisterShaderNoMip( "ui/assets/cursor_knife" );
cgs.media.cursorIcon[OP_CURSOR_LUGER] =				trap_R_RegisterShaderNoMip( "ui/assets/cursor_luger" );
cgs.media.cursorIcon[OP_CURSOR_COLT] =				trap_R_RegisterShaderNoMip( "ui/assets/cursor_colt" );

Do a search for ‘cursorIcon’ and adjust it to select the one you want. You will need to determine on your own where you use it, but here is an example of how it would be used
Previous Statement:

CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon );

New Statement:

CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon[OP_CURSOR_KNIFE] );

================
TIP: When declaring arrays, make sure to use defines to max numbers and also use defines to refer to the elements of the arrays to keep things clear and remove simple errors (off by 1, mistakingly using wrong array element, etc).
You will find in doing so arrays will be much nicer to use and debug, as you will always have a visual reminder of exactly what each array element will do/store.


(Xterm1n8or) #3

Hi Elite :smiley:

Mmmm, i think i get what you’ve put, i’m just not sure why. I had a look at cursorIcon and not really sure what it does. What’s the difference? Let me explain at how i’m looking at this:

....."cursor", cvarFloatList { "Default" 0 "Knife" 1 "Luger" 2 "Colt" 3 }..... 

I thought that, if i selected the Colt for example, “cursor” would = 2. I guess that should be changed to cursor[] and also in cg_local.h to qHandle_t cursor[]
Then in global.menu where it says;

cursor      "ui/assets/cursor_luger"

replace it for:

cursor[0]      "ui/assets/3_cursor3" 
cursor[1]      "ui/assets/cursor_knife" 
cursor[2]      "ui/assets/cursor_luger" 
cursor[3]      "ui/assets/cursor_colt"

The cursor “” is the only thing i have changed to make this work with one single icon. I don’t know why i need the rest?

I’m sorry if that all sounds really dumb, i’m still learning. At the moment though i’m a bit sick of ‘Hello World’ programs, and they don’t seem to have much relevance in what i’m trying to do in this game :roll:

Anyway, i’m more than happy to take your word for it, just trying to understand.

Cheers :smiley:


(Elite) #4

Huh?? :slight_smile:

===========

I’m somewhat confused…

Ok, you need a cvar to hold ur current selected crosshair, so create ur cvar like normal. When you change the value of it, whenever you want to change it, set it to the defines like this:

if(we are changing cursor) {
   cursorcvar = OP_CURSOR_KNIFE;
}

Then you’re array will always be accessed like so:

cgs.media.cursorIcon[cursorcvar]

You don’t need to store a ton of values in the cvar, the cvar will only be used to select which cursor is being used currently, so it only needs one value.

There’s not really any “other stuff” except the defines to access the proper elements of the array, and also adding the media icons in the media structure, which you need in order to load ur new cursors.

That make any sense? If so, that all you need?


(Xterm1n8or) #5

Hi :smiley:

Sorry, confusing myself :roll:

Ok, so i put in the #defs and changed the qHandle_t in cg_local.h
I have also made the changes to cg_main.c. 2 questions at this point to do with CG_DrawPic:[ul]1. There are several instances of this. Do i need to change all of them to cgs.media.cursorIcon[OP_CURSOR_KNIFE]?
2. Why are we telling it to be [OP_CURSOR_KNIFE] in particular?[/ul]Next you say:

Ok, you need a cvar to hold ur current selected crosshair, so create ur cvar like normal.

Unfortunately i don’t know what normal is :slight_smile: That’s actually not a joke, you should come round my house sometime :weird:

Do you mean something like this:

vmCvar_t		cg_customCursor;

There is also a cvar table:

cvarTable_t		cvarTable[] = {
	{ &cg_customCursor, "cg_customCursor", "0", CVAR_ARCHIVE },

I’m at a bit of a loss as to what to do next, sorry :?

Many thanks :smiley:


(Elite) #6

=================
DrawPic Questions Section
=================

The DrawPic stuff was just an example. I don’t know when/where you want to change the icon, so that is something you need to figure out.
When you want to draw the new cursor, it will still need to be changed/added with a DrawPic function call same as the way the cursor is drawn in all the other places, and that example displays how to change it. If you use a cvar, the array element you would use would just be the cvarname instead of a number/define, like this (note, again this is just an example):

//original function call in stock source
CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon );
//modified function call to accomodate custom cursors
CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon[cvarname.Integer] );

=================
Cvar Creation Section
=================

To create a new cvar, follow this example. In this example, we will create a serverside cvar. Clientside cvars are created exactly the same way as in this example, with the only difference being they are placed in the cgame files. ANyhow, example of a server cvar:

Open the file g_main.c. Locate the section for declaring cvars near the top of the file around line 27, and add this cvar declaration to the rest of them:

vmCvar_t g_anExampleCvar;

Now, in the same file around line 213, just under the cvar variable declarations is an initialization statement (the thing is huge, can’t miss it) for the gameCvarTable variable. Add your cvar appropriately into this table (check out the cvarTable_t structure just above the cvar declarations in the same file if you are unsure of what the proper values should be):

{ &g_anExampleCvar, "g_anExampleCvar", "1", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },

Now, 3rd and final step, open the file g_local.h. We need to allow all source files access to this cvar, so we need to make it an extern. Add the following in the extern cvars portion of the code to the rest of them around line 1579:

extern vmCvar_t g_anExampleCvar;

Done, you now have a new cvar.


(Xterm1n8or) #7

Hello :smiley:

Just so that were on the same wavelength:

I don’t know when/where you want to change the icon

The ‘when’ would be whenever i go to my Outpost menu and select which icon to use.
The ‘where’ would be everywhere. Whether it’s when ET starts or in-game. I have Outpost in both main and ingame main menus.

It would seem i was kind of on the right track with the cvar. I thought i’d put this clientside as some people may not like my icons or prefer another, so i wanted it so that the client can choose which icon to use. With this in mind, this is what i have now (If it’s wrong i can change it no problem)

outpost.menu:

   SUBWINDOW( 6, 32, (SUBWINDOW_WIDTH), 28, "Cursor" ) 
   MULTI( 8, 48, (SUBWINDOW_WIDTH)-4, 10, "Cursor Icon:", .2, 8, "cg_customCursor", cvarFloatList { "Default" 0 "Knife" 1 "Luger" 2 "Colt" 3 }, "Selects alternate cursor icons" )

cg_local.h:

extern	vmCvar_t		cg_customCursor;

cg_main.c:

vmCvar_t	cg_customCursor;

cvarTable_t		cvarTable[] = {
    { &cg_customCursor, "cg_customCursor", "0", CVAR_ARCHIVE },

I’m guessing i need a VOID cg_customCursor with IF and ELSE IF statements?

Cheers :smiley:


(Elite) #8

Ok, the cvar looks good. Menu should be fine. Now just do a search for “cursorIcon” in all source files and change it like below.

OLD STOCK CODE

cgs.media.cursorIcon

NEW CODE FOR CUSTOM CURSOR

cgs.media.cursorIcon[cg_customCursor.Integer]

What you have just done is basically everywhere it uses the cursor icon, make it choose one out of you’re array. Not much new code is needed, just change any statements to select an array element rather than just the old single value. It should work at this point because ET allready knows how to display cursors, so just compile it and test it out.


(Xterm1n8or) #9

Hi Elite :smiley:

Ok, aswell as the cvar bit above, i still have this:

cg_local.h:

//custom cursors 
#define OP_CURSOR_DEFAULT 0 
#define OP_CURSOR_KNIFE 1 
#define OP_CURSOR_LUGER 2 
#define OP_CURSOR_COLT 3 
#define OP_CURSOR_MAX 4

and this:

//qhandle_t      cursorIcon;
qhandle_t      cursorIcon[OP_CURSOR_MAX];

cg_main.c

//cgs.media.cursorIcon =                             trap_R_RegisterShaderNoMip( "ui/assets/3_cursor3" );
cgs.media.cursorIcon[OP_CURSOR_DEFAULT] =          trap_R_RegisterShaderNoMip( "ui/assets/3_cursor3" ); 
cgs.media.cursorIcon[OP_CURSOR_KNIFE] =            trap_R_RegisterShaderNoMip( "ui/assets/cursor_knife" ); 
cgs.media.cursorIcon[OP_CURSOR_LUGER] =            trap_R_RegisterShaderNoMip( "ui/assets/cursor_luger" ); 
cgs.media.cursorIcon[OP_CURSOR_COLT] =             trap_R_RegisterShaderNoMip( "ui/assets/cursor_colt" );

When i compile i get errors:

Integer is not a member of vmCvar_t
CG_DrawPic: Too few arguments to call

I also changed several CG_DrawPic statements containing cgs.media.cursorIcon to cgs.media.cursorIcon[cg_customCursor.Integer]

Thanks :smiley:


(Elite) #10

Oooooppppssss…my bad :roll:

The .Integer should actually be .integer, with a lower case i

My fault, sorry. Just do a find and replace to change them all quickly. Then recompile and you’re other errors should be fixed nwo as well.


(Xterm1n8or) #11

Hi Elite :smiley:

Ok, changed them all to ‘interger’ It compiles without error now but nothing happens in the game. This is what i have so far:

outpost.menu:

   SUBWINDOW( 6, 32, (SUBWINDOW_WIDTH), 28, "Cursor" ) 
   MULTI( 8, 48, (SUBWINDOW_WIDTH)-4, 10, "Cursor Icon:", .2, 8, "cg_customCursor", cvarFloatList { "Default" 0 "Knife" 1 "Luger" 2 "Colt" 3 }, "Selects alternate cursor icons" )

cg_local.h:

extern   vmCvar_t      cg_customCursor;
//custom cursors 
#define OP_CURSOR_DEFAULT 0 
#define OP_CURSOR_KNIFE 1 
#define OP_CURSOR_LUGER 2 
#define OP_CURSOR_COLT 3 
#define OP_CURSOR_MAX 4
//qhandle_t      cursorIcon; 
qhandle_t      cursorIcon[OP_CURSOR_MAX];

cg_main.c:

vmCvar_t   cg_customCursor; 

cvarTable_t      cvarTable[] = { 
    { &cg_customCursor, "cg_customCursor", "0", CVAR_ARCHIVE },
//cgs.media.cursorIcon =                             trap_R_RegisterShaderNoMip( "ui/assets/3_cursor3" ); 
cgs.media.cursorIcon[OP_CURSOR_DEFAULT] =          trap_R_RegisterShaderNoMip( "ui/assets/3_cursor3" ); 
cgs.media.cursorIcon[OP_CURSOR_KNIFE] =            trap_R_RegisterShaderNoMip( "ui/assets/cursor_knife" ); 
cgs.media.cursorIcon[OP_CURSOR_LUGER] =            trap_R_RegisterShaderNoMip( "ui/assets/cursor_luger" ); 
cgs.media.cursorIcon[OP_CURSOR_COLT] =             trap_R_RegisterShaderNoMip( "ui/assets/cursor_colt" );

Changed all other ‘cgs.media.cursorIcon’ to: ‘cgs.media.cursorIcon[cg_customCursor.integer]’…These were all found in CG_DrawPic () statements.

Many thanks :smiley:


(Elite) #12

What error’s do you get in the console when you play on the mod? I am thinking perhaps you forgot to add the icons to you’re pk3??


(Xterm1n8or) #13

Hi :smiley:

I’ve added the cursors to the correct dir. If i was to change this entry in the global.menu:

cursor      "ui/assets/3_cursor3"

…to either the knife, luger or colt, it works. ( Not through my menu though )

I have found 1 warning in the console once the match starts:

Warning: setstate called and no entities found

This comes after:

Setting MOTD
Setting Allied autospawn to old city
Setting Axis autospawn to old city

Hope this helps :slight_smile:


(Elite) #14

The setstate is out of you’re control…its just a warnign for the map.

=========

Look in the sourcecode where it parses that menu file. I don’t have the source handy on me, but chances are you need to use an array where the file is parsed as well.

========

EDIT:
Screw it lol, I’m looking in source now. Just give me a min to edit this post after I’m done lookign at it.


(Elite) #15

Ok, found it.

Look for this statement in cg_main.c:

if (Q_stricmp(token.string, "cursor") == 0) {
			if (!PC_String_Parse(handle, &cgDC.Assets.cursorStr)) {
				return qfalse;
			}
			cgDC.Assets.cursor = trap_R_RegisterShaderNoMip( cgDC.Assets.cursorStr);
			continue;
		}

This is where the cursor is parsed in the file. You can see it is just using another name right now (cgDC.Asssets.cursor). ANywhere you see that used, also make it an array just like you did before (cgDC.Assets.cursor[cg_customcursor]). You may have to add some mroe code to get it working right, just looka round and see what needs to be done.


(Xterm1n8or) #16

Hi :smiley:

I did a search for: cgDC.Assets.cursor, this was the only instance of it.
This is what i did:

		if (Q_stricmp(token.string, "cursor") == 0) {
			if (!PC_String_Parse(handle, &cgDC.Assets.cursorStr)) {
				return qfalse;
			}
			// cgDC.Assets.cursor = trap_R_RegisterShaderNoMip( cgDC.Assets.cursorStr);
         cgDC.Assets.cursor[cg_customCursor] = trap_R_RegisterShaderNoMip( cgDC.Assets.cursorStr);
			continue;
		}

I tried with and without () but got the same compile error:

error C2088: '[' : illegal for struct

I’ve also looked for everything with ‘cursor’ but to be honest, i’m not sure what i’m looking for.

Hope you can help further, many thanks :smiley:


(Elite) #17

Ok, sorry I am so confusing in my posts perhaps. Usually I just kind of expect that people I am helping will correct all my typos for my, or syntax errors when they compile it, or deviate from the plan if it is necessary to perform your specific desired results. I don’t usually try to write tutorials, I usually try to just provide some insight or help point out a few things important, and my original intention of this was not a tutorial.

Again, you need to specify the actual value of cg_customCursor, which is in the .integer portion:
cgDC.Assets.cursor[cg_customCursor.integer]

==============

Then I also usually take for granted that once you see a code block like that, you go and look at where everything is defined and what not. cgDC.Assets.cursor is defined somewhere. The best way to find it is to search for cgDC, and just quickly skimming the found lines to see where it is a variable declaration. You will notice cgDC is declared in cg_main.c, right at the top, and it is a variable of type: structure displayContextDef_t. Now, from here you need to search for displayContextDef_t and determine what type of data the .Assets part of cgDC is. So, performing the search will tell you that the data type of .Assets is: structure cachedAssets_t. So then we continue on following the trail, we are close to where we want to be. We now need to search for cachedAssets_t to determine where the .cursor portion of cgDC.Assets.cursor is defined so we can change it to an array. So, just search for cachedAssets_t and you will find it is located in ui.shared.c Make the cursor into an array of cursors with the max elements as OP_CURSOR_MAX assuming you used the same names in the defines earlier for the cursors.

============

I will try to be a bit more clear next time I post. I never have been great with tutorials… :smiley: You still need to pick through the other parts of the system the new cursor interacts with as well.

TIP: Think of programming statement like simple math.
When you were first learning math, specifically balancing equations, you were taught that to balance it you need to do exactly the same thing on one side of the statement as on the other. (you member balancing equations? Hopefully or this tip is useless :slight_smile: ) Well, programming in a way is the same thing. When you change part of an existing system, what you do in one part you need to do to the rest. But, just like in math where you balance an equation, you only balance the equation you are working on… not the rest of the book of problems. So, programming you will only need to balance off the parts you are using. If you add 2 to one side of an equation, you need to add 2 to the other side to balance it. In programming, to balance it so to speak, you added an array of elements on one side, now you need to add an arrary to everything else and that’s it. Hopefully this example maybe makes some things easier, if not, ignore it :stuck_out_tongue:


(Xterm1n8or) #18

Hi :smiley:

No need to apologise, i very much appreciate the help. As i said in my first post, i wanted to try something easy :o Sorry this has turned into a tutorial :oops:

I changed the line to include the .integer. Now on compile it says, it needs an array or pointer. So off i go to make this array.
Instead of going to the end, i followed every step so i could understand how we arrived at cachedAssets_t. Are we now looking at qHandle_t cursor? Is it this that needs to be turned into an array?

BTW. Thanks for the tip. I was wondering why another array was needed (as we’d already made one). I do remember my maths although it was more than 20 years ago now :smiley:


(Elite) #19

Are we now looking at qHandle_t cursor? Is it this that needs to be turned into an array?

Yes.


(Xterm1n8or) #20

Hi :smiley:

Found cvar table in ui.main.c and added this:

    { NULL, "cg_customCursor", "0", CVAR_ARCHIVE },

I’ve tried many things with the qHandle cursor, but i’m afraid i can’t get it.
Ive gone through all the array tuts i can find. I understand them, i just don’t seem to be able to fit it in here.

I know, i should of started my post with ‘This is what i want, can somebody do it for me?’ I honestly didn’t mean it to be this way. I actually thought this would be fairly easy for a noob to do and i could work myself up to the Connect to IP thing again ( That’s a bit over my head at the moment )

Would you be so kind as to help me finish this? I promise i’ll set my sights a little lower and ask questions on stuff that i have done the majority of. :stuck_out_tongue:

Very sorry :oops: Many thanks :smiley: