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