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