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).
18 void() dom_controlpoint_setup;
20 void LogDom(string mode, float team_before, entity actor)
23 if(!cvar("sv_eventlog"))
25 s = strcat(":dom:", mode);
26 s = strcat(s, ":", ftos(team_before));
27 s = strcat(s, ":", ftos(actor.playerid));
28 GameLogEcho(s, FALSE);
31 void() dom_spawnteams;
33 void dompoint_captured ()
37 // now that the delay has expired, switch to the latest team to lay claim to this point
42 LogDom("taken", self.team, self.dmg_inflictor);
43 self.dmg_inflictor = world;
45 self.goalentity = head;
46 self.model = head.mdl;
47 self.modelindex = head.dmg;
48 self.skin = head.skin;
50 //bprint(head.message);
53 //bprint(^3head.netname);
54 //bprint(head.netname);
55 //bprint(self.message);
58 bprint("^3", head.netname, "^3", self.message, "\n");
61 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
62 if (head.noise1 != "")
63 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
65 //self.nextthink = time + cvar("g_domination_point_rate");
66 //self.think = dompointthink;
67 if(cvar("g_domination_point_rate"))
68 self.delay = time + cvar("g_domination_point_rate");
70 self.delay = time + self.wait;
73 void AnimateDomPoint()
75 if(self.pain_finished > time)
77 self.pain_finished = time + self.t_width;
78 if(self.nextthink > self.pain_finished)
79 self.nextthink = self.pain_finished;
81 self.frame = self.frame + 1;
82 if(self.frame > self.t_length)
86 void() dompointthink =
90 local float teamfragamt;
91 local float individualfragamt;
93 self.nextthink = time + 0.1;
95 //self.frame = self.frame + 1;
96 //if(self.frame > 119)
102 if (gameover) // game has ended, don't keep giving points
105 if(self.delay > time)
117 waittime = cvar("g_domination_point_rate");
119 waittime = self.wait;
120 self.delay = time + waittime;
122 // give credit to all players of the team (rewards large teams)
123 // NOTE: this defaults to 0
124 if (self.goalentity.netname)
126 teamfragamt = cvar("g_domination_point_teamamt");
128 FOR_EACH_PLAYER(head)
129 if (head.team == self.goalentity.team)
130 UpdateFrags(head, teamfragamt);
133 // if the player left the game, changed teams or became spectator, we have to find another player on the same team to give credit to
134 if (!self.enemy.flags || self.enemy.team != self.goalentity.team || self.enemy.killcount == -666) // flags is zero on removed clients
137 FOR_EACH_PLAYER(head)
138 if (head.team == self.goalentity.team)
140 if(self.enemy == other) // search returned no matching player, reset dom point
142 dom_controlpoint_setup();
149 // give credit to the individual player
152 individualfragamt = cvar("g_domination_point_amt");
153 if(!individualfragamt)
154 individualfragamt = self.frags;
155 UpdateFrags(self.enemy, individualfragamt);
159 void() dompointtouch =
162 if (other.classname != "player")
164 if (other.health < 1)
167 // only valid teams can claim it
168 head = find(world, classname, "dom_team");
169 while (head && head.team != other.team)
170 head = find(head, classname, "dom_team");
171 if (!head || head.netname == "" || head == self.goalentity)
176 self.team = self.goalentity.team; // this stores the PREVIOUS team!
178 self.cnt = other.team;
179 self.aiment = head; // team to switch to after the delay
180 self.dmg_inflictor = other;
183 // self.delay = time + cvar("g_domination_point_capturetime");
184 //self.nextthink = time + cvar("g_domination_point_capturetime");
185 //self.think = dompoint_captured;
187 // go to neutral team in the mean time
188 head = find(world, classname, "dom_team");
189 while (head && head.netname != "")
190 head = find(head, classname, "dom_team");
194 self.goalentity = head;
195 self.model = head.mdl;
196 self.modelindex = head.dmg;
197 self.skin = head.skin;
199 self.enemy = other; // individual player scoring
203 /*QUAKED dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
204 Team declaration for Domination gameplay, this allows you to decide what team
205 names and control point models are used in your map.
207 Note: If you use dom_team entities you must define at least 3 and only two
208 can have netname set! The nameless team owns all control points at start.
212 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
214 Scoreboard color of the team (for example 4 is red and 13 is blue)
216 Model to use for control points owned by this team (for example
217 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
220 Skin of the model to use (for team skins on a single model)
222 Sound to play when this team captures a point.
223 (this is a localized sound, like a small alarm or other effect)
225 Narrator speech to play when this team captures a point.
226 (this is a global sound, like "Red team has captured a control point")
231 precache_model(self.model);
232 if (self.noise != "")
233 precache_sound(self.noise);
234 if (self.noise1 != "")
235 precache_sound(self.noise1);
236 self.classname = "dom_team";
237 setmodel(self, self.model); // precision not needed
238 self.mdl = self.model;
239 self.dmg = self.modelindex;
242 // this would have to be changed if used in quakeworld
243 self.team = self.cnt + 1;
246 void() dom_controlpoint_setup =
249 // find the dom_team representing unclaimed points
250 head = find(world, classname, "dom_team");
251 while(head && head.netname != "")
252 head = find(head, classname, "dom_team");
254 objerror("no dom_team with netname \"\" found\n");
256 // copy important properties from dom_team entity
257 self.goalentity = head;
258 setmodel(self, head.mdl); // precision already set
259 self.skin = head.skin;
264 self.message = " has captured a control point";
272 self.t_width = 0.1; // frame animation rate
274 self.t_length = 119; // maximum frame
276 self.think = dompointthink;
277 self.nextthink = time;
278 self.touch = dompointtouch;
279 self.solid = SOLID_TRIGGER;
280 self.flags = FL_ITEM;
281 setsize(self, '-32 -32 -32', '32 32 32');
282 setorigin(self, self.origin + '0 0 20');
288 // player has joined game, get him on a team
290 /*void dom_player_join_team(entity pl)
293 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
294 float balance_teams, force_balance, balance_type;
296 balance_teams = cvar("g_balance_teams");
297 balance_teams = cvar("g_balance_teams_force");
299 c1 = c2 = c3 = c4 = -1;
302 // first find out what teams are allowed
303 head = find(world, classname, "dom_team");
306 if(head.netname != "")
308 //if(head.team == pl.team)
310 if(head.team == COLOR_TEAM1)
314 if(head.team == COLOR_TEAM2)
318 if(head.team == COLOR_TEAM3)
322 if(head.team == COLOR_TEAM4)
327 head = find(head, classname, "dom_team");
330 // make sure there are at least 2 teams to join
332 totalteams = totalteams + 1;
334 totalteams = totalteams + 1;
336 totalteams = totalteams + 1;
338 totalteams = totalteams + 1;
341 error("dom_player_join_team: Too few teams available for domination\n");
343 // whichever teams that are available are set to 0 instead of -1
345 // if we don't care what team he ends up on, put him on whatever team he entered as.
346 // if he's not on a valid team, then put him on the smallest team
347 if(!balance_teams && !force_balance)
349 if( c1 >= 0 && pl.team == COLOR_TEAM1)
350 selectedteam = pl.team;
351 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
352 selectedteam = pl.team;
353 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
354 selectedteam = pl.team;
355 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
356 selectedteam = pl.team;
361 SetPlayerColors(pl, selectedteam - 1);
364 // otherwise end up on the smallest team (handled below)
367 // now count how many players are on each team already
369 head = find(world, classname, "player");
372 //if(head.netname != "")
374 if(head.team == COLOR_TEAM1)
379 if(head.team == COLOR_TEAM2)
384 if(head.team == COLOR_TEAM3)
389 if(head.team == COLOR_TEAM4)
395 head = find(head, classname, "player");
398 // c1...c4 now have counts of each team
399 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
402 smallestteam_count = 999;
404 // 2 gives priority to what team you're already on, 1 goes in order
407 if(balance_type == 1)
409 if(c1 >= 0 && c1 < smallestteam_count)
412 smallestteam_count = c1;
414 if(c2 >= 0 && c2 < smallestteam_count)
417 smallestteam_count = c2;
419 if(c3 >= 0 && c3 < smallestteam_count)
422 smallestteam_count = c3;
424 if(c4 >= 0 && c4 < smallestteam_count)
427 smallestteam_count = c4;
432 if(c1 >= 0 && (c1 < smallestteam_count ||
433 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
436 smallestteam_count = c1;
438 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
439 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
442 smallestteam_count = c2;
444 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
445 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
448 smallestteam_count = c3;
450 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
451 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
454 smallestteam_count = c4;
458 if(smallestteam == 1)
460 selectedteam = COLOR_TEAM1 - 1;
462 if(smallestteam == 2)
464 selectedteam = COLOR_TEAM2 - 1;
466 if(smallestteam == 3)
468 selectedteam = COLOR_TEAM3 - 1;
470 if(smallestteam == 4)
472 selectedteam = COLOR_TEAM4 - 1;
475 SetPlayerColors(pl, selectedteam);
478 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
479 Control point for Domination gameplay.
481 void() dom_controlpoint =
483 if(!cvar("g_domination"))
488 self.think = dom_controlpoint_setup;
489 self.nextthink = time + 0.1;
494 //if(!self.glow_size)
495 // self.glow_size = cvar("g_domination_point_glow");
496 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
499 // code from here on is just to support maps that don't have control point and team entities
500 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
502 local entity oldself;
505 self.classname = "dom_team";
506 self.netname = teamname;
507 self.cnt = teamcolor;
508 self.model = pointmodel;
509 self.skin = pointskin;
510 self.noise = capsound;
511 self.noise1 = capnarration;
512 self.message = capmessage;
514 // this code is identical to dom_team
515 setmodel(self, self.model); // precision not needed
516 self.mdl = self.model;
517 self.dmg = self.modelindex;
520 // this would have to be changed if used in quakeworld
521 self.team = self.cnt + 1;
527 void(vector org) dom_spawnpoint =
529 local entity oldself;
532 self.classname = "dom_controlpoint";
533 self.think = dom_controlpoint;
534 self.nextthink = time;
540 // spawn some default teams if the map is not set up for domination
541 void() dom_spawnteams =
545 numteams = cvar("g_domination_default_teams");
546 // LordHavoc: edit this if you want to change defaults
547 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
548 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
550 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
552 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
553 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
556 void() dom_delayedinit =
560 self.think = SUB_Remove;
561 self.nextthink = time;
562 // if no teams are found, spawn defaults
563 if (find(world, classname, "dom_team") == world)
565 // if no control points are found, spawn defaults
566 if (find(world, classname, "dom_controlpoint") == world)
568 // here follow default domination points for each map
570 if (world.model == "maps/e1m1.bsp")
572 dom_spawnpoint('0 0 0');
577 // if no supported map was found, make every deathmatch spawn a point
578 head = find(world, classname, "info_player_deathmatch");
581 dom_spawnpoint(head.origin);
582 head = find(head, classname, "info_player_deathmatch");
591 // we have to precache default models/sounds even if they might not be
592 // used because worldspawn is executed before any other entities are read,
593 // so we don't even know yet if this map is set up for domination...
594 precache_model("models/domination/dom_red.md3");
595 precache_model("models/domination/dom_blue.md3");
596 precache_model("models/domination/dom_yellow.md3");
597 precache_model("models/domination/dom_pink.md3");
598 precache_model("models/domination/dom_unclaimed.md3");
599 precache_sound("domination/claim.wav");
601 e.think = dom_delayedinit;
602 e.nextthink = time + 0.1;
604 // teamplay is always on in domination, defaults to hurt self but not teammates
605 //if(!cvar("teamplay"))
606 // cvar_set("teamplay", "3");