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));
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)
112 local float waittime;
115 self.nextthink = time + 0.1;
117 //self.frame = self.frame + 1;
118 //if(self.frame > 119)
124 if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
127 waittime = cvar("g_domination_point_rate");
129 waittime = self.wait;
130 self.delay = time + waittime;
132 // give credit to the team
133 // NOTE: this defaults to 0
134 if (self.goalentity.netname != "")
136 fragamt = cvar("g_domination_point_amt");
138 fragamt = self.DOMPOINTFRAGS;
139 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
140 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
142 // give credit to the individual player, if he is still there
143 if (self.enemy.playerid == self.enemy_playerid)
145 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
146 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
156 if (other.classname != "player")
158 if (other.health < 1)
161 // only valid teams can claim it
162 head = find(world, classname, "dom_team");
163 while (head && head.team != other.team)
164 head = find(head, classname, "dom_team");
165 if (!head || head.netname == "" || head == self.goalentity)
170 self.team = self.goalentity.team; // this stores the PREVIOUS team!
172 self.cnt = other.team;
173 self.owner = head; // team to switch to after the delay
174 self.dmg_inflictor = other;
177 // self.delay = time + cvar("g_domination_point_capturetime");
178 //self.nextthink = time + cvar("g_domination_point_capturetime");
179 //self.think = dompoint_captured;
181 // go to neutral team in the mean time
182 head = find(world, classname, "dom_team");
183 while (head && head.netname != "")
184 head = find(head, classname, "dom_team");
188 self.goalentity = head;
189 self.model = head.mdl;
190 self.modelindex = head.dmg;
191 self.skin = head.skin;
193 self.enemy = other; // individual player scoring
194 self.enemy_playerid = other.playerid;
198 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
199 Team declaration for Domination gameplay, this allows you to decide what team
200 names and control point models are used in your map.
202 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
203 can have netname set! The nameless team owns all control points at start.
207 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
209 Scoreboard color of the team (for example 4 is red and 13 is blue)
211 Model to use for control points owned by this team (for example
212 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
215 Skin of the model to use (for team skins on a single model)
217 Sound to play when this team captures a point.
218 (this is a localized sound, like a small alarm or other effect)
220 Narrator speech to play when this team captures a point.
221 (this is a global sound, like "Red team has captured a control point")
224 void spawnfunc_dom_team()
231 precache_model(self.model);
232 if (self.noise != "")
233 precache_sound(self.noise);
234 if (self.noise1 != "")
235 precache_sound(self.noise1);
236 self.classname = "dom_team";
237 setmodel(self, self.model); // precision not needed
238 self.mdl = self.model;
239 self.dmg = self.modelindex;
242 // this would have to be changed if used in quakeworld
244 self.team = self.cnt + 1; // WHY are these different anyway?
247 void dom_controlpoint_setup()
250 // find the spawnfunc_dom_team representing unclaimed points
251 head = find(world, classname, "dom_team");
252 while(head && head.netname != "")
253 head = find(head, classname, "dom_team");
255 objerror("no spawnfunc_dom_team with netname \"\" found\n");
257 // copy important properties from spawnfunc_dom_team entity
258 self.goalentity = head;
259 setmodel(self, head.mdl); // precision already set
260 self.skin = head.skin;
265 self.message = " has captured a control point";
267 if(!self.DOMPOINTFRAGS)
268 self.DOMPOINTFRAGS = 1;
273 self.t_width = 0.02; // frame animation rate
275 self.t_length = 239; // maximum frame
277 self.think = dompointthink;
278 self.nextthink = time;
279 self.touch = dompointtouch;
280 self.solid = SOLID_TRIGGER;
281 self.flags = FL_ITEM;
282 setsize(self, '-32 -32 -32', '32 32 32');
283 setorigin(self, self.origin + '0 0 20');
286 waypoint_spawnforitem(self);
291 // player has joined game, get him on a team
293 /*void dom_player_join_team(entity pl)
296 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
297 float balance_teams, force_balance, balance_type;
299 balance_teams = cvar("g_balance_teams");
300 balance_teams = cvar("g_balance_teams_force");
302 c1 = c2 = c3 = c4 = -1;
305 // first find out what teams are allowed
306 head = find(world, classname, "dom_team");
309 if(head.netname != "")
311 //if(head.team == pl.team)
313 if(head.team == COLOR_TEAM1)
317 if(head.team == COLOR_TEAM2)
321 if(head.team == COLOR_TEAM3)
325 if(head.team == COLOR_TEAM4)
330 head = find(head, classname, "dom_team");
333 // make sure there are at least 2 teams to join
335 totalteams = totalteams + 1;
337 totalteams = totalteams + 1;
339 totalteams = totalteams + 1;
341 totalteams = totalteams + 1;
344 error("dom_player_join_team: Too few teams available for domination\n");
346 // whichever teams that are available are set to 0 instead of -1
348 // if we don't care what team he ends up on, put him on whatever team he entered as.
349 // if he's not on a valid team, then put him on the smallest team
350 if(!balance_teams && !force_balance)
352 if( c1 >= 0 && pl.team == COLOR_TEAM1)
353 selectedteam = pl.team;
354 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
355 selectedteam = pl.team;
356 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
357 selectedteam = pl.team;
358 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
359 selectedteam = pl.team;
364 SetPlayerColors(pl, selectedteam - 1);
367 // otherwise end up on the smallest team (handled below)
370 // now count how many players are on each team already
372 head = find(world, classname, "player");
375 //if(head.netname != "")
377 if(head.team == COLOR_TEAM1)
382 if(head.team == COLOR_TEAM2)
387 if(head.team == COLOR_TEAM3)
392 if(head.team == COLOR_TEAM4)
398 head = find(head, classname, "player");
401 // c1...c4 now have counts of each team
402 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
405 smallestteam_count = 999;
407 // 2 gives priority to what team you're already on, 1 goes in order
410 if(balance_type == 1)
412 if(c1 >= 0 && c1 < smallestteam_count)
415 smallestteam_count = c1;
417 if(c2 >= 0 && c2 < smallestteam_count)
420 smallestteam_count = c2;
422 if(c3 >= 0 && c3 < smallestteam_count)
425 smallestteam_count = c3;
427 if(c4 >= 0 && c4 < smallestteam_count)
430 smallestteam_count = c4;
435 if(c1 >= 0 && (c1 < smallestteam_count ||
436 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
439 smallestteam_count = c1;
441 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
442 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
445 smallestteam_count = c2;
447 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
448 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
451 smallestteam_count = c3;
453 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
454 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
457 smallestteam_count = c4;
461 if(smallestteam == 1)
463 selectedteam = COLOR_TEAM1 - 1;
465 if(smallestteam == 2)
467 selectedteam = COLOR_TEAM2 - 1;
469 if(smallestteam == 3)
471 selectedteam = COLOR_TEAM3 - 1;
473 if(smallestteam == 4)
475 selectedteam = COLOR_TEAM4 - 1;
478 SetPlayerColors(pl, selectedteam);
481 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
482 Control point for Domination gameplay.
484 void spawnfunc_dom_controlpoint()
491 self.think = dom_controlpoint_setup;
492 self.nextthink = time + 0.1;
493 self.reset = dom_controlpoint_setup;
498 //if(!self.glow_size)
499 // self.glow_size = cvar("g_domination_point_glow");
500 self.effects = self.effects | EF_LOWPRECISION;
501 if (cvar("g_domination_point_fullbright"))
502 self.effects |= EF_FULLBRIGHT;
505 // code from here on is just to support maps that don't have control point and team entities
506 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
508 local entity oldself;
511 self.classname = "dom_team";
512 self.netname = teamname;
513 self.cnt = teamcolor;
514 self.model = pointmodel;
515 self.skin = pointskin;
516 self.noise = capsound;
517 self.noise1 = capnarration;
518 self.message = capmessage;
520 // this code is identical to spawnfunc_dom_team
521 setmodel(self, self.model); // precision not needed
522 self.mdl = self.model;
523 self.dmg = self.modelindex;
526 // this would have to be changed if used in quakeworld
527 self.team = self.cnt + 1;
533 void dom_spawnpoint(vector org)
535 local entity oldself;
538 self.classname = "dom_controlpoint";
539 self.think = spawnfunc_dom_controlpoint;
540 self.nextthink = time;
542 spawnfunc_dom_controlpoint();
546 // spawn some default teams if the map is not set up for domination
547 void dom_spawnteams()
551 numteams = cvar("g_domination_default_teams");
552 // LordHavoc: edit this if you want to change defaults
553 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
554 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
556 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
558 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
559 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
562 void dom_delayedinit()
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");
596 // we have to precache default models/sounds even if they might not be
597 // used because spawnfunc_worldspawn is executed before any other entities are read,
598 // so we don't even know yet if this map is set up for domination...
599 precache_model("models/domination/dom_red.md3");
600 precache_model("models/domination/dom_blue.md3");
601 precache_model("models/domination/dom_yellow.md3");
602 precache_model("models/domination/dom_pink.md3");
603 precache_model("models/domination/dom_unclaimed.md3");
604 precache_sound("domination/claim.wav");
605 InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);
607 // teamplay is always on in domination, defaults to hurt self but not teammates
608 //if(!cvar("teamplay"))
609 // cvar_set("teamplay", "3");