]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/gamec/teamplay.c
more changes from Wazat
[divverent/nexuiz.git] / qcsrc / gamec / teamplay.c
1 float COLOR_TEAM1       = 5;  // red
2 float COLOR_TEAM2       = 14; // blue
3 float COLOR_TEAM3       = 4;  // green
4 float COLOR_TEAM4       = 13; // yellow
5
6
7 float GAME_DEATHMATCH           = 1;
8 float GAME_TEAM_DEATHMATCH      = 2;
9 float GAME_DOMINATION           = 3;
10 float GAME_CTF                          = 4;
11 float GAME_RUNEMATCH            = 5;
12
13 // client counts for each team
14 float c1, c2, c3, c4;
15 // # of bots on those teams
16 float cb1, cb2, cb3, cb4;
17
18
19 float g_domination, g_ctf, g_tdm;
20
21 float   audit_teams_time;
22
23
24 string TeamName(float t)
25 {
26         // fixme: Search for team entities and get their .netname's!
27         if(t == COLOR_TEAM1)
28                 return "Red Team";
29         if(t == COLOR_TEAM2)
30                 return "Blue Team";
31         if(t == COLOR_TEAM3)
32                 return "Green Team";
33         if(t == COLOR_TEAM4)
34                 return "Yellow Team";
35         return "Neutral Team";
36 }
37 string ColoredTeamName(float t)
38 {
39         // fixme: Search for team entities and get their .netname's!
40         if(t == COLOR_TEAM1)
41                 return "^1Red Team^7";
42         if(t == COLOR_TEAM2)
43                 return "^4Blue Team^7";
44         if(t == COLOR_TEAM3)
45                 return "^2Green Team^7";
46         if(t == COLOR_TEAM4)
47                 return "^3Yellow Team^7";
48         return "Neutral Team";
49 }
50 string TeamNoName(float t)
51 {
52         // fixme: Search for team entities and get their .netname's!
53         if(t == 1)
54                 return "Red Team";
55         if(t == 2)
56                 return "Blue Team";
57         if(t == 3)
58                 return "Green Team";
59         if(t == 4)
60                 return "Yellow Team";
61         return "Neutral Team";
62 }
63
64 void dom_init();
65 void ctf_init();
66 void runematch_init();
67 void tdm_init();
68
69
70 void ResetGameCvars()
71 {
72         cvar_set("g_tdm", "0");
73         cvar_set("g_domination", "0");
74         cvar_set("g_ctf", "0");
75         cvar_set("g_runematch", "0");
76         cvar_set("teamplay", "0");
77
78
79         cvar_set("exit_cfg", "");
80 }
81
82 void ActivateTeamplay()
83 {
84         float teamplay_default;
85         teamplay_default = cvar("teamplay_default");
86
87         if(teamplay_default)
88                 cvar_set("teamplay", ftos(teamplay_default));
89         else
90                 cvar_set("teamplay", "3");
91 }
92
93 string gamemode_name;
94 float teams_matter;
95
96 void InitGameplayMode()
97 {
98         float fraglimit_override, timelimit_override;
99
100         game = cvar ("gamecfg");        // load game options
101
102         // game cvars get reset before map changes
103         // then map's cfg sets them as desired
104
105         // FIXME: also set a message or game mode name to print to players when the join
106
107         timelimit_override = cvar("timelimit_override");
108
109         if(game == GAME_DOMINATION || cvar("g_domination"))
110         {
111                 game = GAME_DOMINATION;
112                 cvar_set("g_domination", "1");
113
114                 ActivateTeamplay();
115
116                 fraglimit_override = cvar("g_domination_point_limit");
117
118                 gamemode_name = "Domination";
119                 teams_matter = 1;
120         }
121         else if(game == GAME_CTF || cvar("g_ctf"))
122         {
123                 game = GAME_CTF;
124                 cvar_set("g_ctf", "1");
125
126                 ActivateTeamplay();
127
128                 fraglimit_override = cvar("g_ctf_capture_limit");
129
130                 gamemode_name = "Capture the Flag";
131                 teams_matter = 1;
132         }
133         else if(game == GAME_RUNEMATCH || cvar("g_runematch"))
134         {
135                 game = GAME_RUNEMATCH;
136                 cvar_set("g_runematch", "1");
137
138                 if(cvar("deathmatch_force_teamplay"))
139                         ActivateTeamplay();
140
141                 fraglimit_override = cvar("g_runematch_point_limit");
142
143                 gamemode_name = "Rune Match";
144                 if(cvar("teamplay"))
145                         teams_matter = 1;
146                 else
147                         teams_matter = 0;
148         }
149         else if(game == GAME_DEATHMATCH || game == GAME_TEAM_DEATHMATCH || cvar("g_tdm"))
150         {
151                 if(!cvar("deathmatch"))
152                         cvar_set("deathmatch", "1");
153
154
155                 if(game == GAME_TEAM_DEATHMATCH || cvar("g_tdm") || cvar("deathmatch_force_teamplay"))
156                 {
157                         game = GAME_TEAM_DEATHMATCH;
158                         gamemode_name = "Team Deathmatch";
159                         ActivateTeamplay();
160                         teams_matter = 1;
161                 }
162                 else
163                 {
164                         game = GAME_DEATHMATCH;
165                         gamemode_name = "Deathmatch";
166                         teams_matter = 0;
167                 }
168
169                 fraglimit_override = cvar("fraglimit_override");
170         }
171         else
172         {
173                 // we can only assume...
174                 gamemode_name = "Deathmatch";
175                 teams_matter = 0;
176         }
177 /*      else if(game == GAME_TEAM_DEATHMATCH)
178         {
179                 if(!cvar("deathmatch"))
180                         cvar_set("deathmatch", "1");
181
182                 //if(!cvar("teamplay"))
183                 //      cvar_set("teamplay", "3");
184                 ActivateTeamplay();
185
186                 fraglimit_override = cvar("fraglimit_override");
187         }*/
188
189         // enforce the server's universal frag/time limits
190         if(fraglimit_override)
191                 cvar_set("fraglimit", ftos(fraglimit_override));
192         if(timelimit_override)
193                 cvar_set("timelimit", ftos(timelimit_override));
194
195         if (game == GAME_DOMINATION)//cvar("g_domination"))
196                 dom_init();
197         else if (game == GAME_CTF)//cvar("g_ctf"))
198                 ctf_init();
199         else if (game == GAME_RUNEMATCH)//cvar("g_runematch"))
200                 runematch_init();
201         else if (game == GAME_TEAM_DEATHMATCH)//cvar("g_runematch"))
202                 tdm_init();
203 }
204
205 void PrintWelcomeMessage(entity pl)
206 {
207         string s, grap_msg, temp;
208         float colored;
209
210         if(self.welcomemessage_time < time)
211                 return;
212         if(self.welcomemessage_time2 > time)
213                 return;
214         self.welcomemessage_time2 = time + 0.8;
215
216         colored = 1;
217         if(colored)
218         {
219                 if(cvar("g_grappling_hook"))
220                         grap_msg = strzone("\n\nBind a key to ^1+hook^8 to use the grappling hook\n");
221
222                 s = strcat("Match type is ^1", gamemode_name, "^8\n");
223                 s = strzone(s);
224
225                 temp = strcat(
226                         "\n\n\n^8Welcome, ", pl.netname, "\n",
227                         s
228                         );
229                 temp = strzone(temp);
230
231                 if(teams_matter)
232                 {
233                         s = strcat(temp,
234                         "You are on ", ColoredTeamName(pl.team), "^8\n\n",
235                         "Go to ^1Menu->Options->Player^8 to change your name, model & team\n",
236                         grap_msg
237                         );
238                 }
239                 else
240                 {
241                         s = strcat(temp,
242                         "Go to ^1Menu->Options->Player^8 to change your name & model\n",
243                         grap_msg
244                         );
245                 }
246         }
247         else
248         {
249                 if(cvar("g_grappling_hook"))
250                         grap_msg = strzone("\n\nBind a key to +hook to use the grappling hook\n");
251
252                 s = strcat("Match type is ", gamemode_name, "\n");
253                 s = strzone(s);
254
255                 temp = strcat(
256                         "\n\n\nWelcome, ", pl.netname, "\n",
257                         s
258                         );
259                 temp = strzone(temp);
260
261                 if(teams_matter)
262                 {
263                         s = strcat(temp,
264                         "You are on ", TeamName(pl.team), "\n\n",
265                         "Go to Menu->Options->Player to\nchange your name, model & team\n",
266                         grap_msg
267                         );
268                 }
269                 else
270                 {
271                         s = strcat(temp,
272                         "Go to Menu->Options->Player to change your name & model\n",
273                         grap_msg
274                         );
275                 }
276         }
277
278         centerprint(pl, s);
279         //sprint(pl, s);
280
281         strunzone(temp);
282 }
283
284
285 void SetPlayerColors(entity pl, float color)
286 {
287         /*string s;
288         s = ftos(cl);
289         stuffcmd(pl, strcat("color ", s, " ", s, "\n")  );
290         pl.team = cl + 1;
291         //pl.clientcolors = pl.clientcolors - (pl.clientcolors & 15) + cl;
292         pl.clientcolors = 16*cl + cl;*/
293
294         setcolor(pl, 16*color + color);
295 }
296
297 void SetPlayerTeam(entity pl, float t, float s, float noprint)
298 {
299         float color;
300         if(t == 4)
301                 color = COLOR_TEAM4 - 1;
302         else if(t == 3)
303                 color = COLOR_TEAM3 - 1;
304         else if(t == 2)
305                 color = COLOR_TEAM2 - 1;
306         else
307                 color = COLOR_TEAM1 - 1;
308         setcolor(pl, 16*color + color);
309
310         if(!noprint && t != s)
311         {
312                 //bprint(strcat(pl.netname, " has changed to ", TeamNoName(t), "\n"));
313                 bprint(strcat(pl.netname, " has changed from ", TeamNoName(s), " to ", TeamNoName(t), "\n"));
314         }
315 }
316
317
318
319
320
321
322 // set c1...c4 to show what teams are allowed
323 void CheckAllowedTeams ()
324 {
325         string teament_name;
326         float dm;
327         entity head;
328
329 //      if(!dom && !ctf)
330 //              dm = 1;
331
332         c1 = c2 = c3 = c4 = -1;
333         cb1 = cb2 = cb3 = cb4 = 0;
334
335         if(g_domination)
336                 teament_name = "dom_team";
337         else if(g_ctf)
338                 teament_name = "ctf_team";
339         else if(g_tdm)
340                 teament_name = "tdm_team";
341         else
342         {
343                 // cover anything else by treating it like tdm with no teams spawned
344                 dm = cvar("g_tdm_teams");
345                 if(dm < 2)
346                         error("g_tdm_teams < 2, not enough teams to play team deathmatch\n");
347
348                 if(dm >= 4)
349                 {
350                         c1 = c2 = c3 = c4 = 0;
351                 }
352                 else if(dm >= 3)
353                 {
354                         c1 = c2 = c3 = 0;
355                 }
356                 else// if(dm >= 2)
357                 {
358                         c1 = c2 = 0;
359                 }
360                 return;
361         }
362
363         // first find out what teams are allowed
364         head = find(world, classname, teament_name);
365         while(head)
366         {
367                 if(!(g_domination && head.netname == ""))
368                 {
369                         if(head.team == COLOR_TEAM1)
370                         {
371                                         c1 = 0;
372                         }
373                         if(head.team == COLOR_TEAM2)
374                         {
375                                         c2 = 0;
376                         }
377                         if(head.team == COLOR_TEAM3)
378                         {
379                                         c3 = 0;
380                         }
381                         if(head.team == COLOR_TEAM4)
382                         {
383                                         c4 = 0;
384                         }
385                 }
386                 head = find(head, classname, teament_name);
387         }
388 }
389
390 // c1...c4 should be set to -1 (not allowed) or 0 (allowed).
391 // teams that are allowed will now have their player counts stored in c1...c4
392 void GetTeamCounts(entity ignore)
393 {
394         entity head;
395         // now count how many players are on each team already
396
397         // FIXME: also find and memorize the lowest-scoring bot on each team (in case players must be shuffled around)
398         // also remember the lowest-scoring player
399
400         head = find(world, classname, "player");
401         while(head)
402         {
403                 if(head != ignore)// && head.netname != "")
404                 {
405                         if(head.team == COLOR_TEAM1)
406                         {
407                                 if(c1 >= 0)
408                                 {
409                                         c1 = c1 + 1;
410                                         cb1 = cb1 + 1;
411                                 }
412                         }
413                         if(head.team == COLOR_TEAM2)
414                         {
415                                 if(c2 >= 0)
416                                 {
417                                         c2 = c2 + 1;
418                                         cb2 = cb2 + 1;
419                                 }
420                         }
421                         if(head.team == COLOR_TEAM3)
422                         {
423                                 if(c3 >= 0)
424                                 {
425                                         c3 = c3 + 1;
426                                         cb3 = cb3 + 1;
427                                 }
428                         }
429                         if(head.team == COLOR_TEAM4)
430                         {
431                                 if(c4 >= 0)
432                                 {
433                                         c4 = c4 + 1;
434                                         cb4 = cb4 + 1;
435                                 }
436                         }
437                 }
438                 head = find(head, classname, "player");
439         }
440 }
441
442 // returns # of smallest team (1, 2, 3, 4)
443 // NOTE: Assumes CheckAllowedTeams has already been called!
444 float FindSmallestTeam(entity pl, float ignore_pl)
445 {
446         float totalteams, smallestteam, smallestteam_count, balance_type;
447         totalteams = 0;
448
449         // find out what teams are available
450         //CheckAllowedTeams();
451
452         // make sure there are at least 2 teams to join
453         if(c1 >= 0)
454                 totalteams = totalteams + 1;
455         if(c2 >= 0)
456                 totalteams = totalteams + 1;
457         if(c3 >= 0)
458                 totalteams = totalteams + 1;
459         if(c4 >= 0)
460                 totalteams = totalteams + 1;
461
462         if(totalteams <= 1)
463         {
464                 if(g_domination)
465                         error("Too few teams available for domination\n");
466                 else if(g_ctf)
467                         error("Too few teams available for ctf\n");
468                 else
469                         error("Too few teams available for team deathmatch\n");
470         }
471
472         
473         // count how many players are in each team
474         if(ignore_pl)
475                 GetTeamCounts(world);
476         else
477                 GetTeamCounts(pl);
478
479
480
481         // c1...c4 now have counts of each team
482         // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
483
484         smallestteam = 0;
485         smallestteam_count = 999;
486
487         // 2 gives priority to what team you're already on, 1 goes in order
488         // 2 doesn't seem to work though...
489         balance_type = 1;
490
491         if(balance_type == 1)
492         {
493                 if(c1 >= 0 && c1 < smallestteam_count)
494                 {
495                         smallestteam = 1;
496                         smallestteam_count = c1;
497                 }
498                 if(c2 >= 0 && c2 < smallestteam_count)
499                 {
500                         smallestteam = 2;
501                         smallestteam_count = c2;
502                 }
503                 if(c3 >= 0 && c3 < smallestteam_count)
504                 {
505                         smallestteam = 3;
506                         smallestteam_count = c3;
507                 }
508                 if(c4 >= 0 && c4 < smallestteam_count)
509                 {
510                         smallestteam = 4;
511                         smallestteam_count = c4;
512                 }
513         }
514         else
515         {
516                 if(c1 >= 0 && (c1 < smallestteam_count || 
517                                         (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
518                 {
519                         smallestteam = 1;
520                         smallestteam_count = c1;
521                 }
522                 if(c2 >= 0 && c2 < (c2 < smallestteam_count || 
523                                         (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
524                 {
525                         smallestteam = 2;
526                         smallestteam_count = c2;
527                 }
528                 if(c3 >= 0 && c3 < (c3 < smallestteam_count || 
529                                         (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
530                 {
531                         smallestteam = 3;
532                         smallestteam_count = c3;
533                 }
534                 if(c4 >= 0 && c4 < (c4 < smallestteam_count || 
535                                         (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
536                 {
537                         smallestteam = 4;
538                         smallestteam_count = c4;
539                 }
540         }
541
542         return smallestteam;
543 }
544
545 float JoinBestTeam(entity pl, float only_return_best)
546 {
547         float smallest, selectedteam;
548
549         g_domination = cvar("g_domination");
550         g_ctf = cvar("g_ctf");
551
552         // don't join a team if we're not playing a team game
553         if(!cvar("teamplay") && !g_domination && !g_ctf)
554                 return 0;
555
556         // find out what teams are available
557         CheckAllowedTeams();
558
559         // if we don't care what team he ends up on, put him on whatever team he entered as.
560         // if he's not on a valid team, then let other code put him on the smallest team
561         if(!cvar("balance_teams") && !cvar("force_balance"))
562         {
563                 if(     c1 >= 0 && pl.team == COLOR_TEAM1)
564                         selectedteam = pl.team;
565                 else if(c2 >= 0 && pl.team == COLOR_TEAM2)
566                         selectedteam = pl.team;
567                 else if(c3 >= 0 && pl.team == COLOR_TEAM3)
568                         selectedteam = pl.team;
569                 else if(c4 >= 0 && pl.team == COLOR_TEAM4)
570                         selectedteam = pl.team;
571                 else
572                         selectedteam = -1;
573                 if(selectedteam > 0)
574                 {
575                         if(!only_return_best)
576                                 SetPlayerColors(pl, selectedteam - 1);
577                         return selectedteam;
578                 }
579                 // otherwise end up on the smallest team (handled below)
580         }
581
582         smallest = FindSmallestTeam(pl, TRUE);
583
584
585         if(!only_return_best)
586         {               
587                 if(smallest == 1)
588                 {
589                         SetPlayerColors(pl, COLOR_TEAM1 - 1);
590                 }
591                 else if(smallest == 2)
592                 {
593                         SetPlayerColors(pl, COLOR_TEAM2 - 1);
594                 }
595                 else if(smallest == 3)
596                 {
597                         SetPlayerColors(pl, COLOR_TEAM3 - 1);
598                 }
599                 else if(smallest == 4)
600                 {
601                         SetPlayerColors(pl, COLOR_TEAM4 - 1);
602                 }
603                 else
604                         error("smallest team: invalid team\n");
605         }
606
607         return smallest;
608 }
609
610
611 void SV_ChangeTeam(float color)
612 {
613         float scolor, dcolor, steam, dteam, dbotcount, scount, dcount;
614
615         scolor = self.clientcolors & 15;
616         dcolor = color & 15;
617
618         if(scolor == COLOR_TEAM1 - 1)
619                 steam = 1;
620         else if(scolor == COLOR_TEAM2 - 1)
621                 steam = 2;
622         else if(scolor == COLOR_TEAM3 - 1)
623                 steam = 3;
624         else if(scolor == COLOR_TEAM4 - 1)
625                 steam = 4;
626         if(dcolor == COLOR_TEAM1 - 1)
627                 dteam = 1;
628         else if(dcolor == COLOR_TEAM2 - 1)
629                 dteam = 2;
630         else if(dcolor == COLOR_TEAM3 - 1)
631                 dteam = 3;
632         else if(dcolor == COLOR_TEAM4 - 1)
633                 dteam = 4;
634
635         // not changing teams
636         if(scolor == dcolor)
637         {
638                 //setcolor(self, 16*color + color);
639                 //bprint("same team change\n");
640                 SetPlayerTeam(self, dteam, steam, TRUE);
641                 return;
642         }
643
644         if(cvar("teamplay"))
645         {
646                 if(cvar("g_changeteam_banned"))
647                 {
648                         sprint(self, "Team changes not allowed\n");
649                         return; // changing teams is not allowed
650                 }
651
652                 if(cvar("g_balance_teams_prevent_imbalance"))
653                 {
654                         // only allow changing to a smaller or equal size team
655
656                         // find out what teams are available
657                         CheckAllowedTeams();
658                         // count how many players on each team
659                         GetTeamCounts(world);
660
661                         // get desired team
662                         if(dteam == 1 && c1 >= 0)//dcolor == COLOR_TEAM1 - 1)
663                         {
664                                 dcount = c1;
665                                 dbotcount = cb1;
666                         }
667                         else if(dteam == 2 && c2 >= 0)//dcolor == COLOR_TEAM2 - 1)
668                         {
669                                 dcount = c2;
670                                 dbotcount = cb2;
671                         }
672                         else if(dteam == 3 && c3 >= 0)//dcolor == COLOR_TEAM3 - 1)
673                         {
674                                 dcount = c3;
675                                 dbotcount = cb3;
676                         }
677                         else if(dteam == 4 && c4 >= 0)//dcolor == COLOR_TEAM4 - 1)
678                         {
679                                 dcount = c4;
680                                 dbotcount = cb4;
681                         }
682                         else
683                         {
684                                 sprint(self, "Cannot change to an invalid team\n");
685
686                                 return;
687                         }
688
689                         // get starting team
690                         if(steam == 1)//scolor == COLOR_TEAM1 - 1)
691                                 scount = c1;
692                         else if(steam == 2)//scolor == COLOR_TEAM2 - 1)
693                                 scount = c2;
694                         else if(steam == 3)//scolor == COLOR_TEAM3 - 1)
695                                 scount = c3;
696                         else if(steam == 4)//scolor == COLOR_TEAM4 - 1)
697                                 scount = c4;
698
699                         if(scount) // started at a valid, nonempty team
700                         {
701                                 // check if we're trying to change to a larger team that doens't have bots to swap with
702                                 if(dcount >= scount && dbotcount <= 0)
703                                 {
704                                         sprint(self, "Cannot change to a larger team\n");
705                                         return; // can't change to a larger team
706                                 }
707                         }
708                 }
709         }
710
711         // reduce frags during a team change
712         self.frags = floor(self.frags * (cvar("g_changeteam_fragtransfer") / 100));
713
714 //      bprint(strcat("allow change teams from ", ftos(steam), " to ", ftos(dteam), "\n"));
715
716         SetPlayerTeam(self, dteam, steam, FALSE);
717         //setcolor(self, 16*dcolor + dcolor);
718 }
719
720
721 void ShufflePlayerOutOfTeam (float source_team)
722 {
723         float smallestteam, smallestteam_count, steam;
724         float lowest_bot_score, lowest_player_score;
725         entity head, lowest_bot, lowest_player, selected;
726
727         smallestteam = 0;
728         smallestteam_count = 999;
729
730         if(c1 >= 0 && c1 < smallestteam_count)
731         {
732                 smallestteam = 1;
733                 smallestteam_count = c1;
734         }
735         if(c2 >= 0 && c2 < smallestteam_count)
736         {
737                 smallestteam = 2;
738                 smallestteam_count = c2;
739         }
740         if(c3 >= 0 && c3 < smallestteam_count)
741         {
742                 smallestteam = 3;
743                 smallestteam_count = c3;
744         }
745         if(c4 >= 0 && c4 < smallestteam_count)
746         {
747                 smallestteam = 4;
748                 smallestteam_count = c4;
749         }
750
751         if(!smallestteam)
752         {
753                 bprint("warning: no smallest team\n");
754                 return;
755         }
756
757         if(source_team == 1)
758                 steam = COLOR_TEAM1;
759         else if(source_team == 2)
760                 steam = COLOR_TEAM2;
761         else if(source_team == 3)
762                 steam = COLOR_TEAM3;
763         else if(source_team == 4)
764                 steam = COLOR_TEAM4;
765
766         lowest_bot = world;
767         lowest_bot_score = 9999;
768         lowest_player = world;
769         lowest_player_score = 9999;
770
771         // find the lowest-scoring player & bot of that team
772         head = find(world, classname, "player");
773         while(head)
774         {
775                 if(head.team == steam)
776                 {
777                         if(head.isbot)
778                         {
779                                 if(head.frags < lowest_bot_score)
780                                 {
781                                         lowest_bot = head;
782                                         lowest_bot_score = head.frags;
783                                 }
784                         }
785                         else
786                         {
787                                 if(head.frags < lowest_player_score)
788                                 {
789                                         lowest_player = head;
790                                         lowest_player_score = head.frags;
791                                 }
792                         }
793                 }
794                 head = find(head, classname, "player");
795         }
796
797         // prefers to move a bot...
798         if(lowest_bot != world)
799                 selected = lowest_bot;
800         // but it will move a player if it has to
801         else
802                 selected = lowest_player;
803         // don't do anything if it couldn't find anyone
804         if(!selected)
805         {
806                 bprint("warning: couldn't find a player to move from team\n");
807                 return;
808         }
809
810         // smallest team gains a member
811         if(smallestteam == 1)
812         {
813                 c1 = c1 + 1;
814         }
815         else if(smallestteam == 2)
816         {
817                 c2 = c2 + 1;
818         }
819         else if(smallestteam == 3)
820         {
821                 c3 = c3 + 1;
822         }
823         else if(smallestteam == 4)
824         {
825                 c4 = c4 + 1;
826         }
827         else
828         {
829                 bprint("warning: destination team invalid\n");
830                 return;
831         }
832         // source team loses a member
833         if(source_team == 1)
834         {
835                 c1 = c1 + 1;
836         }
837         else if(source_team == 2)
838         {
839                 c2 = c2 + 2;
840         }
841         else if(source_team == 3)
842         {
843                 c3 = c3 + 3;
844         }
845         else if(source_team == 4)
846         {
847                 c4 = c4 + 4;
848         }
849         else
850         {
851                 bprint("warning: source team invalid\n");
852                 return;
853         }
854
855         // move the player to the new team
856         SetPlayerTeam(selected, smallestteam, source_team, FALSE);
857 }
858
859 // part of g_balance_teams_force
860 // occasionally perform an audit of the teams to make 
861 // sure they're more or less balanced in player count.
862 void AuditTeams()
863 {
864         float numplayers, numteams, average;
865         if(!cvar("g_balance_teams_force"))
866                 return;
867         if(!cvar("teamplay"))
868                 return;
869
870         if(audit_teams_time > time)
871                 return;
872
873         audit_teams_time = time + 4 + random();
874
875 //      bprint("Auditing teams\n");
876
877         CheckAllowedTeams();
878         GetTeamCounts(world);
879
880
881         numteams = numplayers = 0;
882         if(c1 >= 0)
883         {
884                 numteams = numteams + 1;
885                 numplayers = numplayers + c1;
886         }
887         if(c2 >= 0)
888         {
889                 numteams = numteams + 1;
890                 numplayers = numplayers + c2;
891         }
892         if(c3 >= 0)
893         {
894                 numteams = numteams + 1;
895                 numplayers = numplayers + c3;
896         }
897         if(c4 >= 0)
898         {
899                 numteams = numteams + 1;
900                 numplayers = numplayers + c4;
901         }
902
903         if(numplayers <= 0)
904                 return; // no players to move around
905         if(numteams < 2)
906                 return; // don't bother shuffling if for some reason there aren't any teams
907
908         average = ceil(numplayers / numteams);
909
910         if(average <= 0)
911                 return; // that's weird...
912
913         if(c1 && c1 > average)
914         {
915                 bprint("Rebalancing Teams\n");
916                 //bprint("Shuffle from team 1\n");
917                 ShufflePlayerOutOfTeam(1);
918         }
919         if(c2 && c2 > average)
920         {
921                 bprint("Rebalancing Teams\n");
922                 //bprint("Shuffle from team 2\n");
923                 ShufflePlayerOutOfTeam(2);
924         }
925         if(c3 && c3 > average)
926         {
927                 bprint("Rebalancing Teams\n");
928                 //bprint("Shuffle from team 3\n");
929                 ShufflePlayerOutOfTeam(3);
930         }
931         if(c4 && c4 > average)
932         {
933                 bprint("Rebalancing Teams\n");
934                 //bprint("Shuffle from team 4\n");
935                 ShufflePlayerOutOfTeam(4);
936         }
937
938         // if teams are still unbalanced, balance them further in the next audit,
939         // which will happen sooner (keep doing rapid audits until things are in order)
940         audit_teams_time = time + 0.7 + random()*0.3;
941 }
942
943
944
945 /*void(entity e, float first) UpdateTeamScore =
946 {
947         clientno = e.FIXME;
948         if(first)
949         {
950                 WriteByte (MSG_ALL, SVC_UPDATENAME);
951                 WriteByte (MSG_ALL, clientno);
952                 WriteString (MSG_ALL, e.netname);
953
954                 WriteByte (MSG_ALL, SVC_UPDATECOLORS);
955                 WriteByte (MSG_ALL, clientno);
956                 WriteByte (MSG_ALL, e.b_shirt * 16 + who.b_pants);
957         }
958
959         WriteByte (MSG_ALL, SVC_UPDATEFRAGS);
960         WriteByte (MSG_ALL, clientno);
961         WriteShort (MSG_ALL, e.frags + 10000);
962 };
963
964 */
965
966
967
968
969
970 void() tdm_team =
971 {
972         self.classname = "tdm_team";
973         self.team = self.cnt + 1;
974 };
975
976 // code from here on is just to support maps that don't have team entities
977 void tdm_spawnteam (string teamname, float teamcolor)
978 {
979         local entity oldself;
980         oldself = self;
981         self = spawn();
982         self.classname = "tdm_team";
983         self.netname = teamname;
984         self.cnt = teamcolor;
985
986         tdm_team();
987
988         self = oldself;
989 };
990
991 // spawn some default teams if the map is not set up for tdm
992 void() tdm_spawnteams =
993 {
994         float numteams;
995
996         numteams = cvar("g_tdm_teams");
997
998         tdm_spawnteam("Red", 4);
999         tdm_spawnteam("Blue", 13);
1000 };
1001
1002 void() tdm_delayedinit =
1003 {
1004         self.think = SUB_Remove;
1005         self.nextthink = time;
1006         // if no teams are found, spawn defaults
1007         if (find(world, classname, "tdm_team") == world)
1008                 tdm_spawnteams();
1009 };
1010
1011 void() tdm_init =
1012 {
1013         local entity e;
1014         e = spawn();
1015         e.think = tdm_delayedinit;
1016         e.nextthink = time + 0.1;
1017 };
1018
1019