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, old_team, real_team;
38 // now that the delay has expired, switch to the latest team to lay claim to this point
44 LogDom("taken", self.team, self.dmg_inflictor);
45 self.dmg_inflictor = world;
47 self.goalentity = head;
48 self.model = head.mdl;
49 self.modelindex = head.dmg;
50 self.skin = head.skin;
52 //bprint(head.message);
55 //bprint(^3head.netname);
56 //bprint(head.netname);
57 //bprint(self.message);
60 bprint("^3", head.netname, "^3", self.message, "\n");
63 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
64 if (head.noise1 != "")
65 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
67 //self.nextthink = time + cvar("g_domination_point_rate");
68 //self.think = dompointthink;
70 if(cvar("g_domination_point_rate"))
71 self.delay = time + cvar("g_domination_point_rate");
73 self.delay = time + self.wait;
76 old_delay = self.delay;
78 self.team = real_team;
82 self.delay = old_delay;
86 void AnimateDomPoint()
88 if(self.pain_finished > time)
90 self.pain_finished = time + self.t_width;
91 if(self.nextthink > self.pain_finished)
92 self.nextthink = self.pain_finished;
94 self.frame = self.frame + 1;
95 if(self.frame > self.t_length)
99 void() dompointthink =
102 local float waittime;
103 local float teamfragamt;
104 local float individualfragamt;
106 self.nextthink = time + 0.1;
108 //self.frame = self.frame + 1;
109 //if(self.frame > 119)
115 if (gameover || self.delay > time || time < restart_countdown) // game has ended, don't keep giving points
118 waittime = cvar("g_domination_point_rate");
120 waittime = self.wait;
121 self.delay = time + waittime;
123 // give credit to all players of the team (rewards large teams)
124 // NOTE: this defaults to 0
125 if (self.goalentity.netname)
127 teamfragamt = cvar("g_domination_point_teamamt");
129 FOR_EACH_PLAYER(head)
130 if (head.team == self.goalentity.team)
131 UpdateFrags(head, teamfragamt);
134 // 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
135 if (!self.enemy.flags || self.enemy.team != self.goalentity.team || self.enemy.killcount == -666) // flags is zero on removed clients
138 FOR_EACH_PLAYER(head)
139 if (head.team == self.goalentity.team)
141 if(self.enemy == other) // search returned no matching player, reset dom point
143 dom_controlpoint_setup();
150 // give credit to the individual player
153 individualfragamt = cvar("g_domination_point_amt");
154 if(!individualfragamt)
155 individualfragamt = self.frags;
156 UpdateFrags(self.enemy, individualfragamt);
160 void() dompointtouch =
163 if (other.classname != "player")
165 if (other.health < 1)
168 // only valid teams can claim it
169 head = find(world, classname, "dom_team");
170 while (head && head.team != other.team)
171 head = find(head, classname, "dom_team");
172 if (!head || head.netname == "" || head == self.goalentity)
177 self.team = self.goalentity.team; // this stores the PREVIOUS team!
179 self.cnt = other.team;
180 self.aiment = head; // team to switch to after the delay
181 self.dmg_inflictor = other;
184 // self.delay = time + cvar("g_domination_point_capturetime");
185 //self.nextthink = time + cvar("g_domination_point_capturetime");
186 //self.think = dompoint_captured;
188 // go to neutral team in the mean time
189 head = find(world, classname, "dom_team");
190 while (head && head.netname != "")
191 head = find(head, classname, "dom_team");
195 self.goalentity = head;
196 self.model = head.mdl;
197 self.modelindex = head.dmg;
198 self.skin = head.skin;
200 self.enemy = other; // individual player scoring
204 /*QUAKED dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
205 Team declaration for Domination gameplay, this allows you to decide what team
206 names and control point models are used in your map.
208 Note: If you use dom_team entities you must define at least 3 and only two
209 can have netname set! The nameless team owns all control points at start.
213 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
215 Scoreboard color of the team (for example 4 is red and 13 is blue)
217 Model to use for control points owned by this team (for example
218 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
221 Skin of the model to use (for team skins on a single model)
223 Sound to play when this team captures a point.
224 (this is a localized sound, like a small alarm or other effect)
226 Narrator speech to play when this team captures a point.
227 (this is a global sound, like "Red team has captured a control point")
232 precache_model(self.model);
233 if (self.noise != "")
234 precache_sound(self.noise);
235 if (self.noise1 != "")
236 precache_sound(self.noise1);
237 self.classname = "dom_team";
238 setmodel(self, self.model); // precision not needed
239 self.mdl = self.model;
240 self.dmg = self.modelindex;
243 // this would have to be changed if used in quakeworld
244 self.team = self.cnt + 1;
247 void() dom_controlpoint_setup =
250 // find the dom_team representing unclaimed points
251 head = find(world, classname, "dom_team");
252 while(head && head.netname != "")
253 head = find(head, classname, "dom_team");
255 objerror("no dom_team with netname \"\" found\n");
257 // copy important properties from dom_team entity
258 self.goalentity = head;
259 setmodel(self, head.mdl); // precision already set
260 self.skin = head.skin;
265 self.message = " has captured a control point";
273 self.t_width = 0.1; // frame animation rate
275 self.t_length = 119; // maximum frame
277 self.think = dompointthink;
278 self.nextthink = time;
279 self.touch = dompointtouch;
280 self.solid = SOLID_TRIGGER;
281 self.flags = FL_ITEM;
282 setsize(self, '-32 -32 -32', '32 32 32');
283 setorigin(self, self.origin + '0 0 20');
286 waypoint_spawnforitem(self);
291 // player has joined game, get him on a team
293 /*void dom_player_join_team(entity pl)
296 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
297 float balance_teams, force_balance, balance_type;
299 balance_teams = cvar("g_balance_teams");
300 balance_teams = cvar("g_balance_teams_force");
302 c1 = c2 = c3 = c4 = -1;
305 // first find out what teams are allowed
306 head = find(world, classname, "dom_team");
309 if(head.netname != "")
311 //if(head.team == pl.team)
313 if(head.team == COLOR_TEAM1)
317 if(head.team == COLOR_TEAM2)
321 if(head.team == COLOR_TEAM3)
325 if(head.team == COLOR_TEAM4)
330 head = find(head, classname, "dom_team");
333 // make sure there are at least 2 teams to join
335 totalteams = totalteams + 1;
337 totalteams = totalteams + 1;
339 totalteams = totalteams + 1;
341 totalteams = totalteams + 1;
344 error("dom_player_join_team: Too few teams available for domination\n");
346 // whichever teams that are available are set to 0 instead of -1
348 // if we don't care what team he ends up on, put him on whatever team he entered as.
349 // if he's not on a valid team, then put him on the smallest team
350 if(!balance_teams && !force_balance)
352 if( c1 >= 0 && pl.team == COLOR_TEAM1)
353 selectedteam = pl.team;
354 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
355 selectedteam = pl.team;
356 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
357 selectedteam = pl.team;
358 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
359 selectedteam = pl.team;
364 SetPlayerColors(pl, selectedteam - 1);
367 // otherwise end up on the smallest team (handled below)
370 // now count how many players are on each team already
372 head = find(world, classname, "player");
375 //if(head.netname != "")
377 if(head.team == COLOR_TEAM1)
382 if(head.team == COLOR_TEAM2)
387 if(head.team == COLOR_TEAM3)
392 if(head.team == COLOR_TEAM4)
398 head = find(head, classname, "player");
401 // c1...c4 now have counts of each team
402 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
405 smallestteam_count = 999;
407 // 2 gives priority to what team you're already on, 1 goes in order
410 if(balance_type == 1)
412 if(c1 >= 0 && c1 < smallestteam_count)
415 smallestteam_count = c1;
417 if(c2 >= 0 && c2 < smallestteam_count)
420 smallestteam_count = c2;
422 if(c3 >= 0 && c3 < smallestteam_count)
425 smallestteam_count = c3;
427 if(c4 >= 0 && c4 < smallestteam_count)
430 smallestteam_count = c4;
435 if(c1 >= 0 && (c1 < smallestteam_count ||
436 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
439 smallestteam_count = c1;
441 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
442 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
445 smallestteam_count = c2;
447 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
448 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
451 smallestteam_count = c3;
453 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
454 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
457 smallestteam_count = c4;
461 if(smallestteam == 1)
463 selectedteam = COLOR_TEAM1 - 1;
465 if(smallestteam == 2)
467 selectedteam = COLOR_TEAM2 - 1;
469 if(smallestteam == 3)
471 selectedteam = COLOR_TEAM3 - 1;
473 if(smallestteam == 4)
475 selectedteam = COLOR_TEAM4 - 1;
478 SetPlayerColors(pl, selectedteam);
481 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
482 Control point for Domination gameplay.
484 void() dom_controlpoint =
491 self.think = dom_controlpoint_setup;
492 self.nextthink = time + 0.1;
497 //if(!self.glow_size)
498 // self.glow_size = cvar("g_domination_point_glow");
499 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
502 // code from here on is just to support maps that don't have control point and team entities
503 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
505 local entity oldself;
508 self.classname = "dom_team";
509 self.netname = teamname;
510 self.cnt = teamcolor;
511 self.model = pointmodel;
512 self.skin = pointskin;
513 self.noise = capsound;
514 self.noise1 = capnarration;
515 self.message = capmessage;
517 // this code is identical to dom_team
518 setmodel(self, self.model); // precision not needed
519 self.mdl = self.model;
520 self.dmg = self.modelindex;
523 // this would have to be changed if used in quakeworld
524 self.team = self.cnt + 1;
530 void(vector org) dom_spawnpoint =
532 local entity oldself;
535 self.classname = "dom_controlpoint";
536 self.think = dom_controlpoint;
537 self.nextthink = time;
543 // spawn some default teams if the map is not set up for domination
544 void() dom_spawnteams =
548 numteams = cvar("g_domination_default_teams");
549 // LordHavoc: edit this if you want to change defaults
550 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
551 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
553 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
555 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
556 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
559 void() dom_delayedinit =
563 self.think = SUB_Remove;
564 self.nextthink = time;
565 // if no teams are found, spawn defaults
566 if (find(world, classname, "dom_team") == world)
568 // if no control points are found, spawn defaults
569 if (find(world, classname, "dom_controlpoint") == world)
571 // here follow default domination points for each map
573 if (world.model == "maps/e1m1.bsp")
575 dom_spawnpoint('0 0 0');
580 // if no supported map was found, make every deathmatch spawn a point
581 head = find(world, classname, "info_player_deathmatch");
584 dom_spawnpoint(head.origin);
585 head = find(head, classname, "info_player_deathmatch");
594 // we have to precache default models/sounds even if they might not be
595 // used because worldspawn is executed before any other entities are read,
596 // so we don't even know yet if this map is set up for domination...
597 precache_model("models/domination/dom_red.md3");
598 precache_model("models/domination/dom_blue.md3");
599 precache_model("models/domination/dom_yellow.md3");
600 precache_model("models/domination/dom_pink.md3");
601 precache_model("models/domination/dom_unclaimed.md3");
602 precache_sound("domination/claim.wav");
604 e.think = dom_delayedinit;
605 e.nextthink = time + 0.1;
607 // teamplay is always on in domination, defaults to hurt self but not teammates
608 //if(!cvar("teamplay"))
609 // cvar_set("teamplay", "3");