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).
19 void LogDom(string mode, float team_before, entity actor)
22 if(!cvar("sv_eventlog"))
24 s = strcat(":dom:", mode);
25 s = strcat(s, ":", ftos(team_before));
26 s = strcat(s, ":", ftos(actor.playerid));
27 GameLogEcho(s, FALSE);
30 void() dom_spawnteams;
32 void dompoint_captured ()
36 // now that the delay has expired, switch to the latest team to lay claim to this point
41 LogDom("taken", self.team, self.dmg_inflictor);
42 self.dmg_inflictor = world;
44 self.goalentity = head;
45 self.model = head.mdl;
46 self.modelindex = head.dmg;
47 self.skin = head.skin;
49 //bprint(head.message);
52 //bprint(^3head.netname);
53 //bprint(head.netname);
54 //bprint(self.message);
57 bprint(strcat("^3", head.netname, "^3", self.message, "\n"));
60 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
61 if (head.noise1 != "")
62 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
64 //self.nextthink = time + cvar("g_domination_point_rate");
65 //self.think = dompointthink;
66 self.delay = time + cvar("g_domination_point_rate");
69 void AnimateDomPoint()
71 if(self.pain_finished > time)
73 self.pain_finished = time + self.t_width;
74 if(self.nextthink > self.pain_finished)
75 self.nextthink = self.pain_finished;
77 self.frame = self.frame + 1;
78 if(self.frame > self.t_length)
82 void() dompointthink =
86 local float teamfragamt;
87 local float individualfragamt;
89 self.nextthink = time + 0.1;
91 //self.frame = self.frame + 1;
92 //if(self.frame > 119)
98 if (gameover) // game has ended, don't keep giving points
101 if(self.delay > time)
111 waittime = cvar("g_domination_point_rate");
113 waittime = self.wait;
114 self.delay = time + waittime;
116 // give credit to all players of the team (rewards large teams)
117 // NOTE: this defaults to 0
118 if (self.goalentity.netname)
120 teamfragamt = cvar("g_domination_point_teamamt");
122 head = find(head, classname, "player");
125 if (head.team == self.goalentity.team)
126 head.frags = head.frags + teamfragamt;
127 head = find(head, classname, "player");
131 // if the player left the game, we have to find another player on the same team to give credit to
132 if (!self.enemy.flags) // flags is zero on removed clients
134 head = find(head, classname, "player");
137 if (head.team == self.goalentity.team)
139 head = find(head, classname, "player");
143 // give credit to the individual player
146 individualfragamt = cvar("g_domination_point_amt");
147 if(!individualfragamt)
148 individualfragamt = self.frags;
149 self.enemy.frags = self.enemy.frags + individualfragamt;
153 void() dompointtouch =
156 if (other.classname != "player")
158 if (other.health < 1)
161 // only valid teams can claim it
162 head = find(world, classname, "dom_team");
163 while (head && head.team != other.team)
164 head = find(head, classname, "dom_team");
165 if (!head || head.netname == "" || head == self.goalentity)
170 self.team = self.goalentity.team; // this stores the PREVIOUS team!
172 self.cnt = other.team;
173 self.aiment = head; // team to switch to after the delay
174 self.dmg_inflictor = other;
177 self.delay = time + cvar("g_domination_point_capturetime");
178 //self.nextthink = time + cvar("g_domination_point_capturetime");
179 //self.think = dompoint_captured;
181 // go to neutral team in the mean time
182 head = find(world, classname, "dom_team");
183 while (head && head.netname != "")
184 head = find(head, classname, "dom_team");
188 self.goalentity = head;
189 self.model = head.mdl;
190 self.modelindex = head.dmg;
191 self.skin = head.skin;
193 self.enemy = other; // individual player scoring
196 /*QUAKED dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
197 Team declaration for Domination gameplay, this allows you to decide what team
198 names and control point models are used in your map.
200 Note: If you use dom_team entities you must define at least 3 and only two
201 can have netname set! The nameless team owns all control points at start.
205 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
207 Scoreboard color of the team (for example 4 is red and 13 is blue)
209 Model to use for control points owned by this team (for example
210 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
213 Skin of the model to use (for team skins on a single model)
215 Sound to play when this team captures a point.
216 (this is a localized sound, like a small alarm or other effect)
218 Narrator speech to play when this team captures a point.
219 (this is a global sound, like "Red team has captured a control point")
224 precache_model(self.model);
225 if (self.noise != "")
226 precache_sound(self.noise);
227 if (self.noise1 != "")
228 precache_sound(self.noise1);
229 self.classname = "dom_team";
230 setmodel(self, self.model);
231 self.mdl = self.model;
232 self.dmg = self.modelindex;
235 // this would have to be changed if used in quakeworld
236 self.team = self.cnt + 1;
239 void() dom_controlpoint_setup =
242 // find the dom_team representing unclaimed points
243 head = find(world, classname, "dom_team");
244 while(head && head.netname != "")
245 head = find(head, classname, "dom_team");
247 objerror("no dom_team with netname \"\" found\n");
249 // copy important properties from dom_team entity
250 self.goalentity = head;
251 setmodel(self, head.mdl);
252 self.skin = head.skin;
257 self.message = " has captured a control point";
265 self.t_width = 0.1; // frame animation rate
267 self.t_length = 119; // maximum frame
269 self.think = dompointthink;
270 self.nextthink = time;
271 self.touch = dompointtouch;
272 self.solid = SOLID_TRIGGER;
273 self.flags = FL_ITEM;
274 setsize(self, '-32 -32 -24', '32 32 32');
275 setorigin(self, self.origin + '0 0 20');
281 // player has joined game, get him on a team
283 /*void dom_player_join_team(entity pl)
286 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
287 float balance_teams, force_balance, balance_type;
289 balance_teams = cvar("g_balance_teams");
290 balance_teams = cvar("g_balance_teams_force");
292 c1 = c2 = c3 = c4 = -1;
295 // first find out what teams are allowed
296 head = find(world, classname, "dom_team");
299 if(head.netname != "")
301 //if(head.team == pl.team)
303 if(head.team == COLOR_TEAM1)
307 if(head.team == COLOR_TEAM2)
311 if(head.team == COLOR_TEAM3)
315 if(head.team == COLOR_TEAM4)
320 head = find(head, classname, "dom_team");
323 // make sure there are at least 2 teams to join
325 totalteams = totalteams + 1;
327 totalteams = totalteams + 1;
329 totalteams = totalteams + 1;
331 totalteams = totalteams + 1;
334 error("dom_player_join_team: Too few teams available for domination\n");
336 // whichever teams that are available are set to 0 instead of -1
338 // if we don't care what team he ends up on, put him on whatever team he entered as.
339 // if he's not on a valid team, then put him on the smallest team
340 if(!balance_teams && !force_balance)
342 if( c1 >= 0 && pl.team == COLOR_TEAM1)
343 selectedteam = pl.team;
344 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
345 selectedteam = pl.team;
346 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
347 selectedteam = pl.team;
348 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
349 selectedteam = pl.team;
354 SetPlayerColors(pl, selectedteam - 1);
357 // otherwise end up on the smallest team (handled below)
360 // now count how many players are on each team already
362 head = find(world, classname, "player");
365 //if(head.netname != "")
367 if(head.team == COLOR_TEAM1)
372 if(head.team == COLOR_TEAM2)
377 if(head.team == COLOR_TEAM3)
382 if(head.team == COLOR_TEAM4)
388 head = find(head, classname, "player");
391 // c1...c4 now have counts of each team
392 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
395 smallestteam_count = 999;
397 // 2 gives priority to what team you're already on, 1 goes in order
400 if(balance_type == 1)
402 if(c1 >= 0 && c1 < smallestteam_count)
405 smallestteam_count = c1;
407 if(c2 >= 0 && c2 < smallestteam_count)
410 smallestteam_count = c2;
412 if(c3 >= 0 && c3 < smallestteam_count)
415 smallestteam_count = c3;
417 if(c4 >= 0 && c4 < smallestteam_count)
420 smallestteam_count = c4;
425 if(c1 >= 0 && (c1 < smallestteam_count ||
426 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
429 smallestteam_count = c1;
431 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
432 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
435 smallestteam_count = c2;
437 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
438 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
441 smallestteam_count = c3;
443 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
444 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
447 smallestteam_count = c4;
451 if(smallestteam == 1)
453 selectedteam = COLOR_TEAM1 - 1;
455 if(smallestteam == 2)
457 selectedteam = COLOR_TEAM2 - 1;
459 if(smallestteam == 3)
461 selectedteam = COLOR_TEAM3 - 1;
463 if(smallestteam == 4)
465 selectedteam = COLOR_TEAM4 - 1;
468 SetPlayerColors(pl, selectedteam);
471 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
472 Control point for Domination gameplay.
474 void() dom_controlpoint =
476 if(!cvar("g_domination"))
481 self.think = dom_controlpoint_setup;
482 self.nextthink = time + 0.1;
487 //if(!self.glow_size)
488 // self.glow_size = cvar("g_domination_point_glow");
489 self.effects = self.effects | EF_FULLBRIGHT;
492 // code from here on is just to support maps that don't have control point and team entities
493 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
495 local entity oldself;
498 self.classname = "dom_team";
499 self.netname = teamname;
500 self.cnt = teamcolor;
501 self.model = pointmodel;
502 self.skin = pointskin;
503 self.noise = capsound;
504 self.noise1 = capnarration;
505 self.message = capmessage;
507 // this code is identical to dom_team
508 setmodel(self, self.model);
509 self.mdl = self.model;
510 self.dmg = self.modelindex;
513 // this would have to be changed if used in quakeworld
514 self.team = self.cnt + 1;
520 void(vector org) dom_spawnpoint =
522 local entity oldself;
525 self.classname = "dom_controlpoint";
526 self.think = dom_controlpoint;
527 self.nextthink = time;
533 // spawn some default teams if the map is not set up for domination
534 void() dom_spawnteams =
538 numteams = cvar("g_domination_default_teams");
539 // LordHavoc: edit this if you want to change defaults
540 dom_spawnteam("Red", 4, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
541 dom_spawnteam("Blue", 13, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
543 dom_spawnteam("Pink", 9, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
545 dom_spawnteam("Yellow", 12, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
546 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
549 void() dom_delayedinit =
553 self.think = SUB_Remove;
554 self.nextthink = time;
555 // if no teams are found, spawn defaults
556 if (find(world, classname, "dom_team") == world)
558 // if no control points are found, spawn defaults
559 if (find(world, classname, "dom_controlpoint") == world)
561 // here follow default domination points for each map
563 if (world.model == "maps/e1m1.bsp")
565 dom_spawnpoint('0 0 0');
570 // if no supported map was found, make every deathmatch spawn a point
571 head = find(world, classname, "info_player_deathmatch");
574 dom_spawnpoint(head.origin);
575 head = find(head, classname, "info_player_deathmatch");
584 // we have to precache default models/sounds even if they might not be
585 // used because worldspawn is executed before any other entities are read,
586 // so we don't even know yet if this map is set up for domination...
587 precache_model("models/domination/dom_red.md3");
588 precache_model("models/domination/dom_blue.md3");
589 precache_model("models/domination/dom_pink.md3");
590 precache_model("models/domination/dom_yellow.md3");
591 precache_model("models/domination/dom_unclaimed.md3");
592 precache_sound("domination/claim.wav");
594 e.think = dom_delayedinit;
595 e.nextthink = time + 0.1;
597 // teamplay is always on in domination, defaults to hurt self but not teammates
598 //if(!cvar("teamplay"))
599 // cvar_set("teamplay", "3");