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 g_domination_point_amt;
21 float g_domination_point_rate;
23 .float enemy_playerid;
27 // pps: points per second
31 .float dom_pps_yellow;
38 void set_dom_state(entity e, float connecting)
40 if(connecting) e.dom_total_pps = total_pps;
41 e.dom_pps_red = pps_red;
42 e.dom_pps_blue = pps_blue;
43 if(c3 >= 0) e.dom_pps_yellow = pps_yellow;
44 if(c4 >= 0) e.dom_pps_pink = pps_pink;
47 void() dom_controlpoint_setup;
49 void LogDom(string mode, float team_before, entity actor)
52 if(!cvar("sv_eventlog"))
54 s = strcat(":dom:", mode);
55 s = strcat(s, ":", ftos(team_before));
56 s = strcat(s, ":", ftos(actor.playerid));
60 void() dom_spawnteams;
62 void dompoint_captured ()
65 local float old_delay, old_team, real_team;
67 // now that the delay has expired, switch to the latest team to lay claim to this point
73 LogDom("taken", self.team, self.dmg_inflictor);
74 self.dmg_inflictor = world;
76 self.goalentity = head;
77 self.model = head.mdl;
78 self.modelindex = head.dmg;
79 self.skin = head.skin;
81 //bprint(head.message);
84 //bprint(^3head.netname);
85 //bprint(head.netname);
86 //bprint(self.message);
89 float points, wait_time;
90 if (g_domination_point_amt)
91 points = g_domination_point_amt;
94 if (g_domination_point_rate)
95 wait_time = g_domination_point_rate;
97 wait_time = self.wait;
99 bprint("^3", head.netname, "^3", self.message);
100 bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
102 if(self.enemy.playerid == self.enemy_playerid)
103 PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
107 if (head.noise != "")
109 sound(self.enemy, CHAN_AUTO, head.noise, VOL_BASE, ATTN_NORM);
111 sound(self, CHAN_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
112 if (head.noise1 != "")
113 play2all(head.noise1);
115 //self.nextthink = time + cvar("g_domination_point_rate");
116 //self.think = dompointthink;
118 self.delay = time + wait_time;
121 old_delay = self.delay;
122 old_team = self.team;
123 self.team = real_team;
127 self.delay = old_delay;
128 self.team = old_team;
133 pps_red -= (points/wait_time);
136 pps_blue -= (points/wait_time);
139 pps_yellow -= (points/wait_time);
142 pps_pink -= (points/wait_time);
145 switch(self.goalentity.team)
148 pps_red += (points/wait_time);
149 WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
152 pps_blue += (points/wait_time);
153 WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
156 pps_yellow += (points/wait_time);
157 WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
160 pps_pink += (points/wait_time);
161 WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
164 FOR_EACH_CLIENT(head)
165 set_dom_state(head, FALSE);
167 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
168 WaypointSprite_Ping(self.sprite);
173 void AnimateDomPoint()
175 if(self.pain_finished > time)
177 self.pain_finished = time + self.t_width;
178 if(self.nextthink > self.pain_finished)
179 self.nextthink = self.pain_finished;
181 self.frame = self.frame + 1;
182 if(self.frame > self.t_length)
190 self.nextthink = time + 0.1;
192 //self.frame = self.frame + 1;
193 //if(self.frame > 119)
199 if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
202 if(g_domination_point_rate)
203 self.delay = time + g_domination_point_rate;
205 self.delay = time + self.wait;
207 // give credit to the team
208 // NOTE: this defaults to 0
209 if (self.goalentity.netname != "")
211 if(g_domination_point_amt)
212 fragamt = g_domination_point_amt;
214 fragamt = self.DOMPOINTFRAGS;
215 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
216 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
218 // give credit to the individual player, if he is still there
219 if (self.enemy.playerid == self.enemy_playerid)
221 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
222 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
232 if (other.classname != "player")
234 if (other.health < 1)
237 if(time < self.captime + 0.3)
240 // only valid teams can claim it
241 head = find(world, classname, "dom_team");
242 while (head && head.team != other.team)
243 head = find(head, classname, "dom_team");
244 if (!head || head.netname == "" || head == self.goalentity)
249 self.team = self.goalentity.team; // this stores the PREVIOUS team!
251 self.cnt = other.team;
252 self.owner = head; // team to switch to after the delay
253 self.dmg_inflictor = other;
256 // self.delay = time + cvar("g_domination_point_capturetime");
257 //self.nextthink = time + cvar("g_domination_point_capturetime");
258 //self.think = dompoint_captured;
260 // go to neutral team in the mean time
261 head = find(world, classname, "dom_team");
262 while (head && head.netname != "")
263 head = find(head, classname, "dom_team");
267 WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
268 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
269 WaypointSprite_Ping(self.sprite);
271 self.goalentity = head;
272 self.model = head.mdl;
273 self.modelindex = head.dmg;
274 self.skin = head.skin;
276 self.enemy = other; // individual player scoring
277 self.enemy_playerid = other.playerid;
281 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
282 Team declaration for Domination gameplay, this allows you to decide what team
283 names and control point models are used in your map.
285 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
286 can have netname set! The nameless team owns all control points at start.
290 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
292 Scoreboard color of the team (for example 4 is red and 13 is blue)
294 Model to use for control points owned by this team (for example
295 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
298 Skin of the model to use (for team skins on a single model)
300 Sound to play when this team captures a point.
301 (this is a localized sound, like a small alarm or other effect)
303 Narrator speech to play when this team captures a point.
304 (this is a global sound, like "Red team has captured a control point")
307 void spawnfunc_dom_team()
309 if(!g_domination || cvar("g_domination_teams_override") >= 2)
314 precache_model(self.model);
315 if (self.noise != "")
316 precache_sound(self.noise);
317 if (self.noise1 != "")
318 precache_sound(self.noise1);
319 self.classname = "dom_team";
320 setmodel(self, self.model); // precision not needed
321 self.mdl = self.model;
322 self.dmg = self.modelindex;
325 // this would have to be changed if used in quakeworld
327 self.team = self.cnt + 1; // WHY are these different anyway?
330 void dom_controlpoint_setup()
333 // find the spawnfunc_dom_team representing unclaimed points
334 head = find(world, classname, "dom_team");
335 while(head && head.netname != "")
336 head = find(head, classname, "dom_team");
338 objerror("no spawnfunc_dom_team with netname \"\" found\n");
340 // copy important properties from spawnfunc_dom_team entity
341 self.goalentity = head;
342 setmodel(self, head.mdl); // precision already set
343 self.skin = head.skin;
348 self.message = " has captured a control point";
350 if(!self.DOMPOINTFRAGS)
351 self.DOMPOINTFRAGS = 1;
356 self.t_width = 0.02; // frame animation rate
358 self.t_length = 239; // maximum frame
360 self.think = dompointthink;
361 self.nextthink = time;
362 self.touch = dompointtouch;
363 self.solid = SOLID_TRIGGER;
364 self.flags = FL_ITEM;
365 setsize(self, '-32 -32 -32', '32 32 32');
366 setorigin(self, self.origin + '0 0 20');
369 waypoint_spawnforitem(self);
370 WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite);
371 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
376 // player has joined game, get him on a team
378 /*void dom_player_join_team(entity pl)
381 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
382 float balance_teams, force_balance, balance_type;
384 balance_teams = cvar("g_balance_teams");
385 balance_teams = cvar("g_balance_teams_force");
387 c1 = c2 = c3 = c4 = -1;
390 // first find out what teams are allowed
391 head = find(world, classname, "dom_team");
394 if(head.netname != "")
396 //if(head.team == pl.team)
398 if(head.team == COLOR_TEAM1)
402 if(head.team == COLOR_TEAM2)
406 if(head.team == COLOR_TEAM3)
410 if(head.team == COLOR_TEAM4)
415 head = find(head, classname, "dom_team");
418 // make sure there are at least 2 teams to join
420 totalteams = totalteams + 1;
422 totalteams = totalteams + 1;
424 totalteams = totalteams + 1;
426 totalteams = totalteams + 1;
429 error("dom_player_join_team: Too few teams available for domination\n");
431 // whichever teams that are available are set to 0 instead of -1
433 // if we don't care what team he ends up on, put him on whatever team he entered as.
434 // if he's not on a valid team, then put him on the smallest team
435 if(!balance_teams && !force_balance)
437 if( c1 >= 0 && pl.team == COLOR_TEAM1)
438 selectedteam = pl.team;
439 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
440 selectedteam = pl.team;
441 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
442 selectedteam = pl.team;
443 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
444 selectedteam = pl.team;
449 SetPlayerColors(pl, selectedteam - 1);
452 // otherwise end up on the smallest team (handled below)
455 // now count how many players are on each team already
457 head = find(world, classname, "player");
460 //if(head.netname != "")
462 if(head.team == COLOR_TEAM1)
467 if(head.team == COLOR_TEAM2)
472 if(head.team == COLOR_TEAM3)
477 if(head.team == COLOR_TEAM4)
483 head = find(head, classname, "player");
486 // c1...c4 now have counts of each team
487 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
490 smallestteam_count = 999;
492 // 2 gives priority to what team you're already on, 1 goes in order
495 if(balance_type == 1)
497 if(c1 >= 0 && c1 < smallestteam_count)
500 smallestteam_count = c1;
502 if(c2 >= 0 && c2 < smallestteam_count)
505 smallestteam_count = c2;
507 if(c3 >= 0 && c3 < smallestteam_count)
510 smallestteam_count = c3;
512 if(c4 >= 0 && c4 < smallestteam_count)
515 smallestteam_count = c4;
520 if(c1 >= 0 && (c1 < smallestteam_count ||
521 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
524 smallestteam_count = c1;
526 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
527 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
530 smallestteam_count = c2;
532 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
533 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
536 smallestteam_count = c3;
538 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
539 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
542 smallestteam_count = c4;
546 if(smallestteam == 1)
548 selectedteam = COLOR_TEAM1 - 1;
550 if(smallestteam == 2)
552 selectedteam = COLOR_TEAM2 - 1;
554 if(smallestteam == 3)
556 selectedteam = COLOR_TEAM3 - 1;
558 if(smallestteam == 4)
560 selectedteam = COLOR_TEAM4 - 1;
563 SetPlayerColors(pl, selectedteam);
566 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
567 Control point for Domination gameplay.
569 void spawnfunc_dom_controlpoint()
576 self.think = dom_controlpoint_setup;
577 self.nextthink = time + 0.1;
578 self.reset = dom_controlpoint_setup;
583 //if(!self.glow_size)
584 // self.glow_size = cvar("g_domination_point_glow");
585 self.effects = self.effects | EF_LOWPRECISION;
586 if (cvar("g_domination_point_fullbright"))
587 self.effects |= EF_FULLBRIGHT;
589 float points, waittime;
590 if (g_domination_point_rate)
591 points += g_domination_point_rate;
593 points += self.frags;
594 if (g_domination_point_amt)
595 waittime += g_domination_point_amt;
597 waittime += self.wait;
598 total_pps += points/waittime;
601 // code from here on is just to support maps that don't have control point and team entities
602 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
604 local entity oldself;
607 self.classname = "dom_team";
608 self.netname = teamname;
609 self.cnt = teamcolor;
610 self.model = pointmodel;
611 self.skin = pointskin;
612 self.noise = capsound;
613 self.noise1 = capnarration;
614 self.message = capmessage;
616 // this code is identical to spawnfunc_dom_team
617 setmodel(self, self.model); // precision not needed
618 self.mdl = self.model;
619 self.dmg = self.modelindex;
622 // this would have to be changed if used in quakeworld
623 self.team = self.cnt + 1;
629 void dom_spawnpoint(vector org)
631 local entity oldself;
634 self.classname = "dom_controlpoint";
635 self.think = spawnfunc_dom_controlpoint;
636 self.nextthink = time;
637 setorigin(self, org);
638 spawnfunc_dom_controlpoint();
642 // spawn some default teams if the map is not set up for domination
643 void dom_spawnteams()
646 if(cvar("g_domination_teams_override") < 2)
647 numteams = cvar("g_domination_default_teams");
649 numteams = cvar("g_domination_teams_override");
650 // LordHavoc: edit this if you want to change defaults
651 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
652 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
654 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
656 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
657 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
660 void dom_delayedinit()
664 // if no teams are found, spawn defaults, if custom teams are set, use them
665 if (find(world, classname, "dom_team") == world || cvar("g_domination_teams_override") >= 2)
667 // if no control points are found, spawn defaults
668 if (find(world, classname, "dom_controlpoint") == world)
670 // here follow default domination points for each map
672 if (world.model == "maps/e1m1.bsp")
674 dom_spawnpoint('0 0 0');
679 // if no supported map was found, make every deathmatch spawn a point
680 head = find(world, classname, "info_player_deathmatch");
683 dom_spawnpoint(head.origin);
684 head = find(head, classname, "info_player_deathmatch");
694 // we have to precache default models/sounds even if they might not be
695 // used because spawnfunc_worldspawn is executed before any other entities are read,
696 // so we don't even know yet if this map is set up for domination...
697 precache_model("models/domination/dom_red.md3");
698 precache_model("models/domination/dom_blue.md3");
699 precache_model("models/domination/dom_yellow.md3");
700 precache_model("models/domination/dom_pink.md3");
701 precache_model("models/domination/dom_unclaimed.md3");
702 precache_sound("domination/claim.wav");
703 InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);
705 addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
706 addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
707 addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
708 if(c3 >= 0) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
709 if(c4 >= 0) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);
711 g_domination_point_rate = cvar("g_domination_point_rate");
712 g_domination_point_amt = cvar("g_domination_point_amt");
714 // teamplay is always on in domination, defaults to hurt self but not teammates
716 // cvar_set("teamplay", "3");