]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/gamec/domination.c
some default.cfg teamplay things
[divverent/nexuiz.git] / qcsrc / gamec / domination.c
1
2 /*
3 Domination as a plugin for netquake mods
4 by LordHavoc (lordhavoc@ghdigital.com)
5
6 How to add domination points to a mod:
7 1. Add this line to progs.src above world.qc:
8 domination.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 worldspawn in world.qc:
11 void() dom_init;
12 4. Add this line to the end of worldspawn in world.qc:
13 dom_init();
14
15 Note: The only teams who can use dom control points are identified by dom_team entities (if none exist these default to red and blue and use only quake models/sounds).
16 */
17
18
19 void() dom_spawnteams;
20
21 void() dompointthink =
22 {
23         local entity head;
24         float waittime;
25         float fragamt;
26         if (gameover)   // game has ended, don't keep giving points
27                 return;
28
29         waittime = cvar("g_domination_point_rate");
30         if(!waittime)
31                 waittime = self.wait;
32         self.nextthink = time + waittime;
33
34         if (!self.goalentity.netname)
35                 return;
36
37
38         fragamt = cvar("g_domination_point_amt");
39         if(!fragamt)
40                 fragamt = self.frags;
41
42         head = find(head, classname, "player");
43         while (head)
44         {
45                 if (head.team == self.goalentity.team)
46                         head.frags = head.frags + fragamt;
47                 head = find(head, classname, "player");
48         }
49         self.goalentity.frags = self.goalentity.frags + fragamt;
50 };
51
52 void dompoint_captured ()
53 {
54         local entity head;
55
56         head = self.enemy;
57
58         self.cnt = -1;
59
60         self.goalentity = head;
61         self.model = head.mdl;
62         self.modelindex = head.dmg;
63         self.skin = head.skin;
64
65         //bprint(head.message);
66         //bprint("\n");
67
68         //bprint(^3head.netname);
69         //bprint(head.netname);
70         //bprint(self.message);
71         //bprint("\n");
72
73         bprint(strcat("^3", head.netname, self.message, "\n"));
74
75         if (head.noise != "")
76                 sound(self, CHAN_BODY, head.noise, 1, ATTN_NORM);
77         if (head.noise1 != "")
78                 sound(self, CHAN_VOICE, head.noise1, 1, ATTN_NONE);
79
80         self.nextthink = time + cvar("g_domination_point_rate");
81         self.think = dompointthink;
82 };
83
84 void() dompointtouch =
85 {
86         local entity head;
87         if (other.classname != "player")
88                 return;
89         if (other.health < 1)
90                 return;
91         if(self.cnt > 0 && self.cnt == other.team)
92                 return;
93
94         // only valid teams can claim it
95         head = find(world, classname, "dom_team");
96         while (head && head.team != other.team)
97                 head = find(head, classname, "dom_team");
98         if (!head || head.netname == "" || head == self.goalentity)
99                 return;
100
101         // delay capture
102
103         self.cnt = other.team;
104         self.enemy = head;
105         self.nextthink = time + cvar("g_domination_point_capturetime");
106         self.think = dompoint_captured;
107
108         // go to neutral team in the mean time
109
110         head = find(world, classname, "dom_team");
111         while (head && head.netname != "")
112                 head = find(head, classname, "dom_team");
113         if(head == world)
114                 return;
115
116         self.goalentity = head;
117         self.model = head.mdl;
118         self.modelindex = head.dmg;
119         self.skin = head.skin;
120 };
121
122 /*QUAKED dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
123 Team declaration for Domination gameplay, this allows you to decide what team
124 names and control point models are used in your map.
125
126 Note: If you use dom_team entities you must define at least 3 and only two
127 can have netname set!  The nameless team owns all control points at start.
128
129 Keys:
130 "netname"
131  Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
132 "cnt"
133  Scoreboard color of the team (for example 4 is red and 13 is blue)
134 "model"
135  Model to use for control points owned by this team (for example
136  "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
137  keycard)
138 "skin"
139  Skin of the model to use (for team skins on a single model)
140 "noise"
141  Sound to play when this team captures a point.
142  (this is a localized sound, like a small alarm or other effect)
143 "noise1"
144  Narrator speech to play when this team captures a point.
145  (this is a global sound, like "Red team has captured a control point")
146 */
147
148 void() dom_team =
149 {
150         precache_model(self.model);
151         if (self.noise != "")
152                 precache_sound(self.noise);
153         if (self.noise1 != "")
154                 precache_sound(self.noise1);
155         self.classname = "dom_team";
156         setmodel(self, self.model);
157         self.mdl = self.model;
158         self.dmg = self.modelindex;
159         self.model = "";
160         self.modelindex = 0;
161         // this would have to be changed if used in quakeworld
162         self.team = self.cnt + 1;
163 };
164
165 void() dom_controlpoint_setup =
166 {
167         local entity head;
168         // find the dom_team representing unclaimed points
169         head = find(world, classname, "dom_team");
170         while(head && head.netname != "")
171                 head = find(head, classname, "dom_team");
172         if (!head)
173                 objerror("no dom_team with netname \"\" found\n");
174
175         // copy important properties from dom_team entity
176         self.goalentity = head;
177         setmodel(self, head.mdl);
178         self.skin = head.skin;
179
180         self.cnt = -1;
181
182         if(!self.message)
183                 self.message = " has captured a control point";
184
185         if(!self.frags)
186                 self.frags = 1;
187         if(!self.wait)
188                 self.wait = 5;
189
190         self.think = dompointthink;
191         self.nextthink = time;
192         self.touch = dompointtouch;
193         self.solid = SOLID_TRIGGER;
194         setsize(self, '-32 -32 -24', '32 32 32');
195         setorigin(self, self.origin);
196         droptofloor(0, 0);
197 };
198
199
200
201 // player has joined game, get him on a team
202 // depreciated
203 /*void dom_player_join_team(entity pl)
204 {
205         entity head;
206         float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
207         float balance_teams, force_balance, balance_type;
208
209         balance_teams = cvar("g_balance_teams");
210         balance_teams = cvar("g_balance_teams_force");
211
212         c1 = c2 = c3 = c4 = -1;
213         totalteams = 0;
214
215         // first find out what teams are allowed
216         head = find(world, classname, "dom_team");
217         while(head)
218         {
219                 if(head.netname != "")
220                 {
221                         //if(head.team == pl.team)
222                         //      selected = head;
223                         if(head.team == COLOR_TEAM1)
224                         {
225                                         c1 = 0;
226                         }
227                         if(head.team == COLOR_TEAM2)
228                         {
229                                         c2 = 0;
230                         }
231                         if(head.team == COLOR_TEAM3)
232                         {
233                                         c3 = 0;
234                         }
235                         if(head.team == COLOR_TEAM4)
236                         {
237                                         c4 = 0;
238                         }
239                 }
240                 head = find(head, classname, "dom_team");
241         }
242
243         // make sure there are at least 2 teams to join
244         if(c1 >= 0)
245                 totalteams = totalteams + 1;
246         if(c2 >= 0)
247                 totalteams = totalteams + 1;
248         if(c3 >= 0)
249                 totalteams = totalteams + 1;
250         if(c4 >= 0)
251                 totalteams = totalteams + 1;
252
253         if(totalteams <= 1)
254                 error("dom_player_join_team: Too few teams available for domination\n");
255
256         // whichever teams that are available are set to 0 instead of -1
257
258         // if we don't care what team he ends up on, put him on whatever team he entered as.
259         // if he's not on a valid team, then put him on the smallest team
260         if(!balance_teams && !force_balance)
261         {
262                 if(     c1 >= 0 && pl.team == COLOR_TEAM1)
263                         selectedteam = pl.team;
264                 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
265                         selectedteam = pl.team;
266                 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
267                         selectedteam = pl.team;
268                 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
269                         selectedteam = pl.team;
270                 else
271                         selectedteam = -1;
272                 if(selectedteam > 0)
273                 {
274                         SetPlayerColors(pl, selectedteam - 1);
275                         return;
276                 }
277                 // otherwise end up on the smallest team (handled below)
278         }
279
280         // now count how many players are on each team already
281
282         head = find(world, classname, "player");
283         while(head)
284         {
285                 //if(head.netname != "")
286                 {
287                         if(head.team == COLOR_TEAM1)
288                         {
289                                 if(c1 >= 0)
290                                         c1 = c1 + 1;
291                         }
292                         if(head.team == COLOR_TEAM2)
293                         {
294                                 if(c2 >= 0)
295                                         c2 = c2 + 1;
296                         }
297                         if(head.team == COLOR_TEAM3)
298                         {
299                                 if(c3 >= 0)
300                                         c3 = c3 + 1;
301                         }
302                         if(head.team == COLOR_TEAM4)
303                         {
304                                 if(c4 >= 0)
305                                         c4 = c4 + 1;
306                         }
307                 }
308                 head = find(head, classname, "player");
309         }
310
311         // c1...c4 now have counts of each team
312         // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
313
314         smallestteam = 0;
315         smallestteam_count = 999;
316
317         // 2 gives priority to what team you're already on, 1 goes in order
318         balance_type = 1;
319
320         if(balance_type == 1)
321         {
322                 if(c1 >= 0 && c1 < smallestteam_count)
323                 {
324                         smallestteam = 1;
325                         smallestteam_count = c1;
326                 }
327                 if(c2 >= 0 && c2 < smallestteam_count)
328                 {
329                         smallestteam = 2;
330                         smallestteam_count = c2;
331                 }
332                 if(c3 >= 0 && c3 < smallestteam_count)
333                 {
334                         smallestteam = 3;
335                         smallestteam_count = c3;
336                 }
337                 if(c4 >= 0 && c4 < smallestteam_count)
338                 {
339                         smallestteam = 4;
340                         smallestteam_count = c4;
341                 }
342         }
343         else
344         {
345                 if(c1 >= 0 && (c1 < smallestteam_count || 
346                                         (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
347                 {
348                         smallestteam = 1;
349                         smallestteam_count = c1;
350                 }
351                 if(c2 >= 0 && c2 < (c2 < smallestteam_count || 
352                                         (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
353                 {
354                         smallestteam = 2;
355                         smallestteam_count = c2;
356                 }
357                 if(c3 >= 0 && c3 < (c3 < smallestteam_count || 
358                                         (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
359                 {
360                         smallestteam = 3;
361                         smallestteam_count = c3;
362                 }
363                 if(c4 >= 0 && c4 < (c4 < smallestteam_count || 
364                                         (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
365                 {
366                         smallestteam = 4;
367                         smallestteam_count = c4;
368                 }
369         }
370
371         if(smallestteam == 1)
372         {
373                 selectedteam = COLOR_TEAM1 - 1;
374         }
375         if(smallestteam == 2)
376         {
377                 selectedteam = COLOR_TEAM2 - 1;
378         }
379         if(smallestteam == 3)
380         {
381                 selectedteam = COLOR_TEAM3 - 1;
382         }
383         if(smallestteam == 4)
384         {
385                 selectedteam = COLOR_TEAM4 - 1;
386         }
387
388         SetPlayerColors(pl, selectedteam);
389 }
390 */
391 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
392 Control point for Domination gameplay.
393 */
394 void() dom_controlpoint =
395 {
396         if(!cvar("g_domination"))
397         {
398                 remove(self);
399                 return;
400         }
401         self.think = dom_controlpoint_setup;
402         self.nextthink = time + 0.1;
403
404         if(!self.scale)
405                 self.scale = 0.6;
406
407         if(!self.glow_size)
408                 self.glow_size = cvar("g_domination_point_glow");
409 };
410
411 // code from here on is just to support maps that don't have control point and team entities
412 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage) 
413 {
414         local entity oldself;
415         oldself = self;
416         self = spawn();
417         self.classname = "dom_team";
418         self.netname = teamname;
419         self.cnt = teamcolor;
420         self.model = pointmodel;
421         self.skin = pointskin;
422         self.noise = capsound;
423         self.noise1 = capnarration;
424         self.message = capmessage;
425
426         // this code is identical to dom_team
427         setmodel(self, self.model);
428         self.mdl = self.model;
429         self.dmg = self.modelindex;
430         self.model = "";
431         self.modelindex = 0;
432         // this would have to be changed if used in quakeworld
433         self.team = self.cnt + 1;
434
435         //eprint(self);
436         self = oldself;
437 };
438
439 void(vector org) dom_spawnpoint =
440 {
441         local entity oldself;
442         oldself = self;
443         self = spawn();
444         self.classname = "dom_controlpoint";
445         self.think = dom_controlpoint;
446         self.nextthink = time;
447         self.origin = org;
448         dom_controlpoint();
449         self = oldself;
450 };
451
452 // spawn some default teams if the map is not set up for domination
453 void() dom_spawnteams =
454 {
455         float numteams;
456
457         numteams = cvar("g_domination_default_teams");
458         // LordHavoc: edit this if you want to change defaults
459         dom_spawnteam("Red", 4, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
460         dom_spawnteam("Blue", 13, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
461         if(numteams > 2)
462                 dom_spawnteam("Green", 3, "models/domination/dom_green.md3", 0, "domination/claim.wav", "", "Green team has captured a control point");
463         if(numteams > 3)
464                 dom_spawnteam("Yellow", 12, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
465         dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
466 };
467
468 void() dom_delayedinit =
469 {
470         local entity head;
471
472         self.think = SUB_Remove;
473         self.nextthink = time;
474         // if no teams are found, spawn defaults
475         if (find(world, classname, "dom_team") == world)
476                 dom_spawnteams();
477         // if no control points are found, spawn defaults
478         if (find(world, classname, "dom_controlpoint") == world)
479         {
480                 // here follow default domination points for each map
481                 /*
482                 if (world.model == "maps/e1m1.bsp")
483                 {
484                         dom_spawnpoint('0 0 0');
485                 }
486                 else
487                 */
488                 {
489                         // if no supported map was found, make every deathmatch spawn a point
490                         head = find(world, classname, "info_player_deathmatch");
491                         while (head)
492                         {
493                                 dom_spawnpoint(head.origin);
494                                 head = find(head, classname, "info_player_deathmatch");
495                         }
496                 }
497         }
498 };
499
500 void() dom_init =
501 {
502         local entity e;
503         // we have to precache default models/sounds even if they might not be
504         // used because worldspawn is executed before any other entities are read,
505         // so we don't even know yet if this map is set up for domination...
506         precache_model("models/domination/dom_red.md3");
507         precache_model("models/domination/dom_blue.md3");
508         precache_model("models/domination/dom_green.md3");
509         precache_model("models/domination/dom_yellow.md3");
510         precache_model("models/domination/dom_unclaimed.md3");
511         precache_sound("domination/claim.wav");
512         e = spawn();
513         e.think = dom_delayedinit;
514         e.nextthink = time + 0.1;
515
516         // teamplay is always on in domination, defaults to hurt self but not teammates
517         //if(!cvar("teamplay"))
518         //      cvar_set("teamplay", "3");
519 };
520