Inside3D!
     

Tutorial: Add Vote-Map to most any mod

 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming
View previous topic :: View next topic  
Author Message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Sun Nov 23, 2008 5:00 am    Post subject: Tutorial: Add Vote-Map to most any mod Reply with quote

This work is my documentation of a hard work of a project done by Bam in late 2007. I attempted to post this here as QuakeC tutorial last year, but I wasn't phyiscally around a lot and had some questions about the formatting and due to frequent lack of internet availability, I never got around to doing it. (Original thread for reference: link)

Anyway, I think Inside3D should serve as the central repository of all QuakeC information:

Foreword

Most of the popular mods have some form of map voting, but the burden upon a QuakeC coder to implement a map voting system is high and usually a foreign and rather technical and painful process for someone who typically is writing a mod for fun and doesn't want to be bogged down with figuring out "administrative code".

Instructions:

1. Add bindings.qc and vote.qc to your QuakeC source folder.

Quote:
Download: bindings.qc
Download: vote.qc


Quote:
You will need to edit vote.qc if the mod or the server you intend to use this on is running something other than the standard Quake maps. The changes necessary are pretty obvious.


Just keep in mind that you will need to change line 241 to expand/contract the impulse range if the number of voteable maps change:

Quote:
Line 241 of vote.qc will need changed if voteable maps are customized

if (self.impulse >= 1 && self.impulse <= 38 )


2. In progs.src, you need to add vote.qc and bindings.qc

Quote:
Add highlighted lines to your progs.src

../progs.dat

defs.qc
settings.qc // Add if this does not exist
func.qc
subs.qc
fight.qc
ai.qc
combat.qc
items.qc
vote.qc // Must be before weapons.qc
weapons.qc
world.qc
bindings.qc // Must be before client.qc
client.qc
player.qc
monsters.qc
doors.qc
buttons.qc
triggers.qc
plats.qc
misc.qc


3. Add this to the end of defs.qc

These are new variables and constants to support the vote-map functionality.

Quote:
Add to end of defs.qc

//BAM ADD
.float pflag, vflag;
.float vdelay, vcount;
.float primary_impulse_hack;
string request_mapname;

//==============
// client.pflag
//==============
// client.pflag is used for random things to remember
// about a player, and its value is saved across levels

float POQ_NEW_CLIENT = 1;
float POQ_BINDINGS_SENT = 2;
float POQ_VOTE_IDENTIFY = 4;

//==============
// client.vflag
//==============
// client.vflag is used for voting

float POQ_VOTE_YES = 1;
float POQ_VOTE_NO = 2;


4. Create or add this to settings.qc

Adjust the impulses as appropriate for your mod. Your mod might already use these impulses or may prefer to change them. Impulses can be from 1 to 255, but generally the lower impulses are for weapons and such.

Quote:
Add to end of settings.qc, if settings.qc does not exist then create it.

float VOTE_PRIMARY_IMPULSE = 97;
float VOTE_YES_IMPULSE = 98;
float VOTE_NO_IMPULSE = 99;
float VOTE_PRINT_DELAY = 10;
float VOTE_PRINT_COUNT = 5;


5. Add this to the end of func.qc

To support the new functions.

Quote:
Add to end of func.qc

//BAM ADD
void () vote_init_consider;
void () vote_aliases;
void (string s, void () func) vote_init;
void (float print) vote_print;
void (string mode_name, void () func) vote_spawn;


6. Open Weapons.qc and modify as follows

a. Find void() ImpulseCommands and highlighted text to an appropriate place in the if statements block.

Quote:
Add highlighted lines to ImpulseCommands in weapons.qc

void() ImpulseCommands =
{

if (self.impulse >= 1 && self.impulse <= 8 )
W_ChangeWeapon ();

if (self.impulse == 9)
CheatCommand ();
if (self.impulse == 10)
CycleWeaponCommand ();
if (self.impulse == 11)
ServerflagsCommand ();
if (self.impulse == 12)
CycleWeaponReverseCommand ();
if (self.impulse == VOTE_YES_IMPULSE || self.impulse == VOTE_NO_IMPULSE)
vote_impulse ();
if (self.impulse == VOTE_PRIMARY_IMPULSE)
{
self.pflag = self.pflag | POQ_VOTE_IDENTIFY;
self.primary_impulse_hack = time + 0.3;
return;
}


if (self.impulse == 255)
QuadCheat ();

self.impulse = 0;
};


b. Find void() W_WeaponFrame and insert highlighted text.

This is where the impulses that control map voting are checked.

Quote:
Add Highlighted code as follows to W_WeaponFrame in weapons.qc

