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 void() dom_controlpoint_setup;
29 void LogDom(string mode, float team_before, entity actor)
32 if(!cvar("sv_eventlog"))
34 s = strcat(":dom:", mode);
35 s = strcat(s, ":", ftos(team_before));
36 s = strcat(s, ":", ftos(actor.playerid));
40 void() dom_spawnteams;
42 void dompoint_captured ()
45 local float old_delay, old_team, real_team;
47 // now that the delay has expired, switch to the latest team to lay claim to this point
53 LogDom("taken", self.team, self.dmg_inflictor);
54 self.dmg_inflictor = world;
56 self.goalentity = head;
57 self.model = head.mdl;
58 self.modelindex = head.dmg;
59 self.skin = head.skin;
61 //bprint(head.message);
64 //bprint(^3head.netname);
65 //bprint(head.netname);
66 //bprint(self.message);
69 float points, wait_time;
70 if (g_domination_point_amt)
71 points = g_domination_point_amt;
74 if (g_domination_point_rate)
75 wait_time = g_domination_point_rate;
77 wait_time = self.wait;
79 bprint("^3", head.netname, "^3", self.message);
81 bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
83 bprint(" ^7(", ftos(points), " point every ", ftos(wait_time), " seconds)\n");
85 if(self.enemy.playerid == self.enemy_playerid)
86 PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
92 sound(self.enemy, CHAN_AUTO, head.noise, VOL_BASE, ATTN_NORM);
94 sound(self, CHAN_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
95 if (head.noise1 != "")
96 play2all(head.noise1);
98 //self.nextthink = time + cvar("g_domination_point_rate");
99 //self.think = dompointthink;
101 self.delay = time + wait_time;
104 old_delay = self.delay;
105 old_team = self.team;
106 self.team = real_team;
110 self.delay = old_delay;
111 self.team = old_team;
113 switch(self.goalentity.team)
116 WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
119 WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
122 WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
125 WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
128 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
129 WaypointSprite_Ping(self.sprite);
134 void AnimateDomPoint()
136 if(self.pain_finished > time)
138 self.pain_finished = time + self.t_width;
139 if(self.nextthink > self.pain_finished)
140 self.nextthink = self.pain_finished;
142 self.frame = self.frame + 1;
143 if(self.frame > self.t_length)
151 self.nextthink = time + 0.1;
153 //self.frame = self.frame + 1;
154 //if(self.frame > 119)
160 if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
163 g_domination_point_rate = cvar("g_domination_point_rate");
164 g_domination_point_amt = cvar("g_domination_point_amt");
166 if(g_domination_point_rate)
167 self.delay = time + g_domination_point_rate;
169 self.delay = time + self.wait;
171 // give credit to the team
172 // NOTE: this defaults to 0
173 if (self.goalentity.netname != "")
175 if(g_domination_point_amt)
176 fragamt = g_domination_point_amt;
178 fragamt = self.DOMPOINTFRAGS;
179 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
180 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
182 // give credit to the individual player, if he is still there
183 if (self.enemy.playerid == self.enemy_playerid)
185 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
186 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
196 if (other.classname != "player")
198 if (other.health < 1)
201 if(time < self.captime + 0.3)
204 // only valid teams can claim it
205 head = find(world, classname, "dom_team");
206 while (head && head.team != other.team)
207 head = find(head, classname, "dom_team");
208 if (!head || head.netname == "" || head == self.goalentity)
213 self.team = self.goalentity.team; // this stores the PREVIOUS team!
215 self.cnt = other.team;
216 self.owner = head; // team to switch to after the delay
217 self.dmg_inflictor = other;
220 // self.delay = time + cvar("g_domination_point_capturetime");
221 //self.nextthink = time + cvar("g_domination_point_capturetime");
222 //self.think = dompoint_captured;
224 // go to neutral team in the mean time
225 head = find(world, classname, "dom_team");
226 while (head && head.netname != "")
227 head = find(head, classname, "dom_team");
231 WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
232 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
233 WaypointSprite_Ping(self.sprite);
235 self.goalentity = head;
236 self.model = head.mdl;
237 self.modelindex = head.dmg;
238 self.skin = head.skin;
240 self.enemy = other; // individual player scoring
241 self.enemy_playerid = other.playerid;
245 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
246 Team declaration for Domination gameplay, this allows you to decide what team
247 names and control point models are used in your map.
249 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
250 can have netname set! The nameless team owns all control points at start.
254 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
256 Scoreboard color of the team (for example 4 is red and 13 is blue)
258 Model to use for control points owned by this team (for example
259 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
262 Skin of the model to use (for team skins on a single model)
264 Sound to play when this team captures a point.
265 (this is a localized sound, like a small alarm or other effect)
267 Narrator speech to play when this team captures a point.
268 (this is a global sound, like "Red team has captured a control point")
271 void spawnfunc_dom_team()
273 if(!g_domination || cvar("g_domination_teams_override") >= 2)
278 precache_model(self.model);
279 if (self.noise != "")
280 precache_sound(self.noise);
281 if (self.noise1 != "")
282 precache_sound(self.noise1);
283 self.classname = "dom_team";
284 setmodel(self, self.model); // precision not needed
285 self.mdl = self.model;
286 self.dmg = self.modelindex;
289 // this would have to be changed if used in quakeworld
291 self.team = self.cnt + 1; // WHY are these different anyway?
294 void dom_controlpoint_setup()
297 // find the spawnfunc_dom_team representing unclaimed points
298 head = find(world, classname, "dom_team");
299 while(head && head.netname != "")
300 head = find(head, classname, "dom_team");
302 objerror("no spawnfunc_dom_team with netname \"\" found\n");
304 // copy important properties from spawnfunc_dom_team entity
305 self.goalentity = head;
306 setmodel(self, head.mdl); // precision already set
307 self.skin = head.skin;
312 self.message = " has captured a control point";
314 if(!self.DOMPOINTFRAGS)
315 self.DOMPOINTFRAGS = 1;
320 self.t_width = 0.02; // frame animation rate
322 self.t_length = 239; // maximum frame
324 self.think = dompointthink;
325 self.nextthink = time;
326 self.touch = dompointtouch;
327 self.solid = SOLID_TRIGGER;
328 self.flags = FL_ITEM;
329 setsize(self, '-32 -32 -32', '32 32 32');
330 setorigin(self, self.origin + '0 0 20');
333 waypoint_spawnforitem(self);
334 WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite);
335 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
340 // player has joined game, get him on a team
342 /*void dom_player_join_team(entity pl)
345 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
346 float balance_teams, force_balance, balance_type;
348 balance_teams = cvar("g_balance_teams");
349 balance_teams = cvar("g_balance_teams_force");
351 c1 = c2 = c3 = c4 = -1;
354 // first find out what teams are allowed
355 head = find(world, classname, "dom_team");
358 if(head.netname != "")
360 //if(head.team == pl.team)
362 if(head.team == COLOR_TEAM1)
366 if(head.team == COLOR_TEAM2)
370 if(head.team == COLOR_TEAM3)
374 if(head.team == COLOR_TEAM4)
379 head = find(head, classname, "dom_team");
382 // make sure there are at least 2 teams to join
384 totalteams = totalteams + 1;
386 totalteams = totalteams + 1;
388 totalteams = totalteams + 1;
390 totalteams = totalteams + 1;
393 error("dom_player_join_team: Too few teams available for domination\n");
395 // whichever teams that are available are set to 0 instead of -1
397 // if we don't care what team he ends up on, put him on whatever team he entered as.
398 // if he's not on a valid team, then put him on the smallest team
399 if(!balance_teams && !force_balance)
401 if( c1 >= 0 && pl.team == COLOR_TEAM1)
402 selectedteam = pl.team;
403 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
404 selectedteam = pl.team;
405 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
406 selectedteam = pl.team;
407 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
408 selectedteam = pl.team;
413 SetPlayerColors(pl, selectedteam - 1);
416 // otherwise end up on the smallest team (handled below)
419 // now count how many players are on each team already
421 head = find(world, classname, "player");
424 //if(head.netname != "")
426 if(head.team == COLOR_TEAM1)
431 if(head.team == COLOR_TEAM2)
436 if(head.team == COLOR_TEAM3)
441 if(head.team == COLOR_TEAM4)
447 head = find(head, classname, "player");
450 // c1...c4 now have counts of each team
451 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
454 smallestteam_count = 999;
456 // 2 gives priority to what team you're already on, 1 goes in order
459 if(balance_type == 1)
461 if(c1 >= 0 && c1 < smallestteam_count)
464 smallestteam_count = c1;
466 if(c2 >= 0 && c2 < smallestteam_count)
469 smallestteam_count = c2;
471 if(c3 >= 0 && c3 < smallestteam_count)
474 smallestteam_count = c3;
476 if(c4 >= 0 && c4 < smallestteam_count)
479 smallestteam_count = c4;
484 if(c1 >= 0 && (c1 < smallestteam_count ||
485 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
488 smallestteam_count = c1;
490 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
491 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
494 smallestteam_count = c2;
496 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
497 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
500 smallestteam_count = c3;
502 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
503 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
506 smallestteam_count = c4;
510 if(smallestteam == 1)
512 selectedteam = COLOR_TEAM1 - 1;
514 if(smallestteam == 2)
516 selectedteam = COLOR_TEAM2 - 1;
518 if(smallestteam == 3)
520 selectedteam = COLOR_TEAM3 - 1;
522 if(smallestteam == 4)
524 selectedteam = COLOR_TEAM4 - 1;
527 SetPlayerColors(pl, selectedteam);
530 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
531 Control point for Domination gameplay.
533 void spawnfunc_dom_controlpoint()
540 self.think = dom_controlpoint_setup;
541 self.nextthink = time + 0.1;
542 self.reset = dom_controlpoint_setup;
547 //if(!self.glow_size)
548 // self.glow_size = cvar("g_domination_point_glow");
549 self.effects = self.effects | EF_LOWPRECISION;
550 if (cvar("g_domination_point_fullbright"))
551 self.effects |= EF_FULLBRIGHT;
554 // code from here on is just to support maps that don't have control point and team entities
555 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
557 local entity oldself;
560 self.classname = "dom_team";
561 self.netname = teamname;
562 self.cnt = teamcolor;
563 self.model = pointmodel;
564 self.skin = pointskin;
565 self.noise = capsound;
566 self.noise1 = capnarration;
567 self.message = capmessage;
569 // this code is identical to spawnfunc_dom_team
570 setmodel(self, self.model); // precision not needed
571 self.mdl = self.model;
572 self.dmg = self.modelindex;
575 // this would have to be changed if used in quakeworld
576 self.team = self.cnt + 1;
582 void dom_spawnpoint(vector org)
584 local entity oldself;
587 self.classname = "dom_controlpoint";
588 self.think = spawnfunc_dom_controlpoint;
589 self.nextthink = time;
590 setorigin(self, org);
591 spawnfunc_dom_controlpoint();
595 // spawn some default teams if the map is not set up for domination
596 void dom_spawnteams()
599 if(cvar("g_domination_teams_override") < 2)
600 numteams = cvar("g_domination_default_teams");
602 numteams = cvar("g_domination_teams_override");
603 // LordHavoc: edit this if you want to change defaults
604 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
605 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
607 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
609 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
610 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
613 void dom_delayedinit()
617 // if no teams are found, spawn defaults, if custom teams are set, use them
618 if (find(world, classname, "dom_team") == world || cvar("g_domination_teams_override") >= 2)
620 // if no control points are found, spawn defaults
621 if (find(world, classname, "dom_controlpoint") == world)
623 // here follow default domination points for each map
625 if (world.model == "maps/e1m1.bsp")
627 dom_spawnpoint('0 0 0');
632 // if no supported map was found, make every deathmatch spawn a point
633 head = find(world, classname, "info_player_deathmatch");
636 dom_spawnpoint(head.origin);
637 head = find(head, classname, "info_player_deathmatch");
647 // we have to precache default models/sounds even if they might not be
648 // used because spawnfunc_worldspawn is executed before any other entities are read,
649 // so we don't even know yet if this map is set up for domination...
650 precache_model("models/domination/dom_red.md3");
651 precache_model("models/domination/dom_blue.md3");
652 precache_model("models/domination/dom_yellow.md3");
653 precache_model("models/domination/dom_pink.md3");
654 precache_model("models/domination/dom_unclaimed.md3");
655 precache_sound("domination/claim.wav");
656 InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);
658 // teamplay is always on in domination, defaults to hurt self but not teammates
660 // cvar_set("teamplay", "3");