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;
23 void() dom_controlpoint_setup;
25 void LogDom(string mode, float team_before, entity actor)
28 if(!cvar("sv_eventlog"))
30 s = strcat(":dom:", mode);
31 s = strcat(s, ":", ftos(team_before));
32 s = strcat(s, ":", ftos(actor.playerid));
36 void() dom_spawnteams;
38 void dompoint_captured ()
41 local float old_delay, old_team, real_team;
43 // now that the delay has expired, switch to the latest team to lay claim to this point
49 LogDom("taken", self.team, self.dmg_inflictor);
50 self.dmg_inflictor = world;
52 self.goalentity = head;
53 self.model = head.mdl;
54 self.modelindex = head.dmg;
55 self.skin = head.skin;
57 //bprint(head.message);
60 //bprint(^3head.netname);
61 //bprint(head.netname);
62 //bprint(self.message);
65 bprint("^3", head.netname, "^3", self.message, "\n");
66 if(self.enemy.playerid == self.enemy_playerid)
67 PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
73 sound(self.enemy, CHAN_AUTO, head.noise, VOL_BASE, ATTN_NORM);
75 sound(self, CHAN_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
76 if (head.noise1 != "")
77 play2all(head.noise1);
79 //self.nextthink = time + cvar("g_domination_point_rate");
80 //self.think = dompointthink;
82 if(cvar("g_domination_point_rate"))
83 self.delay = time + cvar("g_domination_point_rate");
85 self.delay = time + self.wait;
88 old_delay = self.delay;
90 self.team = real_team;
94 self.delay = old_delay;
97 switch(self.goalentity.team)
100 WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
103 WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
106 WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
109 WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
112 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
113 WaypointSprite_Ping(self.sprite);
116 void AnimateDomPoint()
118 if(self.pain_finished > time)
120 self.pain_finished = time + self.t_width;
121 if(self.nextthink > self.pain_finished)
122 self.nextthink = self.pain_finished;
124 self.frame = self.frame + 1;
125 if(self.frame > self.t_length)
131 local float waittime;
134 self.nextthink = time + 0.1;
136 //self.frame = self.frame + 1;
137 //if(self.frame > 119)
143 if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
146 waittime = cvar("g_domination_point_rate");
148 waittime = self.wait;
149 self.delay = time + waittime;
151 // give credit to the team
152 // NOTE: this defaults to 0
153 if (self.goalentity.netname != "")
155 fragamt = cvar("g_domination_point_amt");
157 fragamt = self.DOMPOINTFRAGS;
158 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
159 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
161 // give credit to the individual player, if he is still there
162 if (self.enemy.playerid == self.enemy_playerid)
164 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
165 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
175 if (other.classname != "player")
177 if (other.health < 1)
180 // only valid teams can claim it
181 head = find(world, classname, "dom_team");
182 while (head && head.team != other.team)
183 head = find(head, classname, "dom_team");
184 if (!head || head.netname == "" || head == self.goalentity)
189 self.team = self.goalentity.team; // this stores the PREVIOUS team!
191 self.cnt = other.team;
192 self.owner = head; // team to switch to after the delay
193 self.dmg_inflictor = other;
196 // self.delay = time + cvar("g_domination_point_capturetime");
197 //self.nextthink = time + cvar("g_domination_point_capturetime");
198 //self.think = dompoint_captured;
200 // go to neutral team in the mean time
201 head = find(world, classname, "dom_team");
202 while (head && head.netname != "")
203 head = find(head, classname, "dom_team");
207 WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
208 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
209 WaypointSprite_Ping(self.sprite);
211 self.goalentity = head;
212 self.model = head.mdl;
213 self.modelindex = head.dmg;
214 self.skin = head.skin;
216 self.enemy = other; // individual player scoring
217 self.enemy_playerid = other.playerid;
221 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
222 Team declaration for Domination gameplay, this allows you to decide what team
223 names and control point models are used in your map.
225 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
226 can have netname set! The nameless team owns all control points at start.
230 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
232 Scoreboard color of the team (for example 4 is red and 13 is blue)
234 Model to use for control points owned by this team (for example
235 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
238 Skin of the model to use (for team skins on a single model)
240 Sound to play when this team captures a point.
241 (this is a localized sound, like a small alarm or other effect)
243 Narrator speech to play when this team captures a point.
244 (this is a global sound, like "Red team has captured a control point")
247 void spawnfunc_dom_team()
254 precache_model(self.model);
255 if (self.noise != "")
256 precache_sound(self.noise);
257 if (self.noise1 != "")
258 precache_sound(self.noise1);
259 self.classname = "dom_team";
260 setmodel(self, self.model); // precision not needed
261 self.mdl = self.model;
262 self.dmg = self.modelindex;
265 // this would have to be changed if used in quakeworld
267 self.team = self.cnt + 1; // WHY are these different anyway?
270 void dom_controlpoint_setup()
273 // find the spawnfunc_dom_team representing unclaimed points
274 head = find(world, classname, "dom_team");
275 while(head && head.netname != "")
276 head = find(head, classname, "dom_team");
278 objerror("no spawnfunc_dom_team with netname \"\" found\n");
280 // copy important properties from spawnfunc_dom_team entity
281 self.goalentity = head;
282 setmodel(self, head.mdl); // precision already set
283 self.skin = head.skin;
288 self.message = " has captured a control point";
290 if(!self.DOMPOINTFRAGS)
291 self.DOMPOINTFRAGS = 1;
296 self.t_width = 0.02; // frame animation rate
298 self.t_length = 239; // maximum frame
300 self.think = dompointthink;
301 self.nextthink = time;
302 self.touch = dompointtouch;
303 self.solid = SOLID_TRIGGER;
304 self.flags = FL_ITEM;
305 setsize(self, '-32 -32 -32', '32 32 32');
306 setorigin(self, self.origin + '0 0 20');
309 waypoint_spawnforitem(self);
310 WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite);
311 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
316 // player has joined game, get him on a team
318 /*void dom_player_join_team(entity pl)
321 float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
322 float balance_teams, force_balance, balance_type;
324 balance_teams = cvar("g_balance_teams");
325 balance_teams = cvar("g_balance_teams_force");
327 c1 = c2 = c3 = c4 = -1;
330 // first find out what teams are allowed
331 head = find(world, classname, "dom_team");
334 if(head.netname != "")
336 //if(head.team == pl.team)
338 if(head.team == COLOR_TEAM1)
342 if(head.team == COLOR_TEAM2)
346 if(head.team == COLOR_TEAM3)
350 if(head.team == COLOR_TEAM4)
355 head = find(head, classname, "dom_team");
358 // make sure there are at least 2 teams to join
360 totalteams = totalteams + 1;
362 totalteams = totalteams + 1;
364 totalteams = totalteams + 1;
366 totalteams = totalteams + 1;
369 error("dom_player_join_team: Too few teams available for domination\n");
371 // whichever teams that are available are set to 0 instead of -1
373 // if we don't care what team he ends up on, put him on whatever team he entered as.
374 // if he's not on a valid team, then put him on the smallest team
375 if(!balance_teams && !force_balance)
377 if( c1 >= 0 && pl.team == COLOR_TEAM1)
378 selectedteam = pl.team;
379 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
380 selectedteam = pl.team;
381 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
382 selectedteam = pl.team;
383 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
384 selectedteam = pl.team;
389 SetPlayerColors(pl, selectedteam - 1);
392 // otherwise end up on the smallest team (handled below)
395 // now count how many players are on each team already
397 head = find(world, classname, "player");
400 //if(head.netname != "")
402 if(head.team == COLOR_TEAM1)
407 if(head.team == COLOR_TEAM2)
412 if(head.team == COLOR_TEAM3)
417 if(head.team == COLOR_TEAM4)
423 head = find(head, classname, "player");
426 // c1...c4 now have counts of each team
427 // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
430 smallestteam_count = 999;
432 // 2 gives priority to what team you're already on, 1 goes in order
435 if(balance_type == 1)
437 if(c1 >= 0 && c1 < smallestteam_count)
440 smallestteam_count = c1;
442 if(c2 >= 0 && c2 < smallestteam_count)
445 smallestteam_count = c2;
447 if(c3 >= 0 && c3 < smallestteam_count)
450 smallestteam_count = c3;
452 if(c4 >= 0 && c4 < smallestteam_count)
455 smallestteam_count = c4;
460 if(c1 >= 0 && (c1 < smallestteam_count ||
461 (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
464 smallestteam_count = c1;
466 if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
467 (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
470 smallestteam_count = c2;
472 if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
473 (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
476 smallestteam_count = c3;
478 if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
479 (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
482 smallestteam_count = c4;
486 if(smallestteam == 1)
488 selectedteam = COLOR_TEAM1 - 1;
490 if(smallestteam == 2)
492 selectedteam = COLOR_TEAM2 - 1;
494 if(smallestteam == 3)
496 selectedteam = COLOR_TEAM3 - 1;
498 if(smallestteam == 4)
500 selectedteam = COLOR_TEAM4 - 1;
503 SetPlayerColors(pl, selectedteam);
506 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
507 Control point for Domination gameplay.
509 void spawnfunc_dom_controlpoint()
516 self.think = dom_controlpoint_setup;
517 self.nextthink = time + 0.1;
518 self.reset = dom_controlpoint_setup;
523 //if(!self.glow_size)
524 // self.glow_size = cvar("g_domination_point_glow");
525 self.effects = self.effects | EF_LOWPRECISION;
526 if (cvar("g_domination_point_fullbright"))
527 self.effects |= EF_FULLBRIGHT;
530 // code from here on is just to support maps that don't have control point and team entities
531 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
533 local entity oldself;
536 self.classname = "dom_team";
537 self.netname = teamname;
538 self.cnt = teamcolor;
539 self.model = pointmodel;
540 self.skin = pointskin;
541 self.noise = capsound;
542 self.noise1 = capnarration;
543 self.message = capmessage;
545 // this code is identical to spawnfunc_dom_team
546 setmodel(self, self.model); // precision not needed
547 self.mdl = self.model;
548 self.dmg = self.modelindex;
551 // this would have to be changed if used in quakeworld
552 self.team = self.cnt + 1;
558 void dom_spawnpoint(vector org)
560 local entity oldself;
563 self.classname = "dom_controlpoint";
564 self.think = spawnfunc_dom_controlpoint;
565 self.nextthink = time;
567 spawnfunc_dom_controlpoint();
571 // spawn some default teams if the map is not set up for domination
572 void dom_spawnteams()
576 numteams = cvar("g_domination_default_teams");
577 // LordHavoc: edit this if you want to change defaults
578 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
579 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
581 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
583 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
584 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
587 void dom_delayedinit()
591 // if no teams are found, spawn defaults
592 if (find(world, classname, "dom_team") == world)
594 // if no control points are found, spawn defaults
595 if (find(world, classname, "dom_controlpoint") == world)
597 // here follow default domination points for each map
599 if (world.model == "maps/e1m1.bsp")
601 dom_spawnpoint('0 0 0');
606 // if no supported map was found, make every deathmatch spawn a point
607 head = find(world, classname, "info_player_deathmatch");
610 dom_spawnpoint(head.origin);
611 head = find(head, classname, "info_player_deathmatch");
621 // we have to precache default models/sounds even if they might not be
622 // used because spawnfunc_worldspawn is executed before any other entities are read,
623 // so we don't even know yet if this map is set up for domination...
624 precache_model("models/domination/dom_red.md3");
625 precache_model("models/domination/dom_blue.md3");
626 precache_model("models/domination/dom_yellow.md3");
627 precache_model("models/domination/dom_pink.md3");
628 precache_model("models/domination/dom_unclaimed.md3");
629 precache_sound("domination/claim.wav");
630 InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);
632 // teamplay is always on in domination, defaults to hurt self but not teammates
634 // cvar_set("teamplay", "3");