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");
67 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
68 if (head.noise1 != "")
69 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
71 //self.nextthink = time + cvar("g_domination_point_rate");
72 //self.think = dompointthink;
74 if(cvar("g_domination_point_rate"))
75 self.delay = time + cvar("g_domination_point_rate");
77 self.delay = time + self.wait;
80 old_delay = self.delay;
82 self.team = real_team;
86 self.delay = old_delay;
90 void AnimateDomPoint()
92 if(self.pain_finished > time)
94 self.pain_finished = time + self.t_width;
95 if(self.nextthink > self.pain_finished)
96 self.nextthink = self.pain_finished;
98 self.frame = self.frame + 1;
99 if(self.frame > self.t_length)
106 local float waittime;
109 self.nextthink = time + 0.1;
111 //self.frame = self.frame + 1;
112 //if(self.frame > 119)
118 if (gameover || self.delay > time || time < restart_countdown) // game has ended, don't keep giving points
121 waittime = cvar("g_domination_point_rate");
123 waittime = self.wait;
124 self.delay = time + waittime;
126 // give credit to the team
127 // NOTE: this defaults to 0
128 if (self.goalentity.netname)
130 fragamt = cvar("g_domination_point_amt");
132 fragamt = self.DOMPOINTFRAGS;
133 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
134 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_DOMTICKS, fragamt);
136 // give credit to the individual player, if he is still there
137 if (self.enemy.playerid == self.enemy_playerid)
139 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
140 PlayerScore_Add(self.enemy, SP_DOM_DOMTICKS, fragamt);
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
188 self.enemy_playerid = other.playerid;
192 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
193 Team declaration for Domination gameplay, this allows you to decide what team
194 names and control point models are used in your map.
196 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
197 can have netname set! The nameless team owns all control points at start.
201 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
203 Scoreboard color of the team (for example 4 is red and 13 is blue)
205 Model to use for control points owned by this team (for example
206 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
209 Skin of the model to use (for team skins on a single model)
211 Sound to play when this team captures a point.
212 (this is a localized sound, like a small alarm or other effect)
214 Narrator speech to play when this team captures a point.
215 (this is a global sound, like "Red team has captured a control point")
218 void spawnfunc_dom_team()
225 precache_model(self.model);
226 if (self.noise != "")
227 precache_sound(self.noise);
228 if (self.noise1 != "")
229 precache_sound(self.noise1);
230 self.classname = "dom_team";
231 setmodel(self, self.model); // precision not needed
232 self.mdl = self.model;
233 self.dmg = self.modelindex;
236 // this would have to be changed if used in quakeworld
237 self.team = self.cnt + 1;
240 void dom_controlpoint_setup()
243 // find the spawnfunc_dom_team representing unclaimed points
244 head = find(world, classname, "dom_team");
245 while(head && head.netname != "")
246 head = find(head, classname, "dom_team");
248 objerror("no spawnfunc_dom_team with netname \"\" found\n");
250 // copy important properties from spawnfunc_dom_team entity
251 self.goalentity = head;
252 setmodel(self, head.mdl); // precision already set
253 self.skin = head.skin;
258 self.message = " has captured a control point";
260 if(!self.DOMPOINTFRAGS)
261 self.DOMPOINTFRAGS = 1;
266 self.t_width = 0.1; // frame animation rate
268 self.t_length = 119; // maximum frame
270 self.think = dompointthink;
271 self.nextthink = time;
272 self.touch = dompointtouch;
273 self.solid = SOLID_TRIGGER;
274 self.flags = FL_ITEM;
275 setsize(self, '-32 -32 -32', '32 32 32');
276 setorigin(self, self.origin + '0 0 20');
279 waypoint_spawnforitem(self);
284 // player has joined game, get him on a team
286 /*void dom_player_join_team(entity pl)
289 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
290 float balance_teams, force_balance, balance_type;
292 balance_teams = cvar("g_balance_teams");
293 balance_teams = cvar("g_balance_teams_force");
295 c1 = c2 = c3 = c4 = -1;
298 // first find out what teams are allowed
299 head = find(world, classname, "dom_team");
302 if(head.netname != "")
304 //if(head.team == pl.team)
306 if(head.team == COLOR_TEAM1)
310 if(head.team == COLOR_TEAM2)
314 if(head.team == COLOR_TEAM3)
318 if(head.team == COLOR_TEAM4)
323 head = find(head, classname, "dom_team");
326 // make sure there are at least 2 teams to join
328 totalteams = totalteams + 1;
330 totalteams = totalteams + 1;
332 totalteams = totalteams + 1;
334 totalteams = totalteams + 1;
337 error("dom_player_join_team: Too few teams available for domination\n");
339 // whichever teams that are available are set to 0 instead of -1
341 // if we don't care what team he ends up on, put him on whatever team he entered as.
342 // if he's not on a valid team, then put him on the smallest team
343 if(!balance_teams && !force_balance)
345 if( c1 >= 0 && pl.team == COLOR_TEAM1)
346 selectedteam = pl.team;
347 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
348 selectedteam = pl.team;
349 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
350 selectedteam = pl.team;
351 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
352 selectedteam = pl.team;
357 SetPlayerColors(pl, selectedteam - 1);
360 // otherwise end up on the smallest team (handled below)
363 // now count how many players are on each team already
365 head = find(world, classname, "player");
368 //if(head.netname != "")
370 if(head.team == COLOR_TEAM1)
375 if(head.team == COLOR_TEAM2)
380 if(head.team == COLOR_TEAM3)
385 if(head.team == COLOR_TEAM4)
391 head = find(head, classname, "player");
394 // c1...c4 now have counts of each team
395 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
398 smallestteam_count = 999;
400 // 2 gives priority to what team you're already on, 1 goes in order
403 if(balance_type == 1)
405 if(c1 >= 0 && c1 < smallestteam_count)
408 smallestteam_count = c1;
410 if(c2 >= 0 && c2 < smallestteam_count)
413 smallestteam_count = c2;
415 if(c3 >= 0 && c3 < smallestteam_count)
418 smallestteam_count = c3;
420 if(c4 >= 0 && c4 < smallestteam_count)
423 smallestteam_count = c4;
428 if(c1 >= 0 && (c1 < smallestteam_count ||
429 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
432 smallestteam_count = c1;
434 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
435 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
438 smallestteam_count = c2;
440 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
441 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
444 smallestteam_count = c3;
446 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
447 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
450 smallestteam_count = c4;
454 if(smallestteam == 1)
456 selectedteam = COLOR_TEAM1 - 1;
458 if(smallestteam == 2)
460 selectedteam = COLOR_TEAM2 - 1;
462 if(smallestteam == 3)
464 selectedteam = COLOR_TEAM3 - 1;
466 if(smallestteam == 4)
468 selectedteam = COLOR_TEAM4 - 1;
471 SetPlayerColors(pl, selectedteam);
474 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
475 Control point for Domination gameplay.
477 void spawnfunc_dom_controlpoint()
484 self.think = dom_controlpoint_setup;
485 self.nextthink = time + 0.1;
490 //if(!self.glow_size)
491 // self.glow_size = cvar("g_domination_point_glow");
492 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
495 // code from here on is just to support maps that don't have control point and team entities
496 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
498 local entity oldself;
501 self.classname = "dom_team";
502 self.netname = teamname;
503 self.cnt = teamcolor;
504 self.model = pointmodel;
505 self.skin = pointskin;
506 self.noise = capsound;
507 self.noise1 = capnarration;
508 self.message = capmessage;
510 // this code is identical to spawnfunc_dom_team
511 setmodel(self, self.model); // precision not needed
512 self.mdl = self.model;
513 self.dmg = self.modelindex;
516 // this would have to be changed if used in quakeworld
517 self.team = self.cnt + 1;
523 void dom_spawnpoint(vector org)
525 local entity oldself;
528 self.classname = "dom_controlpoint";
529 self.think = spawnfunc_dom_controlpoint;
530 self.nextthink = time;
532 spawnfunc_dom_controlpoint();
536 // spawn some default teams if the map is not set up for domination
537 void dom_spawnteams()
541 numteams = cvar("g_domination_default_teams");
542 // LordHavoc: edit this if you want to change defaults
543 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
544 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
546 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
548 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
549 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
552 void dom_delayedinit()
556 self.think = SUB_Remove;
557 self.nextthink = time;
558 // if no teams are found, spawn defaults
559 if (find(world, classname, "dom_team") == world)
561 // if no control points are found, spawn defaults
562 if (find(world, classname, "dom_controlpoint") == world)
564 // here follow default domination points for each map
566 if (world.model == "maps/e1m1.bsp")
568 dom_spawnpoint('0 0 0');
573 // if no supported map was found, make every deathmatch spawn a point
574 head = find(world, classname, "info_player_deathmatch");
577 dom_spawnpoint(head.origin);
578 head = find(head, classname, "info_player_deathmatch");
589 // we have to precache default models/sounds even if they might not be
590 // used because spawnfunc_worldspawn is executed before any other entities are read,
591 // so we don't even know yet if this map is set up for domination...
592 precache_model("models/domination/dom_red.md3");
593 precache_model("models/domination/dom_blue.md3");
594 precache_model("models/domination/dom_yellow.md3");
595 precache_model("models/domination/dom_pink.md3");
596 precache_model("models/domination/dom_unclaimed.md3");
597 precache_sound("domination/claim.wav");
599 e.think = dom_delayedinit;
600 e.nextthink = time + 0.1;
602 // teamplay is always on in domination, defaults to hurt self but not teammates
603 //if(!cvar("teamplay"))
604 // cvar_set("teamplay", "3");