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 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 ()
36 local float old_delay, old_team, real_team;
38 // now that the delay has expired, switch to the latest team to lay claim to this point
44 LogDom("taken", self.team, self.dmg_inflictor);
45 self.dmg_inflictor = world;
47 self.goalentity = head;
48 self.model = head.mdl;
49 self.modelindex = head.dmg;
50 self.skin = head.skin;
52 //bprint(head.message);
55 //bprint(^3head.netname);
56 //bprint(head.netname);
57 //bprint(self.message);
60 bprint("^3", head.netname, "^3", self.message, "\n");
63 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
64 if (head.noise1 != "")
65 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
67 //self.nextthink = time + cvar("g_domination_point_rate");
68 //self.think = dompointthink;
70 if(cvar("g_domination_point_rate"))
71 self.delay = time + cvar("g_domination_point_rate");
73 self.delay = time + self.wait;
76 old_delay = self.delay;
78 self.team = real_team;
82 self.delay = old_delay;
86 void AnimateDomPoint()
88 if(self.pain_finished > time)
90 self.pain_finished = time + self.t_width;
91 if(self.nextthink > self.pain_finished)
92 self.nextthink = self.pain_finished;
94 self.frame = self.frame + 1;
95 if(self.frame > self.t_length)
102 local float waittime;
103 local float teamfragamt;
104 local float individualfragamt;
106 self.nextthink = time + 0.1;
108 //self.frame = self.frame + 1;
109 //if(self.frame > 119)
115 if (gameover || self.delay > time || time < restart_countdown) // game has ended, don't keep giving points
118 waittime = cvar("g_domination_point_rate");
120 waittime = self.wait;
121 self.delay = time + waittime;
123 // give credit to the team
124 // NOTE: this defaults to 0
125 if (self.goalentity.netname)
127 teamfragamt = cvar("g_domination_point_teamamt");
129 teamfragamt = self.frags;
130 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_DOMPOINTS, teamfragamt);
133 // 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
134 if (!self.enemy.flags || self.enemy.team != self.goalentity.team || self.enemy.killcount == -666) // flags is zero on removed clients
137 FOR_EACH_PLAYER(head)
138 if (head.team == self.goalentity.team)
140 if(self.enemy == other) // search returned no matching player, reset dom point
142 dom_controlpoint_setup();
149 // give credit to the individual player
152 individualfragamt = cvar("g_domination_point_amt");
153 if(!individualfragamt)
154 individualfragamt = self.frags;
155 UpdateFrags(self.enemy, individualfragamt);
156 PlayerScore_Add(self.enemy, SP_DOM_DOMPOINTS, individualfragamt);
163 if (other.classname != "player")
165 if (other.health < 1)
168 // only valid teams can claim it
169 head = find(world, classname, "dom_team");
170 while (head && head.team != other.team)
171 head = find(head, classname, "dom_team");
172 if (!head || head.netname == "" || head == self.goalentity)
177 self.team = self.goalentity.team; // this stores the PREVIOUS team!
179 self.cnt = other.team;
180 self.aiment = head; // team to switch to after the delay
181 self.dmg_inflictor = other;
184 // self.delay = time + cvar("g_domination_point_capturetime");
185 //self.nextthink = time + cvar("g_domination_point_capturetime");
186 //self.think = dompoint_captured;
188 // go to neutral team in the mean time
189 head = find(world, classname, "dom_team");
190 while (head && head.netname != "")
191 head = find(head, classname, "dom_team");
195 self.goalentity = head;
196 self.model = head.mdl;
197 self.modelindex = head.dmg;
198 self.skin = head.skin;
200 self.enemy = other; // individual player scoring
204 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
205 Team declaration for Domination gameplay, this allows you to decide what team
206 names and control point models are used in your map.
208 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
209 can have netname set! The nameless team owns all control points at start.
213 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
215 Scoreboard color of the team (for example 4 is red and 13 is blue)
217 Model to use for control points owned by this team (for example
218 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
221 Skin of the model to use (for team skins on a single model)
223 Sound to play when this team captures a point.
224 (this is a localized sound, like a small alarm or other effect)
226 Narrator speech to play when this team captures a point.
227 (this is a global sound, like "Red team has captured a control point")
230 void spawnfunc_dom_team()
237 precache_model(self.model);
238 if (self.noise != "")
239 precache_sound(self.noise);
240 if (self.noise1 != "")
241 precache_sound(self.noise1);
242 self.classname = "dom_team";
243 setmodel(self, self.model); // precision not needed
244 self.mdl = self.model;
245 self.dmg = self.modelindex;
248 // this would have to be changed if used in quakeworld
249 self.team = self.cnt + 1;
252 void dom_controlpoint_setup()
255 // find the spawnfunc_dom_team representing unclaimed points
256 head = find(world, classname, "dom_team");
257 while(head && head.netname != "")
258 head = find(head, classname, "dom_team");
260 objerror("no spawnfunc_dom_team with netname \"\" found\n");
262 // copy important properties from spawnfunc_dom_team entity
263 self.goalentity = head;
264 setmodel(self, head.mdl); // precision already set
265 self.skin = head.skin;
270 self.message = " has captured a control point";
278 self.t_width = 0.1; // frame animation rate
280 self.t_length = 119; // maximum frame
282 self.think = dompointthink;
283 self.nextthink = time;
284 self.touch = dompointtouch;
285 self.solid = SOLID_TRIGGER;
286 self.flags = FL_ITEM;
287 setsize(self, '-32 -32 -32', '32 32 32');
288 setorigin(self, self.origin + '0 0 20');
291 waypoint_spawnforitem(self);
296 // player has joined game, get him on a team
298 /*void dom_player_join_team(entity pl)
301 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
302 float balance_teams, force_balance, balance_type;
304 balance_teams = cvar("g_balance_teams");
305 balance_teams = cvar("g_balance_teams_force");
307 c1 = c2 = c3 = c4 = -1;
310 // first find out what teams are allowed
311 head = find(world, classname, "dom_team");
314 if(head.netname != "")
316 //if(head.team == pl.team)
318 if(head.team == COLOR_TEAM1)
322 if(head.team == COLOR_TEAM2)
326 if(head.team == COLOR_TEAM3)
330 if(head.team == COLOR_TEAM4)
335 head = find(head, classname, "dom_team");
338 // make sure there are at least 2 teams to join
340 totalteams = totalteams + 1;
342 totalteams = totalteams + 1;
344 totalteams = totalteams + 1;
346 totalteams = totalteams + 1;
349 error("dom_player_join_team: Too few teams available for domination\n");
351 // whichever teams that are available are set to 0 instead of -1
353 // if we don't care what team he ends up on, put him on whatever team he entered as.
354 // if he's not on a valid team, then put him on the smallest team
355 if(!balance_teams && !force_balance)
357 if( c1 >= 0 && pl.team == COLOR_TEAM1)
358 selectedteam = pl.team;
359 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
360 selectedteam = pl.team;
361 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
362 selectedteam = pl.team;
363 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
364 selectedteam = pl.team;
369 SetPlayerColors(pl, selectedteam - 1);
372 // otherwise end up on the smallest team (handled below)
375 // now count how many players are on each team already
377 head = find(world, classname, "player");
380 //if(head.netname != "")
382 if(head.team == COLOR_TEAM1)
387 if(head.team == COLOR_TEAM2)
392 if(head.team == COLOR_TEAM3)
397 if(head.team == COLOR_TEAM4)
403 head = find(head, classname, "player");
406 // c1...c4 now have counts of each team
407 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
410 smallestteam_count = 999;
412 // 2 gives priority to what team you're already on, 1 goes in order
415 if(balance_type == 1)
417 if(c1 >= 0 && c1 < smallestteam_count)
420 smallestteam_count = c1;
422 if(c2 >= 0 && c2 < smallestteam_count)
425 smallestteam_count = c2;
427 if(c3 >= 0 && c3 < smallestteam_count)
430 smallestteam_count = c3;
432 if(c4 >= 0 && c4 < smallestteam_count)
435 smallestteam_count = c4;
440 if(c1 >= 0 && (c1 < smallestteam_count ||
441 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
444 smallestteam_count = c1;
446 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
447 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
450 smallestteam_count = c2;
452 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
453 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
456 smallestteam_count = c3;
458 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
459 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
462 smallestteam_count = c4;
466 if(smallestteam == 1)
468 selectedteam = COLOR_TEAM1 - 1;
470 if(smallestteam == 2)
472 selectedteam = COLOR_TEAM2 - 1;
474 if(smallestteam == 3)
476 selectedteam = COLOR_TEAM3 - 1;
478 if(smallestteam == 4)
480 selectedteam = COLOR_TEAM4 - 1;
483 SetPlayerColors(pl, selectedteam);
486 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
487 Control point for Domination gameplay.
489 void spawnfunc_dom_controlpoint()
496 self.think = dom_controlpoint_setup;
497 self.nextthink = time + 0.1;
502 //if(!self.glow_size)
503 // self.glow_size = cvar("g_domination_point_glow");
504 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
507 // code from here on is just to support maps that don't have control point and team entities
508 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
510 local entity oldself;
513 self.classname = "dom_team";
514 self.netname = teamname;
515 self.cnt = teamcolor;
516 self.model = pointmodel;
517 self.skin = pointskin;
518 self.noise = capsound;
519 self.noise1 = capnarration;
520 self.message = capmessage;
522 // this code is identical to spawnfunc_dom_team
523 setmodel(self, self.model); // precision not needed
524 self.mdl = self.model;
525 self.dmg = self.modelindex;
528 // this would have to be changed if used in quakeworld
529 self.team = self.cnt + 1;
535 void dom_spawnpoint(vector org)
537 local entity oldself;
540 self.classname = "dom_controlpoint";
541 self.think = spawnfunc_dom_controlpoint;
542 self.nextthink = time;
544 spawnfunc_dom_controlpoint();
548 // spawn some default teams if the map is not set up for domination
549 void dom_spawnteams()
553 numteams = cvar("g_domination_default_teams");
554 // LordHavoc: edit this if you want to change defaults
555 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
556 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
558 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
560 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
561 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
564 void dom_delayedinit()
568 self.think = SUB_Remove;
569 self.nextthink = time;
570 // if no teams are found, spawn defaults
571 if (find(world, classname, "dom_team") == world)
573 // if no control points are found, spawn defaults
574 if (find(world, classname, "dom_controlpoint") == world)
576 // here follow default domination points for each map
578 if (world.model == "maps/e1m1.bsp")
580 dom_spawnpoint('0 0 0');
585 // if no supported map was found, make every deathmatch spawn a point
586 head = find(world, classname, "info_player_deathmatch");
589 dom_spawnpoint(head.origin);
590 head = find(head, classname, "info_player_deathmatch");
599 // we have to precache default models/sounds even if they might not be
600 // used because spawnfunc_worldspawn is executed before any other entities are read,
601 // so we don't even know yet if this map is set up for domination...
602 precache_model("models/domination/dom_red.md3");
603 precache_model("models/domination/dom_blue.md3");
604 precache_model("models/domination/dom_yellow.md3");
605 precache_model("models/domination/dom_pink.md3");
606 precache_model("models/domination/dom_unclaimed.md3");
607 precache_sound("domination/claim.wav");
609 e.think = dom_delayedinit;
610 e.nextthink = time + 0.1;
612 // teamplay is always on in domination, defaults to hurt self but not teammates
613 //if(!cvar("teamplay"))
614 // cvar_set("teamplay", "3");