]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into z411/bai-server
authorz411 <z411@omaera.org>
Fri, 1 Nov 2024 23:24:21 +0000 (20:24 -0300)
committerz411 <z411@omaera.org>
Fri, 1 Nov 2024 23:24:21 +0000 (20:24 -0300)
69 files changed:
1  2 
commands.cfg
gamemodes-server.cfg
notifications.cfg
qcsrc/client/hud/hud.qh
qcsrc/client/hud/panel/_mod.inc
qcsrc/client/hud/panel/_mod.qh
qcsrc/client/hud/panel/centerprint.qc
qcsrc/client/hud/panel/chat.qc
qcsrc/client/hud/panel/infomessages.qc
qcsrc/client/hud/panel/quickmenu.qc
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/client/hud/panel/timer.qc
qcsrc/client/hud/panel/weapons.qc
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/client/view.qc
qcsrc/common/ent_cs.qc
qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc
qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc
qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc
qcsrc/common/gamemodes/gamemode/race/sv_race.qc
qcsrc/common/mapinfo.qh
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/_mod.inc
qcsrc/common/mutators/mutator/_mod.qh
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
qcsrc/common/mutators/mutator/overkill/oknex.qc
qcsrc/common/net_linked.qh
qcsrc/common/notifications/all.inc
qcsrc/common/notifications/all.qc
qcsrc/common/notifications/all.qh
qcsrc/common/replicate.qh
qcsrc/common/scores.qh
qcsrc/common/sounds/all.inc
qcsrc/common/stats.qh
qcsrc/common/teams.qh
qcsrc/common/util.qc
qcsrc/common/weapons/weapon/devastator.qc
qcsrc/common/weapons/weapon/electro.qc
qcsrc/common/weapons/weapon/mortar.qc
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/common/weapons/weapon/vortex.qc
qcsrc/common/weapons/weapon/vortex.qh
qcsrc/ecs/systems/sv_physics.qc
qcsrc/server/chat.qc
qcsrc/server/chat.qh
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/command/cmd.qc
qcsrc/server/command/common.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/command/vote.qh
qcsrc/server/damage.qc
qcsrc/server/ipban.qc
qcsrc/server/items/items.qc
qcsrc/server/mutators/events.qh
qcsrc/server/round_handler.qc
qcsrc/server/round_handler.qh
qcsrc/server/scores.qc
qcsrc/server/teamplay.qc
qcsrc/server/teamplay.qh
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/weaponsystem.qc
qcsrc/server/world.qc
qcsrc/server/world.qh
xonotic-server.cfg

diff --cc commands.cfg
Simple merge
index 8863cc496f1ad6737628b5f59eb74357ce3e9c51,a98bab43e13c530dabda26c7cfc3eb261d3cad70..acbe800f34957ae6ebc7c671c60b458adb8556d3
@@@ -262,9 -263,9 +263,10 @@@ set g_ca_spectate_enemies 0 "allow elim
  set g_ca_warmup 10 "time players get to run around before the round starts"
  set g_ca_damage2score 100  "every this amount of damage done give players 1 point"
  set g_ca_round_timelimit 180 "round time limit in seconds"
 +set g_ca_round_stop 0 "freeze the game after round stops" // BaI mod
+ set g_ca_round_enddelay 1 "seconds of delay for score evaluation after round could end"
  set g_ca_teams_override 0
- set g_ca_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+ set g_ca_team_spawns 1 "when 1, players spawn from the team spawnpoints of the map, if any"
  set g_ca_teams 0
  set g_ca_prevent_stalemate 0 "When round time ends instead of instant stalemate give round win to the team with 1: most survivors. 2: most total health. 3: most survivors or if equal then most total health"
  set g_ca_weaponarena "most" "starting weapons - takes the same options as g_weaponarena"
@@@ -414,8 -414,7 +416,9 @@@ set g_freezetag_revive_nade 1 "Enable r
  set g_freezetag_revive_nade_health 40 "Amount of health player has if they revived from their own nade explosion"
  set g_freezetag_revive_time_to_score 1.5 "every this amount of seconds give players reviving a frozen teammate 1 point"
  set g_freezetag_round_timelimit 360 "round time limit in seconds"
 +set g_freezetag_round_stop 0 "freeze game when round ends" // BaI mod
 +set g_freezetag_round_respawn 0 // BaI mod
+ set g_freezetag_round_enddelay 1 "seconds of delay for score evaluation after round could end"
  set g_freezetag_revive_auto 1 "automatically revive frozen players after some time (g_freezetag_frozen_maxtime)"
  set g_freezetag_revive_auto_progress 1 "start the automatic reviving progress as soon as the player gets frozen"
  set g_freezetag_revive_auto_reducible 1 "reduce auto-revival time when frozen players are hit by enemies; set to -1 to reduce it even when they are hit by teammates"
@@@ -425,10 -423,9 +428,10 @@@ set g_freezetag_revive_respawn 1 "respa
  set g_freezetag_revive_spawnshield 1 "apply spawnshield for this time in seconds after the player has been revived"
  set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
  set g_freezetag_teams_override 0
- set g_freezetag_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+ set g_freezetag_team_spawns 1 "when 1, players spawn from the team spawnpoints of the map, if any"
  set g_freezetag_teams 0
 -set g_freezetag_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena"
 +// BaI mod changes this
 +set g_freezetag_weaponarena "0" "starting weapons - takes the same options as g_weaponarena"
  
  
  // ==========
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index d344b33ab369984a58a33a1998e5b4e01655a330,a4e1b64d78c9599c72deb40a4a64798c8183a277..fc34d1b846d826687ddc72cec3778ec1bc2356b8
@@@ -159,9 -157,11 +159,12 @@@ void HUD_Chat(
        if (autocvar_con_chat != floor(mySize.y / autocvar_con_chatsize - 0.5))
                cvar_set("con_chat", ftos(floor(mySize.y / autocvar_con_chatsize - 0.5)));
  
 +      //vector chatsize = '1 1 0' * autocvar_con_chatsize;
        if(autocvar__hud_configure)
        {
+               float alpha = 1; // engine can display chat only at full alpha
+               if (hud_configure_menu_open == 2 && highlightedPanel != panel)
+                       alpha = hud_fade_alpha; // fade only when the settings menu of another panel is open
                vector chatsize = '1 1 0' * autocvar_con_chatsize;
                if (cvar_string("con_chatrect_x") != "9001")
                        cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over alpha and such
index 5342c77c1339b2c4f83d7261fc597f217ee87921,5866271ef97c6944f64fd22aaece9099bc765190..898b000b5aa493e38ea8a115de3de23e2889054e
@@@ -122,20 -121,26 +124,29 @@@ void HUD_InfoMessages(
                                                break;
                                }
                                InfoMessage(s);
 -                      }
 +                      }*/
  
 -                      bool mutator_returnvalue = MUTATOR_CALLHOOK(DrawInfoMessages, pos, mySize, img_curr_group);
 -                      pos = M_ARGV(0, vector);
 -                      img_curr_group = M_ARGV(2, int);
 +                      //bool mutator_returnvalue = MUTATOR_CALLHOOK(DrawInfoMessages, pos, mySize, img_curr_group);
 +                      //pos = M_ARGV(0, vector);
 +                      //img_curr_group = M_ARGV(2, int);
  
 -                      if(!mutator_returnvalue)
 +                      if(entcs_GetWantsJoin(current_player))
                        {
-                               int tm = Team_IndexToTeam(entcs_GetWantsJoin(current_player));
-                               s = sprintf(_("^2You're queued to join the %s%s^2 team"), Team_ColorCode(tm), Team_ColorName(tm));
+                               int tm = entcs_GetWantsJoin(current_player);
+                               if(tm > 0)
+                               {
+                                       tm = Team_IndexToTeam(tm);
+                                       s = sprintf(_("^2You're queued to join the %s%s^2 team"), Team_ColorCode(tm), Team_ColorName(tm));
+                               }
+                               else if (tm < 0)
+                                       s = sprintf(_("^2You're queued to join any available team"));
+                               else
+                                       s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey(_("jump"), "+jump"));
+                               InfoMessage(s);
                        }
 +                      else
 +                              s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey(_("jump"), "+jump"));
 +                      InfoMessage(s);
                }
  
                if (time < STAT(GAMESTARTTIME))
                                InfoMessage(s);
                        }
                }
 +
 +              // z411
 +              if (motd_permanent != "")
 +                      InfoMessage(motd_permanent);
 +              
 +              MUTATOR_CALLHOOK(DrawInfoMessages, pos, mySize, img_curr_group);
