Post by Paril on Jun 2, 2006 19:45:20 GMT -5
Difficulty: Moderate (You need to know what you're doing. You can always ask for help though.)
Ever wonder how StroggDM got it's Classes? Ever wanted a class-based mod of your own? Now you can!
Learn the secrets of a class-based mod!
========================================
Let's start!
First, we need to place the client's class number (for identification) somewhere, but we also want it to stay with the player when he respawns. Therefore, we must go to the structure "client_respawn_t". Edit it so the bottom looks like this:
Good! Now, the client can identify it's class.
But, how can it identify it if we don't set it? Let's go to g_cmds under ClientCommand, and let's make it so it looks like this:
Now, typing class1 or class2 will change the class to whatever you need. Also, you must be dead before changing classes.
Now, let's say you want each class to have different weapons on respawn. Sure, why not! Let's do it!
Head to p_client.c, in the function InitClientPersistant
We'll be changing the whole function, so I'll just post it all here.
Good, now class1 will start with Rocket Launchers, and class2 Railguns!
Change it to whatever pleases you.
Now, we don't want invisible players running around, do we? Well, you can if you want, but I doubt people will like it
Change player_die (p_client.c) first part to this:
Now, we're going for the hard part. Head to "ClientBeginDeathmatch" function, and change the first part to this:
BUT, we're not done. From that same function, remove this block:
Now, we're going to add a whole new function. Add this somewhere above ClientBeginDeathmatch:
Now, go to ClientThink, and find this block:
change it to this:
We're done! Now, go test your new Class-Based mod off!
==============================================
ADDONS
==============================================
Now, let's say you don't want weapons to spawn, just ammo. If you do, go to SpawnItem, and change the beginning to:
Now, lets say you don't want people to drop their weapon when they die. Go to player_die, remove this line
All done. Now, have fun with it. You can change this into a simple tourney game, a Team Fortress, maybe a Capture the Something? Either way, have fun with it. The possibilities are endless.
Next tutorial, I'll show you how the server can save stats like health and if you're making an rpg, experience and levels!
Have fun.
-Paril Kalashnikov
Ever wonder how StroggDM got it's Classes? Ever wanted a class-based mod of your own? Now you can!
Learn the secrets of a class-based mod!
========================================
Let's start!
First, we need to place the client's class number (for identification) somewhere, but we also want it to stay with the player when he respawns. Therefore, we must go to the structure "client_respawn_t". Edit it so the bottom looks like this:
int class; //Paril - added for the class variable
} client_respawn_t;
Good! Now, the client can identify it's class.
But, how can it identify it if we don't set it? Let's go to g_cmds under ClientCommand, and let's make it so it looks like this:
else if (Q_stricmp (cmd, "class1") == 0)
{
if (ent->health > 0)
{
gi.cprintf (ent, PRINT_HIGH, "You must be dead to change classes!\n"); // They can't change while alive
return;
}
ent->client->resp.class = 1;
EndObserverMode(ent);
}
else if (Q_stricmp (cmd, "class2") == 0)
{
if (ent->health > 0)
{
gi.cprintf (ent, PRINT_HIGH, "You must be dead to change classes!\n"); // They can't change while alive
return;
}
ent->client->resp.class = 2;
EndObserverMode(ent);
}
else if (Q_stricmp (cmd, "class") == 0)
{
if (ent->client->resp.class == 1)
gi.cprintf(ent, PRINT_HIGH, "You are Class 1.\n");
else if (ent->client->resp.class == 2)
gi.cprintf(ent, PRINT_HIGH, "You are Class 2.\n");
else
gi.cprintf(ent, PRINT_HIGH, "You are an OBSERVER.\n");
}
else // anything that doesn't match a command will be a chat
Cmd_Say_f (ent, false, true);
Now, typing class1 or class2 will change the class to whatever you need. Also, you must be dead before changing classes.
Now, let's say you want each class to have different weapons on respawn. Sure, why not! Let's do it!
Head to p_client.c, in the function InitClientPersistant
We'll be changing the whole function, so I'll just post it all here.
/*
==============
InitClientPersistant
This is only called when the game first initializes in single player,
but is called after each death and level change in deathmatch.
Paril - Make sure to name everything right, or you will
get a crash.
==============
*/
void InitClientPersistant (gclient_t *client)
{
if (client->resp.class == 1)
{
//Class 1
gitem_t *item;
memset (&client->pers, 0, sizeof(client->pers));
item = FindItem("Blaster");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 1;
item = FindItem("Jacket Armor");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 25;
item = FindItem("Rockets");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 30;
item = FindItem("Rocket Launcher");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 1;
client->pers.weapon = item;
}
else if (client->resp.class == 2)
{
//Class 2
gitem_t *item;
memset (&client->pers, 0, sizeof(client->pers));
item = FindItem("Blaster");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 1;
item = FindItem("Combat Armor");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 50;
item = FindItem("Slugs");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 30;
item = FindItem("Railgun");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 1;
client->pers.weapon = item;
}
else
{
//Observer mode, doesn't really matter what they have
gitem_t *item;
memset (&client->pers, 0, sizeof(client->pers));
item = FindItem("Combat Armor");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 1;
client->pers.weapon = item;
}
gitem_t *item;
memset (&client->pers, 0, sizeof(client->pers));
item = FindItem("Blaster");
client->pers.selected_item = ITEM_INDEX(item);
client->pers.inventory[client->pers.selected_item] = 1;
client->pers.weapon = item;
client->pers.health = 100;
client->pers.max_health = 100;
client->pers.max_bullets = 200;
client->pers.max_shells = 100;
client->pers.max_rockets = 50;
client->pers.max_grenades = 50;
client->pers.max_cells = 200;
client->pers.max_slugs = 50;
client->pers.connected = true;
}
Good, now class1 will start with Rocket Launchers, and class2 Railguns!
Change it to whatever pleases you.
Now, we don't want invisible players running around, do we? Well, you can if you want, but I doubt people will like it
Change player_die (p_client.c) first part to this:
/*
==================
player_die
==================
*/
void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
int n;
self->svflags &= ~SVF_NOCLIENT;
Now, we're going for the hard part. Head to "ClientBeginDeathmatch" function, and change the first part to this:
/*
=====================
ClientBeginDeathmatch
A client has just connected to the server in
deathmatch mode, so clear everything out before starting them.
=====================
*/
void ClientBeginDeathmatch (edict_t *ent)
{
G_InitEdict (ent);
InitClientResp (ent->client);
// locate ent at a spawn point
PutClientInServer (ent);
ent->client->ps.gunindex = 0;
gi.linkentity (ent);
BUT, we're not done. From that same function, remove this block:
if (level.intermissiontime)
{
MoveClientToIntermission (ent);
}
else
{
// send effect
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (ent-g_edicts);
gi.WriteByte (MZ_LOGIN);
gi.multicast (ent->s.origin, MULTICAST_PVS);
}
Now, we're going to add a whole new function. Add this somewhere above ClientBeginDeathmatch:
void EndObserverMode(edict_t* ent)
{
ent->movetype &= ~MOVETYPE_NOCLIP;
ent->solid &= ~SOLID_NOT;
ent->svflags &= ~SVF_NOCLIENT;
PutClientInServer (ent);
if (level.intermissiontime)
{
MoveClientToIntermission (ent);
}
else
{
// send effect
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (ent-g_edicts);
gi.WriteByte (MZ_LOGIN);
gi.multicast (ent->s.origin, MULTICAST_PVS);
}
if (ent->client->resp.class == 1)
gi.bprintf (PRINT_HIGH, "%s is Class 2\n", ent->client->pers.netname);
else if (ent->client->resp.class == 2)
gi.bprintf (PRINT_HIGH, "%s is Class 1\n", ent->client->pers.netname);
}
Now, go to ClientThink, and find this block:
if (ent->movetype == MOVETYPE_NOCLIP)
client->ps.pmove.pm_type = PM_SPECTATOR;
else if (ent->s.modelindex != 255)
client->ps.pmove.pm_type = PM_GIB;
else if (ent->deadflag)
client->ps.pmove.pm_type = PM_DEAD;
else
client->ps.pmove.pm_type = PM_NORMAL;
client->ps.pmove.gravity = sv_gravity->value;
pm.s = client->ps.pmove;
change it to this:
if (ent->movetype == MOVETYPE_NOCLIP)
client->ps.pmove.pm_type = PM_SPECTATOR;
else if (ent->s.modelindex != 255)
client->ps.pmove.pm_type = PM_GIB;
else if (ent->deadflag)
client->ps.pmove.pm_type = PM_DEAD;
else
client->ps.pmove.pm_type = PM_NORMAL;
client->ps.pmove.gravity = sv_gravity->value;
if (ent->client->resp.class < 1)
{
ent->solid = SOLID_NOT;
ent->movetype = MOVETYPE_NOCLIP;
ent->svflags |= SVF_NOCLIENT;
}
pm.s = client->ps.pmove;
We're done! Now, go test your new Class-Based mod off!
==============================================
ADDONS
==============================================
Now, let's say you don't want weapons to spawn, just ammo. If you do, go to SpawnItem, and change the beginning to:
/*
============
SpawnItem
Sets the clipping size and plants the object on the floor.
Items can't be immediately dropped to floor, because they might
be on an entity that hasn't spawned yet.
============
*/
void SpawnItem (edict_t *ent, gitem_t *item)
{
if (deathmatch->value)
{
if (strcmp(ent->classname, "weapon_shotgun") == 0)
{
ent->classname = "ammo_shells";
item = FindItemByClassname ("ammo_shells");
}
if (strcmp(ent->classname, "weapon_supershotgun") == 0)
{
ent->classname = "ammo_shells";
item = FindItemByClassname ("ammo_shells");
}
if (strcmp(ent->classname, "weapon_machinegun") == 0)
{
ent->classname = "ammo_bullets";
item = FindItemByClassname ("ammo_bullets");
}
if (strcmp(ent->classname, "weapon_chaingun") == 0)
{
ent->classname = "ammo_bullets";
item = FindItemByClassname ("ammo_bullets");
}
if (strcmp(ent->classname, "weapon_grenadelauncher") == 0)
{
ent->classname = "ammo_grenades";
item = FindItemByClassname ("ammo_grenades");
}
if (strcmp(ent->classname, "weapon_rocketlauncher") == 0)
{
ent->classname = "ammo_rockets";
item = FindItemByClassname ("ammo_rockets");
}
if (strcmp(ent->classname, "weapon_railgun") == 0)
{
ent->classname = "ammo_slugs";
item = FindItemByClassname ("ammo_slugs");
}
if (strcmp(ent->classname, "weapon_hyperblaster") == 0)
{
ent->classname = "ammo_cells";
item = FindItemByClassname ("ammo_cells");
}
if (strcmp(ent->classname, "weapon_bfg") == 0)
{
ent->classname = "ammo_cells";
item = FindItemByClassname ("ammo_cells");
}
}
PrecacheItem (item);
Now, lets say you don't want people to drop their weapon when they die. Go to player_die, remove this line
TossClientWeapon (self);
All done. Now, have fun with it. You can change this into a simple tourney game, a Team Fortress, maybe a Capture the Something? Either way, have fun with it. The possibilities are endless.
Next tutorial, I'll show you how the server can save stats like health and if you're making an rpg, experience and levels!
Have fun.
-Paril Kalashnikov