3 Domination as a plugin for netquake mods
4 by LordHavoc (lordhavoc@ghdigital.com)
6 How to add domination points to a mod:
7 1. Add this line to progs.src above world.qc:
9 2. Comment out all lines in ClientObituary in client.qc that begin with targ.frags or attacker.frags.
10 3. Add this above worldspawn in world.qc:
12 4. Add this line to the end of worldspawn in world.qc:
15 Note: The only teams who can use dom control points are identified by dom_team entities (if none exist these default to red and blue and use only quake models/sounds).
19 void LogDom(string mode, float team_before, entity actor)
22 if(!cvar("sv_eventlog"))
24 s = strcat(":dom:", mode);
25 s = strcat(s, ":", ftos(team_before));
26 s = strcat(s, ":", ftos(actor.playerid));
27 GameLogEcho(s, FALSE);
30 void() dom_spawnteams;
32 void dompoint_captured ()
40 LogDom("taken", self.team, self.dmg_inflictor);
41 self.dmg_inflictor = world;
43 self.goalentity = head;
44 self.model = head.mdl;
45 self.modelindex = head.dmg;
46 self.skin = head.skin;
48 //bprint(head.message);
51 //bprint(^3head.netname);
52 //bprint(head.netname);
53 //bprint(self.message);
56 bprint(strcat("^3", head.netname, "^3", self.message, "\n"));
59 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
60 if (head.noise1 != "")
61 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
63 //self.nextthink = time + cvar("g_domination_point_rate");
64 //self.think = dompointthink;
65 self.delay = time + cvar("g_domination_point_rate");
68 void AnimateDomPoint()
70 if(self.pain_finished > time)
72 self.pain_finished = time + self.t_width;
73 if(self.nextthink > self.pain_finished)
74 self.nextthink = self.pain_finished;
76 self.frame = self.frame + 1;
77 if(self.frame > self.t_length)
81 void() dompointthink =
87 self.nextthink = time + 0.1;
89 //self.frame = self.frame + 1;
90 //if(self.frame > 119)
96 if (gameover) // game has ended, don't keep giving points
109 waittime = cvar("g_domination_point_rate");
111 waittime = self.wait;
112 self.delay = time + waittime;
114 if (!self.goalentity.netname)
118 fragamt = cvar("g_domination_point_amt");
120 fragamt = self.frags;
122 head = find(head, classname, "player");
125 if (head.team == self.goalentity.team)
126 head.frags = head.frags + fragamt;
127 head = find(head, classname, "player");
129 self.goalentity.frags = self.goalentity.frags + fragamt;
132 void() dompointtouch =
135 if (other.classname != "player")
137 if (other.health < 1)
139 if(self.cnt > 0 && self.cnt == other.team)
142 // only valid teams can claim it
143 head = find(world, classname, "dom_team");
144 while (head && head.team != other.team)
145 head = find(head, classname, "dom_team");
146 if (!head || head.netname == "" || head == self.goalentity)
151 self.team = self.goalentity.team; // this stores the PREVIOUS team!
153 self.cnt = other.team;
155 self.dmg_inflictor = other;
158 self.delay = time + cvar("g_domination_point_capturetime");
159 //self.nextthink = time + cvar("g_domination_point_capturetime");
160 //self.think = dompoint_captured;
161 // go to neutral team in the mean time
163 head = find(world, classname, "dom_team");
164 while (head && head.netname != "")
165 head = find(head, classname, "dom_team");
169 self.goalentity = head;
170 self.model = head.mdl;
171 self.modelindex = head.dmg;
172 self.skin = head.skin;
175 /*QUAKED dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
176 Team declaration for Domination gameplay, this allows you to decide what team
177 names and control point models are used in your map.
179 Note: If you use dom_team entities you must define at least 3 and only two
180 can have netname set! The nameless team owns all control points at start.
184 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
186 Scoreboard color of the team (for example 4 is red and 13 is blue)
188 Model to use for control points owned by this team (for example
189 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
192 Skin of the model to use (for team skins on a single model)
194 Sound to play when this team captures a point.
195 (this is a localized sound, like a small alarm or other effect)
197 Narrator speech to play when this team captures a point.
198 (this is a global sound, like "Red team has captured a control point")
203 precache_model(self.model);
204 if (self.noise != "")
205 precache_sound(self.noise);
206 if (self.noise1 != "")
207 precache_sound(self.noise1);
208 self.classname = "dom_team";
209 setmodel(self, self.model);
210 self.mdl = self.model;
211 self.dmg = self.modelindex;
214 // this would have to be changed if used in quakeworld
215 self.team = self.cnt + 1;
218 void() dom_controlpoint_setup =
221 // find the dom_team representing unclaimed points
222 head = find(world, classname, "dom_team");
223 while(head && head.netname != "")
224 head = find(head, classname, "dom_team");
226 objerror("no dom_team with netname \"\" found\n");
228 // copy important properties from dom_team entity
229 self.goalentity = head;
230 setmodel(self, head.mdl);
231 self.skin = head.skin;
236 self.message = " has captured a control point";
244 self.t_width = 0.1; // frame animation rate
246 self.t_length = 119; // maximum frame
248 self.think = dompointthink;
249 self.nextthink = time;
250 self.touch = dompointtouch;
251 self.solid = SOLID_TRIGGER;
252 self.flags = FL_ITEM;
253 setsize(self, '-32 -32 -24', '32 32 32');
254 setorigin(self, self.origin + '0 0 20');
260 // player has joined game, get him on a team
262 /*void dom_player_join_team(entity pl)
265 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
266 float balance_teams, force_balance, balance_type;
268 balance_teams = cvar("g_balance_teams");
269 balance_teams = cvar("g_balance_teams_force");
271 c1 = c2 = c3 = c4 = -1;
274 // first find out what teams are allowed
275 head = find(world, classname, "dom_team");
278 if(head.netname != "")
280 //if(head.team == pl.team)
282 if(head.team == COLOR_TEAM1)
286 if(head.team == COLOR_TEAM2)
290 if(head.team == COLOR_TEAM3)
294 if(head.team == COLOR_TEAM4)
299 head = find(head, classname, "dom_team");
302 // make sure there are at least 2 teams to join
304 totalteams = totalteams + 1;
306 totalteams = totalteams + 1;
308 totalteams = totalteams + 1;
310 totalteams = totalteams + 1;
313 error("dom_player_join_team: Too few teams available for domination\n");
315 // whichever teams that are available are set to 0 instead of -1
317 // if we don't care what team he ends up on, put him on whatever team he entered as.
318 // if he's not on a valid team, then put him on the smallest team
319 if(!balance_teams && !force_balance)
321 if( c1 >= 0 && pl.team == COLOR_TEAM1)
322 selectedteam = pl.team;
323 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
324 selectedteam = pl.team;
325 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
326 selectedteam = pl.team;
327 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
328 selectedteam = pl.team;
333 SetPlayerColors(pl, selectedteam - 1);
336 // otherwise end up on the smallest team (handled below)
339 // now count how many players are on each team already
341 head = find(world, classname, "player");
344 //if(head.netname != "")
346 if(head.team == COLOR_TEAM1)
351 if(head.team == COLOR_TEAM2)
356 if(head.team == COLOR_TEAM3)
361 if(head.team == COLOR_TEAM4)
367 head = find(head, classname, "player");
370 // c1...c4 now have counts of each team
371 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
374 smallestteam_count = 999;
376 // 2 gives priority to what team you're already on, 1 goes in order
379 if(balance_type == 1)
381 if(c1 >= 0 && c1 < smallestteam_count)
384 smallestteam_count = c1;
386 if(c2 >= 0 && c2 < smallestteam_count)
389 smallestteam_count = c2;
391 if(c3 >= 0 && c3 < smallestteam_count)
394 smallestteam_count = c3;
396 if(c4 >= 0 && c4 < smallestteam_count)
399 smallestteam_count = c4;
404 if(c1 >= 0 && (c1 < smallestteam_count ||
405 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
408 smallestteam_count = c1;
410 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
411 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
414 smallestteam_count = c2;
416 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
417 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
420 smallestteam_count = c3;
422 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
423 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
426 smallestteam_count = c4;
430 if(smallestteam == 1)
432 selectedteam = COLOR_TEAM1 - 1;
434 if(smallestteam == 2)
436 selectedteam = COLOR_TEAM2 - 1;
438 if(smallestteam == 3)
440 selectedteam = COLOR_TEAM3 - 1;
442 if(smallestteam == 4)
444 selectedteam = COLOR_TEAM4 - 1;
447 SetPlayerColors(pl, selectedteam);
450 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
451 Control point for Domination gameplay.
453 void() dom_controlpoint =
455 if(!cvar("g_domination"))
460 self.think = dom_controlpoint_setup;
461 self.nextthink = time + 0.1;
466 //if(!self.glow_size)
467 // self.glow_size = cvar("g_domination_point_glow");
468 self.effects = self.effects | EF_FULLBRIGHT;
471 // code from here on is just to support maps that don't have control point and team entities
472 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
474 local entity oldself;
477 self.classname = "dom_team";
478 self.netname = teamname;
479 self.cnt = teamcolor;
480 self.model = pointmodel;
481 self.skin = pointskin;
482 self.noise = capsound;
483 self.noise1 = capnarration;
484 self.message = capmessage;
486 // this code is identical to dom_team
487 setmodel(self, self.model);
488 self.mdl = self.model;
489 self.dmg = self.modelindex;
492 // this would have to be changed if used in quakeworld
493 self.team = self.cnt + 1;
499 void(vector org) dom_spawnpoint =
501 local entity oldself;
504 self.classname = "dom_controlpoint";
505 self.think = dom_controlpoint;
506 self.nextthink = time;
512 // spawn some default teams if the map is not set up for domination
513 void() dom_spawnteams =
517 numteams = cvar("g_domination_default_teams");
518 // LordHavoc: edit this if you want to change defaults
519 dom_spawnteam("Red", 4, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
520 dom_spawnteam("Blue", 13, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
522 dom_spawnteam("Pink", 9, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
524 dom_spawnteam("Yellow", 12, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
525 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
528 void() dom_delayedinit =
532 self.think = SUB_Remove;
533 self.nextthink = time;
534 // if no teams are found, spawn defaults
535 if (find(world, classname, "dom_team") == world)
537 // if no control points are found, spawn defaults
538 if (find(world, classname, "dom_controlpoint") == world)
540 // here follow default domination points for each map
542 if (world.model == "maps/e1m1.bsp")
544 dom_spawnpoint('0 0 0');
549 // if no supported map was found, make every deathmatch spawn a point
550 head = find(world, classname, "info_player_deathmatch");
553 dom_spawnpoint(head.origin);
554 head = find(head, classname, "info_player_deathmatch");
563 // we have to precache default models/sounds even if they might not be
564 // used because worldspawn is executed before any other entities are read,
565 // so we don't even know yet if this map is set up for domination...
566 precache_model("models/domination/dom_red.md3");
567 precache_model("models/domination/dom_blue.md3");
568 precache_model("models/domination/dom_pink.md3");
569 precache_model("models/domination/dom_yellow.md3");
570 precache_model("models/domination/dom_unclaimed.md3");
571 precache_sound("domination/claim.wav");
573 e.think = dom_delayedinit;
574 e.nextthink = time + 0.1;
576 // teamplay is always on in domination, defaults to hurt self but not teammates
577 //if(!cvar("teamplay"))
578 // cvar_set("teamplay", "3");