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 // pps: points per second
28 .float dom_pps_yellow;
35 void set_dom_state(entity e)
37 e.dom_total_pps = total_pps;
38 e.dom_pps_red = pps_red;
39 e.dom_pps_blue = pps_blue;
41 e.dom_pps_yellow = pps_yellow;
43 e.dom_pps_pink = pps_pink;
46 void() dom_controlpoint_setup;
48 void LogDom(string mode, float team_before, entity actor)
51 if(!autocvar_sv_eventlog)
53 s = strcat(":dom:", mode);
54 s = strcat(s, ":", ftos(team_before));
55 s = strcat(s, ":", ftos(actor.playerid));
59 void() dom_spawnteams;
61 void dompoint_captured ()
64 float old_delay, old_team, real_team;
66 // now that the delay has expired, switch to the latest team to lay claim to this point
72 LogDom("taken", self.team, self.dmg_inflictor);
73 self.dmg_inflictor = world;
75 self.goalentity = head;
76 self.model = head.mdl;
77 self.modelindex = head.dmg;
78 self.skin = head.skin;
80 //bprint(head.message);
83 //bprint(^3head.netname);
84 //bprint(head.netname);
85 //bprint(self.message);
88 float points, wait_time;
89 if (autocvar_g_domination_point_amt)
90 points = autocvar_g_domination_point_amt;
93 if (autocvar_g_domination_point_rate)
94 wait_time = autocvar_g_domination_point_rate;
96 wait_time = self.wait;
98 bprint("^3", head.netname, "^3", self.message);
100 bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
102 bprint(" ^7(", ftos(points), " point every ", ftos(wait_time), " seconds)\n");
104 if(self.enemy.playerid == self.enemy_playerid)
105 PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
109 if (head.noise != "")
111 sound(self.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
113 sound(self, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
114 if (head.noise1 != "")
115 play2all(head.noise1);
117 //self.nextthink = time + autocvar_g_domination_point_rate;
118 //self.think = dompointthink;
120 self.delay = time + wait_time;
123 old_delay = self.delay;
124 old_team = self.team;
125 self.team = real_team;
129 self.delay = old_delay;
130 self.team = old_team;
132 switch(self.goalentity.team)
135 WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
138 WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
141 WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
144 WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
147 total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
148 for(head = world; (head = find(head, classname, "dom_controlpoint")) != world; )
150 if (autocvar_g_domination_point_amt)
151 points = autocvar_g_domination_point_amt;
154 if (autocvar_g_domination_point_rate)
155 wait_time = autocvar_g_domination_point_rate;
157 wait_time = head.wait;
158 switch(head.goalentity.team)
161 pps_red += points/wait_time;
164 pps_blue += points/wait_time;
167 pps_yellow += points/wait_time;
170 pps_pink += points/wait_time;
172 total_pps += points/wait_time;
175 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
176 WaypointSprite_Ping(self.sprite);
180 FOR_EACH_REALCLIENT(head)
184 void AnimateDomPoint()
186 if(self.pain_finished > time)
188 self.pain_finished = time + self.t_width;
189 if(self.nextthink > self.pain_finished)
190 self.nextthink = self.pain_finished;
192 self.frame = self.frame + 1;
193 if(self.frame > self.t_length)
201 self.nextthink = time + 0.1;
203 //self.frame = self.frame + 1;
204 //if(self.frame > 119)
210 if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
213 if(autocvar_g_domination_point_rate)
214 self.delay = time + autocvar_g_domination_point_rate;
216 self.delay = time + self.wait;
218 // give credit to the team
219 // NOTE: this defaults to 0
220 if (self.goalentity.netname != "")
222 if(autocvar_g_domination_point_amt)
223 fragamt = autocvar_g_domination_point_amt;
225 fragamt = self.DOMPOINTFRAGS;
226 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
227 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
229 // give credit to the individual player, if he is still there
230 if (self.enemy.playerid == self.enemy_playerid)
232 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
233 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
243 if (other.classname != "player")
245 if (other.health < 1)
248 if(time < self.captime + 0.3)
251 // only valid teams can claim it
252 head = find(world, classname, "dom_team");
253 while (head && head.team != other.team)
254 head = find(head, classname, "dom_team");
255 if (!head || head.netname == "" || head == self.goalentity)
260 self.team = self.goalentity.team; // this stores the PREVIOUS team!
262 self.cnt = other.team;
263 self.owner = head; // team to switch to after the delay
264 self.dmg_inflictor = other;
267 // self.delay = time + cvar("g_domination_point_capturetime");
268 //self.nextthink = time + cvar("g_domination_point_capturetime");
269 //self.think = dompoint_captured;
271 // go to neutral team in the mean time
272 head = find(world, classname, "dom_team");
273 while (head && head.netname != "")
274 head = find(head, classname, "dom_team");
278 WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
279 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
280 WaypointSprite_Ping(self.sprite);
282 self.goalentity = head;
283 self.model = head.mdl;
284 self.modelindex = head.dmg;
285 self.skin = head.skin;
287 self.enemy = other; // individual player scoring
288 self.enemy_playerid = other.playerid;
292 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
293 Team declaration for Domination gameplay, this allows you to decide what team
294 names and control point models are used in your map.
296 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
297 can have netname set! The nameless team owns all control points at start.
301 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
303 Scoreboard color of the team (for example 4 is red and 13 is blue)
305 Model to use for control points owned by this team (for example
306 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
309 Skin of the model to use (for team skins on a single model)
311 Sound to play when this team captures a point.
312 (this is a localized sound, like a small alarm or other effect)
314 Narrator speech to play when this team captures a point.
315 (this is a global sound, like "Red team has captured a control point")
318 void spawnfunc_dom_team()
320 if(!g_domination || autocvar_g_domination_teams_override >= 2)
325 precache_model(self.model);
326 if (self.noise != "")
327 precache_sound(self.noise);
328 if (self.noise1 != "")
329 precache_sound(self.noise1);
330 self.classname = "dom_team";
331 setmodel(self, self.model); // precision not needed
332 self.mdl = self.model;
333 self.dmg = self.modelindex;
336 // this would have to be changed if used in quakeworld
338 self.team = self.cnt + 1; // WHY are these different anyway?
341 void dom_controlpoint_setup()
344 // find the spawnfunc_dom_team representing unclaimed points
345 head = find(world, classname, "dom_team");
346 while(head && head.netname != "")
347 head = find(head, classname, "dom_team");
349 objerror("no spawnfunc_dom_team with netname \"\" found\n");
351 // copy important properties from spawnfunc_dom_team entity
352 self.goalentity = head;
353 setmodel(self, head.mdl); // precision already set
354 self.skin = head.skin;
359 self.message = " has captured a control point";
361 if(self.DOMPOINTFRAGS <= 0)
362 self.DOMPOINTFRAGS = 1;
366 float points, waittime;
367 if (autocvar_g_domination_point_amt)
368 points = autocvar_g_domination_point_amt;
371 if (autocvar_g_domination_point_rate)
372 waittime = autocvar_g_domination_point_rate;
374 waittime = self.wait;
376 total_pps += points/waittime;
379 self.t_width = 0.02; // frame animation rate
381 self.t_length = 239; // maximum frame
383 self.think = dompointthink;
384 self.nextthink = time;
385 self.touch = dompointtouch;
386 self.solid = SOLID_TRIGGER;
387 self.flags = FL_ITEM;
388 setsize(self, '-32 -32 -32', '32 32 32');
389 setorigin(self, self.origin + '0 0 20');
392 waypoint_spawnforitem(self);
393 WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite, RADARICON_DOMPOINT, '0 1 1');
398 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
399 Control point for Domination gameplay.
401 void spawnfunc_dom_controlpoint()
408 self.think = dom_controlpoint_setup;
409 self.nextthink = time + 0.1;
410 self.reset = dom_controlpoint_setup;
415 //if(!self.glow_size)
416 // self.glow_size = cvar("g_domination_point_glow");
417 self.effects = self.effects | EF_LOWPRECISION;
418 if (autocvar_g_domination_point_fullbright)
419 self.effects |= EF_FULLBRIGHT;
422 // code from here on is just to support maps that don't have control point and team entities
423 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
428 self.classname = "dom_team";
429 self.netname = teamname;
430 self.cnt = teamcolor;
431 self.model = pointmodel;
432 self.skin = pointskin;
433 self.noise = capsound;
434 self.noise1 = capnarration;
435 self.message = capmessage;
437 // this code is identical to spawnfunc_dom_team
438 setmodel(self, self.model); // precision not needed
439 self.mdl = self.model;
440 self.dmg = self.modelindex;
443 // this would have to be changed if used in quakeworld
444 self.team = self.cnt + 1;
450 void dom_spawnpoint(vector org)
455 self.classname = "dom_controlpoint";
456 self.think = spawnfunc_dom_controlpoint;
457 self.nextthink = time;
458 setorigin(self, org);
459 spawnfunc_dom_controlpoint();
463 // spawn some default teams if the map is not set up for domination
464 void dom_spawnteams()
467 if(autocvar_g_domination_teams_override < 2)
468 numteams = autocvar_g_domination_default_teams;
470 numteams = autocvar_g_domination_teams_override;
471 // LordHavoc: edit this if you want to change defaults
472 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
473 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
475 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
477 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
478 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
481 void dom_delayedinit()
485 // if no teams are found, spawn defaults, if custom teams are set, use them
486 if (find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
488 // if no control points are found, spawn defaults
489 if (find(world, classname, "dom_controlpoint") == world)
491 // TODO in a few months (maybe 2011/08): change this into error() and remove this very poor dom point selection
492 backtrace("This map contains no dom_controlpoint entities. A very poor dom point placement will be chosen. Please fix the map.");
494 // if no supported map was found, make every deathmatch spawn a point
495 head = find(world, classname, "info_player_deathmatch");
498 dom_spawnpoint(head.origin);
499 head = find(head, classname, "info_player_deathmatch");
508 // we have to precache default models/sounds even if they might not be
509 // used because spawnfunc_worldspawn is executed before any other entities are read,
510 // so we don't even know yet if this map is set up for domination...
511 precache_model("models/domination/dom_red.md3");
512 precache_model("models/domination/dom_blue.md3");
513 precache_model("models/domination/dom_yellow.md3");
514 precache_model("models/domination/dom_pink.md3");
515 precache_model("models/domination/dom_unclaimed.md3");
516 precache_sound("domination/claim.wav");
517 InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);
519 addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
520 addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
521 addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
522 if(c3 >= 0) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
523 if(c4 >= 0) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);