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;
24 void() dom_controlpoint_setup;
26 void LogDom(string mode, float team_before, entity actor)
29 if(!autocvar_sv_eventlog)
31 s = strcat(":dom:", mode);
32 s = strcat(s, ":", ftos(team_before));
33 s = strcat(s, ":", ftos(actor.playerid));
37 void() dom_spawnteams;
39 void dompoint_captured ()
42 local float old_delay, old_team, real_team;
44 // now that the delay has expired, switch to the latest team to lay claim to this point
50 LogDom("taken", self.team, self.dmg_inflictor);
51 self.dmg_inflictor = world;
53 self.goalentity = head;
54 self.model = head.mdl;
55 self.modelindex = head.dmg;
56 self.skin = head.skin;
58 //bprint(head.message);
61 //bprint(^3head.netname);
62 //bprint(head.netname);
63 //bprint(self.message);
66 float points, wait_time;
67 if (autocvar_g_domination_point_amt)
68 points = autocvar_g_domination_point_amt;
71 if (autocvar_g_domination_point_rate)
72 wait_time = autocvar_g_domination_point_rate;
74 wait_time = self.wait;
76 bprint("^3", head.netname, "^3", self.message);
78 bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
80 bprint(" ^7(", ftos(points), " point every ", ftos(wait_time), " seconds)\n");
82 if(self.enemy.playerid == self.enemy_playerid)
83 PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
89 sound(self.enemy, CHAN_AUTO, head.noise, VOL_BASE, ATTN_NORM);
91 sound(self, CHAN_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
92 if (head.noise1 != "")
93 play2all(head.noise1);
95 //self.nextthink = time + autocvar_g_domination_point_rate;
96 //self.think = dompointthink;
98 self.delay = time + wait_time;
101 old_delay = self.delay;
102 old_team = self.team;
103 self.team = real_team;
107 self.delay = old_delay;
108 self.team = old_team;
110 switch(self.goalentity.team)
113 WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
116 WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
119 WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
122 WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
125 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
126 WaypointSprite_Ping(self.sprite);
131 void AnimateDomPoint()
133 if(self.pain_finished > time)
135 self.pain_finished = time + self.t_width;
136 if(self.nextthink > self.pain_finished)
137 self.nextthink = self.pain_finished;
139 self.frame = self.frame + 1;
140 if(self.frame > self.t_length)
148 self.nextthink = time + 0.1;
150 //self.frame = self.frame + 1;
151 //if(self.frame > 119)
157 if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
160 if(autocvar_g_domination_point_rate)
161 self.delay = time + autocvar_g_domination_point_rate;
163 self.delay = time + self.wait;
165 // give credit to the team
166 // NOTE: this defaults to 0
167 if (self.goalentity.netname != "")
169 if(autocvar_g_domination_point_amt)
170 fragamt = autocvar_g_domination_point_amt;
172 fragamt = self.DOMPOINTFRAGS;
173 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
174 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
176 // give credit to the individual player, if he is still there
177 if (self.enemy.playerid == self.enemy_playerid)
179 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
180 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
190 if (other.classname != "player")
192 if (other.health < 1)
195 if(time < self.captime + 0.3)
198 // only valid teams can claim it
199 head = find(world, classname, "dom_team");
200 while (head && head.team != other.team)
201 head = find(head, classname, "dom_team");
202 if (!head || head.netname == "" || head == self.goalentity)
207 self.team = self.goalentity.team; // this stores the PREVIOUS team!
209 self.cnt = other.team;
210 self.owner = head; // team to switch to after the delay
211 self.dmg_inflictor = other;
214 // self.delay = time + cvar("g_domination_point_capturetime");
215 //self.nextthink = time + cvar("g_domination_point_capturetime");
216 //self.think = dompoint_captured;
218 // go to neutral team in the mean time
219 head = find(world, classname, "dom_team");
220 while (head && head.netname != "")
221 head = find(head, classname, "dom_team");
225 WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
226 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
227 WaypointSprite_Ping(self.sprite);
229 self.goalentity = head;
230 self.model = head.mdl;
231 self.modelindex = head.dmg;
232 self.skin = head.skin;
234 self.enemy = other; // individual player scoring
235 self.enemy_playerid = other.playerid;
239 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
240 Team declaration for Domination gameplay, this allows you to decide what team
241 names and control point models are used in your map.
243 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
244 can have netname set! The nameless team owns all control points at start.
248 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
250 Scoreboard color of the team (for example 4 is red and 13 is blue)
252 Model to use for control points owned by this team (for example
253 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
256 Skin of the model to use (for team skins on a single model)
258 Sound to play when this team captures a point.
259 (this is a localized sound, like a small alarm or other effect)
261 Narrator speech to play when this team captures a point.
262 (this is a global sound, like "Red team has captured a control point")
265 void spawnfunc_dom_team()
267 if(!g_domination || autocvar_g_domination_teams_override >= 2)
272 precache_model(self.model);
273 if (self.noise != "")
274 precache_sound(self.noise);
275 if (self.noise1 != "")
276 precache_sound(self.noise1);
277 self.classname = "dom_team";
278 setmodel(self, self.model); // precision not needed
279 self.mdl = self.model;
280 self.dmg = self.modelindex;
283 // this would have to be changed if used in quakeworld
285 self.team = self.cnt + 1; // WHY are these different anyway?
288 void dom_controlpoint_setup()
291 // find the spawnfunc_dom_team representing unclaimed points
292 head = find(world, classname, "dom_team");
293 while(head && head.netname != "")
294 head = find(head, classname, "dom_team");
296 objerror("no spawnfunc_dom_team with netname \"\" found\n");
298 // copy important properties from spawnfunc_dom_team entity
299 self.goalentity = head;
300 setmodel(self, head.mdl); // precision already set
301 self.skin = head.skin;
306 self.message = " has captured a control point";
308 if(!self.DOMPOINTFRAGS)
309 self.DOMPOINTFRAGS = 1;
314 self.t_width = 0.02; // frame animation rate
316 self.t_length = 239; // maximum frame
318 self.think = dompointthink;
319 self.nextthink = time;
320 self.touch = dompointtouch;
321 self.solid = SOLID_TRIGGER;
322 self.flags = FL_ITEM;
323 setsize(self, '-32 -32 -32', '32 32 32');
324 setorigin(self, self.origin + '0 0 20');
327 waypoint_spawnforitem(self);
328 WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite);
329 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
334 // player has joined game, get him on a team
336 /*void dom_player_join_team(entity pl)
339 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
340 float balance_teams, force_balance, balance_type;
342 balance_teams = autocvar_g_balance_teams;
343 balance_teams = autocvar_g_balance_teams_force;
345 c1 = c2 = c3 = c4 = -1;
348 // first find out what teams are allowed
349 head = find(world, classname, "dom_team");
352 if(head.netname != "")
354 //if(head.team == pl.team)
356 if(head.team == COLOR_TEAM1)
360 if(head.team == COLOR_TEAM2)
364 if(head.team == COLOR_TEAM3)
368 if(head.team == COLOR_TEAM4)
373 head = find(head, classname, "dom_team");
376 // make sure there are at least 2 teams to join
378 totalteams = totalteams + 1;
380 totalteams = totalteams + 1;
382 totalteams = totalteams + 1;
384 totalteams = totalteams + 1;
387 error("dom_player_join_team: Too few teams available for domination\n");
389 // whichever teams that are available are set to 0 instead of -1
391 // if we don't care what team he ends up on, put him on whatever team he entered as.
392 // if he's not on a valid team, then put him on the smallest team
393 if(!balance_teams && !force_balance)
395 if( c1 >= 0 && pl.team == COLOR_TEAM1)
396 selectedteam = pl.team;
397 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
398 selectedteam = pl.team;
399 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
400 selectedteam = pl.team;
401 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
402 selectedteam = pl.team;
407 SetPlayerColors(pl, selectedteam - 1);
410 // otherwise end up on the smallest team (handled below)
413 // now count how many players are on each team already
415 head = find(world, classname, "player");
418 //if(head.netname != "")
420 if(head.team == COLOR_TEAM1)
425 if(head.team == COLOR_TEAM2)
430 if(head.team == COLOR_TEAM3)
435 if(head.team == COLOR_TEAM4)
441 head = find(head, classname, "player");
444 // c1...c4 now have counts of each team
445 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
448 smallestteam_count = 999;
450 // 2 gives priority to what team you're already on, 1 goes in order
453 if(balance_type == 1)
455 if(c1 >= 0 && c1 < smallestteam_count)
458 smallestteam_count = c1;
460 if(c2 >= 0 && c2 < smallestteam_count)
463 smallestteam_count = c2;
465 if(c3 >= 0 && c3 < smallestteam_count)
468 smallestteam_count = c3;
470 if(c4 >= 0 && c4 < smallestteam_count)
473 smallestteam_count = c4;
478 if(c1 >= 0 && (c1 < smallestteam_count ||
479 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
482 smallestteam_count = c1;
484 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
485 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
488 smallestteam_count = c2;
490 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
491 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
494 smallestteam_count = c3;
496 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
497 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
500 smallestteam_count = c4;
504 if(smallestteam == 1)
506 selectedteam = COLOR_TEAM1 - 1;
508 if(smallestteam == 2)
510 selectedteam = COLOR_TEAM2 - 1;
512 if(smallestteam == 3)
514 selectedteam = COLOR_TEAM3 - 1;
516 if(smallestteam == 4)
518 selectedteam = COLOR_TEAM4 - 1;
521 SetPlayerColors(pl, selectedteam);
524 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
525 Control point for Domination gameplay.
527 void spawnfunc_dom_controlpoint()
534 self.think = dom_controlpoint_setup;
535 self.nextthink = time + 0.1;
536 self.reset = dom_controlpoint_setup;
541 //if(!self.glow_size)
542 // self.glow_size = cvar("g_domination_point_glow");
543 self.effects = self.effects | EF_LOWPRECISION;
544 if (autocvar_g_domination_point_fullbright)
545 self.effects |= EF_FULLBRIGHT;
548 // code from here on is just to support maps that don't have control point and team entities
549 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
551 local entity oldself;
554 self.classname = "dom_team";
555 self.netname = teamname;
556 self.cnt = teamcolor;
557 self.model = pointmodel;
558 self.skin = pointskin;
559 self.noise = capsound;
560 self.noise1 = capnarration;
561 self.message = capmessage;
563 // this code is identical to spawnfunc_dom_team
564 setmodel(self, self.model); // precision not needed
565 self.mdl = self.model;
566 self.dmg = self.modelindex;
569 // this would have to be changed if used in quakeworld
570 self.team = self.cnt + 1;
576 void dom_spawnpoint(vector org)
578 local entity oldself;
581 self.classname = "dom_controlpoint";
582 self.think = spawnfunc_dom_controlpoint;
583 self.nextthink = time;
584 setorigin(self, org);
585 spawnfunc_dom_controlpoint();
589 // spawn some default teams if the map is not set up for domination
590 void dom_spawnteams()
593 if(autocvar_g_domination_teams_override < 2)
594 numteams = autocvar_g_domination_default_teams;
596 numteams = autocvar_g_domination_teams_override;
597 // LordHavoc: edit this if you want to change defaults
598 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
599 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
601 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
603 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
604 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
607 void dom_delayedinit()
611 // if no teams are found, spawn defaults, if custom teams are set, use them
612 if (find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
614 // if no control points are found, spawn defaults
615 if (find(world, classname, "dom_controlpoint") == world)
617 // TODO in a few months (maybe 2011/08): change this into error() and remove this very poor dom point selection
618 backtrace("This map contains no dom_controlpoint entities. A very poor dom point placement will be chosen. Please fix the map.");
620 // if no supported map was found, make every deathmatch spawn a point
621 head = find(world, classname, "info_player_deathmatch");
624 dom_spawnpoint(head.origin);
625 head = find(head, classname, "info_player_deathmatch");
634 // we have to precache default models/sounds even if they might not be
635 // used because spawnfunc_worldspawn is executed before any other entities are read,
636 // so we don't even know yet if this map is set up for domination...
637 precache_model("models/domination/dom_red.md3");
638 precache_model("models/domination/dom_blue.md3");
639 precache_model("models/domination/dom_yellow.md3");
640 precache_model("models/domination/dom_pink.md3");
641 precache_model("models/domination/dom_unclaimed.md3");
642 precache_sound("domination/claim.wav");
643 InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);