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 ()
36 local float old_delay;
38 // now that the delay has expired, switch to the latest team to lay claim to this point
43 LogDom("taken", self.team, self.dmg_inflictor);
44 self.dmg_inflictor = world;
46 self.goalentity = head;
47 self.model = head.mdl;
48 self.modelindex = head.dmg;
49 self.skin = head.skin;
51 //bprint(head.message);
54 //bprint(^3head.netname);
55 //bprint(head.netname);
56 //bprint(self.message);
59 bprint("^3", head.netname, "^3", self.message, "\n");
62 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
63 if (head.noise1 != "")
64 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
66 //self.nextthink = time + cvar("g_domination_point_rate");
67 //self.think = dompointthink;
69 if(cvar("g_domination_point_rate"))
70 self.delay = time + cvar("g_domination_point_rate");
72 self.delay = time + self.wait;
75 old_delay = self.delay;
79 self.delay = old_delay;
82 void AnimateDomPoint()
84 if(self.pain_finished > time)
86 self.pain_finished = time + self.t_width;
87 if(self.nextthink > self.pain_finished)
88 self.nextthink = self.pain_finished;
90 self.frame = self.frame + 1;
91 if(self.frame > self.t_length)
95 void() dompointthink =
99 local float teamfragamt;
100 local float individualfragamt;
102 self.nextthink = time + 0.1;
104 //self.frame = self.frame + 1;
105 //if(self.frame > 119)
111 if (gameover || self.delay > time || time < restart_countdown) // game has ended, don't keep giving points
114 waittime = cvar("g_domination_point_rate");
116 waittime = self.wait;
117 self.delay = time + waittime;
119 // give credit to all players of the team (rewards large teams)
120 // NOTE: this defaults to 0
121 if (self.goalentity.netname)
123 teamfragamt = cvar("g_domination_point_teamamt");
125 FOR_EACH_PLAYER(head)
126 if (head.team == self.goalentity.team)
127 UpdateFrags(head, teamfragamt);
130 // 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
131 if (!self.enemy.flags || self.enemy.team != self.goalentity.team || self.enemy.killcount == -666) // flags is zero on removed clients
134 FOR_EACH_PLAYER(head)
135 if (head.team == self.goalentity.team)
137 if(self.enemy == other) // search returned no matching player, reset dom point
139 dom_controlpoint_setup();
146 // give credit to the individual player
149 individualfragamt = cvar("g_domination_point_amt");
150 if(!individualfragamt)
151 individualfragamt = self.frags;
152 UpdateFrags(self.enemy, individualfragamt);
156 void() dompointtouch =
159 if (other.classname != "player")
161 if (other.health < 1)
164 // only valid teams can claim it
165 head = find(world, classname, "dom_team");
166 while (head && head.team != other.team)
167 head = find(head, classname, "dom_team");
168 if (!head || head.netname == "" || head == self.goalentity)
173 self.team = self.goalentity.team; // this stores the PREVIOUS team!
175 self.cnt = other.team;
176 self.aiment = head; // team to switch to after the delay
177 self.dmg_inflictor = other;
180 // self.delay = time + cvar("g_domination_point_capturetime");
181 //self.nextthink = time + cvar("g_domination_point_capturetime");
182 //self.think = dompoint_captured;
184 // go to neutral team in the mean time
185 head = find(world, classname, "dom_team");
186 while (head && head.netname != "")
187 head = find(head, classname, "dom_team");
191 self.goalentity = head;
192 self.model = head.mdl;
193 self.modelindex = head.dmg;
194 self.skin = head.skin;
196 self.enemy = other; // individual player scoring
200 /*QUAKED dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
201 Team declaration for Domination gameplay, this allows you to decide what team
202 names and control point models are used in your map.
204 Note: If you use dom_team entities you must define at least 3 and only two
205 can have netname set! The nameless team owns all control points at start.
209 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
211 Scoreboard color of the team (for example 4 is red and 13 is blue)
213 Model to use for control points owned by this team (for example
214 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
217 Skin of the model to use (for team skins on a single model)
219 Sound to play when this team captures a point.
220 (this is a localized sound, like a small alarm or other effect)
222 Narrator speech to play when this team captures a point.
223 (this is a global sound, like "Red team has captured a control point")
228 precache_model(self.model);
229 if (self.noise != "")
230 precache_sound(self.noise);
231 if (self.noise1 != "")
232 precache_sound(self.noise1);
233 self.classname = "dom_team";
234 setmodel(self, self.model); // precision not needed
235 self.mdl = self.model;
236 self.dmg = self.modelindex;
239 // this would have to be changed if used in quakeworld
240 self.team = self.cnt + 1;
243 void() dom_controlpoint_setup =
246 // find the dom_team representing unclaimed points
247 head = find(world, classname, "dom_team");
248 while(head && head.netname != "")
249 head = find(head, classname, "dom_team");
251 objerror("no dom_team with netname \"\" found\n");
253 // copy important properties from dom_team entity
254 self.goalentity = head;
255 setmodel(self, head.mdl); // precision already set
256 self.skin = head.skin;
261 self.message = " has captured a control point";
269 self.t_width = 0.1; // frame animation rate
271 self.t_length = 119; // maximum frame
273 self.think = dompointthink;
274 self.nextthink = time;
275 self.touch = dompointtouch;
276 self.solid = SOLID_TRIGGER;
277 self.flags = FL_ITEM;
278 setsize(self, '-32 -32 -32', '32 32 32');
279 setorigin(self, self.origin + '0 0 20');
282 waypoint_spawnforitem(self);
287 // player has joined game, get him on a team
289 /*void dom_player_join_team(entity pl)
292 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
293 float balance_teams, force_balance, balance_type;
295 balance_teams = cvar("g_balance_teams");
296 balance_teams = cvar("g_balance_teams_force");
298 c1 = c2 = c3 = c4 = -1;
301 // first find out what teams are allowed
302 head = find(world, classname, "dom_team");
305 if(head.netname != "")
307 //if(head.team == pl.team)
309 if(head.team == COLOR_TEAM1)
313 if(head.team == COLOR_TEAM2)
317 if(head.team == COLOR_TEAM3)
321 if(head.team == COLOR_TEAM4)
326 head = find(head, classname, "dom_team");
329 // make sure there are at least 2 teams to join
331 totalteams = totalteams + 1;
333 totalteams = totalteams + 1;
335 totalteams = totalteams + 1;
337 totalteams = totalteams + 1;
340 error("dom_player_join_team: Too few teams available for domination\n");
342 // whichever teams that are available are set to 0 instead of -1
344 // if we don't care what team he ends up on, put him on whatever team he entered as.
345 // if he's not on a valid team, then put him on the smallest team
346 if(!balance_teams && !force_balance)
348 if( c1 >= 0 && pl.team == COLOR_TEAM1)
349 selectedteam = pl.team;
350 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
351 selectedteam = pl.team;
352 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
353 selectedteam = pl.team;
354 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
355 selectedteam = pl.team;
360 SetPlayerColors(pl, selectedteam - 1);
363 // otherwise end up on the smallest team (handled below)
366 // now count how many players are on each team already
368 head = find(world, classname, "player");
371 //if(head.netname != "")
373 if(head.team == COLOR_TEAM1)
378 if(head.team == COLOR_TEAM2)
383 if(head.team == COLOR_TEAM3)
388 if(head.team == COLOR_TEAM4)
394 head = find(head, classname, "player");
397 // c1...c4 now have counts of each team
398 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
401 smallestteam_count = 999;
403 // 2 gives priority to what team you're already on, 1 goes in order
406 if(balance_type == 1)
408 if(c1 >= 0 && c1 < smallestteam_count)
411 smallestteam_count = c1;
413 if(c2 >= 0 && c2 < smallestteam_count)
416 smallestteam_count = c2;
418 if(c3 >= 0 && c3 < smallestteam_count)
421 smallestteam_count = c3;
423 if(c4 >= 0 && c4 < smallestteam_count)
426 smallestteam_count = c4;
431 if(c1 >= 0 && (c1 < smallestteam_count ||
432 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
435 smallestteam_count = c1;
437 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
438 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
441 smallestteam_count = c2;
443 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
444 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
447 smallestteam_count = c3;
449 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
450 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
453 smallestteam_count = c4;
457 if(smallestteam == 1)
459 selectedteam = COLOR_TEAM1 - 1;
461 if(smallestteam == 2)
463 selectedteam = COLOR_TEAM2 - 1;
465 if(smallestteam == 3)
467 selectedteam = COLOR_TEAM3 - 1;
469 if(smallestteam == 4)
471 selectedteam = COLOR_TEAM4 - 1;
474 SetPlayerColors(pl, selectedteam);
477 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
478 Control point for Domination gameplay.
480 void() dom_controlpoint =
487 self.think = dom_controlpoint_setup;
488 self.nextthink = time + 0.1;
493 //if(!self.glow_size)
494 // self.glow_size = cvar("g_domination_point_glow");
495 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
498 // code from here on is just to support maps that don't have control point and team entities
499 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
501 local entity oldself;
504 self.classname = "dom_team";
505 self.netname = teamname;
506 self.cnt = teamcolor;
507 self.model = pointmodel;
508 self.skin = pointskin;
509 self.noise = capsound;
510 self.noise1 = capnarration;
511 self.message = capmessage;
513 // this code is identical to dom_team
514 setmodel(self, self.model); // precision not needed
515 self.mdl = self.model;
516 self.dmg = self.modelindex;
519 // this would have to be changed if used in quakeworld
520 self.team = self.cnt + 1;
526 void(vector org) dom_spawnpoint =
528 local entity oldself;
531 self.classname = "dom_controlpoint";
532 self.think = dom_controlpoint;
533 self.nextthink = time;
539 // spawn some default teams if the map is not set up for domination
540 void() dom_spawnteams =
544 numteams = cvar("g_domination_default_teams");
545 // LordHavoc: edit this if you want to change defaults
546 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
547 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
549 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
551 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
552 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
555 void() dom_delayedinit =
559 self.think = SUB_Remove;
560 self.nextthink = time;
561 // if no teams are found, spawn defaults
562 if (find(world, classname, "dom_team") == world)
564 // if no control points are found, spawn defaults
565 if (find(world, classname, "dom_controlpoint") == world)
567 // here follow default domination points for each map
569 if (world.model == "maps/e1m1.bsp")
571 dom_spawnpoint('0 0 0');
576 // if no supported map was found, make every deathmatch spawn a point
577 head = find(world, classname, "info_player_deathmatch");
580 dom_spawnpoint(head.origin);
581 head = find(head, classname, "info_player_deathmatch");
590 // we have to precache default models/sounds even if they might not be
591 // used because worldspawn is executed before any other entities are read,
592 // so we don't even know yet if this map is set up for domination...
593 precache_model("models/domination/dom_red.md3");
594 precache_model("models/domination/dom_blue.md3");
595 precache_model("models/domination/dom_yellow.md3");
596 precache_model("models/domination/dom_pink.md3");
597 precache_model("models/domination/dom_unclaimed.md3");
598 precache_sound("domination/claim.wav");
600 e.think = dom_delayedinit;
601 e.nextthink = time + 0.1;
603 // teamplay is always on in domination, defaults to hurt self but not teammates
604 //if(!cvar("teamplay"))
605 // cvar_set("teamplay", "3");