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 || self.delay > time || time < restart_countdown) // game has ended, don't keep giving points
105 waittime = cvar("g_domination_point_rate");
107 waittime = self.wait;
108 self.delay = time + waittime;
110 // give credit to all players of the team (rewards large teams)
111 // NOTE: this defaults to 0
112 if (self.goalentity.netname)
114 teamfragamt = cvar("g_domination_point_teamamt");
116 FOR_EACH_PLAYER(head)
117 if (head.team == self.goalentity.team)
118 UpdateFrags(head, teamfragamt);
121 // 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
122 if (!self.enemy.flags || self.enemy.team != self.goalentity.team || self.enemy.killcount == -666) // flags is zero on removed clients
125 FOR_EACH_PLAYER(head)
126 if (head.team == self.goalentity.team)
128 if(self.enemy == other) // search returned no matching player, reset dom point
130 dom_controlpoint_setup();
137 // give credit to the individual player
140 individualfragamt = cvar("g_domination_point_amt");
141 if(!individualfragamt)
142 individualfragamt = self.frags;
143 UpdateFrags(self.enemy, individualfragamt);
147 void() dompointtouch =
150 if (other.classname != "player")
152 if (other.health < 1)
155 // only valid teams can claim it
156 head = find(world, classname, "dom_team");
157 while (head && head.team != other.team)
158 head = find(head, classname, "dom_team");
159 if (!head || head.netname == "" || head == self.goalentity)
164 self.team = self.goalentity.team; // this stores the PREVIOUS team!
166 self.cnt = other.team;
167 self.aiment = head; // team to switch to after the delay
168 self.dmg_inflictor = other;
171 // self.delay = time + cvar("g_domination_point_capturetime");
172 //self.nextthink = time + cvar("g_domination_point_capturetime");
173 //self.think = dompoint_captured;
175 // go to neutral team in the mean time
176 head = find(world, classname, "dom_team");
177 while (head && head.netname != "")
178 head = find(head, classname, "dom_team");
182 self.goalentity = head;
183 self.model = head.mdl;
184 self.modelindex = head.dmg;
185 self.skin = head.skin;
187 self.enemy = other; // individual player scoring
191 /*QUAKED dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
192 Team declaration for Domination gameplay, this allows you to decide what team
193 names and control point models are used in your map.
195 Note: If you use dom_team entities you must define at least 3 and only two
196 can have netname set! The nameless team owns all control points at start.
200 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
202 Scoreboard color of the team (for example 4 is red and 13 is blue)
204 Model to use for control points owned by this team (for example
205 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
208 Skin of the model to use (for team skins on a single model)
210 Sound to play when this team captures a point.
211 (this is a localized sound, like a small alarm or other effect)
213 Narrator speech to play when this team captures a point.
214 (this is a global sound, like "Red team has captured a control point")
219 precache_model(self.model);
220 if (self.noise != "")
221 precache_sound(self.noise);
222 if (self.noise1 != "")
223 precache_sound(self.noise1);
224 self.classname = "dom_team";
225 setmodel(self, self.model); // precision not needed
226 self.mdl = self.model;
227 self.dmg = self.modelindex;
230 // this would have to be changed if used in quakeworld
231 self.team = self.cnt + 1;
234 void() dom_controlpoint_setup =
237 // find the dom_team representing unclaimed points
238 head = find(world, classname, "dom_team");
239 while(head && head.netname != "")
240 head = find(head, classname, "dom_team");
242 objerror("no dom_team with netname \"\" found\n");
244 // copy important properties from dom_team entity
245 self.goalentity = head;
246 setmodel(self, head.mdl); // precision already set
247 self.skin = head.skin;
252 self.message = " has captured a control point";
260 self.t_width = 0.1; // frame animation rate
262 self.t_length = 119; // maximum frame
264 self.think = dompointthink;
265 self.nextthink = time;
266 self.touch = dompointtouch;
267 self.solid = SOLID_TRIGGER;
268 self.flags = FL_ITEM;
269 setsize(self, '-32 -32 -32', '32 32 32');
270 setorigin(self, self.origin + '0 0 20');
276 // player has joined game, get him on a team
278 /*void dom_player_join_team(entity pl)
281 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
282 float balance_teams, force_balance, balance_type;
284 balance_teams = cvar("g_balance_teams");
285 balance_teams = cvar("g_balance_teams_force");
287 c1 = c2 = c3 = c4 = -1;
290 // first find out what teams are allowed
291 head = find(world, classname, "dom_team");
294 if(head.netname != "")
296 //if(head.team == pl.team)
298 if(head.team == COLOR_TEAM1)
302 if(head.team == COLOR_TEAM2)
306 if(head.team == COLOR_TEAM3)
310 if(head.team == COLOR_TEAM4)
315 head = find(head, classname, "dom_team");
318 // make sure there are at least 2 teams to join
320 totalteams = totalteams + 1;
322 totalteams = totalteams + 1;
324 totalteams = totalteams + 1;
326 totalteams = totalteams + 1;
329 error("dom_player_join_team: Too few teams available for domination\n");
331 // whichever teams that are available are set to 0 instead of -1
333 // if we don't care what team he ends up on, put him on whatever team he entered as.
334 // if he's not on a valid team, then put him on the smallest team
335 if(!balance_teams && !force_balance)
337 if( c1 >= 0 && pl.team == COLOR_TEAM1)
338 selectedteam = pl.team;
339 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
340 selectedteam = pl.team;
341 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
342 selectedteam = pl.team;
343 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
344 selectedteam = pl.team;
349 SetPlayerColors(pl, selectedteam - 1);
352 // otherwise end up on the smallest team (handled below)
355 // now count how many players are on each team already
357 head = find(world, classname, "player");
360 //if(head.netname != "")
362 if(head.team == COLOR_TEAM1)
367 if(head.team == COLOR_TEAM2)
372 if(head.team == COLOR_TEAM3)
377 if(head.team == COLOR_TEAM4)
383 head = find(head, classname, "player");
386 // c1...c4 now have counts of each team
387 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
390 smallestteam_count = 999;
392 // 2 gives priority to what team you're already on, 1 goes in order
395 if(balance_type == 1)
397 if(c1 >= 0 && c1 < smallestteam_count)
400 smallestteam_count = c1;
402 if(c2 >= 0 && c2 < smallestteam_count)
405 smallestteam_count = c2;
407 if(c3 >= 0 && c3 < smallestteam_count)
410 smallestteam_count = c3;
412 if(c4 >= 0 && c4 < smallestteam_count)
415 smallestteam_count = c4;
420 if(c1 >= 0 && (c1 < smallestteam_count ||
421 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
424 smallestteam_count = c1;
426 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
427 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
430 smallestteam_count = c2;
432 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
433 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
436 smallestteam_count = c3;
438 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
439 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
442 smallestteam_count = c4;
446 if(smallestteam == 1)
448 selectedteam = COLOR_TEAM1 - 1;
450 if(smallestteam == 2)
452 selectedteam = COLOR_TEAM2 - 1;
454 if(smallestteam == 3)
456 selectedteam = COLOR_TEAM3 - 1;
458 if(smallestteam == 4)
460 selectedteam = COLOR_TEAM4 - 1;
463 SetPlayerColors(pl, selectedteam);
466 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
467 Control point for Domination gameplay.
469 void() dom_controlpoint =
476 self.think = dom_controlpoint_setup;
477 self.nextthink = time + 0.1;
482 //if(!self.glow_size)
483 // self.glow_size = cvar("g_domination_point_glow");
484 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
487 // code from here on is just to support maps that don't have control point and team entities
488 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
490 local entity oldself;
493 self.classname = "dom_team";
494 self.netname = teamname;
495 self.cnt = teamcolor;
496 self.model = pointmodel;
497 self.skin = pointskin;
498 self.noise = capsound;
499 self.noise1 = capnarration;
500 self.message = capmessage;
502 // this code is identical to dom_team
503 setmodel(self, self.model); // precision not needed
504 self.mdl = self.model;
505 self.dmg = self.modelindex;
508 // this would have to be changed if used in quakeworld
509 self.team = self.cnt + 1;
515 void(vector org) dom_spawnpoint =
517 local entity oldself;
520 self.classname = "dom_controlpoint";
521 self.think = dom_controlpoint;
522 self.nextthink = time;
528 // spawn some default teams if the map is not set up for domination
529 void() dom_spawnteams =
533 numteams = cvar("g_domination_default_teams");
534 // LordHavoc: edit this if you want to change defaults
535 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
536 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
538 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
540 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
541 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
544 void() dom_delayedinit =
548 self.think = SUB_Remove;
549 self.nextthink = time;
550 // if no teams are found, spawn defaults
551 if (find(world, classname, "dom_team") == world)
553 // if no control points are found, spawn defaults
554 if (find(world, classname, "dom_controlpoint") == world)
556 // here follow default domination points for each map
558 if (world.model == "maps/e1m1.bsp")
560 dom_spawnpoint('0 0 0');
565 // if no supported map was found, make every deathmatch spawn a point
566 head = find(world, classname, "info_player_deathmatch");
569 dom_spawnpoint(head.origin);
570 head = find(head, classname, "info_player_deathmatch");
579 // we have to precache default models/sounds even if they might not be
580 // used because worldspawn is executed before any other entities are read,
581 // so we don't even know yet if this map is set up for domination...
582 precache_model("models/domination/dom_red.md3");
583 precache_model("models/domination/dom_blue.md3");
584 precache_model("models/domination/dom_yellow.md3");
585 precache_model("models/domination/dom_pink.md3");
586 precache_model("models/domination/dom_unclaimed.md3");
587 precache_sound("domination/claim.wav");
589 e.think = dom_delayedinit;
590 e.nextthink = time + 0.1;
592 // teamplay is always on in domination, defaults to hurt self but not teammates
593 //if(!cvar("teamplay"))
594 // cvar_set("teamplay", "3");