void() W_WeaponFrame =
{
if (time < self.attack_finished)
return;

if (self.impulse)
{
//BAM: Primary/Secondary vote impulse handling
if (!self.pflag & POQ_VOTE_IDENTIFY)
ImpulseCommands ();
else
{
// BAM: If a player uses the primary impulse
// (impulse <VOTE_PRIMARY_IMPULSE>) it goes into an endless loop.
if (self.primary_impulse_hack < time)
{
self.pflag = self.pflag - (self.pflag & POQ_VOTE_IDENTIFY);
self.impulse = 0;
}
else
vote_init_consider ();
}
}


// check for attack
if (self.button0)
{
SuperDamageSound ();
W_Attack ();
}
};


7. Finally open client.qc for the following adjustments

You may need to adjust this for some mods. This is where is bindings are sent to the client.

The process works like this: a client connects and it will need the vote aliases sent to it and .pflag is keeps track of whether or not the aliases have been sent yet or not. This needs to preserve across map changes because clients only need the aliases sent once.

If a client connects, it automatically needs them. If a client disconnects, this must be reset and the voting state of the client needs reset as well (.vflag).

Quote:
Near the top, preferably right before void() SetChangeParms add this function in client.qc

void ()
SetNewParms2 =
{
parm1 = IT_SHOTGUN | IT_AXE;
parm2 = 100;
parm3 = 0;
parm4 = 25;
parm5 = 0;
parm6 = 0;
parm7 = 0;
parm8 = 1;
parm9 = 0;
parm10 = self.pflag;
};


Quote:
Now in SetChangeParms, add the highlighted code

void ()
SetChangeParms =
{
if (self.health <= 0)
{
SetNewParms2 ();
return;
}

//remove items
self.items = self.items - (self.items &
(IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) );

//cap super health
if (self.health > 100)
self.health = 100;
if (self.health < 50)
self.health = 50;
parm1 = self.items;
parm2 = self.health;
parm3 = self.armorvalue;
if (self.ammo_shells < 25)
parm4 = 25;
else
parm4 = self.ammo_shells;
parm5 = self.ammo_nails;
parm6 = self.ammo_rockets;
parm7 = self.ammo_cells;
parm8 = self.weapon;
parm9 = self.armortype * 100;
parm10 = self.pflag;
};


Quote:
Add this single highlighted line to SetNewParms in client.qc

void ()
SetNewParms =
{
parm1 = IT_SHOTGUN | IT_AXE;
parm2 = 100;
parm3 = 0;
parm4 = 25;
parm5 = 0;
parm6 = 0;
parm7 = 0;
parm8 = 1;
parm9 = 0;
parm10 = POQ_NEW_CLIENT; // New client! They will need aliases sent!
};


Quote:
Add/change highlighted lines in DecodeLevelParms in client.qc

void ()
DecodeLevelParms =
{
if (serverflags)
{
if (world.model == "maps/start.bsp")
SetNewParms2 (); // This WAS SetNewParms(); but we are changing!
}

self.items = parm1;
self.health = parm2;
self.armorvalue = parm3;
self.ammo_shells = parm4;
self.ammo_nails = parm5;
self.ammo_rockets = parm6;
self.ammo_cells = parm7;
self.weapon = parm8;
self.armortype = parm9 * 0.01;
self.pflag = parm10; // Need to preserve state of whether aliases have been sent
};


Quote:
Change highlighted line in respawn in client.qc

void ()
respawn =
{
if (coop)
{
// make a copy of the dead body for appearances sake
CopyToBodyQue (self);
// get the spawn parms as they were at level start
setspawnparms (self);
// respawn
PutClientInServer ();
}
else if (deathmatch)
{
// make a copy of the dead body for appearances sake
CopyToBodyQue (self);
// set default spawn parms
SetNewParms2 (); // Was SetNewParms();
// respawn
PutClientInServer ();
}
else
{ // restart the entire server
localcmd ("restart\n");
}
};


Quote:
Change code in ClientConnect in client.qc to mirror this

