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);
72 sound(self.enemy, CHAN_AUTO, head.noise, VOL_BASE, ATTN_NORM);
74 sound(self, CHAN_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
75 if (head.noise1 != "")
76 play2all(head.noise1);
78 //self.nextthink = time + cvar("g_domination_point_rate");
79 //self.think = dompointthink;
81 if(cvar("g_domination_point_rate"))
82 self.delay = time + cvar("g_domination_point_rate");
84 self.delay = time + self.wait;
87 old_delay = self.delay;
89 self.team = real_team;
93 self.delay = old_delay;
97 void AnimateDomPoint()
99 if(self.pain_finished > time)
101 self.pain_finished = time + self.t_width;
102 if(self.nextthink > self.pain_finished)
103 self.nextthink = self.pain_finished;
105 self.frame = self.frame + 1;
106 if(self.frame > self.t_length)
113 local float waittime;
116 self.nextthink = time + 0.1;
118 //self.frame = self.frame + 1;
119 //if(self.frame > 119)
125 if (gameover || self.delay > time || time < restart_countdown) // game has ended, don't keep giving points
128 waittime = cvar("g_domination_point_rate");
130 waittime = self.wait;
131 self.delay = time + waittime;
133 // give credit to the team
134 // NOTE: this defaults to 0
135 if (self.goalentity.netname != "")
137 fragamt = cvar("g_domination_point_amt");
139 fragamt = self.DOMPOINTFRAGS;
140 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
141 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
143 // give credit to the individual player, if he is still there
144 if (self.enemy.playerid == self.enemy_playerid)
146 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
147 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
157 if (other.classname != "player")
159 if (other.health < 1)
162 // only valid teams can claim it
163 head = find(world, classname, "dom_team");
164 while (head && head.team != other.team)
165 head = find(head, classname, "dom_team");
166 if (!head || head.netname == "" || head == self.goalentity)
171 self.team = self.goalentity.team; // this stores the PREVIOUS team!
173 self.cnt = other.team;
174 self.aiment = head; // team to switch to after the delay
175 self.dmg_inflictor = other;
178 // self.delay = time + cvar("g_domination_point_capturetime");
179 //self.nextthink = time + cvar("g_domination_point_capturetime");
180 //self.think = dompoint_captured;
182 // go to neutral team in the mean time
183 head = find(world, classname, "dom_team");
184 while (head && head.netname != "")
185 head = find(head, classname, "dom_team");
189 self.goalentity = head;
190 self.model = head.mdl;
191 self.modelindex = head.dmg;
192 self.skin = head.skin;
194 self.enemy = other; // individual player scoring
195 self.enemy_playerid = other.playerid;
199 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
200 Team declaration for Domination gameplay, this allows you to decide what team
201 names and control point models are used in your map.
203 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
204 can have netname set! The nameless team owns all control points at start.
208 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
210 Scoreboard color of the team (for example 4 is red and 13 is blue)
212 Model to use for control points owned by this team (for example
213 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
216 Skin of the model to use (for team skins on a single model)
218 Sound to play when this team captures a point.
219 (this is a localized sound, like a small alarm or other effect)
221 Narrator speech to play when this team captures a point.
222 (this is a global sound, like "Red team has captured a control point")
225 void spawnfunc_dom_team()
232 precache_model(self.model);
233 if (self.noise != "")
234 precache_sound(self.noise);
235 if (self.noise1 != "")
236 precache_sound(self.noise1);
237 self.classname = "dom_team";
238 setmodel(self, self.model); // precision not needed
239 self.mdl = self.model;
240 self.dmg = self.modelindex;
243 // this would have to be changed if used in quakeworld
245 self.team = self.cnt + 1; // WHY are these different anyway?
248 void dom_controlpoint_setup()
251 // find the spawnfunc_dom_team representing unclaimed points
252 head = find(world, classname, "dom_team");
253 while(head && head.netname != "")
254 head = find(head, classname, "dom_team");
256 objerror("no spawnfunc_dom_team with netname \"\" found\n");
258 // copy important properties from spawnfunc_dom_team entity
259 self.goalentity = head;
260 setmodel(self, head.mdl); // precision already set
261 self.skin = head.skin;
266 self.message = " has captured a control point";
268 if(!self.DOMPOINTFRAGS)
269 self.DOMPOINTFRAGS = 1;
274 self.t_width = 0.1; // frame animation rate
276 self.t_length = 119; // maximum frame
278 self.think = dompointthink;
279 self.nextthink = time;
280 self.touch = dompointtouch;
281 self.solid = SOLID_TRIGGER;
282 self.flags = FL_ITEM;
283 setsize(self, '-32 -32 -32', '32 32 32');
284 setorigin(self, self.origin + '0 0 20');
287 waypoint_spawnforitem(self);
292 // player has joined game, get him on a team
294 /*void dom_player_join_team(entity pl)
297 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
298 float balance_teams, force_balance, balance_type;
300 balance_teams = cvar("g_balance_teams");
301 balance_teams = cvar("g_balance_teams_force");
303 c1 = c2 = c3 = c4 = -1;
306 // first find out what teams are allowed
307 head = find(world, classname, "dom_team");
310 if(head.netname != "")
312 //if(head.team == pl.team)
314 if(head.team == COLOR_TEAM1)
318 if(head.team == COLOR_TEAM2)
322 if(head.team == COLOR_TEAM3)
326 if(head.team == COLOR_TEAM4)
331 head = find(head, classname, "dom_team");
334 // make sure there are at least 2 teams to join
336 totalteams = totalteams + 1;
338 totalteams = totalteams + 1;
340 totalteams = totalteams + 1;
342 totalteams = totalteams + 1;
345 error("dom_player_join_team: Too few teams available for domination\n");
347 // whichever teams that are available are set to 0 instead of -1
349 // if we don't care what team he ends up on, put him on whatever team he entered as.
350 // if he's not on a valid team, then put him on the smallest team
351 if(!balance_teams && !force_balance)
353 if( c1 >= 0 && pl.team == COLOR_TEAM1)
354 selectedteam = pl.team;
355 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
356 selectedteam = pl.team;
357 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
358 selectedteam = pl.team;
359 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
360 selectedteam = pl.team;
365 SetPlayerColors(pl, selectedteam - 1);
368 // otherwise end up on the smallest team (handled below)
371 // now count how many players are on each team already
373 head = find(world, classname, "player");
376 //if(head.netname != "")
378 if(head.team == COLOR_TEAM1)
383 if(head.team == COLOR_TEAM2)
388 if(head.team == COLOR_TEAM3)
393 if(head.team == COLOR_TEAM4)
399 head = find(head, classname, "player");
402 // c1...c4 now have counts of each team
403 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
406 smallestteam_count = 999;
408 // 2 gives priority to what team you're already on, 1 goes in order
411 if(balance_type == 1)
413 if(c1 >= 0 && c1 < smallestteam_count)
416 smallestteam_count = c1;
418 if(c2 >= 0 && c2 < smallestteam_count)
421 smallestteam_count = c2;
423 if(c3 >= 0 && c3 < smallestteam_count)
426 smallestteam_count = c3;
428 if(c4 >= 0 && c4 < smallestteam_count)
431 smallestteam_count = c4;
436 if(c1 >= 0 && (c1 < smallestteam_count ||
437 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
440 smallestteam_count = c1;
442 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
443 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
446 smallestteam_count = c2;
448 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
449 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
452 smallestteam_count = c3;
454 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
455 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
458 smallestteam_count = c4;
462 if(smallestteam == 1)
464 selectedteam = COLOR_TEAM1 - 1;
466 if(smallestteam == 2)
468 selectedteam = COLOR_TEAM2 - 1;
470 if(smallestteam == 3)
472 selectedteam = COLOR_TEAM3 - 1;
474 if(smallestteam == 4)
476 selectedteam = COLOR_TEAM4 - 1;
479 SetPlayerColors(pl, selectedteam);
482 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
483 Control point for Domination gameplay.
485 void spawnfunc_dom_controlpoint()
492 self.think = dom_controlpoint_setup;
493 self.nextthink = time + 0.1;
498 //if(!self.glow_size)
499 // self.glow_size = cvar("g_domination_point_glow");
500 self.effects = self.effects | EF_FULLBRIGHT | EF_LOWPRECISION;
503 // code from here on is just to support maps that don't have control point and team entities
504 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
506 local entity oldself;
509 self.classname = "dom_team";
510 self.netname = teamname;
511 self.cnt = teamcolor;
512 self.model = pointmodel;
513 self.skin = pointskin;
514 self.noise = capsound;
515 self.noise1 = capnarration;
516 self.message = capmessage;
518 // this code is identical to spawnfunc_dom_team
519 setmodel(self, self.model); // precision not needed
520 self.mdl = self.model;
521 self.dmg = self.modelindex;
524 // this would have to be changed if used in quakeworld
525 self.team = self.cnt + 1;
531 void dom_spawnpoint(vector org)
533 local entity oldself;
536 self.classname = "dom_controlpoint";
537 self.think = spawnfunc_dom_controlpoint;
538 self.nextthink = time;
540 spawnfunc_dom_controlpoint();
544 // spawn some default teams if the map is not set up for domination
545 void dom_spawnteams()
549 numteams = cvar("g_domination_default_teams");
550 // LordHavoc: edit this if you want to change defaults
551 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
552 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
554 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
556 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
557 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
560 void dom_delayedinit()
564 self.think = SUB_Remove;
565 self.nextthink = time;
566 // if no teams are found, spawn defaults
567 if (find(world, classname, "dom_team") == world)
569 // if no control points are found, spawn defaults
570 if (find(world, classname, "dom_controlpoint") == world)
572 // here follow default domination points for each map
574 if (world.model == "maps/e1m1.bsp")
576 dom_spawnpoint('0 0 0');
581 // if no supported map was found, make every deathmatch spawn a point
582 head = find(world, classname, "info_player_deathmatch");
585 dom_spawnpoint(head.origin);
586 head = find(head, classname, "info_player_deathmatch");
597 // we have to precache default models/sounds even if they might not be
598 // used because spawnfunc_worldspawn is executed before any other entities are read,
599 // so we don't even know yet if this map is set up for domination...
600 precache_model("models/domination/dom_red.md3");
601 precache_model("models/domination/dom_blue.md3");
602 precache_model("models/domination/dom_yellow.md3");
603 precache_model("models/domination/dom_pink.md3");
604 precache_model("models/domination/dom_unclaimed.md3");
605 precache_sound("domination/claim.wav");
607 e.think = dom_delayedinit;
608 e.nextthink = time + 0.1;
610 // teamplay is always on in domination, defaults to hurt self but not teammates
611 //if(!cvar("teamplay"))
612 // cvar_set("teamplay", "3");