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
242 self.team = self.cnt + 1; // WHY are these different anyway?
245 void dom_controlpoint_setup()
248 // find the spawnfunc_dom_team representing unclaimed points
249 head = find(world, classname, "dom_team");
250 while(head && head.netname != "")
251 head = find(head, classname, "dom_team");
253 objerror("no spawnfunc_dom_team with netname \"\" found\n");
255 // copy important properties from spawnfunc_dom_team entity
256 self.goalentity = head;
257 setmodel(self, head.mdl); // precision already set
258 self.skin = head.skin;
263 self.message = " has captured a control point";
265 if(!self.DOMPOINTFRAGS)
266 self.DOMPOINTFRAGS = 1;
271 self.t_width = 0.1; // frame animation rate
273 self.t_length = 119; // maximum frame
275 self.think = dompointthink;
276 self.nextthink = time;
277 self.touch = dompointtouch;
278 self.solid = SOLID_TRIGGER;
279 self.flags = FL_ITEM;
280 setsize(self, '-32 -32 -32', '32 32 32');
281 setorigin(self, self.origin + '0 0 20');
284 waypoint_spawnforitem(self);
289 // player has joined game, get him on a team
291 /*void dom_player_join_team(entity pl)
294 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
295 float balance_teams, force_balance, balance_type;
297 balance_teams = cvar("g_balance_teams");
298 balance_teams = cvar("g_balance_teams_force");
300 c1 = c2 = c3 = c4 = -1;
303 // first find out what teams are allowed
304 head = find(world, classname, "dom_team");
307 if(head.netname != "")
309 //if(head.team == pl.team)
311 if(head.team == COLOR_TEAM1)
315 if(head.team == COLOR_TEAM2)
319 if(head.team == COLOR_TEAM3)
323 if(head.team == COLOR_TEAM4)
328 head = find(head, classname, "dom_team");
331 // make sure there are at least 2 teams to join
333 totalteams = totalteams + 1;
335 totalteams = totalteams + 1;
337 totalteams = totalteams + 1;
339 totalteams = totalteams + 1;
342 error("dom_player_join_team: Too few teams available for domination\n");
344 // whichever teams that are available are set to 0 instead of -1
346 // if we don't care what team he ends up on, put him on whatever team he entered as.
347 // if he's not on a valid team, then put him on the smallest team
348 if(!balance_teams && !force_balance)
350 if( c1 >= 0 && pl.team == COLOR_TEAM1)
351 selectedteam = pl.team;
352 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
353 selectedteam = pl.team;
354 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
355 selectedteam = pl.team;
356 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
357 selectedteam = pl.team;
362 SetPlayerColors(pl, selectedteam - 1);
365 // otherwise end up on the smallest team (handled below)
368 // now count how many players are on each team already
370 head = find(world, classname, "player");
373 //if(head.netname != "")
375 if(head.team == COLOR_TEAM1)
380 if(head.team == COLOR_TEAM2)
385 if(head.team == COLOR_TEAM3)
390 if(head.team == COLOR_TEAM4)
396 head = find(head, classname, "player");
399 // c1...c4 now have counts of each team
400 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
403 smallestteam_count = 999;
405 // 2 gives priority to what team you're already on, 1 goes in order
408 if(balance_type == 1)
410 if(c1 >= 0 && c1 < smallestteam_count)
413 smallestteam_count = c1;
415 if(c2 >= 0 && c2 < smallestteam_count)
418 smallestteam_count = c2;
420 if(c3 >= 0 && c3 < smallestteam_count)
423 smallestteam_count = c3;
425 if(c4 >= 0 && c4 < smallestteam_count)
428 smallestteam_count = c4;
433 if(c1 >= 0 && (c1 < smallestteam_count ||
434 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
437 smallestteam_count = c1;
439 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
440 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
443 smallestteam_count = c2;
445 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
446 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
449 smallestteam_count = c3;
451 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
452 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
455 smallestteam_count = c4;
459 if(smallestteam == 1)
461 selectedteam = COLOR_TEAM1 - 1;
463 if(smallestteam == 2)
465 selectedteam = COLOR_TEAM2 - 1;
467 if(smallestteam == 3)
469 selectedteam = COLOR_TEAM3 - 1;
471 if(smallestteam == 4)
473 selectedteam = COLOR_TEAM4 - 1;
476 SetPlayerColors(pl, selectedteam);
479 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
480 Control point for Domination gameplay.
482 void spawnfunc_dom_controlpoint()
489 self.think = dom_controlpoint_setup;
490 self.nextthink = time + 0.1;
495 //if(!self.glow_size)
496 // self.glow_size = cvar("g_domination_point_glow");
497 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
500 // code from here on is just to support maps that don't have control point and team entities
501 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
503 local entity oldself;
506 self.classname = "dom_team";
507 self.netname = teamname;
508 self.cnt = teamcolor;
509 self.model = pointmodel;
510 self.skin = pointskin;
511 self.noise = capsound;
512 self.noise1 = capnarration;
513 self.message = capmessage;
515 // this code is identical to spawnfunc_dom_team
516 setmodel(self, self.model); // precision not needed
517 self.mdl = self.model;
518 self.dmg = self.modelindex;
521 // this would have to be changed if used in quakeworld
522 self.team = self.cnt + 1;
528 void dom_spawnpoint(vector org)
530 local entity oldself;
533 self.classname = "dom_controlpoint";
534 self.think = spawnfunc_dom_controlpoint;
535 self.nextthink = time;
537 spawnfunc_dom_controlpoint();
541 // spawn some default teams if the map is not set up for domination
542 void dom_spawnteams()
546 numteams = cvar("g_domination_default_teams");
547 // LordHavoc: edit this if you want to change defaults
548 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
549 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
551 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
553 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
554 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
557 void dom_delayedinit()
561 self.think = SUB_Remove;
562 self.nextthink = time;
563 // if no teams are found, spawn defaults
564 if (find(world, classname, "dom_team") == world)
566 // if no control points are found, spawn defaults
567 if (find(world, classname, "dom_controlpoint") == world)
569 // here follow default domination points for each map
571 if (world.model == "maps/e1m1.bsp")
573 dom_spawnpoint('0 0 0');
578 // if no supported map was found, make every deathmatch spawn a point
579 head = find(world, classname, "info_player_deathmatch");
582 dom_spawnpoint(head.origin);
583 head = find(head, classname, "info_player_deathmatch");
594 // we have to precache default models/sounds even if they might not be
595 // used because spawnfunc_worldspawn is executed before any other entities are read,
596 // so we don't even know yet if this map is set up for domination...
597 precache_model("models/domination/dom_red.md3");
598 precache_model("models/domination/dom_blue.md3");
599 precache_model("models/domination/dom_yellow.md3");
600 precache_model("models/domination/dom_pink.md3");
601 precache_model("models/domination/dom_unclaimed.md3");
602 precache_sound("domination/claim.wav");
604 e.think = dom_delayedinit;
605 e.nextthink = time + 0.1;
607 // teamplay is always on in domination, defaults to hurt self but not teammates
608 //if(!cvar("teamplay"))
609 // cvar_set("teamplay", "3");