void ()
ClientConnect =
{
// a client connecting during an intermission can cause problems
if (intermission_running)
ExitIntermission ();

if (parm10 & POQ_NEW_CLIENT) // this is a new client
{
[color=#ff3333]bprint (self.netname); // Note these 2 lines have been moved
bprint (" entered the game\n"); // to below the intermission check to ensure aliases are sent


player_bindings_begin (); // send aliases
parm10 = parm10 - POQ_NEW_CLIENT;
}
[/color]
};


Finally ...

Quote:
Add to end of ClientDisconnect in client.qc

void ()
ClientDisconnect =
{
if (gameover)
return;
// if the level end trigger has been activated, just return
// since they aren't *really* leaving

// let everyone else know
bprint (self.netname);
bprint (" left the game with ");
bprint (ftos(self.frags));
bprint (" frags\n");
sound (self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
set_suicide_frame ();
self.pflag = 0; // Reset "alias sent" status
self.vflag = 0; // Reset voting state (yes/no) status

};
Back to top
View user's profile Send private message
ceriux



Joined: 06 Sep 2008
Posts: 968
Location: Florida, USA

PostPosted: Sun Nov 23, 2008 6:01 am    Post subject: Reply with quote

good tutorials Very Happy wish there was more like it, it explains more... so not only can u take ur time to read through the qc, but you also get to see what certain parts do exactly. do you think you could add a weapon changing and adding tutorial like it? maybe with some kind of moc weapon thats easy to edit?
_________________
QuakeDB - Quake ModDB Group
Back to top
View user's profile Send private message Yahoo Messenger
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Sun Nov 23, 2008 6:27 am    Post subject: Reply with quote

ceriux wrote:
good tutorials Very Happy wish there was more like it, it explains more... so not only can u take ur time to read through the qc, but you also get to see what certain parts do exactly. do you think you could add a weapon changing and adding tutorial like it? maybe with some kind of moc weapon thats easy to edit?


I wrote this tutorial based on someone else's work. It was sort of a cooperative effort.

I'm great at documentation but suck at QuakeC. I don't get to use QuakeC very often because my time is spent modifying engine stuff.

As a result, I spend most of my time writing engine stuff so I never get a lot of time to get serious about QuakeC.

Sorry. Sad

[Things would be different if I could replicate myself via fission like blue-green algae do, but alas my efforts have so far been unsuccessful. Very Happy But I haven't entirely given up on the idea.]
Back to top
View user's profile Send private message
ceriux



Joined: 06 Sep 2008
Posts: 968
Location: Florida, USA

PostPosted: Sun Nov 23, 2008 7:16 am    Post subject: Reply with quote

lol would be cool. once i get good at QC im going to try out engine coding, but i think i should learn the easyier of the two before i move on to the next.
_________________
QuakeDB - Quake ModDB Group
Back to top
View user's profile Send private message Yahoo Messenger
Lardarse



Joined: 05 Nov 2005
Posts: 243
Location: Bristol, UK

PostPosted: Mon Nov 24, 2008 9:14 am    Post subject: Reply with quote

Nice tutorial. There's some mismatched tags near the end, though. Remember: last in, first out.
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Tue Nov 25, 2008 12:30 am    Post subject: Reply with quote

Lardarse wrote:
Nice tutorial. There's some mismatched tags near the end, though. Remember: last in, first out.


LA, could you explain in more detail ....
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Tue Nov 25, 2008 4:59 pm    Post subject: Reply with quote

Note for future:

This code would be even better if it read maxplayers value and had at least an option to bypass all this if == 1 (aka single player or someone just walking around a map even if deathmatch isn't 0).

Vote-map is pointless when run locally and the above method is somewhat inappropriate for someone playing single player.

/Maybe create a mini-utility or javascripty page to easily generate the vote.qc code based on a map list.
Back to top
View user's profile Send private message
Electro



Joined: 29 Dec 2004
Posts: 241
Location: Brisbane, Australia

PostPosted: Tue Nov 25, 2008 8:05 pm    Post subject: Reply with quote

Shouldn't check the maxplayers for that really, should just check the number of players actually in the game. So it'd work in dm if they're the only person on the server.
_________________
Unit reporting!
http://www.bendarling.net/
Back to top
View user's profile Send private message Visit poster's website MSN Messenger
Lardarse



Joined: 05 Nov 2005
Posts: 243
Location: Bristol, UK

PostPosted: Wed Nov 26, 2008 5:31 am    Post subject: Reply with quote

Baker wrote:
Lardarse wrote:
Nice tutorial. There's some mismatched tags near the end, though. Remember: last in, first out.

LA, could you explain in more detail ....

In the Change code in ClientConnect in client.qc to mirror this section.
Back to top
View user's profile Send private message
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Wed Nov 26, 2008 11:27 am    Post subject: Reply with quote

Just for reference, a common feature of QuakeWorld engines is that they forget any aliases that were sent to them during a map.
This started with fuhquake, I believe (maybe zquake?), but can be found in all popular clients. Well, ezquake and other fuhquake derivatives, and FTE.

Basically the following assumption (quoted from the initial post) is not true in the case of quakeworld: 'This needs to preserve across map changes because clients only need the aliases sent once.'

The first 'needs' is an exaggeration in any engine. :P


Oh... And fix the red armour precision bug some time. :P
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Wed Nov 26, 2008 1:11 pm    Post subject: Reply with quote

Spike wrote:
Just for reference, a common feature of QuakeWorld engines is that they forget any aliases that were sent to them during a map.
This started with fuhquake, I believe (maybe zquake?), but can be found in all popular clients. Well, ezquake and other fuhquake derivatives, and FTE.


Is that the "clear all server aliases on disconnect" feature?

So that was added to ZQuake and not a feature of original Quakeworld then?

I think I noticed that a year ago. It sounds like a pretty solid idea of a client-side feature (avoids accumulated aliases that do who knows what if connecting from server to server).
Back to top
View user's profile Send private message
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Wed Nov 26, 2008 2:23 pm    Post subject: Reply with quote

Yeah, its important if you support saving all aliases into configs, or at least the tracking is.
I think it was added with fuhquake. Its certainly not a base feature, but its pretty much base in QW nowadays.
Worth a mention in your tutorial.
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group