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(strcat("^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 head = find(head, classname, "player");
131 if (head.team == self.goalentity.team)
132 head.frags = head.frags + teamfragamt;
133 head = find(head, classname, "player");
137 // 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
138 if (!self.enemy.flags || self.enemy.team != self.goalentity.team || self.enemy.killcount == -666) // flags is zero on removed clients
141 head = find(head, classname, "player");
144 if (head.team == self.goalentity.team)
146 head = find(head, classname, "player");
148 if(self.enemy == other) // search returned no matching player, reset dom point
150 dom_controlpoint_setup();
157 // give credit to the individual player
160 individualfragamt = cvar("g_domination_point_amt");
161 if(!individualfragamt)
162 individualfragamt = self.frags;
163 self.enemy.frags = self.enemy.frags + individualfragamt;
167 void() dompointtouch =
170 if (other.classname != "player")
172 if (other.health < 1)
175 // only valid teams can claim it
176 head = find(world, classname, "dom_team");
177 while (head && head.team != other.team)
178 head = find(head, classname, "dom_team");
179 if (!head || head.netname == "" || head == self.goalentity)
184 self.team = self.goalentity.team; // this stores the PREVIOUS team!
186 self.cnt = other.team;
187 self.aiment = head; // team to switch to after the delay
188 self.dmg_inflictor = other;
191 // self.delay = time + cvar("g_domination_point_capturetime");
192 //self.nextthink = time + cvar("g_domination_point_capturetime");
193 //self.think = dompoint_captured;
195 // go to neutral team in the mean time
196 head = find(world, classname, "dom_team");
197 while (head && head.netname != "")
198 head = find(head, classname, "dom_team");
202 self.goalentity = head;
203 self.model = head.mdl;
204 self.modelindex = head.dmg;
205 self.skin = head.skin;
207 self.enemy = other; // individual player scoring
211 /*QUAKED dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
212 Team declaration for Domination gameplay, this allows you to decide what team
213 names and control point models are used in your map.
215 Note: If you use dom_team entities you must define at least 3 and only two
216 can have netname set! The nameless team owns all control points at start.
220 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
222 Scoreboard color of the team (for example 4 is red and 13 is blue)
224 Model to use for control points owned by this team (for example
225 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
228 Skin of the model to use (for team skins on a single model)
230 Sound to play when this team captures a point.
231 (this is a localized sound, like a small alarm or other effect)
233 Narrator speech to play when this team captures a point.
234 (this is a global sound, like "Red team has captured a control point")
239 precache_model(self.model);
240 if (self.noise != "")
241 precache_sound(self.noise);
242 if (self.noise1 != "")
243 precache_sound(self.noise1);
244 self.classname = "dom_team";
245 setmodel(self, self.model);
246 self.mdl = self.model;
247 self.dmg = self.modelindex;
250 // this would have to be changed if used in quakeworld
251 self.team = self.cnt + 1;
254 void() dom_controlpoint_setup =
257 // find the dom_team representing unclaimed points
258 head = find(world, classname, "dom_team");
259 while(head && head.netname != "")
260 head = find(head, classname, "dom_team");
262 objerror("no dom_team with netname \"\" found\n");
264 // copy important properties from dom_team entity
265 self.goalentity = head;
266 setmodel(self, head.mdl);
267 self.skin = head.skin;
272 self.message = " has captured a control point";
280 self.t_width = 0.1; // frame animation rate
282 self.t_length = 119; // maximum frame
284 self.think = dompointthink;
285 self.nextthink = time;
286 self.touch = dompointtouch;
287 self.solid = SOLID_TRIGGER;
288 self.flags = FL_ITEM;
289 setsize(self, '-32 -32 -32', '32 32 32');
290 setorigin(self, self.origin + '0 0 20');
296 // player has joined game, get him on a team
298 /*void dom_player_join_team(entity pl)
301 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
302 float balance_teams, force_balance, balance_type;
304 balance_teams = cvar("g_balance_teams");
305 balance_teams = cvar("g_balance_teams_force");
307 c1 = c2 = c3 = c4 = -1;
310 // first find out what teams are allowed
311 head = find(world, classname, "dom_team");
314 if(head.netname != "")
316 //if(head.team == pl.team)
318 if(head.team == COLOR_TEAM1)
322 if(head.team == COLOR_TEAM2)
326 if(head.team == COLOR_TEAM3)
330 if(head.team == COLOR_TEAM4)
335 head = find(head, classname, "dom_team");
338 // make sure there are at least 2 teams to join
340 totalteams = totalteams + 1;
342 totalteams = totalteams + 1;
344 totalteams = totalteams + 1;
346 totalteams = totalteams + 1;
349 error("dom_player_join_team: Too few teams available for domination\n");
351 // whichever teams that are available are set to 0 instead of -1
353 // if we don't care what team he ends up on, put him on whatever team he entered as.
354 // if he's not on a valid team, then put him on the smallest team
355 if(!balance_teams && !force_balance)
357 if( c1 >= 0 && pl.team == COLOR_TEAM1)
358 selectedteam = pl.team;
359 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
360 selectedteam = pl.team;
361 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
362 selectedteam = pl.team;
363 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
364 selectedteam = pl.team;
369 SetPlayerColors(pl, selectedteam - 1);
372 // otherwise end up on the smallest team (handled below)
375 // now count how many players are on each team already
377 head = find(world, classname, "player");
380 //if(head.netname != "")
382 if(head.team == COLOR_TEAM1)
387 if(head.team == COLOR_TEAM2)
392 if(head.team == COLOR_TEAM3)
397 if(head.team == COLOR_TEAM4)
403 head = find(head, classname, "player");
406 // c1...c4 now have counts of each team
407 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
410 smallestteam_count = 999;
412 // 2 gives priority to what team you're already on, 1 goes in order
415 if(balance_type == 1)
417 if(c1 >= 0 && c1 < smallestteam_count)
420 smallestteam_count = c1;
422 if(c2 >= 0 && c2 < smallestteam_count)
425 smallestteam_count = c2;
427 if(c3 >= 0 && c3 < smallestteam_count)
430 smallestteam_count = c3;
432 if(c4 >= 0 && c4 < smallestteam_count)
435 smallestteam_count = c4;
440 if(c1 >= 0 && (c1 < smallestteam_count ||
441 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
444 smallestteam_count = c1;
446 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
447 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
450 smallestteam_count = c2;
452 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
453 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
456 smallestteam_count = c3;
458 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
459 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
462 smallestteam_count = c4;
466 if(smallestteam == 1)
468 selectedteam = COLOR_TEAM1 - 1;
470 if(smallestteam == 2)
472 selectedteam = COLOR_TEAM2 - 1;
474 if(smallestteam == 3)
476 selectedteam = COLOR_TEAM3 - 1;
478 if(smallestteam == 4)
480 selectedteam = COLOR_TEAM4 - 1;
483 SetPlayerColors(pl, selectedteam);
486 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
487 Control point for Domination gameplay.
489 void() dom_controlpoint =
491 if(!cvar("g_domination"))
496 self.think = dom_controlpoint_setup;
497 self.nextthink = time + 0.1;
502 //if(!self.glow_size)
503 // self.glow_size = cvar("g_domination_point_glow");
504 self.effects = self.effects | EF_FULLBRIGHT;
507 // code from here on is just to support maps that don't have control point and team entities
508 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
510 local entity oldself;
513 self.classname = "dom_team";
514 self.netname = teamname;
515 self.cnt = teamcolor;
516 self.model = pointmodel;
517 self.skin = pointskin;
518 self.noise = capsound;
519 self.noise1 = capnarration;
520 self.message = capmessage;
522 // this code is identical to dom_team
523 setmodel(self, self.model);
524 self.mdl = self.model;
525 self.dmg = self.modelindex;
528 // this would have to be changed if used in quakeworld
529 self.team = self.cnt + 1;
535 void(vector org) dom_spawnpoint =
537 local entity oldself;
540 self.classname = "dom_controlpoint";
541 self.think = dom_controlpoint;
542 self.nextthink = time;
548 // spawn some default teams if the map is not set up for domination
549 void() dom_spawnteams =
553 numteams = cvar("g_domination_default_teams");
554 // LordHavoc: edit this if you want to change defaults
555 dom_spawnteam("Red", 4, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
556 dom_spawnteam("Blue", 13, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
558 dom_spawnteam("Pink", 9, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
560 dom_spawnteam("Yellow", 12, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
561 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
564 void() dom_delayedinit =
568 self.think = SUB_Remove;
569 self.nextthink = time;
570 // if no teams are found, spawn defaults
571 if (find(world, classname, "dom_team") == world)
573 // if no control points are found, spawn defaults
574 if (find(world, classname, "dom_controlpoint") == world)
576 // here follow default domination points for each map
578 if (world.model == "maps/e1m1.bsp")
580 dom_spawnpoint('0 0 0');
585 // if no supported map was found, make every deathmatch spawn a point
586 head = find(world, classname, "info_player_deathmatch");
589 dom_spawnpoint(head.origin);
590 head = find(head, classname, "info_player_deathmatch");
599 // we have to precache default models/sounds even if they might not be
600 // used because worldspawn is executed before any other entities are read,
601 // so we don't even know yet if this map is set up for domination...
602 precache_model("models/domination/dom_red.md3");
603 precache_model("models/domination/dom_blue.md3");
604 precache_model("models/domination/dom_pink.md3");
605 precache_model("models/domination/dom_yellow.md3");
606 precache_model("models/domination/dom_unclaimed.md3");
607 precache_sound("domination/claim.wav");
609 e.think = dom_delayedinit;
610 e.nextthink = time + 0.1;
612 // teamplay is always on in domination, defaults to hurt self but not teammates
613 //if(!cvar("teamplay"))
614 // cvar_set("teamplay", "3");