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');
273 waypoint_spawnforitem(self);
278 // player has joined game, get him on a team
280 /*void dom_player_join_team(entity pl)
283 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
284 float balance_teams, force_balance, balance_type;
286 balance_teams = cvar("g_balance_teams");
287 balance_teams = cvar("g_balance_teams_force");
289 c1 = c2 = c3 = c4 = -1;
292 // first find out what teams are allowed
293 head = find(world, classname, "dom_team");
296 if(head.netname != "")
298 //if(head.team == pl.team)
300 if(head.team == COLOR_TEAM1)
304 if(head.team == COLOR_TEAM2)
308 if(head.team == COLOR_TEAM3)
312 if(head.team == COLOR_TEAM4)
317 head = find(head, classname, "dom_team");
320 // make sure there are at least 2 teams to join
322 totalteams = totalteams + 1;
324 totalteams = totalteams + 1;
326 totalteams = totalteams + 1;
328 totalteams = totalteams + 1;
331 error("dom_player_join_team: Too few teams available for domination\n");
333 // whichever teams that are available are set to 0 instead of -1
335 // if we don't care what team he ends up on, put him on whatever team he entered as.
336 // if he's not on a valid team, then put him on the smallest team
337 if(!balance_teams && !force_balance)
339 if( c1 >= 0 && pl.team == COLOR_TEAM1)
340 selectedteam = pl.team;
341 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
342 selectedteam = pl.team;
343 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
344 selectedteam = pl.team;
345 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
346 selectedteam = pl.team;
351 SetPlayerColors(pl, selectedteam - 1);
354 // otherwise end up on the smallest team (handled below)
357 // now count how many players are on each team already
359 head = find(world, classname, "player");
362 //if(head.netname != "")
364 if(head.team == COLOR_TEAM1)
369 if(head.team == COLOR_TEAM2)
374 if(head.team == COLOR_TEAM3)
379 if(head.team == COLOR_TEAM4)
385 head = find(head, classname, "player");
388 // c1...c4 now have counts of each team
389 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
392 smallestteam_count = 999;
394 // 2 gives priority to what team you're already on, 1 goes in order
397 if(balance_type == 1)
399 if(c1 >= 0 && c1 < smallestteam_count)
402 smallestteam_count = c1;
404 if(c2 >= 0 && c2 < smallestteam_count)
407 smallestteam_count = c2;
409 if(c3 >= 0 && c3 < smallestteam_count)
412 smallestteam_count = c3;
414 if(c4 >= 0 && c4 < smallestteam_count)
417 smallestteam_count = c4;
422 if(c1 >= 0 && (c1 < smallestteam_count ||
423 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
426 smallestteam_count = c1;
428 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
429 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
432 smallestteam_count = c2;
434 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
435 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
438 smallestteam_count = c3;
440 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
441 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
444 smallestteam_count = c4;
448 if(smallestteam == 1)
450 selectedteam = COLOR_TEAM1 - 1;
452 if(smallestteam == 2)
454 selectedteam = COLOR_TEAM2 - 1;
456 if(smallestteam == 3)
458 selectedteam = COLOR_TEAM3 - 1;
460 if(smallestteam == 4)
462 selectedteam = COLOR_TEAM4 - 1;
465 SetPlayerColors(pl, selectedteam);
468 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
469 Control point for Domination gameplay.
471 void() dom_controlpoint =
478 self.think = dom_controlpoint_setup;
479 self.nextthink = time + 0.1;
484 //if(!self.glow_size)
485 // self.glow_size = cvar("g_domination_point_glow");
486 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
489 // code from here on is just to support maps that don't have control point and team entities
490 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
492 local entity oldself;
495 self.classname = "dom_team";
496 self.netname = teamname;
497 self.cnt = teamcolor;
498 self.model = pointmodel;
499 self.skin = pointskin;
500 self.noise = capsound;
501 self.noise1 = capnarration;
502 self.message = capmessage;
504 // this code is identical to dom_team
505 setmodel(self, self.model); // precision not needed
506 self.mdl = self.model;
507 self.dmg = self.modelindex;
510 // this would have to be changed if used in quakeworld
511 self.team = self.cnt + 1;
517 void(vector org) dom_spawnpoint =
519 local entity oldself;
522 self.classname = "dom_controlpoint";
523 self.think = dom_controlpoint;
524 self.nextthink = time;
530 // spawn some default teams if the map is not set up for domination
531 void() dom_spawnteams =
535 numteams = cvar("g_domination_default_teams");
536 // LordHavoc: edit this if you want to change defaults
537 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
538 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
540 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
542 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
543 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
546 void() dom_delayedinit =
550 self.think = SUB_Remove;
551 self.nextthink = time;
552 // if no teams are found, spawn defaults
553 if (find(world, classname, "dom_team") == world)
555 // if no control points are found, spawn defaults
556 if (find(world, classname, "dom_controlpoint") == world)
558 // here follow default domination points for each map
560 if (world.model == "maps/e1m1.bsp")
562 dom_spawnpoint('0 0 0');
567 // if no supported map was found, make every deathmatch spawn a point
568 head = find(world, classname, "info_player_deathmatch");
571 dom_spawnpoint(head.origin);
572 head = find(head, classname, "info_player_deathmatch");
581 // we have to precache default models/sounds even if they might not be
582 // used because worldspawn is executed before any other entities are read,
583 // so we don't even know yet if this map is set up for domination...
584 precache_model("models/domination/dom_red.md3");
585 precache_model("models/domination/dom_blue.md3");
586 precache_model("models/domination/dom_yellow.md3");
587 precache_model("models/domination/dom_pink.md3");
588 precache_model("models/domination/dom_unclaimed.md3");
589 precache_sound("domination/claim.wav");
591 e.think = dom_delayedinit;
592 e.nextthink = time + 0.1;
594 // teamplay is always on in domination, defaults to hurt self but not teammates
595 //if(!cvar("teamplay"))
596 // cvar_set("teamplay", "3");