]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/gamec/domination.c
add g_navnodeedit
[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() 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         self.flags = FL_ITEM;
236         setsize(self, '-32 -32 -24', '32 32 32');
237         setorigin(self, self.origin + '0 0 20');
238         droptofloor(0, 0);
239 };
240
241
242
243 // player has joined game, get him on a team
244 // depreciated
245 /*void dom_player_join_team(entity pl)
246 {
247         entity head;
248         float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
249         float balance_teams, force_balance, balance_type;
250
251         balance_teams = cvar("g_balance_teams");
252         balance_teams = cvar("g_balance_teams_force");
253
254         c1 = c2 = c3 = c4 = -1;
255         totalteams = 0;
256
257         // first find out what teams are allowed
258         head = find(world, classname, "dom_team");
259         while(head)
260         {
261                 if(head.netname != "")
262                 {
263                         //if(head.team == pl.team)
264                         //      selected = head;
265                         if(head.team == COLOR_TEAM1)
266                         {
267                                         c1 = 0;
268                         }
269                         if(head.team == COLOR_TEAM2)
270                         {
271                                         c2 = 0;
272                         }
273                         if(head.team == COLOR_TEAM3)
274                         {
275                                         c3 = 0;
276                         }
277                         if(head.team == COLOR_TEAM4)
278                         {
279                                         c4 = 0;
280                         }
281                 }
282                 head = find(head, classname, "dom_team");
283         }
284
285         // make sure there are at least 2 teams to join
286         if(c1 >= 0)
287                 totalteams = totalteams + 1;
288         if(c2 >= 0)
289                 totalteams = totalteams + 1;
290         if(c3 >= 0)
291                 totalteams = totalteams + 1;
292         if(c4 >= 0)
293                 totalteams = totalteams + 1;
294
295         if(totalteams <= 1)
296                 error("dom_player_join_team: Too few teams available for domination\n");
297
298         // whichever teams that are available are set to 0 instead of -1
299
300         // if we don't care what team he ends up on, put him on whatever team he entered as.
301         // if he's not on a valid team, then put him on the smallest team
302         if(!balance_teams && !force_balance)
303         {
304                 if(     c1 >= 0 && pl.team == COLOR_TEAM1)
305                         selectedteam = pl.team;
306                 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
307                         selectedteam = pl.team;
308                 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
309                         selectedteam = pl.team;
310                 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
311                         selectedteam = pl.team;
312                 else
313                         selectedteam = -1;
314                 if(selectedteam > 0)
315                 {
316                         SetPlayerColors(pl, selectedteam - 1);
317                         return;
318                 }
319                 // otherwise end up on the smallest team (handled below)
320         }
321
322         // now count how many players are on each team already
323
324         head = find(world, classname, "player");
325         while(head)
326         {
327                 //if(head.netname != "")
328                 {
329                         if(head.team == COLOR_TEAM1)
330                         {
331                                 if(c1 >= 0)
332                                         c1 = c1 + 1;
333                         }
334                         if(head.team == COLOR_TEAM2)
335                         {
336                                 if(c2 >= 0)
337                                         c2 = c2 + 1;
338                         }
339                         if(head.team == COLOR_TEAM3)
340                         {
341                                 if(c3 >= 0)
342                                         c3 = c3 + 1;
343                         }
344                         if(head.team == COLOR_TEAM4)
345                         {
346                                 if(c4 >= 0)
347                                         c4 = c4 + 1;
348                         }
349                 }
350                 head = find(head, classname, "player");
351         }
352
353         // c1...c4 now have counts of each team
354         // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
355
356         smallestteam = 0;
357         smallestteam_count = 999;
358
359         // 2 gives priority to what team you're already on, 1 goes in order
360         balance_type = 1;
361
362         if(balance_type == 1)
363         {
364                 if(c1 >= 0 && c1 < smallestteam_count)
365                 {
366                         smallestteam = 1;
367                         smallestteam_count = c1;
368                 }
369                 if(c2 >= 0 && c2 < smallestteam_count)
370                 {
371                         smallestteam = 2;
372                         smallestteam_count = c2;
373                 }
374                 if(c3 >= 0 && c3 < smallestteam_count)
375                 {
376                         smallestteam = 3;
377                         smallestteam_count = c3;
378                 }
379                 if(c4 >= 0 && c4 < smallestteam_count)
380                 {
381                         smallestteam = 4;
382                         smallestteam_count = c4;
383                 }
384         }
385         else
386         {
387                 if(c1 >= 0 && (c1 < smallestteam_count || 
388                                         (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
389                 {
390                         smallestteam = 1;
391                         smallestteam_count = c1;
392                 }
393                 if(c2 >= 0 && c2 < (c2 < smallestteam_count || 
394                                         (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
395                 {
396                         smallestteam = 2;
397                         smallestteam_count = c2;
398                 }
399                 if(c3 >= 0 && c3 < (c3 < smallestteam_count || 
400                                         (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
401                 {
402                         smallestteam = 3;
403                         smallestteam_count = c3;
404                 }
405                 if(c4 >= 0 && c4 < (c4 < smallestteam_count || 
406                                         (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
407                 {
408                         smallestteam = 4;
409                         smallestteam_count = c4;
410                 }
411         }
412
413         if(smallestteam == 1)
414         {
415                 selectedteam = COLOR_TEAM1 - 1;
416         }
417         if(smallestteam == 2)
418         {
419                 selectedteam = COLOR_TEAM2 - 1;
420         }
421         if(smallestteam == 3)
422         {
423                 selectedteam = COLOR_TEAM3 - 1;
424         }
425         if(smallestteam == 4)
426         {
427                 selectedteam = COLOR_TEAM4 - 1;
428         }
429
430         SetPlayerColors(pl, selectedteam);
431 }
432 */
433 /*QUAKED dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
434 Control point for Domination gameplay.
435 */
436 void() dom_controlpoint =
437 {
438         if(!cvar("g_domination"))
439         {
440                 remove(self);
441                 return;
442         }
443         self.think = dom_controlpoint_setup;
444         self.nextthink = time + 0.1;
445
446         if(!self.scale)
447                 self.scale = 0.6;
448
449         //if(!self.glow_size)
450         //      self.glow_size = cvar("g_domination_point_glow");
451         self.effects = self.effects | EF_FULLBRIGHT;    
452 };
453
454 // code from here on is just to support maps that don't have control point and team entities
455 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage) 
456 {
457         local entity oldself;
458         oldself = self;
459         self = spawn();
460         self.classname = "dom_team";
461         self.netname = teamname;
462         self.cnt = teamcolor;
463         self.model = pointmodel;
464         self.skin = pointskin;
465         self.noise = capsound;
466         self.noise1 = capnarration;
467         self.message = capmessage;
468
469         // this code is identical to dom_team
470         setmodel(self, self.model);
471         self.mdl = self.model;
472         self.dmg = self.modelindex;
473         self.model = "";
474         self.modelindex = 0;
475         // this would have to be changed if used in quakeworld
476         self.team = self.cnt + 1;
477
478         //eprint(self);
479         self = oldself;
480 };
481
482 void(vector org) dom_spawnpoint =
483 {
484         local entity oldself;
485         oldself = self;
486         self = spawn();
487         self.classname = "dom_controlpoint";
488         self.think = dom_controlpoint;
489         self.nextthink = time;
490         self.origin = org;
491         dom_controlpoint();
492         self = oldself;
493 };
494
495 // spawn some default teams if the map is not set up for domination
496 void() dom_spawnteams =
497 {
498         float numteams;
499
500         numteams = cvar("g_domination_default_teams");
501         // LordHavoc: edit this if you want to change defaults
502         dom_spawnteam("Red", 4, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
503         dom_spawnteam("Blue", 13, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
504         if(numteams > 2)
505                 dom_spawnteam("Pink", 9, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
506         if(numteams > 3)
507                 dom_spawnteam("Yellow", 12, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
508         dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
509 };
510
511 void() dom_delayedinit =
512 {
513         local entity head;
514
515         self.think = SUB_Remove;
516         self.nextthink = time;
517         // if no teams are found, spawn defaults
518         if (find(world, classname, "dom_team") == world)
519                 dom_spawnteams();
520         // if no control points are found, spawn defaults
521         if (find(world, classname, "dom_controlpoint") == world)
522         {
523                 // here follow default domination points for each map
524                 /*
525                 if (world.model == "maps/e1m1.bsp")
526                 {
527                         dom_spawnpoint('0 0 0');
528                 }
529                 else
530                 */
531                 {
532                         // if no supported map was found, make every deathmatch spawn a point
533                         head = find(world, classname, "info_player_deathmatch");
534                         while (head)
535                         {
536                                 dom_spawnpoint(head.origin);
537                                 head = find(head, classname, "info_player_deathmatch");
538                         }
539                 }
540         }
541 };
542
543 void() dom_init =
544 {
545         local entity e;
546         // we have to precache default models/sounds even if they might not be
547         // used because worldspawn is executed before any other entities are read,
548         // so we don't even know yet if this map is set up for domination...
549         precache_model("models/domination/dom_red.md3");
550         precache_model("models/domination/dom_blue.md3");
551         precache_model("models/domination/dom_pink.md3");
552         precache_model("models/domination/dom_yellow.md3");
553         precache_model("models/domination/dom_unclaimed.md3");
554         precache_sound("domination/claim.wav");
555         e = spawn();
556         e.think = dom_delayedinit;
557         e.nextthink = time + 0.1;
558
559         // teamplay is always on in domination, defaults to hurt self but not teammates
560         //if(!cvar("teamplay"))
561         //      cvar_set("teamplay", "3");
562 };
563