Commodus Posted February 1, 2003 Share Posted February 1, 2003 Okiedokie folks, here it is, short and sweet: how to add classes to your mod. It isn't mine (it actually came from this thread at Quake3World) so if you decide to use it, it is only fair to credit Tim0tholt and the others in the thread who helped with various things. Razor_Ace said that the community should try to strengthen itself and share knowledge. This is exactly what I'm doing. Now, while there was nothing wrong with the tutorial @ Code3Arena, it changed your class according to model. I didn't like that for various reasons (what happens if you add new models?) so with the help of the thread, I was able to make a system which will change your class via a console command. Technically speaking it uses similar declarations as the class tutorial on Code3Arena but it works slightly different. The files that you will need to change are: cg_consolecmds.c, g_local.h, bg_public.h, g_cmds.c and g_client.c . First bg_public.h: Go to some logical spot (I went to line 660, after the declaration of the team_t enum) and stick in an enum declaration like this one: typedef enum { PCLASS_SCOUT, PCLASS_SNIPER, PCLASS_SOLDIER, PCLASS_DEMOMAN, PCLASS_MEDIC, PCLASS_HWGUY, PCLASS_PYRO, PCLASS_SPY, PCLASS_ENGINEER, PCLASS_NUM_CLASSES } pclass_t; { Do not forget the comma after the last enum (PCLASS_ENGINEER) and do not forget PCLASS_NUM_CLASSES, otherwise it will cause problems. Second, g_local.h: Now we are going to declare two variables in the clientPersistant_t structure to store the current class of the player and the next class of the player. Go to line 333 in g_local.h and add pclass_t playerclass; pclass_t nextplayerclass; underneath "qboolean teamInfo;" What we are going to do is when the player changes his class, we are going to stick the new class value (the player's class after death) into the variable of type pclass_t called nextplayerclass. Then, when the function in g_client.c ClientSpawn() is called, nextplayerclass is stuck into playerclass, and depending on which class the guy is, the person will get different weapons and other goodness. Third, cg_consolecmds.c: Open it up and go to line 555 and add trap_AddCommand ("changeclass"); under "trap_AddCommand ("loaddefered");". After some looking around, I noticed trap_AddCommand() just adds the string argument (i.e. for trap_AddCommand ("changeclass") the argument is "changeclass") to a tab- completion list.... so if you forget how to change classes and you type in change and you press TAB it will complete it for you. Or that's what I think it does, I'm not exactly sure... I'm sure it does more than that (I think it allows the client to send that command to the server or something like that). Stick it there and don't test it... it's better to be safe than sorry. Fourth, g_cmds.c: Open it up and on line 2717 add else if (Q_stricmp (cmd, "changeclass") == 0) Cmd_ChangeClass_f( ent ); It doesn't make a difference where you put the two lines in the if- then-else tree but it has to be there. What it does is it checks if the command you typed into the console is changeclass by using stricmp (stricmp compares two strings and returns 0 if they are equal, and something else if they're not.... very useful). If the command is changeclass, then it runs the function Cmd_ChangeClass_f(). Now, you have to define the function Cmd_ChangeClass_f() somewhere. It doesn't matter where but it must be before the function ClientCommand, which contains the if-then-else tree we edited just now. If you put it after it, the compiler is going to think that it doesn't exist (because it hasn't been defined yet). I stuck mine on line 1722, after Cmd_Vote_f(). Copy and paste this code but try to understand it as well. void Cmd_ChangeClass_f (gentity_t *ent) { char *name; int i; // Get second parameter name = ConcatArgs( 1 ); //I'm not really sure about this one - I think it gets the value of the first argument. if you passed 0 as an argument it would return the command itself i think... Com_Printf("2nd parameter (%i chars) is %s\n", strlen(name), name); //this is just for debugging. remove it later when you feel it works without any glitches... // the next part is a massive if-then-else tree. like i said b4, if two strings are equal, then the function Q_stricmp() should return 0. here, the argument of the changeclass command is checked against various names like scout, sniper, spy, etc. //if any of the expressions evaluates to true, then the corresponding class is assigned to the player. Otherwise it continues checking until it finds a matching class. // if it finds that the class you want to change to doesn't exist, then it will give you a message "Invalid class <classname>" where <classname> is the class you gave. if (Q_stricmp(name, "scout") == 0) { ent->client->pers.nextplayerclass = PCLASS_SCOUT; } else if (Q_stricmp(name, "sniper") == 0) { ent->client->pers.nextplayerclass = PCLASS_SNIPER; } else if (Q_stricmp(name, "soldier") == 0) { ent->client->pers.nextplayerclass = PCLASS_SOLDIER; } else if (Q_stricmp(name, "demoman") == 0) { ent->client->pers.nextplayerclass = PCLASS_DEMOMAN; } else if (Q_stricmp(name, "medic") == 0) { ent->client->pers.nextplayerclass = PCLASS_MEDIC; } else if (Q_stricmp(name, "hwguy") == 0) { ent->client->pers.nextplayerclass = PCLASS_HWGUY; } else if (Q_stricmp(name, "pyro") == 0) { ent->client->pers.nextplayerclass = PCLASS_PYRO; } else if (Q_stricmp(name, "spy") == 0) { ent->client->pers.nextplayerclass = PCLASS_SPY; } else if (Q_stricmp(name, "engineer") == 0) { ent->client->pers.nextplayerclass = PCLASS_ENGINEER; } else { Com_Printf("Invalid class %s \n", name); return; } Com_Printf("Your next player class is %s \n", name); return; } Now I can guarantee you that some coder who is more knowledgeable in C will suggest a better way to make this thing work. For the moment, it works pretty well. Another thing I might get smacked for is Com_Printf - last time I heard it printed the message to the server console (or everybody's console, not sure) which might be problematic (i.e. everybody knows that you're a spy or you don't know whether the changeclass command actually worked or mucked up somewhere). Fifth, g_client.c: Now, open up g_client.c . This is the source file which is perhaps the easiest to understand (for me at least)... Go to the definition of the function ClientSpawn() (line 1585) and add at the very beginning (after the index = ent - g_entities; client = ent->client; bit) client->pers.playerclass = client->pers.nextplayerclass; Now, scroll down to about line 1945, and add the following code (this is to do with weapon changing according to class). if (g_gametype.integer >= GT_TEAM) { client->ps.stats[sTAT_WEAPONS] &= ~((1 << WP_SABER) | (1 << WP_STUN_BATON)); client->ps.stats[sTAT_WEAPONS] |= (1 << WP_BRYAR_PISTOL); // coders are probably thinking "WTF" because what I just did was take out // everybodies lightsabers and stun batons, and have given everybody a bryar pistol. // a player's weapons are stored in a 16-bit variable with every bit position //corresponding to a weapon - so if the very first bit is switched on, a person has the weapon corresponding to that bit (stunbaton) //the WP_ enumerators store the bit position of the weapon. They are declared in bg_weapons.h . //by doing ... &= ~((.... we are switching off the saber and stun baton bits so that nobody has them... yet. You need to have a good understanding of bitwise operators to understand what's going on. switch (client->pers.playerclass) { case PCLASS_SCOUT: client->ps.stats[sTAT_WEAPONS] |= (1 << WP_BLASTER); client->ps.ammo[AMMO_BLASTER] = 100; break; case PCLASS_SNIPER: client->ps.stats[sTAT_WEAPONS] |= ((1 << WP_DISRUPTOR) | (1 << WP_SABER)); client->ps.ammo[AMMO_POWERCELL] = 100; break; case PCLASS_ENGINEER: client->ps.stats[sTAT_WEAPONS] |= (1 << WP_BOWCASTER); client->ps.ammo[AMMO_POWERCELL] = 100; break; case PCLASS_HWGUY: client->ps.stats[sTAT_WEAPONS] |= (1 << WP_REPEATER); client->ps.ammo[AMMO_METAL_BOLTS] = 100; break; case PCLASS_SPY: client->ps.stats[sTAT_WEAPONS] |= (1 << WP_SABER); break; default: client->ps.stats[sTAT_WEAPONS] |= (1 << WP_SABER); break; } } Congratulations - you are finished! Do not be afraid to experiment with the classes and give them different things. If you want the model to change according to class, look in ClientUserinfoChanged() in g_client.c and play around with the things there to get the desired model (use Q_strncpyz to copy model names into the variable containing the model name - I *think* it's called model but I can't be sure). Again, if you use it, credit the forumers who posted in the thread mentioned at the beginning. Link to comment Share on other sites More sharing options...
Wudan Posted February 1, 2003 Share Posted February 1, 2003 Mind if I write that up into a tutorial and post it to my site? I don't have much of anything else yet (just a few other little write-ups), but I've been thinking about it. Tutorials I plan to write up would be based on things that I've modded from other tutorials, or things that I've learned, or descriptions of main functions. I'd have to get permission from some of the original authors (such as the weather code from Q3f, Eternal's Jetpack code, the JediMod things, et cetera.) I think one of the stipulations of JediMod's source should have been "if you use it, you must release your source to the public as well" - that would have been pretty spiffy, because I can't think of a mod that doesn't at least need Tchouky's sabers ... hmmm ... Anyway, that's besides the point. Link to comment Share on other sites More sharing options...
Commodus Posted February 2, 2003 Author Share Posted February 2, 2003 Oh yeah sure. Your site was actually a help, I only found out at the last minute that the WP_ enums were in bg_weapons.h through your Doxygen'd source Originally posted by Wudan doesn't at least need Tchouky's sabers That is quite ironic - the mod I'm making doesn't need Tchouky's saber stuff, nor does it need the dual saber stuff.... the only thing we really need is the hilt stuff, because we're going to have different sword-like weapons. BTW, have you tried compiling the cgame code of JediMod? I tried the other day and it just wouldn't budge.... Link to comment Share on other sites More sharing options...
Pnut_Man Posted February 2, 2003 Share Posted February 2, 2003 Would you be the guy working on the RPG mod? O_o.. I'm Pnut_Tomar from Role Players Anonymous Link to comment Share on other sites More sharing options...
Commodus Posted February 2, 2003 Author Share Posted February 2, 2003 Nope, sorry - I'm working on a different mod... Link to comment Share on other sites More sharing options...
Wudan Posted February 2, 2003 Share Posted February 2, 2003 I started my code based on Raven's code instead of JediMod, because everybody started with JediMod. But, I need to code in JediMod's features , so now I realize I should have just swallowed my pride and built my changes on JediMod. Link to comment Share on other sites More sharing options...
Wudan Posted February 2, 2003 Share Posted February 2, 2003 Tutorial is up that: The Dark Side Compendium Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.