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 spawnfunc_worldspawn in world.qc:
12 4. Add this line to the end of spawnfunc_worldspawn in world.qc:
15 Note: The only teams who can use dom control points are identified by spawnfunc_dom_team entities (if none exist these default to red and blue and use only quake models/sounds).
18 #define DOMPOINTFRAGS frags
20 .float enemy_playerid;
22 void() dom_controlpoint_setup;
24 void LogDom(string mode, float team_before, entity actor)
27 if(!cvar("sv_eventlog"))
29 s = strcat(":dom:", mode);
30 s = strcat(s, ":", ftos(team_before));
31 s = strcat(s, ":", ftos(actor.playerid));
32 GameLogEcho(s, FALSE);
35 void() dom_spawnteams;
37 void dompoint_captured ()
40 local float old_delay, old_team, real_team;
42 // now that the delay has expired, switch to the latest team to lay claim to this point
48 LogDom("taken", self.team, self.dmg_inflictor);
49 self.dmg_inflictor = world;
51 self.goalentity = head;
52 self.model = head.mdl;
53 self.modelindex = head.dmg;
54 self.skin = head.skin;
56 //bprint(head.message);
59 //bprint(^3head.netname);
60 //bprint(head.netname);
61 //bprint(self.message);
64 bprint("^3", head.netname, "^3", self.message, "\n");
65 if(self.enemy.playerid == self.enemy_playerid)
66 PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
71 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
72 if (head.noise1 != "")
73 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
75 //self.nextthink = time + cvar("g_domination_point_rate");
76 //self.think = dompointthink;
78 if(cvar("g_domination_point_rate"))
79 self.delay = time + cvar("g_domination_point_rate");
81 self.delay = time + self.wait;
84 old_delay = self.delay;
86 self.team = real_team;
90 self.delay = old_delay;
94 void AnimateDomPoint()
96 if(self.pain_finished > time)
98 self.pain_finished = time + self.t_width;
99 if(self.nextthink > self.pain_finished)
100 self.nextthink = self.pain_finished;
102 self.frame = self.frame + 1;
103 if(self.frame > self.t_length)
110 local float waittime;
113 self.nextthink = time + 0.1;
115 //self.frame = self.frame + 1;
116 //if(self.frame > 119)
122 if (gameover || self.delay > time || time < restart_countdown) // game has ended, don't keep giving points
125 waittime = cvar("g_domination_point_rate");
127 waittime = self.wait;
128 self.delay = time + waittime;
130 // give credit to the team
131 // NOTE: this defaults to 0
132 if (self.goalentity.netname)
134 fragamt = cvar("g_domination_point_amt");
136 fragamt = self.DOMPOINTFRAGS;
137 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
138 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
140 // give credit to the individual player, if he is still there
141 if (self.enemy.playerid == self.enemy_playerid)
143 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
144 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
154 if (other.classname != "player")
156 if (other.health < 1)
159 // only valid teams can claim it
160 head = find(world, classname, "dom_team");
161 while (head && head.team != other.team)
162 head = find(head, classname, "dom_team");
163 if (!head || head.netname == "" || head == self.goalentity)
168 self.team = self.goalentity.team; // this stores the PREVIOUS team!
170 self.cnt = other.team;
171 self.aiment = head; // team to switch to after the delay
172 self.dmg_inflictor = other;
175 // self.delay = time + cvar("g_domination_point_capturetime");
176 //self.nextthink = time + cvar("g_domination_point_capturetime");
177 //self.think = dompoint_captured;
179 // go to neutral team in the mean time
180 head = find(world, classname, "dom_team");
181 while (head && head.netname != "")
182 head = find(head, classname, "dom_team");
186 self.goalentity = head;
187 self.model = head.mdl;
188 self.modelindex = head.dmg;
189 self.skin = head.skin;
191 self.enemy = other; // individual player scoring
192 self.enemy_playerid = other.playerid;
196 /*QUAKED spawnfunc_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 spawnfunc_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")
222 void spawnfunc_dom_team()
229 precache_model(self.model);
230 if (self.noise != "")
231 precache_sound(self.noise);
232 if (self.noise1 != "")
233 precache_sound(self.noise1);
234 self.classname = "dom_team";
235 setmodel(self, self.model); // precision not needed
236 self.mdl = self.model;
237 self.dmg = self.modelindex;
240 // this would have to be changed if used in quakeworld
241 self.team = self.cnt + 1;
244 void dom_controlpoint_setup()
247 // find the spawnfunc_dom_team representing unclaimed points
248 head = find(world, classname, "dom_team");
249 while(head && head.netname != "")
250 head = find(head, classname, "dom_team");
252 objerror("no spawnfunc_dom_team with netname \"\" found\n");
254 // copy important properties from spawnfunc_dom_team entity
255 self.goalentity = head;
256 setmodel(self, head.mdl); // precision already set
257 self.skin = head.skin;
262 self.message = " has captured a control point";
264 if(!self.DOMPOINTFRAGS)
265 self.DOMPOINTFRAGS = 1;
270 self.t_width = 0.1; // frame animation rate
272 self.t_length = 119; // maximum frame
274 self.think = dompointthink;
275 self.nextthink = time;
276 self.touch = dompointtouch;
277 self.solid = SOLID_TRIGGER;
278 self.flags = FL_ITEM;
279 setsize(self, '-32 -32 -32', '32 32 32');
280 setorigin(self, self.origin + '0 0 20');
283 waypoint_spawnforitem(self);
288 // player has joined game, get him on a team
290 /*void dom_player_join_team(entity pl)
293 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
294 float balance_teams, force_balance, balance_type;
296 balance_teams = cvar("g_balance_teams");
297 balance_teams = cvar("g_balance_teams_force");
299 c1 = c2 = c3 = c4 = -1;
302 // first find out what teams are allowed
303 head = find(world, classname, "dom_team");
306 if(head.netname != "")
308 //if(head.team == pl.team)
310 if(head.team == COLOR_TEAM1)
314 if(head.team == COLOR_TEAM2)
318 if(head.team == COLOR_TEAM3)
322 if(head.team == COLOR_TEAM4)
327 head = find(head, classname, "dom_team");
330 // make sure there are at least 2 teams to join
332 totalteams = totalteams + 1;
334 totalteams = totalteams + 1;
336 totalteams = totalteams + 1;
338 totalteams = totalteams + 1;
341 error("dom_player_join_team: Too few teams available for domination\n");
343 // whichever teams that are available are set to 0 instead of -1
345 // if we don't care what team he ends up on, put him on whatever team he entered as.
346 // if he's not on a valid team, then put him on the smallest team
347 if(!balance_teams && !force_balance)
349 if( c1 >= 0 && pl.team == COLOR_TEAM1)
350 selectedteam = pl.team;
351 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
352 selectedteam = pl.team;
353 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
354 selectedteam = pl.team;
355 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
356 selectedteam = pl.team;
361 SetPlayerColors(pl, selectedteam - 1);
364 // otherwise end up on the smallest team (handled below)
367 // now count how many players are on each team already
369 head = find(world, classname, "player");
372 //if(head.netname != "")
374 if(head.team == COLOR_TEAM1)
379 if(head.team == COLOR_TEAM2)
384 if(head.team == COLOR_TEAM3)
389 if(head.team == COLOR_TEAM4)
395 head = find(head, classname, "player");
398 // c1...c4 now have counts of each team
399 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
402 smallestteam_count = 999;
404 // 2 gives priority to what team you're already on, 1 goes in order
407 if(balance_type == 1)
409 if(c1 >= 0 && c1 < smallestteam_count)
412 smallestteam_count = c1;
414 if(c2 >= 0 && c2 < smallestteam_count)
417 smallestteam_count = c2;
419 if(c3 >= 0 && c3 < smallestteam_count)
422 smallestteam_count = c3;
424 if(c4 >= 0 && c4 < smallestteam_count)
427 smallestteam_count = c4;
432 if(c1 >= 0 && (c1 < smallestteam_count ||
433 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
436 smallestteam_count = c1;
438 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
439 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
442 smallestteam_count = c2;
444 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
445 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
448 smallestteam_count = c3;
450 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
451 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
454 smallestteam_count = c4;
458 if(smallestteam == 1)
460 selectedteam = COLOR_TEAM1 - 1;
462 if(smallestteam == 2)
464 selectedteam = COLOR_TEAM2 - 1;
466 if(smallestteam == 3)
468 selectedteam = COLOR_TEAM3 - 1;
470 if(smallestteam == 4)
472 selectedteam = COLOR_TEAM4 - 1;
475 SetPlayerColors(pl, selectedteam);
478 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
479 Control point for Domination gameplay.
481 void spawnfunc_dom_controlpoint()
488 self.think = dom_controlpoint_setup;
489 self.nextthink = time + 0.1;
494 //if(!self.glow_size)
495 // self.glow_size = cvar("g_domination_point_glow");
496 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
499 // code from here on is just to support maps that don't have control point and team entities
500 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
502 local entity oldself;
505 self.classname = "dom_team";
506 self.netname = teamname;
507 self.cnt = teamcolor;
508 self.model = pointmodel;
509 self.skin = pointskin;
510 self.noise = capsound;
511 self.noise1 = capnarration;
512 self.message = capmessage;
514 // this code is identical to spawnfunc_dom_team
515 setmodel(self, self.model); // precision not needed
516 self.mdl = self.model;
517 self.dmg = self.modelindex;
520 // this would have to be changed if used in quakeworld
521 self.team = self.cnt + 1;
527 void dom_spawnpoint(vector org)
529 local entity oldself;
532 self.classname = "dom_controlpoint";
533 self.think = spawnfunc_dom_controlpoint;
534 self.nextthink = time;
536 spawnfunc_dom_controlpoint();
540 // spawn some default teams if the map is not set up for domination
541 void dom_spawnteams()
545 numteams = cvar("g_domination_default_teams");
546 // LordHavoc: edit this if you want to change defaults
547 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
548 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
550 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
552 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
553 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
556 void dom_delayedinit()
560 self.think = SUB_Remove;
561 self.nextthink = time;
562 // if no teams are found, spawn defaults
563 if (find(world, classname, "dom_team") == world)
565 // if no control points are found, spawn defaults
566 if (find(world, classname, "dom_controlpoint") == world)
568 // here follow default domination points for each map
570 if (world.model == "maps/e1m1.bsp")
572 dom_spawnpoint('0 0 0');
577 // if no supported map was found, make every deathmatch spawn a point
578 head = find(world, classname, "info_player_deathmatch");
581 dom_spawnpoint(head.origin);
582 head = find(head, classname, "info_player_deathmatch");
593 // we have to precache default models/sounds even if they might not be
594 // used because spawnfunc_worldspawn is executed before any other entities are read,
595 // so we don't even know yet if this map is set up for domination...
596 precache_model("models/domination/dom_red.md3");
597 precache_model("models/domination/dom_blue.md3");
598 precache_model("models/domination/dom_yellow.md3");
599 precache_model("models/domination/dom_pink.md3");
600 precache_model("models/domination/dom_unclaimed.md3");
601 precache_sound("domination/claim.wav");
603 e.think = dom_delayedinit;
604 e.nextthink = time + 0.1;
606 // teamplay is always on in domination, defaults to hurt self but not teammates
607 //if(!cvar("teamplay"))
608 // cvar_set("teamplay", "3");