++
+               if(autocvar_cl_race_checkpoint_splits_hud && !spectatee_status) {
+                       int lines[6];
+                       int ln = 5;
+                       // show up to race_nextcheckpoint (not including) or everything
+                       // if you are before start (0 or 254)
+                       // (except race_laptime != 0 for race, means next is
+                       // start+finish so don't show previous lap finish)
+                       int i;
+                       if (race_checkpoint != 0 && race_checkpoint != 254)
+                       { // middle of run/race
+                               i = race_checkpoint;
+                       }
+                       else if (ISGAMETYPE(RACE) && race_nextcheckpoint == 0)
+                       { // before start, but on race, so don't keep old finish visible
+                               i = 253;
+                       }
+                       else
+                       { // before start, not on race (cts), keep old run cps visible
+                               i = 255;
+                       }
+                       for (; ln >= 0 && i >= 0; --i)
+                       {
+                               if (race_checkpoint_splits[i])
+                               {
+                                       lines[ln] = i;
+                                       --ln;
+                               }
+                       }
+                       for (int j = 0; j < 6; ++j)
+                               InfoMessage(race_checkpoint_splits[lines[j]]);
+               }
        }
        else
        {
Simple merge
index 92e0f4ed0d51935374314977255453640bf6f134,af56134c2affa170ff7a142f5cb8b9c17336ee1b..c0f6ff5ff1c6c2df010cb021044366a80bdcd604
@@@ -44,22 -43,17 +44,27 @@@ const int MAX_SBT_FIELDS = MAX_SCORE
  PlayerScoreField sbt_field[MAX_SBT_FIELDS + 1];
  float sbt_field_size[MAX_SBT_FIELDS + 1];
  string sbt_field_title[MAX_SBT_FIELDS + 1];
+ float sbt_field_title_condense_factor[MAX_SBT_FIELDS + 1];
+ float sbt_field_title_width[MAX_SBT_FIELDS + 1];
  int sbt_num_fields;
+ float sbt_field_title_maxwidth;
+ float sbt_field_title_maxwidth_factor;
  
  string autocvar_hud_fontsize;
- string hud_fontsize_str;
  float max_namesize;
+ float name_field_index;
+ int sb_field_sizes_init;
  
 +vector duel_score_fontsize;
 +vector duel_name_fontsize;
 +vector duel_score_size;
 +vector team_score_fontsize;
 +vector team_name_fontsize;
 +vector team_score_size;
 +int total_medals;
 +
 +float autocvar_hud_panel_scoreboard_duel_weapon_scale = 1.25; // z411
 +
  float sbt_bg_alpha;
  float sbt_fg_alpha;
  float sbt_fg_alpha_self;
@@@ -149,11 -130,10 +144,11 @@@ string Label_getInfo(string label, int 
                SCO_LABEL(_("SCO^bctime"),        "bctime", "             ", _("Total amount of time holding the ball in Keepaway"));
                SCO_LABEL(_("SCO^caps"),          "caps", "               ", _("How often a flag (CTF) or a key (KeyHunt) was captured"));
                SCO_LABEL(_("SCO^captime"),       "captime", "            ", _("Time of fastest capture (CTF)"));
 +              SCO_LABEL(_("SCO^cn"),            "cn", "                 ", _("Country of player"));
                SCO_LABEL(_("SCO^deaths"),        "deaths", "             ", _("Number of deaths"));
-               SCO_LABEL(_("SCO^destroyed"),     "destroyed", "          ", _("Number of keys destroyed by pushing them into void"));
-               SCO_LABEL(_("SCO^damage"),        "dmg", "                ", _("The total damage done"));
-               SCO_LABEL(_("SCO^dmgtaken"),      "dmgtaken", "           ", _("The total damage taken"));
+               SCO_LABEL(_("SCO^destructions"),  "destructions", "       ", _("Number of keys destroyed by pushing them into void"));
+               SCO_LABEL(_("SCO^damage dealt"),  "dmg", "                ", _("The total damage dealt"));
+               SCO_LABEL(_("SCO^damage taken"),  "dmgtaken", "           ", _("The total damage taken"));
                SCO_LABEL(_("SCO^drops"),         "drops", "              ", _("Number of flag drops"));
                SCO_LABEL(_("SCO^elo"),           "elo", "                ", _("Player ELO"));
                SCO_LABEL(_("SCO^fastest"),       "fastest", "            ", _("Time of fastest lap (Race/CTS)"));
@@@ -1301,22 -1263,41 +1331,44 @@@ string Scoreboard_FixColumnWidth(int i
        return str;
  }
  
- void Scoreboard_initFieldSizes()
+ void Scoreboard_initFieldSizes(bool compress_more)
  {
+       if (compress_more)
+       {
+               float sbt_field_title_maxwidth_factor_prev = sbt_field_title_maxwidth_factor;
+               sbt_field_title_maxwidth_factor -= 0.05;
+               if (sbt_field_title_maxwidth * sbt_field_title_maxwidth_factor < 0.01 * vid_conwidth)
+               {
+                       sbt_field_title_maxwidth_factor = (0.01 * vid_conwidth) / sbt_field_title_maxwidth;
+                       if (sbt_field_title_maxwidth_factor_prev == sbt_field_title_maxwidth_factor)
+                               return;
+               }
+       }
+       else
+               sbt_field_title_maxwidth_factor = 1;
        for(int i = 0; i < sbt_num_fields; ++i)
        {
-               sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
-               Scoreboard_FixColumnWidth(i, "");
+               if (sbt_field[i] == SP_NAME)
+               {
+                       name_field_index = i;
+                       continue;
+               }
+               Scoreboard_FixColumnWidth(i, "", true);
        }
+       // update name field size in the end as it takes remaining space
+       Scoreboard_FixColumnWidth(name_field_index, "", true);
  }
  
 -vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players)
 +vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players, int team)
  {
        int i;
 +      string title_str;
 +      vector title_rgb;
        vector column_dim = eY * panel_size.y;
 +      
        if(other_players)
                column_dim.y -= 1.25 * hud_fontsize.y;
        vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
                if (sbt_highlight)
                        if (i % 2)
                                drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
++
 +              drawstring(pos + text_offset + text_offset_center, title_str, hud_fontsize, title_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
++
+               vector prev_drawfontscale = drawfontscale;
+               if (sbt_field_title_condense_factor[i])
+                       drawfontscale.x *= sbt_field_title_condense_factor[i];
+               drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
+               if (sbt_field_title_condense_factor[i])
+               {
+                       drawfontscale.x *= sbt_field_title_condense_factor[i];
+                       drawfontscale = prev_drawfontscale;
+               }
++
                pos.x += column_dim.x;
        }
        if(sbt_field[i] == SP_SEPARATOR)
        {
                pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
@@@ -1518,26 -1504,16 +1596,35 @@@ vector Scoreboard_DrawOthers(vector ite
                        continue;
                if(pl == ignored_pl)
                        continue;
 +              
 +              string flag_name = "";
 +              vector flag_size = '0 0 0';
 +              Scoreboard_GetField(pl, SP_COUNTRY, autocvar_hud_panel_scoreboard_scores_per_round);
 +              
 +              if(sbt_field_icon3 != "") {
 +                      sz = draw_getimagesize(sbt_field_icon3);
 +                      flag_name = sbt_field_icon3;
 +                      flag_size = vec2(hud_fontsize.x * (sz.x / sz.y), hud_fontsize.y);
 +              }
 +              
 +              if(entcs_GetWantsJoin(pl.sv_entnum))
 +              {
 +                      vector tmcolor = Team_ColorRGB(Team_IndexToTeam(entcs_GetWantsJoin(pl.sv_entnum)));
 +                      tmcolor -= tmcolor * sin(2*M_PI*time);
 +
 +                      drawstring(pos, "(Q)", hud_fontsize, tmcolor, sbt_fg_alpha, DRAWFLAG_NORMAL);
 +                      pos.x += stringwidth("(Q) ", true, hud_fontsize);
 +              }
  
+               if(entcs_GetWantsJoin(pl.sv_entnum))
+               {
+                       vector tmcolor = Team_ColorRGB(Team_IndexToTeam(entcs_GetWantsJoin(pl.sv_entnum)));
+                       tmcolor -= tmcolor * sin(2*M_PI*time);
+                       drawstring(pos, "(Q)", hud_fontsize, tmcolor, sbt_fg_alpha, DRAWFLAG_NORMAL);
+                       pos.x += stringwidth("(Q) ", true, hud_fontsize);
+               }
                field = "";
                if(this_team == NUM_SPECTATOR)
                {
@@@ -3083,8 -2632,10 +3197,12 @@@ void Scoreboard_Draw(
                pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
        }
  
 +      pos = Scoreboard_MedalStats_Draw(pos);
 +      
+       // if the name column is too small, try to compress all other field titles
+       if (sbt_field_size[name_field_index] < sbt_field_title_width[name_field_index] + hud_fontsize.x)
+               sb_field_sizes_init = 2;
        // draw scoreboard spectators before accuracy and item stats
        if (autocvar_hud_panel_scoreboard_spectators_position == 0) {
                pos = Scoreboard_Spectators_Draw(pos);
index 003f1c4b736662ff8a9754be77bfeda5881e2b39,ae796fbdd3a89a626542d1d3d257d362dff9a454..b1206791c419a772f9990c7e56005e52166cc94e
@@@ -149,13 -121,28 +149,26 @@@ void HUD_Timer(
        int overtimes = STAT(OVERTIMES);
  
        if(warmup_stage || autocvar__hud_configure)
-               subtext = _("Warmup");
+       {
+               if (STAT(WARMUP_TIMELIMIT) > 0)
+                       subtext = _("Warmup");
+               else
+               {
+                       Scoreboard_UpdatePlayerTeams(); // ensure numplayers is current
+                       if (srv_minplayers - numplayers > 0)
+                               subtext = _("Warmup: too few players!");
+                       else if (teamnagger && (ts_max - ts_min) >= teamnagger)
+                               subtext = _("Warmup: teams unbalanced!");
+                       else
+                               subtext = _("Warmup: no time limit");
+               }
+       }
        else if(STAT(TIMEOUT_STATUS) == 2)
                subtext = _("Timeout");
 -      else if (overtimes == -1)
 -              subtext = _("Sudden Death");
 -      else if(overtimes == 1)
 -              subtext = _("Overtime");
 -      else if (overtimes >= 2)
 +      else if(overtimes >= 2)
                subtext = sprintf(_("Overtime #%d"), overtimes);
 +      else if(overtimes != 0)
 +              subtext = _("Overtime");
  
        subtext_size  = vec2(mySize.x, mySize.y / 3);
        timer_size    = vec2(mySize.x, mySize.y - subtext_size.y);
Simple merge
Simple merge
Simple merge
Simple merge
index 3dfea8f98973ff4288dc0a527668e82242d950f1,cca9b70150df8d53d5b44c6032bb5d83df318a51..7666710d7c9daa9b210a36cf33c50c46ae9eb7ce
@@@ -151,18 -151,11 +151,19 @@@ ENTCS_PROP(CLIENTCOLORS, true, clientco
  ENTCS_PROP(FRAGS, true, frags, frags, ENTCS_SET_NORMAL,
        { WriteShort(chan, ent.frags); },
        { ent.frags = ReadShort(); })
 +      
 +ENTCS_PROP(COUNTRYCODE, true, countrycode, countrycode, ENTCS_SET_NORMAL,
 +      { WriteByte(chan, ent.countrycode); },
 +      { ent.countrycode = ReadByte(); })
 +
 +ENTCS_PROP(RANK, true, rank, rank, ENTCS_SET_NORMAL,
 +      { WriteString(chan, ent.rank); },
 +      { strcpy(ent.rank, ReadString()); })
  
+ // index of join queue team selection, max 127 because -1 means any available team
  ENTCS_PROP(WANTSJOIN, true, wants_join, wants_join, ENTCS_SET_NORMAL,
-       { WriteByte(chan, ent.wants_join); },
-       { ent.wants_join = ReadByte(); })
+       { WriteChar(chan, ent.wants_join); },
+       { ent.wants_join = ReadChar(); })
  
  // use sv_solid to avoid changing solidity state of entcs entities
  ENTCS_PROP(SOLID, true, sv_solid, solid, ENTCS_SET_NORMAL,
index 2c4a097d6f5fe4dde48b81bcc46ca9ba8d93d331,dcdc11414a3dc9f8e459e4e43201a7a71d891db4..bc9cdd30428ff20e38c3d51c23dd7431ee1b1045
@@@ -180,26 -158,35 +181,48 @@@ float CA_CheckWinner(
        if (!winner_team)
                winner_team = Team_GetWinnerAliveTeam();
        if (!winner_team)
+       {
+               // Dr. Jaska:
+               // reset delay time here only for consistency
+               // CA players currently have no known ways to resurrect
+               round_handler_ResetEndDelayTime();
                return 0;
+       }
+       // delay round ending a bit
+       if (autocvar_g_ca_round_enddelay
+               && round_handler_GetEndTime() > 0
+               && round_handler_GetEndTime() - time > 0) // don't delay past timelimit
+       {
+               if (round_handler_GetEndDelayTime() == -1)
+               {
+                       round_handler_SetEndDelayTime(min(time + autocvar_g_ca_round_enddelay, round_handler_GetEndTime()));
+                       return 0;
+               }
+               else if (round_handler_GetEndDelayTime() >= time)
+               {
+                       return 0;
+               }
+       }
  
 +      bool perfect = false;
        if(winner_team > 0)
        {
 +              entity tm = Team_GetTeam(winner_team);
 +              entity last_pl = ca_LastPlayer(winner_team);
 +              
 +              if(last_pl && Team_GetNumberOfPlayers(tm) >= 3) {
 +                      Give_Medal(last_pl, DEFENSE);
 +              }
 +              
                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
 +              if(fragsleft > 1) Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, APP_TEAM_NUM(winner_team, ANNCE_ROUND_TEAM_WIN));
                TeamScore_AddToTeam(winner_team, ST_CA_ROUNDS, +1);
 +              
 +              if (Team_GetNumberOfPlayers(tm) >= 3 &&
 +                      Team_GetNumberOfAlivePlayers(tm) == Team_GetNumberOfPlayers(tm))
 +                              perfect = true;
        }
        else if(winner_team == -1)
        {
index 32bd7acb218793b8365c59670d1b906f35cc4744,a9f0143a87ab742f4fb36e20edbd0ed253df1cf4..4aba3f82df93a650e8ab1bb834b74e34ad70d3a6
@@@ -86,24 -85,11 +85,27 @@@ bool freezetag_CheckTeams(
  void nades_Clear(entity);
  void nades_GiveBonus(entity player, float score);
  
 +entity freezetag_LastPlayer(float tm)
 +{
 +      entity last_pl = NULL;
 +      FOREACH_CLIENT(IS_PLAYER(it) && it.team == tm, {
 +              if (STAT(FROZEN, it) != FROZEN_NORMAL && GetResource(it, RES_HEALTH) >= 1)
 +              {
 +                      if (!last_pl)
 +                              last_pl = it;
 +                      else
 +                              return NULL;
 +              }
 +      });
 +      return last_pl;
 +}
 +
+ float autocvar_g_freezetag_round_enddelay = 1;
++
  bool freezetag_CheckWinner()
  {
-       if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+       if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0
+               && round_handler_GetEndDelayTime() == -1)
        {
                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER);
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER);
Simple merge
Simple merge
index 57e591d48ee6c95309174dadc0117838f757823b,d07e3eae5910399cb1bc920bea39ae3321dbaa8d..961927003e81ecf9d6780ccc311aa6255c33a203
@@@ -1,6 -1,5 +1,6 @@@
- // generated file; do not modify
+ // genmod.sh autogenerated file; do not modify
  
 +#include <common/mutators/mutator/attackertext/_mod.inc>
  #include <common/mutators/mutator/bloodloss/_mod.inc>
  #include <common/mutators/mutator/breakablehook/_mod.inc>
  #include <common/mutators/mutator/buffs/_mod.inc>
Simple merge
Simple merge
index 2adf9371ed7c919a0f2c3de1daca08bf3b8ab9b2,8527bdf293e53cc90632e77b74e1c07923f68965..8f6418a23e0e08806941787c37615639d1baf67a
@@@ -505,9 -425,9 +507,10 @@@ string multiteam_info_sprintf(string in
      MSG_INFO_NOTIF(POWERUP_STRENGTH,                        N_CONSOLE,  1, 0, "s1", "s1",       "strength",       _("^BG%s^K1 picked up Strength"), "")
  
      MSG_INFO_NOTIF(QUIT_DISCONNECT,                         N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 disconnected"), "")
 +    MSG_INFO_NOTIF(QUIT_KICK,                               N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked"), "")
      MSG_INFO_NOTIF(QUIT_KICK_IDLING,                        N_CHATCON,  1, 1, "s1 f1", "",      "",             _("^BG%s^F3 was kicked after idling for %s seconds"), "")
      MSG_INFO_NOTIF(MOVETOSPEC_IDLING,                       N_CHATCON,  1, 1, "s1 f1", "",      "",             _("^BG%s^F3 was moved to^BG spectators^F3 after idling for %s seconds"), "")
+     MSG_INFO_NOTIF(MOVETOSPEC_IDLING_QUEUE,                 N_CHATCON,  1, 1, "s1 f1", "",      "",             _("^BG%s^F3 has left the join queue after idling for %s seconds"), "")
      MSG_INFO_NOTIF(MOVETOSPEC_REMOVE,                       N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was moved to^BG spectators^F3 for balance reasons"), "")
      MSG_INFO_NOTIF(QUIT_KICK_SPECTATING,                    N_CONSOLE,  0, 0, "", "",           "",             _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "")
      MSG_INFO_NOTIF(QUIT_KICK_TEAMKILL,                      N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for excessive teamkilling"), "")
Simple merge
Simple merge
index 868b0b3b3defac79f1e46b5526b3279c93680d80,6c66771f8aab4a812e50b9030385a3d688c19651..0b3e78feacfa28ecde33efd3dfe18095f883cc8b
@@@ -5,16 -7,18 +7,20 @@@ REPLICATE_INIT(bool, cvar_cl_autoswitch
  REPLICATE_INIT(int, cvar_cl_autoscreenshot);
  REPLICATE_INIT(bool, cvar_cl_clippedspectating);
  REPLICATE_INIT(bool, cvar_cl_cts_noautoswitch);
- REPLICATE_INIT(float, cvar_cl_handicap);
+ REPLICATE_INIT(vector, cvar_cl_handicap);
+ REPLICATE_INIT(float, cvar_cl_handicap_damage_given);
+ REPLICATE_INIT(float, cvar_cl_handicap_damage_taken);
  REPLICATE_INIT(bool, cvar_cl_noantilag);
 +REPLICATE_INIT(bool, cvar_cl_chat_sounds);
  REPLICATE_INIT(string, cvar_g_xonoticversion);
  REPLICATE(cvar_cl_autoswitch, bool, "cl_autoswitch");
  REPLICATE(cvar_cl_autoscreenshot, int, "cl_autoscreenshot");
  REPLICATE(cvar_cl_clippedspectating, bool, "cl_clippedspectating");
  REPLICATE(cvar_cl_cts_noautoswitch, bool, "cl_cts_noautoswitch");
- REPLICATE(cvar_cl_handicap, float, "cl_handicap");
+ REPLICATE(cvar_cl_handicap, vector, "cl_handicap");
+ REPLICATE(cvar_cl_handicap_damage_given, float, "cl_handicap_damage_given");
+ REPLICATE(cvar_cl_handicap_damage_taken, float, "cl_handicap_damage_taken");
  REPLICATE(cvar_cl_noantilag, bool, "cl_noantilag");
 +REPLICATE(cvar_cl_chat_sounds, bool, "cl_chat_sounds");
  REPLICATE(cvar_g_xonoticversion, string, "g_xonoticversion");
  #endif
Simple merge
Simple merge
Simple merge
index e99b001b255d676b55e5b6409e61da9def8ef435,dfedb2a274e346c161af01fc739e687bcd573e5f..cb53919f2f102c17779ae51324444c918f99d333
@@@ -89,24 -83,9 +89,24 @@@ vector Team_ColorRGB(int teamid
                case NUM_TEAM_4: return '1 0.0625 1'; // 0xFF0FFF
        }
  
-       return '0 0 0';
+       return '1 1 1';
  }
  
 +#ifdef CSQC
 +string Team_CustomName(int teamid)
 +{
 +      switch(teamid)
 +      {
 +              case NUM_TEAM_1: return ((teamname_red != "") ? teamname_red : "^1RED^7 team");
 +              case NUM_TEAM_2: return ((teamname_blue != "")? teamname_blue : "^4BLUE^7 team");
 +              case NUM_TEAM_3: return ((teamname_yellow != "") ? teamname_yellow : "^3YELLOW^7 team");
 +              case NUM_TEAM_4: return ((teamname_pink != "") ? teamname_pink : "^6PINK^7 team");
 +      }
 +
 +    return NAME_NEUTRAL;
 +}
 +#endif
 +
  string Team_ColorName(int teamid)
  {
        switch(teamid)
Simple merge
index a5c2b86e8bd067d434ade4edf6a57f3324dd2c6d,8d4b85628808aa47903760a9a4187c9ef97a6fe2..f041b6afbd878eb4deb2e41b0c47bf9876e17dff
@@@ -81,14 -82,13 +82,15 @@@ void W_Electro_Explode(entity this, ent
                if(IS_PLAYER(directhitentity))
                        if(DIFF_TEAM(this.realowner, directhitentity))
                                if(!IS_DEAD(directhitentity))
 -                                      if(IsFlying(directhitentity))
 -                                              Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
 -
 +                                      if(IsFlying(directhitentity)) {
 +                                              Give_Medal(this.realowner, ELECTROBITCH);
 +                                      }
 +      */
 +      
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
-       this.velocity = this.movedir; // .velocity must be != '0 0 0' for particle fx and decal to work
+       if (!this.velocity)
+               this.velocity = this.movedir; // .velocity must be != '0 0 0' for particle fx and decal to work
  
        if(this.move_movetype == MOVETYPE_BOUNCE || this.classname == "electro_orb") // TODO: classname is more reliable anyway?
        {
Simple merge
Simple merge
Simple merge
Simple merge
index 62307cc15b7fea51f973ac46212bfcc299a3efc4,2e7faa7b15c6f3a9bb0a9e16b0fe833c8900acb8..c9a7a28ec121ac3f47c4d0b743d3ba57e98a8842
@@@ -88,15 -87,12 +88,18 @@@ int Say(entity source, int teamsay, ent
        */
  
        string namestr = "";
 -      if (source)
 -              namestr = playername(source.netname, source.team, (autocvar_g_chat_teamcolors && IS_PLAYER(source)));
 +      if (source) {
 +              namestr = playername(source.netname, source.team, (autocvar_g_chat_teamcolors));
 +              
 +              if (IS_DEAD(source) || source.frags == FRAGS_PLAYER_OUT_OF_GAME)
 +                      namestr = strcat("(DEAD) ", namestr);
 +              else if (IS_OBSERVER(source) || IS_SPEC(source))
 +                      namestr = strcat("(s) ", namestr);
 +      }
  
+       if (autocvar_g_chat_show_playerid)
+               namestr = strcat(namestr, " ^9#", itos(etof(source)), "^7");
        string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7";
  
        string msgstr = "", cmsgstr = "";
        return ret;
  }
  
 +bool play_chatsound(entity source, string msgin)
 +{
 +      if(autocvar_sv_chat_sounds && CS_CVAR(source).cvar_cl_chat_sounds) {
 +              var .float flood_sound = floodcontrol_chatsound;
 +              
 +              if (source.(flood_sound) < time - autocvar_sv_chat_sounds_flood) {
 +                      string rawmsg;
 +                      bool found = false;
 +                      rawmsg = strreplace("\n", " ", msgin);
 +                      
 +                      FOREACH_WORD(autocvar_sv_chat_sounds_list, it == rawmsg, { found = true; });
 +                      if (found) {
 +                              FOREACH_CLIENT(IS_REAL_CLIENT(it) && CS_CVAR(it).cvar_cl_chat_sounds, {
 +                                      msg_entity = it;
 +                                      WriteHeader(MSG_ONE, TE_CSQC_CHATSOUND);
 +                                      WriteString(MSG_ONE, rawmsg);
 +                              });
 +                              source.(flood_sound) = time;
 +                              return true;
 +                      }
 +              }
 +      }
 +      
 +      return false;
 +}
 +
  entity findnearest(vector point, bool checkitems, vector axismod)
  {
-     vector dist;
-     int num_nearest = 0;
-     IL_EACH(((checkitems) ? g_items : g_locations), ((checkitems) ? (it.target == "###item###") : (it.classname == "target_location")),
-     {
-         if ((it.items == IT_KEY1 || it.items == IT_KEY2) && it.target == "###item###")
-             dist = it.oldorigin;
-         else
-             dist = it.origin;
-         dist = dist - point;
-         dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
-         float len = vlen2(dist);
-         int l;
-         for (l = 0; l < num_nearest; ++l)
-         {
-             if (len < nearest_length[l])
-                 break;
-         }
-         // now i tells us where to insert at
-         //   INSERTION SORT! YOU'VE SEEN IT! RUN!
-         if (l < NUM_NEAREST_ENTITIES)
-         {
-             for (int j = NUM_NEAREST_ENTITIES - 1; j >= l; --j)
-             {
-                 nearest_length[j + 1] = nearest_length[j];
-                 nearest_entity[j + 1] = nearest_entity[j];
-             }
-             nearest_length[l] = len;
-             nearest_entity[l] = it;
-             if (num_nearest < NUM_NEAREST_ENTITIES)
-                 num_nearest = num_nearest + 1;
-         }
-     });
-     // now use the first one from our list that we can see
-     for (int j = 0; j < num_nearest; ++j)
-     {
-         traceline(point, nearest_entity[j].origin, true, NULL);
-         if (trace_fraction == 1)
-         {
-             if (j != 0)
-                 LOG_TRACEF("Nearest point (%s) is not visible, using a visible one.", nearest_entity[0].netname);
-             return nearest_entity[j];
-         }
-     }
-     if (num_nearest == 0)
-         return NULL;
-     LOG_TRACE("Not seeing any location point, using nearest as fallback.");
-     /* DEBUGGING CODE:
-     dprint("Candidates were: ");
-     for(j = 0; j < num_nearest; ++j)
-     {
-       if(j != 0)
-               dprint(", ");
-       dprint(nearest_entity[j].netname);
-     }
-     dprint("\n");
-     */
-     return nearest_entity[0];
+       vector dist;
+       int num_nearest = 0;
+       IntrusiveList list = ((checkitems) ? g_items : g_locations);
+       IL_EACH(list, (checkitems ? (it.target == "###item###") : (it.classname == "target_location")),
+       {
+               if ((it.items == IT_KEY1 || it.items == IT_KEY2) && it.target == "###item###")
+                       dist = it.oldorigin;
+               else
+                       dist = it.origin;
+               dist = dist - point;
+               dist = vec3(dist.x * axismod.x, dist.y * axismod.y, dist.z * axismod.z);
+               float len = vlen2(dist);
+               int l;
+               for (l = 0; l < num_nearest; ++l)
+               {
+                       if (len < nearest_length[l])
+                               break;
+               }
+               // now i tells us where to insert at
+               //       INSERTION SORT! YOU'VE SEEN IT! RUN!
+               if (l < NUM_NEAREST_ENTITIES)
+               {
+                       for (int j = NUM_NEAREST_ENTITIES - 2; j >= l; --j)
+                       {
+                               nearest_length[j + 1] = nearest_length[j];
+                               nearest_entity[j + 1] = nearest_entity[j];
+                       }
+                       nearest_length[l] = len;
+                       nearest_entity[l] = it;
+                       if (num_nearest < NUM_NEAREST_ENTITIES)
+                               num_nearest = num_nearest + 1;
+               }
+       });
+       // now use the first one from our list that we can see
+       for (int j = 0; j < num_nearest; ++j)
+       {
+               traceline(point, nearest_entity[j].origin, true, NULL);
+               if (trace_fraction == 1)
+               {
+                       if (j != 0)
+                       {
+                               LOG_TRACEF("Nearest point (%s) is not visible, using a visible one.",
+                                       nearest_entity[0].netname);
+                       }
+                       return nearest_entity[j];
+               }
+       }
+       if (num_nearest == 0)
+               return NULL;
+       LOG_TRACE("Not seeing any location point, using nearest as fallback.");
+       /* DEBUGGING CODE:
+       dprint("Candidates were: ");
+       for(j = 0; j < num_nearest; ++j)
+       {
+               if(j != 0)
+                       dprint(", ");
+               dprint(nearest_entity[j].netname);
+       }
+       dprint("\n");
+       */
+       return nearest_entity[0];
  }
  
  string NearestLocation(vector p)
index c07a23124974da2bb6367ffb55d461f63286d953,1b2f970b46b33b753529729db1873bcf049fc196..62bd8d05c4eb05e026d579c98584ba959f755d49
@@@ -17,11 -17,8 +17,12 @@@ bool autocvar_g_chat_team_allowed
  int autocvar_g_chat_nospectators;
  bool autocvar_g_chat_teamcolors;
  bool autocvar_g_chat_tellprivacy;
+ bool autocvar_g_chat_show_playerid;
  
 +bool autocvar_sv_chat_sounds;
 +float autocvar_sv_chat_sounds_flood;
 +string autocvar_sv_chat_sounds_list;
 +
  const float NUM_NEAREST_ENTITIES = 4;
  entity nearest_entity[NUM_NEAREST_ENTITIES];
  float nearest_length[NUM_NEAREST_ENTITIES];
index 18051aa08a607d4bea777cd59b08ec568c36d7b7,8d0db75dac1d04da1fa33703ecdf0a4ce30aa520..17dd5118615b7562bc55e989c07b6e5b9dd30c2e
@@@ -97,20 -97,6 +97,20 @@@ STATIC_METHOD(Client, Remove, void(Clie
      ClientDisconnect(this);
  }
  
- void send_CSQC_teamnagger() {
-       WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
- }
++/* TODO void send_CSQC_teamnagger() {
++       WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
++}*/
 +
 +void send_TeamNames(int channel, entity to) {
-       msg_entity = to;
-       
-       WriteHeader(channel, TE_CSQC_TEAMNAMES);
-       WriteString(channel, autocvar_g_teamnames_red);
-       WriteString(channel, autocvar_g_teamnames_blue);
-       WriteString(channel, autocvar_g_teamnames_yellow);
-       WriteString(channel, autocvar_g_teamnames_pink);
++        msg_entity = to;
++
++        WriteHeader(channel, TE_CSQC_TEAMNAMES);
++        WriteString(channel, autocvar_g_teamnames_red);
++        WriteString(channel, autocvar_g_teamnames_blue);
++        WriteString(channel, autocvar_g_teamnames_yellow);
++        WriteString(channel, autocvar_g_teamnames_pink);
 +}
 +
  int CountSpectators(entity player, entity to)
  {
        if(!player) { return 0; } // not sure how, but best to be safe
@@@ -424,12 -427,9 +441,13 @@@ void PutObserverInServer(entity this, b
  
        if (CS(this).just_joined)
                CS(this).just_joined = false;
-       if (this.wants_join)
-               this.wants_join = 0;
  
 +      // for RJZ
 +      if (autocvar_rjz_count_shards)
 +              send_TotalShards(this);
++
+       if (recount_ready)
+               ReadyCount(); // must be called after SetPlayerTeam() and TRANSMUTE(Observer
  }
  
  int player_getspecies(entity this)
@@@ -554,34 -554,16 +572,32 @@@ void FixPlayermodel(entity player
                                setcolor(player, stof(autocvar_sv_defaultplayercolors));
  }
  
 -void GiveWarmupResources(entity this)
 +void ResetPlayerResources(entity this)
  {
 -      SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
 -      SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
 -      SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
 -      SetResource(this, RES_CELLS, warmup_start_ammo_cells);
 -      SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
 -      SetResource(this, RES_HEALTH, warmup_start_health);
 -      SetResource(this, RES_ARMOR, warmup_start_armorvalue);
 -      STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
 +      if (warmup_stage) {
-               SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
-               SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
-               SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
-               SetResource(this, RES_CELLS, warmup_start_ammo_cells);
-               SetResource(this, RES_PLASMA, warmup_start_ammo_plasma);
-               SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
-               SetResource(this, RES_HEALTH, warmup_start_health);
-               SetResource(this, RES_ARMOR, warmup_start_armorvalue);
-               STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
++                      SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
++                      SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
++                      SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
++                      SetResource(this, RES_CELLS, warmup_start_ammo_cells);
++                      SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
++                      SetResource(this, RES_HEALTH, warmup_start_health);
++                      SetResource(this, RES_ARMOR, warmup_start_armorvalue);
++                      STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
 +      } else {
-               SetResource(this, RES_SHELLS, start_ammo_shells);
-               SetResource(this, RES_BULLETS, start_ammo_nails);
-               SetResource(this, RES_ROCKETS, start_ammo_rockets);
-               SetResource(this, RES_CELLS, start_ammo_cells);
-               SetResource(this, RES_PLASMA, start_ammo_plasma);
-               SetResource(this, RES_FUEL, start_ammo_fuel);
-               SetResource(this, RES_HEALTH, start_health);
-               SetResource(this, RES_ARMOR, start_armorvalue);
-               STAT(WEAPONS, this) = start_weapons;
-               if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false)
-               {
-                       GiveRandomWeapons(this, random_start_weapons_count,
-                               autocvar_g_random_start_weapons, random_start_ammo);
-               }
++                      SetResource(this, RES_SHELLS, start_ammo_shells);
++                      SetResource(this, RES_BULLETS, start_ammo_nails);
++                      SetResource(this, RES_ROCKETS, start_ammo_rockets);
++                      SetResource(this, RES_CELLS, start_ammo_cells);
++                      SetResource(this, RES_FUEL, start_ammo_fuel);
++                      SetResource(this, RES_HEALTH, start_health);
++                      SetResource(this, RES_ARMOR, start_armorvalue);
++                      STAT(WEAPONS, this) = start_weapons;
++                      if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false)
++                      {
++                                      GiveRandomWeapons(this, random_start_weapons_count,
++                                                      autocvar_g_random_start_weapons, random_start_ammo);
++                      }
 +      }
  }
  
  void PutPlayerInServer(entity this)
        this.takedamage = DAMAGE_AIM;
        this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
  
 -      if (warmup_stage)
 -              GiveWarmupResources(this);
 -      else
 -      {
 -              SetResource(this, RES_SHELLS, start_ammo_shells);
 -              SetResource(this, RES_BULLETS, start_ammo_nails);
 -              SetResource(this, RES_ROCKETS, start_ammo_rockets);
 -              SetResource(this, RES_CELLS, start_ammo_cells);
 -              SetResource(this, RES_FUEL, start_ammo_fuel);
 -              SetResource(this, RES_HEALTH, start_health);
 -              SetResource(this, RES_ARMOR, start_armorvalue);
 -              STAT(WEAPONS, this) = start_weapons;
 -              if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false)
 -              {
 -                      GiveRandomWeapons(this, random_start_weapons_count,
 -                              autocvar_g_random_start_weapons, random_start_ammo);
 -              }
 -      }
 +      ResetPlayerResources(this);
        SetSpectatee_status(this, 0);
  
        PS(this).dual_weapons = '0 0 0';
@@@ -866,9 -872,7 +889,11 @@@ void PutClientInServer(entity this
        } else if (IS_PLAYER(this)) {
                PutPlayerInServer(this);
        }
 +      // send team names
 +      if(teamplay && IS_REAL_CLIENT(this))
 +              send_TeamNames(MSG_ONE, this);
++
        bot_relinkplayerlist();
  }
  
@@@ -908,13 -912,6 +933,13 @@@ void ClientInit_misc(entity this
        WriteByte(channel, this.cnt * 255.0); // g_balance_damagepush_speedfactor
        WriteByte(channel, serverflags);
        WriteCoord(channel, autocvar_g_trueaim_minrange);
-       
++
 +      // z411 send full hostname
 +      WriteString(channel, (autocvar_hostname_full != "" ? autocvar_hostname_full : autocvar_hostname));
 +      WriteString(channel, autocvar_sv_motd_permanent);
-       
++
 +      // z411 send client countdown type
 +      WriteByte(channel, autocvar_sv_timer_countdown);
  }
  
  void ClientInit_CheckUpdate(entity this)
@@@ -1140,13 -1153,9 +1181,13 @@@ void ClientConnect(entity this
        else
                CS(this).allowed_timeouts = autocvar_sv_timeout_number;
  
 -      if (autocvar_sv_eventlog)
 +      if (autocvar_sv_eventlog) {
                GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? GameLog_ProcessIP(this.netaddress) : "bot"), ":", playername(this.netname, this.team, false)));
-               
 +              /* z411 for RJZ */
 +              if(autocvar_rjz_ranks) GameLogEcho(strcat(":idfp:", ftos(etof(this)), ":", this.crypto_idfp));
 +      }
 +
        CS(this).just_joined = true;  // stop spamming the eventlog with additional lines when the client connects
        this.wants_join = 0;
  
@@@ -2202,8 -2306,8 +2338,9 @@@ bool PlayerThink(entity this
                return false;
        }
  
 -      if (timeout_status == TIMEOUT_ACTIVE) {
++      //if (timeout_status == TIMEOUT_ACTIVE) {
 +      if (game_timeout) {
-         // don't allow the player to turn around while game is paused
+               // don't allow the player to turn around while game is paused
                // FIXME turn this into CSQC stuff
                this.v_angle = this.lastV_angle;
                this.angles = this.lastV_angle;
index cb71a3eaa59298632061a5276fbeb6475dc5910c,cd3c162baf9778a789f98319183f6bccbcb10494..65fadd997da3db036f5cdf2326240cb9cedaed53
@@@ -225,8 -209,8 +227,9 @@@ CLASS(Client, Object
      ATTRIB(Client, cvar_cl_pokenade_type, string, this.cvar_cl_pokenade_type);
      ATTRIB(Client, cvar_cl_spawn_near_teammate, bool, this.cvar_cl_spawn_near_teammate);
      ATTRIB(Client, cvar_cl_gunalign, int, this.cvar_cl_gunalign);
-     ATTRIB(Client, cvar_cl_handicap, float, this.cvar_cl_handicap);
 +      ATTRIB(Client, cvar_cl_chat_sounds, bool, this.cvar_cl_chat_sounds);
+     ATTRIB(Client, cvar_cl_handicap_damage_given, float, this.cvar_cl_handicap_damage_given);
+     ATTRIB(Client, cvar_cl_handicap_damage_taken, float, this.cvar_cl_handicap_damage_taken);
      ATTRIB(Client, cvar_cl_clippedspectating, bool, this.cvar_cl_clippedspectating);
      ATTRIB(Client, cvar_cl_autoscreenshot, int, this.cvar_cl_autoscreenshot);
      ATTRIB(Client, cvar_cl_jetpack_jump, bool, this.cvar_cl_jetpack_jump);
@@@ -330,15 -314,8 +333,11 @@@ bool independent_players
  #define IS_INDEPENDENT_PLAYER(e) ((e).solid == SOLID_TRIGGER)
  #define MAKE_INDEPENDENT_PLAYER(e) (((e).solid = SOLID_TRIGGER), ((e).frags = FRAGS_PLAYER_OUT_OF_GAME))
  
 +.float lastkill;
 +.int countrycode;
  .int killcount;
 +.string rank; // RJZ
  
- //flood fields
- .float nickspamtime; // time of last nick change
- .float nickspamcount;
  void SendWelcomeMessage(entity this, int msg_type);
  
  // respawning
index 52ff6a644db1f02f590d7df5e0fb4726f9f6273a,5bae26bbd9053b74949a5876d6a71e000b2749fd..3f4f1de83ca7fba7be98abbc91405a87f08f2f9e
@@@ -356,10 -439,10 +439,10 @@@ void ClientCommand_join(entity caller, 
        {
                case CMD_REQUEST_COMMAND:
                {
 -                      if (!game_stopped && IS_CLIENT(caller) && !IS_PLAYER(caller))
 +                      if (!game_stopped && !game_timeout && IS_CLIENT(caller) && !IS_PLAYER(caller))
                        {
-                               if (joinAllowed(caller))
-                                       Join(caller);
+                               if (joinAllowed(caller, caller.wants_join))
+                                       Join(caller, teamplay);
                                else if(time < CS(caller).jointime + MIN_SPEC_TIME)
                                        CS(caller).autojoin_checked = -1;
                        }
index 33fc4a3ab4bf72336e28e307e1bee168bc6cf87f,71da040ed31f204ae3be49d7e46696d6b1365b0d..7dae83ce79defee6e73e6e0c4b8a0a669b913c3a
@@@ -263,20 -242,8 +263,13 @@@ void timeout_handler_think(entity this
                                timeout_status = TIMEOUT_ACTIVE;
  
                                // set the slowmo value to the timeout default slowmo value
 -                              cvar_set("slowmo", ftos(TIMEOUT_SLOWMO_VALUE));
 +                              //cvar_set("slowmo", ftos(TIMEOUT_SLOWMO_VALUE));
 +                              game_timeout = true;
 +                              timeout_last = time;
 +                              
 +                              // play timeout sound
 +                              sound(NULL, CH_INFO, SND_TIMEOUT, VOL_BASE, ATTN_NONE);
  
-                               // reset all the flood variables
-                               FOREACH_CLIENT(true, {
-                                       it.nickspamcount = it.nickspamtime = it.floodcontrol_chat =
-                                               it.floodcontrol_chatteam = it.floodcontrol_chattell =
-                                                       it.floodcontrol_voice = it.floodcontrol_voiceteam = 0;
-                               });
                                // copy .v_angle to .lastV_angle for every player in order to fix their view during pause (see PlayerPreThink)
                                FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
                                        it.lastV_angle = it.v_angle;
Simple merge
index e27c5fe8a120914b51a59d5228ac9f1405efc650,7c008c10e9a981570df10b6d4bf0e700d4c24dd0..970d913ae37ec045c01f9283e7545ce8d199759b
@@@ -569,17 -561,21 +575,21 @@@ void ReadyCount(
                {
                        warmup_stage = autocvar_g_warmup; // CAN change it AFTER calling Nagger_ReadyCounted() this frame
                        game_starttime = time;
-                       Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, COUNTDOWN_STOP, minplayers);
+                       if (total_players < minplayers)
+                               Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, COUNTDOWN_STOP_MINPLAYERS, minplayers);
+                       else
+                               Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, COUNTDOWN_STOP_BADTEAMS);
                        if (!sv_ready_restart_after_countdown) // if we ran reset_map() at start of countdown
 -                              FOREACH_CLIENT(IS_PLAYER(it), { GiveWarmupResources(it); });
 +                              FOREACH_CLIENT(IS_PLAYER(it), { ResetPlayerResources(it); });
                }
-               if (warmup_limit > 0)
-                       warmup_limit = -1;
-               return; // don't ReadyRestart if players are ready but too few
+               warmup_limit = -1;
+               return; // don't ReadyRestart if players are ready but too few or teams are bad
        }
-       else if (minplayers && warmup_limit <= 0)
+       else if (warmup_limit <= 0
+       && game_starttime <= time) // No countdown in progress, check prevents early countdown end if only player leaves
        {
-               // there's enough players now but we're still in infinite warmup
+               // there's enough players now and teams are ok
+               // but we're still in infinite warmup and may need to switch to timed warmup
                warmup_limit = cvar("g_warmup_limit");
                if (warmup_limit == 0)
                        warmup_limit = autocvar_timelimit * 60;
Simple merge
Simple merge
Simple merge
index 86ede0b7bd20cd481dc0830905737dfb188d70d6,6adc4f9b439d88c2fbd97d20adad62d6157db77f..ad83450d1f796f94041deced64e87793ae040164
@@@ -566,15 -555,7 +566,14 @@@ bool Item_GiveTo(entity item, entity pl
        pickedup |= Item_GiveAmmoTo(item, player, RES_BULLETS, g_pickup_nails_max);
        pickedup |= Item_GiveAmmoTo(item, player, RES_ROCKETS, g_pickup_rockets_max);
        pickedup |= Item_GiveAmmoTo(item, player, RES_CELLS, g_pickup_cells_max);
-       pickedup |= Item_GiveAmmoTo(item, player, RES_PLASMA, g_pickup_plasma_max);
        pickedup |= Item_GiveAmmoTo(item, player, RES_FUEL, g_pickup_fuel_max);
 +      
 +      // for RJZ
 +      if (autocvar_rjz_count_shards && !warmup_stage && item.itemdef == ITEM_ArmorSmall) {
 +              total_shards++;
 +              send_TotalShardsAll();
 +      }
 +      
        if (item.itemdef.instanceOfWeaponPickup)
        {
                WepSet w, wp;
Simple merge
index 12da5149f9c3263a6afc788926e09596f881ce11,191051e4d6dfd4b6fbe80faaa7c7c25573c34043..c4c96025fe463052094457efb56da978f7094fc2
@@@ -71,9 -65,9 +71,10 @@@ void round_handler_Think(entity this
                if (this.canRoundEnd())
                {
                        // schedule a new round
 +                      round_delaytime = time;
                        this.wait = true;
                        this.nextthink = time + this.delay;
+                       round_handler_ResetEndDelayTime();
                }
                else
                {
@@@ -114,7 -109,9 +115,8 @@@ void round_handler_Spawn(bool() canRoun
        this.canRoundEnd = canRoundEnd_func;
        this.roundStart = roundStart_func;
        this.wait = false;
+       round_handler_ResetEndDelayTime();
        round_handler_Init(5, 5, 180);
 -      this.nextthink = time;
  
        ScoreInfo_SetLabel_PlayerScore(SP_ROUNDS_PL, "rounds_pl", 0);
  }
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 3848659d18f196df683a45f7a5000103cfe31f2e,cf0448b2772d534fb52c5832e3adfe8157c5cde2..caf7a176bf598731bd887f423d61b4e96d59b208
@@@ -464,9 -430,8 +463,10 @@@ void cvar_changes_init(
                BADPREFIX("sv_timeout_");
                BADPREFIX("sv_vote_");
                BADPREFIX("timelimit_");
 +              BADPREFIX("sv_chat_");
 +              BADPREFIX("sv_jingle_");
                BADPRESUFFIX("g_", "_round_timelimit");
+               BADPRESUFFIX("g_", "_round_enddelay");
  
                // allowed changes to server admins (please sync this to server.cfg)
                // vi commands:
index dd0b18134c96bd09602273a6dc819432467f4c40,9a07bb60dc87da155775b5c6586a9f81551bbc8b..03959c99b51829db15d606774048a37fdcecd698
@@@ -171,15 -158,8 +165,15 @@@ void readplayerstartcvars()
  void readlevelcvars();
  
  .vector dropped_origin;
- void droptofloor(entity this);
+ void DropToFloor_QC_DelayedInit(entity this);
  
 +/* z411 for RJZ */
 +bool autocvar_rjz_count_shards = false;
 +bool autocvar_rjz_ranks = false;
 +int  total_shards = 0;
 +void send_TotalShards(entity to);
 +void send_TotalShardsAll();
 +
  IntrusiveList g_moveables;
  STATIC_INIT(g_moveables) { g_moveables = IL_NEW(); }
  
Simple merge