Back | Last changes: 1999-08-23 | Contact Maddes |
"NoExit" without dying
(inspired by Robert "Frog" Field)
Sometimes it is really annoying when you loose a frag, because you accidentally stepped into an exit during a heated firefight and "noexit" is enabled. :(
The following code disallows players to exit a map, but he isn't killed if someone tries to and "noexit" is set to 3 or 4 (similar to 1 and 2).
Here's the code:
"Client.qc"
void() changelevel_touch =
{
...
if ((cvar("noexit") == 1) || ((cvar("noexit") == 2) && (mapname != "start")))
{
T_Damage (other, self, self, 50000);
return;
}
// 1998-07-16 Noexit without dying by Maddes (inspired by Robert Field) start
if ((cvar("noexit") == 3) || ((cvar("noexit") == 4) && (mapname != "start")))
{
return;
}
// 1998-07-16 Noexit without dying by Maddes (inspired by Robert Field) end
...
};
Message Of The Day
The message of the day (MOTD) will be displayed when a new client connects and after level changes. It's just for welcoming the clients, giving general information or rejecting client-side bots, hence normally "centerprint()" is used for it.
First create a new file called "Motd.qc" which will contain the displaying and managing functions. Remember that "centerprint()" needs all the text in one argument, and that the clients have a display delay of around 0.5 seconds when connecting.
Normally most functions are for players only, so don't forget to check the entity class.
Include the features, your name, email address and homepage (if available) in your MOTD.
The file should look like this:
"Motd.qc"
/*
This file handles the Message Of The Day (MOTD)
*/
// 1998-07-17 Reject client bots by Maddes
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes
// adding initializing and changing function
// expanding display function
// 1997-12-24 Message Of The Day (MOTD) by Maddes
// file added
// adding printing function of MOTD
void(entity self) DisplayMOTD =
{
if (self.classname != "player")
return;
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes start
// determine which text to display
if (self.motdtext == 1)
{
centerprint(self, "No Bots Please\n"); // 1998-07-17 Reject client bots by Maddes
}
else if (self.motdtext == 0)
{
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes end
centerprint(self,"ULTIMATE REGULAR QUAKE PATCH\n* Auto-aim toggle *\n* Skins *\n* Noexit 3/4 without dying *\n\nby Maddes\nVersion 1.02\n1998-07-17\n\nhttp://www.quake-info-pool.net/\n");
} // 1998-07-17 Series of Message Of The Day (MOTD) by Maddes
};
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes start
void(entity self) InitMOTD =
{
self.motdtext = 2; // quantity of MOTD messages
self.motdtime = time - 0.01; // the past is always to late
};
void(entity self) ChangeMOTD =
{
// next MOTD
self.motdtext = self.motdtext - 1; // counting down to zero
// define timeframe of MOTD being displayed
if (self.motdtext == 1)
{
self.motdtime = time + 0.2; // 1998-07-17 Reject client bots by Maddes
}
else if (self.motdtext == 0)
{
self.motdtime = time + 1.5;
}
};
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes end
Now you have to inform the compiler about the new file by putting the following line at the end of "Progs.src".
"Progs.src"
motd.qc // 1997-12-24 Message Of The Day (MOTD) by Maddes
After this you have to declare the functions in "Defs.qc", so you can call them from the other files.
"Defs.qc"
// 1997-12-24 Message Of The Day (MOTD) by Maddes start
//
// client.qc / motd.qc
//
.float motdtime; // add timeflag for MOTD to entities
.float motdtext; // 1998-07-17 Series of Message Of The Day (MOTD) by Maddes
// add textcounter for MOTD to entities
void(entity self) DisplayMOTD;
void(entity self) InitMOTD; // 1998-07-17 Series of Message Of The Day (MOTD) by Maddes
void(entity self) ChangeMOTD; // 1998-07-17 Series of Message Of The Day (MOTD) by Maddes
// 1997-12-24 Message Of The Day (MOTD) by Maddes end
The handling of the MOTD is done in "Client.qc".
"Client.qc"
void() PlayerPostThink =
{
...
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes start
if ((self.motdtext) && (self.motdtime < time)) // change to next when not last MOTD and time is reached
{
ChangeMOTD(self);
}
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes end
// 1997-12-24 Message Of The Day (MOTD) by Maddes start
if (self.motdtime >= time) // display until time is reached
{
DisplayMOTD(self);
}
// 1997-12-24 Message Of The Day (MOTD) by Maddes end
};
...
void() ClientConnect =
{
...
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes start
InitMOTD(self); // start with first MOTD
// self.motdtime = time + 1.5; // 1997-12-24 Message Of The Day (MOTD) by Maddes
// display some seconds so player can read
// 1998-07-17 Series of Message Of The Day (MOTD) by Maddes end
};
Displaying info on respawn and level changes
This is used to tell the player which settings are in effect for him and which impulses he can use to change them. Also "sprint()" is used so the player can reread the messages in the console.
The QuakeC code is similar to the one of the MOTD, you have to define a timeflag within the entity structure and check for it in the function "PlayerPostThink()".
The timeflag is not only set in "ClientConnect()" but also in "respawn()", and unlike to the MOTD the messages are printed once after the time has reached.
Here's the solution:
"Defs.qc"// 1997-12-24 Message on respawn by Maddes start // // client.qc // .float spawn_info; // add timeflag for displaying respawn info to entities // 1997-12-24 Message on respawn by Maddes end
"Client.qc"void() respawn = { ... self.spawn_info = time + 0.1; // 1997-12-24 Message on respawn by Maddes wait a game tick }; ... void() PlayerPostThink = { ... // 1997-12-24 Message on respawn by Maddes start if (self.spawn_info) // time set? { if (self.spawn_info <= time) // print after time is reached { self.spawn_info = 0; // clear time local string text; // here come all the print functions } } // 1997-12-24 Message on respawn by Maddes end }; ... void() ClientConnect = { ... self.spawn_info = time + 1; // 1997-12-24 Message on respawn by Maddes // wait some seconds so client is ready and player can read the message };
Auto-aim toggle for every client
Once again you have to define a new variable for entities to separate the auto-aim toggle for each client.
Also you need to define which impulse toggles it.
So put something like this in "Defs.qc":
"Defs.qc"
// 1997-12-26 auto-aim toggle by Maddes start
//
// weapons.qc / client.qc
//
float IMPULSE_TOGGLE_AIM = 250; // setting impulse for toggle
.float autoaim_off; // add flag for auto-aim to entities
// 1997-12-26 auto-aim toggle by Maddes end
In "Weapons.qc" you have to recognize the player's auto-aim toggle when firing.
"Weapons.qc"
void() W_FireShotgun =
{
local vector dir;
// 1997-12-26 auto-aim toggle by Maddes start
local float save_aim;
local string set_aim;
if (self.autoaim_off)
{
if (correct_cvars)
{
save_aim = cvar("sv_aim");
set_aim = ftos(save_aim);
}
cvar_set("sv_aim", "1"); // disable auto-aiming by setting sv_aim to 1
}
// 1997-12-26 auto-aim toggle by Maddes end
...
// 1997-12-26 auto-aim toggle by Maddes start
if (self.autoaim_off)
{
if (correct_cvars)
{
cvar_set("sv_aim", set_aim); // reset sv_aim to previous setting
}
else
{
cvar_set("sv_aim", "0.93"); // set sv_aim to id's standard of 0.93
}
}
// 1997-12-26 auto-aim toggle by Maddes end
};
...
void() W_FireSuperShotgun =
{
local vector dir;
if (self.currentammo == 1)
{
W_FireShotgun ();
return;
}
// 1997-12-26 auto-aim toggle by Maddes start
local float save_aim;
local string set_aim;
if (self.autoaim_off)
{
if (correct_cvars)
{
save_aim = cvar("sv_aim");
set_aim = ftos(save_aim);
}
cvar_set("sv_aim", "1"); // disable auto-aiming by setting sv_aim to 1
}
// 1997-12-26 auto-aim toggle by Maddes end
...
// 1997-12-26 auto-aim toggle by Maddes start
if (self.autoaim_off)
{
if (correct_cvars)
{
cvar_set("sv_aim", set_aim); // reset sv_aim to previous setting
}
else
{
cvar_set("sv_aim", "0.93"); // set sv_aim to id's standard of 0.93
}
}
// 1997-12-26 auto-aim toggle by Maddes end
};
...
void() W_FireRocket =
{
local entity missile, mpuff;
// 1997-12-26 auto-aim toggle by Maddes start
local float save_aim;
local string set_aim;
if (self.autoaim_off)
{
if (correct_cvars)
{
save_aim = cvar("sv_aim");
set_aim = ftos(save_aim);
}
cvar_set("sv_aim", "1"); // disable auto-aiming by setting sv_aim to 1
}
// 1997-12-26 auto-aim toggle by Maddes end
...
// 1997-12-26 auto-aim toggle by Maddes start
if (self.autoaim_off)
{
if (correct_cvars)
{
cvar_set("sv_aim", set_aim); // reset sv_aim to previous setting
}
else
{
cvar_set("sv_aim", "0.93"); // set sv_aim to id's standard of 0.93
}
}
// 1997-12-26 auto-aim toggle by Maddes end
};
...
void() W_FireGrenade =
{
local entity missile, mpuff;
// 1997-12-26 auto-aim toggle by Maddes start
local float save_aim;
local string set_aim;
if (self.autoaim_off)
{
if (correct_cvars)
{
save_aim = cvar("sv_aim");
set_aim = ftos(save_aim);
}
cvar_set("sv_aim", "1"); // disable auto-aiming by setting sv_aim to 1
}
// 1997-12-26 auto-aim toggle by Maddes end
...
// 1997-12-26 auto-aim toggle by Maddes start
if (self.autoaim_off)
{
if (correct_cvars)
{
cvar_set("sv_aim", set_aim); // reset sv_aim to previous setting
}
else
{
cvar_set("sv_aim", "0.93"); // set sv_aim to id's standard of 0.93
}
}
// 1997-12-26 auto-aim toggle by Maddes end
};
...
void() W_FireSuperSpikes =
{
local vector dir;
local entity old;
// 1997-12-26 auto-aim toggle by Maddes start
local float save_aim;
local string set_aim;
if (self.autoaim_off)
{
if (correct_cvars)
{
save_aim = cvar("sv_aim");
set_aim = ftos(save_aim);
}
cvar_set("sv_aim", "1"); // disable auto-aiming by setting sv_aim to 1
}
// 1997-12-26 auto-aim toggle by Maddes end
...
// 1997-12-26 auto-aim toggle by Maddes start
if (self.autoaim_off)
{
if (correct_cvars)
{
cvar_set("sv_aim", set_aim); // reset sv_aim to previous setting
}
else
{
cvar_set("sv_aim", "0.93"); // set sv_aim to id's standard of 0.93
}
}
// 1997-12-26 auto-aim toggle by Maddes end
};
...
void(float ox) W_FireSpikes =
{
local vector dir;
local entity old;
makevectors (self.v_angle);
if (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN)
{
W_FireSuperSpikes ();
return;
}
if (self.ammo_nails < 1)
{
self.weapon = W_BestWeapon ();
W_SetCurrentAmmo ();
return;
}
// 1997-12-26 auto-aim toggle by Maddes start
local float save_aim;
local string set_aim;
if (self.autoaim_off)
{
if (correct_cvars)
{
save_aim = cvar("sv_aim");
set_aim = ftos(save_aim);
}
cvar_set("sv_aim", "1"); // disable auto-aiming by setting sv_aim to 1
}
// 1997-12-26 auto-aim toggle by Maddes end
...
// 1997-12-26 auto-aim toggle by Maddes start
if (self.autoaim_off)
{
if (correct_cvars)
{
cvar_set("sv_aim", set_aim); // reset sv_aim to previous setting
}
else
{
cvar_set("sv_aim", "0.93"); // set sv_aim to id's standard of 0.93
}
}
// 1997-12-26 auto-aim toggle by Maddes end
};
...
void() ImpulseCommands =
{
if (self.impulse >= 1 && self.impulse <= 8)
W_ChangeWeapon ();
if (self.impulse == 9)
CheatCommand ();
...
// 1997-12-26 auto-aim toggle by Maddes start
if (self.impulse == IMPULSE_TOGGLE_AIM) // Impulse to toggle auto-aim on/off
{
self.autoaim_off = 1 - self.autoaim_off;
sprint(self,"Auto-Aiming toggled ");
if (self.autoaim_off) sprint(self,"off\n");
else sprint(self,"on\n");
}
// 1997-12-26 auto-aim toggle by Maddes end
if (self.impulse == 255)
QuadCheat ();
self.impulse = 0;
};
Right now the player is able to toggle auto-aiming on or off, but has to redo this each time he respawns or after a level change.
"Client.qc"
void() SetChangeParms =
{
...
parm9 = self.armortype * 100;
parm16 = self.autoaim_off; // 1997-12-26 auto-aim toggle by Maddes save auto-aim on level changes
};
void() SetNewParms =
{
...
parm9 = 0;
// 1997-12-26 auto-aim toggle by Maddes self.autoaim_off is only saved in parm16 on respawn (see there)
};
void() DecodeLevelParms =
{
...
self.armortype = parm9 * 0.01;
// 1997-12-26 auto-aim toggle by Maddes start
self.autoaim_off = parm16; // remember auto-aim after respawn and level changes
parm16 = 0; // has to be cleared, because new clients get a copy of the "Parm..." variables from another client
// and all clients should start with auto-aiming on
// 1997-12-26 auto-aim toggle by Maddes end
};
...
void() respawn =
{
if (coop)
{
...
// get the spawn parms as they were at level start
setspawnparms (self);
parm16 = self.autoaim_off; // 1997-12-26 auto-aim toggle by Maddes save auto-aim on respawn
// respawn
PutClientInServer ();
}
else if (deathmatch)
{
...
// set default spawn parms
SetNewParms ();
parm16 = self.autoaim_off; // 1997-12-26 auto-aim toggle by Maddes save auto-aim on respawn
// respawn
PutClientInServer ();
}
else
{ // restart the entire server
localcmd ("restart\n");
}
self.spawn_info = time + 0.1; // 1997-12-24 Message on respawn by Maddes wait a game tick
};
...
void() PlayerPostThink =
{
...
// 1997-12-24 Message on respawn by Maddes start
if (self.spawn_info) // time set?
{
if (self.spawn_info <= time) // print after time is reached
{
self.spawn_info = 0; // clear time
local string text;
// here come all the print functions
// 1997-12-26 auto-aim toggle by Maddes start
sprint(self,"\"impulse ");
text = ftos(IMPULSE_TOGGLE_AIM);
sprint(self,text);
sprint(self,"\" to toggle auto-aim\n");
// 1997-12-26 auto-aim toggle by Maddes end
// 1997-12-26 auto-aim toggle by Maddes start
sprint(self,"Auto-Aiming is ");
if (self.autoaim_off) sprint(self,"off\n");
else sprint(self,"on\n");
// 1997-12-26 auto-aim toggle by Maddes end
}
}
// 1997-12-24 Message on respawn by Maddes end
};
Multiple skins support for all players
A long time ago Toni Wilen posted a QuakeC workaround for displaying skins correctly in GLQuake. After every ".skin = ...;" on a player you should also clear ".colormap" and ".modelindex", some how this helped a lot with the old GLQuake. With the current version (v0.97) of GLQuake this is no more necessary, although there are still some problems with dead players and skins. Read more about this on my bugs page.
By the way, clearing ".colormap" avoids seeing player colors and ".modelindex" is changed a lot during the game, so clearing it only after ".skin = ..." doesn't last long.
Now to the patch:
First you have to define a new entity variable again, this will let you save the selected skin separated from the current shown skin, so skins depending on an event (like wearing biosuit, etc.) are possible.
You need some functions to let the player choose his skin and let him know which skin is currently selected. The skin selection should be able to skip skins by a given value, to avoid event skins and only allow skins in a given range.
Also a function which returns the name of a skin is fine, but not necessary because the skins on the clients may vary.
The routines select skin #0 when you pickup the ring of shadows, as the eyes model doesn't need multiple skins. When a player dies or suicides he gets his selected skin back.
On the finale intermission after killing Shub the shown player model has correct colors and skin.
"Skins.qc"Now you have to code the two impulses for selecting the next or previous skin. Also when a player dies his corpse should keep the skin and finally his skin selection should be remembered on respawn and level changes, just like with the auto-aim toggle./* This file handles skin selections */ // 1997-12-30 skin support by Maddes // file added // skin select and print functions void(entity ent, float prev_or_next) ChangeSkin = { local float skin_new; // For calculating issues local float number; // For calculating issues local string text; // For printing issues if (ent.classname != "player") return; skin_new = ent.skin_selected + prev_or_next; // Add or Subtract the skin-number while ( (skin_new < 0) || (skin_new >= SKINS_MAX_SUPPORTED) ) { if (skin_new < 0) // Cant be below zero (normally Quake Guy) { skin_new = 0; prev_or_next = 1; // change direction sprint(ent,"No skins before #1\n"); // Print "No skins before #1" plus new-line } else if (skin_new >= SKINS_MAX_SUPPORTED) // Cant be equal to maximum skins { skin_new = SKINS_MAX_SUPPORTED - 1; prev_or_next = -1; // change direction number = SKINS_MAX_SUPPORTED; text = ftos(number); // Convert float to string for printing number sprint(ent,"Last skin is #"); // Print "Last skin is #<number>" plus new-line sprint(ent,text); sprint(ent,"\n"); } else { skin_new = skin_new + prev_or_next; // Add or Subtract the skin-number } } ent.skin_selected = skin_new; SetSkin(ent); DisplaySkinName(ent); }; void(entity ent) SetSkin = { if (ent.invisible_finished) // eyes model does not need multiple skins ent.skin = 0; else ent.skin = ent.skin_selected; }; void(entity ent) DisplaySkinName = { local float number; // For calculating issues local string text; // For printing issues if (ent.classname != "player") return; number = ent.skin_selected + 1; // skin number text = ftos(number); // Convert float to string for printing number sprint(ent,"Skin #"); // Print "Skin #<number>" sprint(ent,text); text = GetSkinName(ent); if (text) { sprint(ent," \""); // Print ""<name>"" sprint(ent,text); sprint(ent,"\""); } sprint(ent,"\n"); // Print new-line }; string(entity ent) GetSkinName = { if (ent.classname != "player") return; // Here you enter what skin number goes with what text, just type the name of the skin where you see <name> // and don't forget to remove the comment characters "//" from the line if (ent.skin_selected == 0) return("Quake Guy"); else if (ent.skin_selected == 1) return("Bot Skill 0 Skin"); else if (ent.skin_selected == 2) return("Bot Skill 1 Skin"); else if (ent.skin_selected == 3) return("Bot Skill 2 Skin"); else if (ent.skin_selected == 4) return("Bot Skill 3 Skin"); else if (ent.skin_selected == 5) return("Imperial Stormtrooper"); else if (ent.skin_selected == 6) return("Mandalorian Commando"); else if (ent.skin_selected == 7) return("Executor"); else if (ent.skin_selected == 8) return("Punisher"); else if (ent.skin_selected == 9) return("Jules Winfield"); else if (ent.skin_selected == 10) return("Vincent Vega"); else if (ent.skin_selected == 11) return("Predator"); else if (ent.skin_selected == 12) return("Klingon"); else if (ent.skin_selected == 13) return("Crow"); else if (ent.skin_selected == 14) return("Spawn"); else if (ent.skin_selected == 15) return("Terminator"); else if (ent.skin_selected == 16) return("Hulk"); else if (ent.skin_selected == 17) return("Cyclops"); else if (ent.skin_selected == 18) return("Cable"); else if (ent.skin_selected == 19) return("Ghostbuster"); else if (ent.skin_selected == 20) return("Ranger"); else if (ent.skin_selected == 21) return("BioSuit"); else if (ent.skin_selected == 22) return("Boosk"); else if (ent.skin_selected == 23) return("X-Wing Pilot"); else if (ent.skin_selected == 24) return("Doom Marine"); else if (ent.skin_selected == 25) return("MoD"); else if (ent.skin_selected == 26) return("Lobo"); else if (ent.skin_selected == 27) return("Toad"); else if (ent.skin_selected == 28) return("Duke Nukem"); else if (ent.skin_selected == 29) return("Wolf"); else if (ent.skin_selected == 30) return("Guybrush Threepwood"); else if (ent.skin_selected == 31) return("Star Trek: TNG"); return; };
"Progs.src"skins.qc // 1997-12-30 skin support by Maddes
"Defs.qc"// 1997-12-30 skin support by Maddes start // // weapons.qc / client.qc / oldone.qc / skins.qc // float IMPULSE_SKIN_NEXT = 200; // setting impulses for switches float IMPULSE_SKIN_PREV = 201; float SKINS_MAX_SUPPORTED = 32; // maximum number of skins the player.mdl can have .float skin_selected; // extra variable for choosen skin to enable event skins (biosuit, pent, etc.) entity ShubKiller; // player who killed Shub for displaying right colors and skin on finale intermission void(entity ent, float prev_or_next) ChangeSkin; void(entity ent) SetSkin; string(entity ent) GetSkinName; void(entity ent) DisplaySkinName; // 1997-12-30 skin support by Maddes end
"Weapons.qc"To provide event handling for skins, here for the biosuit, you have to define which skins are event-only and recognize them in the selection and display functions. Also you should set the players skin back to his selection, when he dies.void() ImpulseCommands = { ... if (self.impulse == 12) CycleWeaponReverseCommand (); // 1997-12-30 skin support by Maddes start if (self.impulse == IMPULSE_SKIN_NEXT) // Impulse to select next skin { ChangeSkin(self, 1); } if (self.impulse == IMPULSE_SKIN_PREV) // Impulse to select previous skin { ChangeSkin(self, -1); } // 1997-12-30 skin support by Maddes end if (self.impulse == 255) QuadCheat (); self.impulse = 0; };
"World.qc"void(entity ent) CopyToBodyQue = { bodyque_head.skin = ent.skin; // 1997-12-30 skin support by Maddes copy the skin on the dead player ... };
"Client.qc"void() SetChangeParms = { ... parm9 = self.armortype * 100; parm15 = self.skin_selected; // 1997-12-30 skin support by Maddes save skin on level changes }; void() SetNewParms = { ... parm9 = 0; // 1997-12-30 skin support by Maddes self.skin_selected is only saved in parm15 on respawn (see there) }; void() DecodeLevelParms = { ... self.armortype = parm9 * 0.01; // 1997-12-30 skin support by Maddes start self.skin_selected = parm15; // remember skin after respawn and level changes parm15 = 0; // has to be cleared, because new clients get a copy of the "Parm..." variables from another client // and all clients should start with skin #0 // 1997-12-30 skin support by Maddes end }; ... void() respawn = { if (coop) { ... // get the spawn parms as they were at level start setspawnparms (self); parm15 = self.skin_selected; // 1997-12-30 skin support by Maddes save skin on respawn // respawn PutClientInServer (); } else if (deathmatch) { ... // set default spawn parms SetNewParms (); parm15 = self.skin_selected; // 1997-12-30 skin support by Maddes save skin on respawn // respawn PutClientInServer (); } else { // restart the entire server localcmd ("restart\n"); } self.spawn_info = time + 0.1; // 1997-12-24 Message on respawn by Maddes wait a game tick }; ... void() ClientKill = { ... self.modelindex = modelindex_player; self.skin = self.skin_selected; // 1997-12-30 skin support by Maddes ... }; ... void() PutClientInServer = { ... // oh, this is a hack! self.skin = 0; // 1997-12-30 skin support by Maddes setmodel (self, "progs/eyes.mdl"); modelindex_eyes = self.modelindex; setmodel (self, "progs/player.mdl"); modelindex_player = self.modelindex; self.skin = self.skin_selected; // 1997-12-30 skin support by Maddes ... }; ... void() CheckPowerups = { if (self.health <= 0) return; // invisibility if (self.invisible_finished) { ... // use the eyes self.frame = 0; SetSkin(self); // 1997-12-30 skin support by Maddes self.modelindex = modelindex_eyes; } else { self.modelindex = modelindex_player; // don't use eyes SetSkin(self); // 1997-12-30 skin support by Maddes } }; ... void() PlayerPostThink = { ... // 1997-12-24 Message on respawn by Maddes start if (self.spawn_info) // time set? { if (self.spawn_info <= time) // print after time is reached { self.spawn_info = 0; // clear time local string text; // here come all the print functions // 1997-12-30 skin support by Maddes start sprint(self,"\"impulse "); text = ftos(IMPULSE_SKIN_NEXT); sprint(self,text); sprint(self,"/"); text = ftos(IMPULSE_SKIN_PREV); sprint(self,text); sprint(self,"\" for next/prev skin\n"); // 1997-12-30 skin support by Maddes end DisplaySkinName(self); // 1997-12-30 skin support by Maddes } } // 1997-12-24 Message on respawn by Maddes end }; ... void(entity targ, entity attacker) ClientObituary = { local float rnum; local string deathstring, deathstring2; rnum = random(); // 1997-12-30 skin support by Maddes/Zhenga start if (targ.classname == "monster_oldone") { if (attacker.owner) ShubKiller = attacker.owner; } // 1997-12-30 skin support by Maddes/Zhenga end ... };
"Oldone.qc"void() finale_4 = { ... // put a player model down n = spawn(); setmodel (n, "progs/player.mdl"); // 1997-12-30 skin support by Maddes/Zhenga start if (ShubKiller) { n.skin = ShubKiller.skin_selected; n.colormap = ShubKiller.colormap; } // 1997-12-30 skin support by Maddes/Zhenga end oldo = oldo - '32 264 0'; setorigin (n, oldo); n.angles = '0 290 0'; n.frame = 1; ... };
"Player.qc"void() PlayerDie = { ... self.modelindex = modelindex_player; // don't use eyes self.skin = self.skin_selected; // 1997-12-30 skin support by Maddes if (deathmatch || coop) DropBackpack(); ... };
"Defs.qc"// 1998-01-04 event skin support by Maddes start // // skins.qc // float SKINS_BIOSUIT = 21; // index of biosuit skin (number - 1) // 1998-01-04 event skin support by Maddes end
"Skins.qc"void(entity ent, float prev_or_next) ChangeSkin = { ... while ( (skin_new < 0) || (skin_new == SKINS_BIOSUIT) // 1998-01-04 event skin support by Maddes recognize biosuit || (skin_new >= SKINS_MAX_SUPPORTED) ) { if (skin_new < 0) // Cant be below zero (normally Quake Guy) { ... } else if (skin_new >= SKINS_MAX_SUPPORTED) // Cant be equal to maximum skins { skin_new = SKINS_MAX_SUPPORTED - 1; prev_or_next = -1; // change direction number = SKINS_MAX_SUPPORTED; // 1998-01-04 event skin support by Maddes start correct number if (number > SKINS_BIOSUIT) // biosuit { number = number - 1; } // 1998-01-04 event skin support by Maddes end correct number text = ftos(number); // Convert float to string for printing number sprint(ent,"Last skin is #"); // Print "Last skin is #<number>" plus new-line sprint(ent,text); sprint(ent,"\n"); } else { skin_new = skin_new + prev_or_next; // Add or Subtract the skin-number } } ... }; void(entity ent) SetSkin = { if (ent.invisible_finished) // eyes model does not need multiple skins ent.skin = 0; // 1998-01-04 event skin support by Maddes start else if (ent.radsuit_finished) // biosuit ent.skin = SKINS_BIOSUIT; // 1998-01-04 event skin support by Maddes end else ent.skin = ent.skin_selected; }; ... void(entity ent) DisplaySkinName = { local float number; // For calculating issues local string text; // For printing issues if (ent.classname != "player") return; number = ent.skin_selected + 1; // skin number // 1998-01-04 event skin support by Maddes start correct number if (number > SKINS_BIOSUIT) { number = number - 1; } // 1998-01-04 event skin support by Maddes end correct number ... };
"Client.qc"void() CheckPowerups = { ... // suit if (self.radsuit_finished) { ... if (self.radsuit_finished < time) { // just stopped self.items = self.items - IT_SUIT; self.rad_time = 0; self.radsuit_finished = 0; SetSkin(self); // 1998-01-04 event skin support by Maddes } } };
"Items.qc"void() powerup_touch = { ... // do the apropriate action if (self.classname == "item_artifact_envirosuit") { other.rad_time = 1; other.radsuit_finished = time + 30; SetSkin(other); // 1998-01-04 event skin support by Maddes set biosuit skin } ... };
All SVC commands by Yun Zheng "Zhenga" Hu
Not all SVC commands are defined by default, although some of them can be very useful.
Just add them to your source:
"Defs.qc"
...
// protocol bytes
// 1998-08-08 Complete SVC list by Zhenga start
float SVC_BAD = 0;
float SVC_NOP = 1;
float SVC_DISCONNECT = 2;
float SVC_UPDATESTAT = 3;
float SVC_VERSION = 4;
float SVC_SETVIEW = 5;
float SVC_SOUND = 6;
float SVC_TIME = 7;
float SVC_PRINT = 8;
float SVC_STUFFTEXT = 9;
float SVC_SETANGLE = 10;
float SVC_SERVERINFO = 11;
float SVC_LIGHTSTYLE = 12;
float SVC_UPDATENAME = 13;
float SVC_UPDATEFRAGS = 14;
float SVC_CLIENTDATA = 15;
float SVC_STOPSOUND = 16;
float SVC_UPDATECOLORS = 17;
float SVC_PARTICLE = 18;
float SVC_DAMAGE = 19;
float SVC_SPAWNSTATIC = 20;
float SVC_SPAWNBINARY = 21;
float SVC_SPAWNBASELINE = 22;
// 1998-08-08 Complete SVC list by Zhenga end
float SVC_TEMPENTITY = 23;
// 1998-08-08 Complete SVC list by Zhenga start
float SVC_SETPAUSE = 24;
float SVC_SIGNONNUM = 25;
float SVC_CENTERPRINT = 26;
// 1998-08-08 Complete SVC list by Zhenga end
float SVC_KILLEDMONSTER = 27;
float SVC_FOUNDSECRET = 28;
float SVC_SPAWNSTATICSOUND = 29; // 1998-08-08 Complete SVC list by Zhenga
float SVC_INTERMISSION = 30;
float SVC_FINALE = 31;
float SVC_CDTRACK = 32;
float SVC_SELLSCREEN = 33;
float SVC_CUTSCENE = 34; // 1998-08-08 Complete SVC list by Zhenga
...
Centerprint with multiple strings by Yun Zheng "Zhenga" Hu
It is possible to centerprint multiple strings at once, you just have to define some new centerprint functions.
The maximum number of strings is 7 with the original QCC, some other precompilers can only compile 6 or less.
The maximum number of characters in one string or multiple strings printed at once is around 2000.
The maximum number of characters per screen line is 40, independent from the current resolution.
The maximum number of lines per screen is 13, as more will cause weird problems in 320x200.
You can not print more than one variable at a time, or you have to rebuild each centerprint with a lot of WriteBytes.
"Defs.qc"
...
void(entity client, string s) centerprint = #73; // sprint, but in middle
// 1998-08-08 Centerprint with multiple strings by Zhenga start
void(entity client, string s, string s) centerprint2 = #73;
void(entity client, string s, string s, string s) centerprint3 = #73;
void(entity client, string s, string s, string s, string s) centerprint4 = #73;
void(entity client, string s, string s, string s, string s, string s) centerprint5 = #73;
void(entity client, string s, string s, string s, string s, string s, string s) centerprint6 = #73;
void(entity client, string s, string s, string s, string s, string s, string s, string s) centerprint7 = #73;
// 1998-08-08 Centerprint with multiple strings by Zhenga end
...
An additonal workaround by Dirk "VoodooBug" Gerrits:
...
// 1999-03-08 Centerprint with any number of strings by VoodooBug start
//void(entity client, string s) centerprint = #73; // sprint, but in middle
void(...) centerprint = #73; // sprint, but in middle
// 1999-03-08 Centerprint with any number of strings by VoodooBug end
...
Player drops backpack on suicide
This avoids one reason why some very "cool" guys kill themselves before being fragged.
Instead of respawning immediately, just use the normal death functions. Set the player's health to zero (died for the engine) and call the "Killed()" function.
As this also uses the "ClientObituary" function we have to define a new deathtype for commiting suicide.
"Client.qc"
void() ClientKill =
{
// 1998-07-27 Suicide during intermission fix by Zhenga start
if ((intermission_running)&&((coop)||(deathmatch))) // not allowed during intermission
return;
// 1998-07-27 Suicide during intermission fix by Zhenga end
// 1998-08-10 Player drops backpack on suicide by Maddes start
/*
bprint (self.netname);
bprint (" suicides\n");
set_suicide_frame ();
self.modelindex = modelindex_player;
self.skin = self.skin_selected; // 1997-12-30 skin support by Maddes
self.frags = self.frags - 2; // extra penalty
respawn ();
*/
self.deathtype = "suicide";
self.health = 0;
Killed(self,self);
// 1998-08-10 Player drops backpack on suicide by Maddes end
};
...
void(entity targ, entity attacker) ClientObituary =
{
...
if (targ == attacker)
{
// killed self
attacker.frags = attacker.frags - 1;
bprint (targ.netname);
// 1998-08-10 Player drops backpack on suicide by Maddes start
if (targ.deathtype == "suicide")
{
attacker.frags = attacker.frags - 1; // extra penalty
bprint (" suicides\n");
return;
}
// 1998-08-10 Player drops backpack on suicide by Maddes end
...
};
Smooth plat movement by Matt Barnett
Plats only move at 10 frames per seconds, which is very choppy when playing with 30fps.
Just decrease the time between movements to 0.01, so it can move at 100 frames per seconds making it much smoother.
"Plats.qc"
void() train_wait =
{
if (self.wait)
{
self.nextthink = self.ltime + self.wait;
sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
}
else
// 1998-08-13 Smoother plat movement by Matt Barnett start
// self.nextthink = self.ltime + 0.1;
self.nextthink = self.ltime + 0.01;
// 1998-08-13 Smoother plat movement by Matt Barnett end
self.think = train_next;
};
...
void() func_train_find =
{
local entity targ;
targ = find (world, targetname, self.target);
self.target = targ.target;
setorigin (self, targ.origin - self.mins);
if (!self.targetname)
{ // not triggered, so start immediately
// 1998-08-13 Smoother plat movement by Matt Barnett start
// self.nextthink = self.ltime + 0.1;
self.nextthink = self.ltime + 0.01;
// 1998-08-13 Smoother plat movement by Matt Barnett end
self.think = train_next;
}
};
void() func_train =
{
...
// start trains on the second frame, to make sure their targets have had
// a chance to spawn
// 1998-08-13 Smoother plat movement by Matt Barnett start
// self.nextthink = self.ltime + 0.1;
self.nextthink = self.ltime + 0.01;
// 1998-08-13 Smoother plat movement by Matt Barnett end
self.think = func_train_find;
};
void() misc_teleporttrain =
{
...
// start trains on the second frame, to make sure their targets have had
// a chance to spawn
// 1998-08-13 Smoother plat movement by Matt Barnett start
// self.nextthink = self.ltime + 0.1;
self.nextthink = self.ltime + 0.01;
// 1998-08-13 Smoother plat movement by Matt Barnett end
self.think = func_train_find;
};
Better performance in handling of powerups
In each frame the player or eye model (for the ring of shadows) is set, and also the glowing for quad or pentagram, which can consume some time. The following code just sets the corresponding model or glowing when a powerup is picked up or expires.
The modelindex varaibles will be used across some files, so we have to define them in "Defs.qc".
Setting the eye model or glowing will be done when picking up an item, only the running out is still done in the "CheckPowerup()" function.
"Defs.qc"// 1998-07-23 Better performance in handling of powerups by Maddes start // // defs.qc / client.qc / items.qc / player.qc // float modelindex_eyes, modelindex_player; // 1998-07-23 Better performance in handling of powerups by Maddes end
"Client.qc"... // float modelindex_eyes, modelindex_player; // 1998-07-23 Better performance in handling of powerups by Maddes ... void() CheckPowerups = { ... // invisibility if (self.invisible_finished) { ... if (self.invisible_finished < time) { // just stopped self.items = self.items - IT_INVISIBILITY; self.invisible_finished = 0; self.invisible_time = 0; self.modelindex = modelindex_player; // 1998-07-23 Better performance in handling of powerups by Maddes // don't use eyes SetSkin(self); // 1997-12-30 skin support by Maddes } // 1998-07-23 Better performance in handling of powerups by Maddes start /* // use the eyes self.frame = 0; SetSkin(self); // 1997-12-30 skin support by Maddes self.modelindex = modelindex_eyes; */ // 1998-07-23 Better performance in handling of powerups by Maddes end } // 1998-07-23 Better performance in handling of powerups by Maddes start /* else { self.modelindex = modelindex_player; // don't use eyes SetSkin(self); // 1997-12-30 skin support by Maddes } */ // 1998-07-23 Better performance in handling of powerups by Maddes end // invincibility if (self.invincible_finished) { ... if (self.invincible_finished < time) { // just stopped self.items = self.items - IT_INVULNERABILITY; self.invincible_time = 0; self.invincible_finished = 0; // 1998-07-23 Better performance in handling of powerups by Maddes start if (!self.super_damage_finished) self.effects = self.effects - (self.effects & EF_DIMLIGHT); // 1998-07-23 Better performance in handling of powerups by Maddes end } // 1998-07-23 Better performance in handling of powerups by Maddes start /* if (self.invincible_finished > time) self.effects = self.effects | EF_DIMLIGHT; else self.effects = self.effects - (self.effects & EF_DIMLIGHT); */ // 1998-07-23 Better performance in handling of powerups by Maddes end } // super damage if (self.super_damage_finished) { ... if (self.super_damage_finished < time) { // just stopped self.items = self.items - IT_QUAD; self.super_damage_finished = 0; self.super_time = 0; // 1998-07-23 Better performance in handling of powerups by Maddes start if (!self.invincible_finished) self.effects = self.effects - (self.effects & EF_DIMLIGHT); // 1998-07-23 Better performance in handling of powerups by Maddes end } // 1998-07-23 Better performance in handling of powerups by Maddes start /* if (self.super_damage_finished > time) self.effects = self.effects | EF_DIMLIGHT; else self.effects = self.effects - (self.effects & EF_DIMLIGHT); */ // 1998-07-23 Better performance in handling of powerups by Maddes end } ... };
"Items.qc"void() powerup_touch = { ... // do the apropriate action if (self.classname == "item_artifact_envirosuit") { other.rad_time = 1; other.radsuit_finished = time + 30; SetSkin(other); // 1998-01-04 event skin support by Maddes set biosuit skin } if (self.classname == "item_artifact_invulnerability") { other.invincible_time = 1; other.invincible_finished = time + 30; other.effects = other.effects | EF_DIMLIGHT; // 1998-07-23 Better performance in handling of powerups by Maddes } if (self.classname == "item_artifact_invisibility") { other.invisible_time = 1; other.invisible_finished = time + 30; SetSkin(other); // 1997-12-30 skin support by Maddes // 1998-07-23 Better performance in handling of powerups by Maddes start other.frame = 0; other.modelindex = modelindex_eyes; // 1998-07-23 Better performance in handling of powerups by Maddes end } if (self.classname == "item_artifact_super_damage") { other.super_time = 1; other.super_damage_finished = time + 30; other.effects = other.effects | EF_DIMLIGHT; // 1998-07-23 Better performance in handling of powerups by Maddes } activator = other; SUB_UseTargets(); // fire all targets / killtargets };
Drowning doesn't hurt armor
When a player is drowning his armor takes part of the damage. This is unrealistic or does it breathe too?
The following code will only hurt the player himself, but armor is still hurt by slime and lava. Be careful with this as some maps may become unplayable with it (long underwater runways, etc.).
"Client.qc"void() WaterMove = { ... if (self.waterlevel != 3) { ... } else if (self.air_finished < time) { // drown! if (self.pain_finished < time) { self.dmg = self.dmg + 2; if (self.dmg > 15) self.dmg = 10; self.deathtype = "drowning"; // 1998-08-12 optional: Drowning doesn't hurt armor by Maddes/Athos T_Damage (self, world, world, self.dmg); self.pain_finished = time + 1; } } ... };
"Combat.qc"void(entity targ, entity inflictor, entity attacker, float damage) T_Damage= { ... // save damage based on the target's armor level // 1998-08-12 Drowning doesn't hurt armor by Maddes/Athos start if (targ.deathtype != "drowning") { // 1998-08-12 Drowning doesn't hurt armor by Maddes/Athos end save = ceil(targ.armortype*damage); if (save >= targ.armorvalue) { save = targ.armorvalue; targ.armortype = 0; // lost all armor targ.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)); } // 1998-08-12 Drowning doesn't hurt armor by Maddes/Athos start } else save = 0; // 1998-08-12 Drowning doesn't hurt armor by Maddes/Athos end ... };
Improved weapon switching on pickups
The following code enhances the weapon switching when picking up weapons, ammo and backpacks.
If the player gets a new weapon, which is better than his current weapon, it will be switched to it just like before.
In all other cases weapon switching may occur only if a player was using his best weapon, and another weapon becomes his best weapon.
"Items.qc"
// 1998-08-15 Improved weapon switching on pickups by Maddes start
//float(float w) RankForWeapon =
float(entity ent, float w) RankForWeapon =
// 1998-08-15 Improved weapon switching on pickups by Maddes end
{
if (w == IT_LIGHTNING && ent.waterlevel <= 1) // 1997-12-23 Thunderbolt fix by Maddes recognize waterlevel
return 1;
if (w == IT_ROCKET_LAUNCHER)
return 2;
if (w == IT_SUPER_NAILGUN)
return 3;
if (w == IT_GRENADE_LAUNCHER)
return 4;
if (w == IT_SUPER_SHOTGUN)
return 5;
if (w == IT_NAILGUN)
return 6;
return 7;
};
// 1998-08-15 Improved weapon switching on pickups by Maddes start
//void(float old, float new) Deathmatch_Weapon =
void(entity ent, float new) Deathmatch_Weapon =
// 1998-08-15 Improved weapon switching on pickups by Maddes end
{
local float or, nr;
// change self.weapon if desired
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// or = RankForWeapon (self.weapon);
// nr = RankForWeapon (new);
or = RankForWeapon (ent, ent.weapon);
nr = RankForWeapon (ent, new);
// 1998-08-15 Improved weapon switching on pickups by Maddes end
if ( nr < or )
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// self.weapon = new;
ent.weapon = new;
// 1998-08-15 Improved weapon switching on pickups by Maddes end
};
float() W_BestWeapon;
void() weapon_touch =
{
// local float hadammo; // 1998-08-15 Do not take unnecessary items but fire all targets by Maddes
// local float old; // 1998-08-15 Improved weapon switching on pickups by Maddes
local float best, new;
local entity stemp;
local float leave;
local float donttake; // 1998-08-15 Do not take unnecessary items but fire all targets by Maddes
...
bound_other_ammo ();
// change to the weapon
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// old = other.items;
// ...always if new one
if (!other.items & new)
Deathmatch_Weapon (other, new);
// 1998-08-15 Improved weapon switching on pickups by Maddes end
other.items = other.items | new;
stemp = self;
self = other;
// 1997-12-23 Thunderbolt fix by Maddes start
/* don't separate between SinglePlayer/Coop and Deathmatch
if (!deathmatch)
self.weapon = new;
else
*/
// 1997-12-23 Thunderbolt fix by Maddes end
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// Deathmatch_Weapon (old, new);
if ( other.weapon == best )
{
self.weapon = W_BestWeapon();
}
// 1998-08-15 Improved weapon switching on pickups by Maddes end
W_SetCurrentAmmo();
...
};
...
void() ammo_touch =
{
...
// change to a better weapon if appropriate
// 1998-08-15 Improved weapon switching on pickups by Maddes start
stemp = self;
self = other;
// 1998-08-15 Improved weapon switching on pickups by Maddes end
if ( other.weapon == best )
{
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// stemp = self;
// self = other;
// 1998-08-15 Improved weapon switching on pickups by Maddes end
self.weapon = W_BestWeapon();
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// W_SetCurrentAmmo ();
// self = stemp;
// 1998-08-15 Improved weapon switching on pickups by Maddes end
}
// if changed current ammo, update it
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// stemp = self;
// self = other;
// 1998-08-15 Improved weapon switching on pickups by Maddes end
W_SetCurrentAmmo();
...
};
...
void() BackpackTouch =
{
local string s;
local float best, new;
// local float old; // 1998-08-15 Improved weapon switching on pickups by Maddes
local entity stemp;
local float acount;
...
// change weapons
other.ammo_shells = other.ammo_shells + self.ammo_shells;
other.ammo_nails = other.ammo_nails + self.ammo_nails;
other.ammo_rockets = other.ammo_rockets + self.ammo_rockets;
other.ammo_cells = other.ammo_cells + self.ammo_cells;
new = self.items;
if (!new)
new = other.weapon;
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// old = other.items;
// ...always if new one
if (!other.items & new)
Deathmatch_Weapon (other, new);
// 1998-08-15 Improved weapon switching on pickups by Maddes end
other.items = other.items | new;
bound_other_ammo ();
...
// remove the backpack, change self to the player
remove(self);
self = other;
// change to the weapon
// 1997-12-23 Thunderbolt fix by Maddes start
/* don't separate between SinglePlayer/Coop and Deathmatch
if (!deathmatch)
self.weapon = new;
else
*/
// 1997-12-23 Thunderbolt fix by Maddes end
// 1998-08-15 Improved weapon switching on pickups by Maddes start
// Deathmatch_Weapon (old, new);
if ( other.weapon == best )
{
self.weapon = W_BestWeapon();
}
// 1998-08-15 Improved weapon switching on pickups by Maddes end
W_SetCurrentAmmo ();
};