]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/Juhu/scoreboard-strafe' into morosophos/server...
authorNick S <nick@teichisma.info>
Tue, 17 Jan 2023 21:15:18 +0000 (23:15 +0200)
committerNick S <nick@teichisma.info>
Tue, 17 Jan 2023 21:15:18 +0000 (23:15 +0200)
1  2 
_hud_common.cfg
qcsrc/common/constants.qh
qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc
qcsrc/common/weapons/weapon/crylink.qc
qcsrc/server/command/sv_cmd.qc

diff --combined _hud_common.cfg
index 0c71712d3ef9064faec081afd86bb51b08581402,0c808dd3784f54e960cbfe4897606f9dea07c000..ffbd47b6e48cf9b2b7ca79dd6c034b0598191ec2
@@@ -49,6 -49,7 +49,7 @@@ seta hud_panel_scoreboard_accuracy 1 "s
  seta hud_panel_scoreboard_ctf_leaderboard 1 "show a capture time rankings leaderboard in the scoreboard if allowed by the server"
  seta hud_panel_scoreboard_itemstats 1 "show item stats panel in the scoreboard"
  seta hud_panel_strafehud        3 "enable this panel, 1 = show if not observing, 2 = show always, 3 = show only in race/cts if not observing"
+ seta hud_panel_pickup           1 "enable this panel"
  
  seta hud_panel_weapons_dynamichud          1 "apply the dynamic hud effects to this panel"
  seta hud_panel_ammo_dynamichud             1 "apply the dynamic hud effects to this panel"
@@@ -69,6 -70,7 +70,7 @@@ seta hud_panel_centerprint_dynamichu
  seta hud_panel_itemstime_dynamichud        1 "apply the dynamic hud effects to this panel"
  seta hud_panel_scoreboard_dynamichud       0 "apply the dynamic hud effects to this panel"
  seta hud_panel_strafehud_dynamichud        1 "apply the dynamic hud effects to this panel"
+ seta hud_panel_pickup_dynamichud           1 "apply the dynamic hud effects to this panel"
  
  seta hud_panel_weapons_ammo_full_shells 60 "show 100% of the status bar at this ammo count"
  seta hud_panel_weapons_ammo_full_nails 320 "show 100% of the status bar at this ammo count"
@@@ -151,63 -153,50 +153,63 @@@ seta hud_panel_scoreboard_itemstats_sho
  
  seta _hud_panel_strafehud_demo "0" "strafehud changes angle during configure"
  seta hud_panel_strafehud_mode "0" "strafehud mode which controls whether the strafehud is centered at \"0\" = view angle, \"1\" = velocity angle"
 -seta hud_panel_strafehud_range "0" "the angle range up to 360 degrees displayed on the strafehud, \"0\" = dynamic (chooses the minimum range required to still see the whole area needed for accelerating at once)"
 -seta hud_panel_strafehud_style "1" "\"0\" = no styling, \"1\" = progress bar style for the strafe bar, \"2\" = gradient for the strafe bar"
 +seta hud_panel_strafehud_range "90" "the angle range up to 360 degrees displayed on the strafehud, \"0\" = dynamic (chooses the minimum range required to still see the whole area needed for accelerating)"
 +seta hud_panel_strafehud_style "2" "\"0\" = no styling, \"1\" = progress bar style for the strafe bar, \"2\" = gradient for the strafe bar"
  seta hud_panel_strafehud_unit "1" "speed unit (1 = qu/s, 2 = m/s, 3 = km/h, 4 = mph, 5 = knots), length unit (1 = qu, 2 = m, 3 = km, 4 = mi, 5 = nmi)"
  seta hud_panel_strafehud_unit_show "1" "show units"
 +seta hud_panel_strafehud_uncapped "0" "set to \"1\" to remove some safety restrictions, useful to set thinner indicator lines down to 1px or for trying out higher values for some performance degrading operations (warning: elements may turn invisible if too thin, other configurations may crash your game or look horribly ugly)"
 +seta hud_panel_strafehud_bar_preaccel "1" "set to \"1\" to extend the acceleration zone by the strafe meter zone before full acceleration can be achieved"
  seta hud_panel_strafehud_bar_neutral_color "1 1 1" "color of the strafe meter neutral zone"
 -seta hud_panel_strafehud_bar_neutral_alpha "0.3" "opacity of the strafe meter neutral zone"
 +seta hud_panel_strafehud_bar_neutral_alpha "0.1" "opacity of the strafe meter neutral zone"
  seta hud_panel_strafehud_bar_accel_color "0 1 0" "color of the strafe meter acceleration zone"
 -seta hud_panel_strafehud_bar_accel_alpha "0.3" "opacity of the strafe meter acceleration zone"
 +seta hud_panel_strafehud_bar_accel_alpha "0.5" "opacity of the strafe meter acceleration zone"
  seta hud_panel_strafehud_bar_overturn_color "1 0 1" "color of the strafe meter overturn zone"
 -seta hud_panel_strafehud_bar_overturn_alpha "0.3" "opacity of the strafe meter overturn zone"
 +seta hud_panel_strafehud_bar_overturn_alpha "0.5" "opacity of the strafe meter overturn zone"
 +seta hud_panel_strafehud_angle_style "0" "set the angle indicator style: 0 = none, 1 = solid line, 2 = dashed line"
 +seta hud_panel_strafehud_angle_dashes "4" "determines the amount of dashes if the angle indicator uses a dashed line"
  seta hud_panel_strafehud_angle_alpha "0.8" "opacity of the indicator showing the player's current angle"
 -seta hud_panel_strafehud_angle_height "1.5" "height of the indicator showing the player's current angle (relative to the panel height)"
 -seta hud_panel_strafehud_angle_width "0.005" "width of the indicator showing the player's current angle (relative to the panel width)"
 -seta hud_panel_strafehud_angle_neutral_color "1 1 0" "color of the indicator showing the player's current angle if the player's angle is within the neutral zone"
 -seta hud_panel_strafehud_angle_accel_color "0 1 1" "color of the indicator showing the player's current angle if the player's angle is within the acceleration zone"
 -seta hud_panel_strafehud_angle_overturn_color "1 0 1" "color of the indicator showing the player's current angle if the player's angle is within the overturn zone"
 -seta hud_panel_strafehud_switch_minspeed "-1" "minimum speed in qu/s at which switch indicators which are used to aid changing strafe direction will be shown (uses physics maxspeed + antiflicker speed if negative)"
 -seta hud_panel_strafehud_switch_active_color "0 1 0" "color of the switch indicator on the current side"
 -seta hud_panel_strafehud_switch_active_alpha "1" "opacity of the switch indicator on the current side"
 -seta hud_panel_strafehud_switch_inactive_color "1 1 0" "color of the switch indicator on the opposite side"
 -seta hud_panel_strafehud_switch_inactive_alpha "1" "opacity of the switch indicator on the opposite side"
 -seta hud_panel_strafehud_switch_width "0.0075" "width of the strafe angle indicators (relative to the strafe bar width)"
 +seta hud_panel_strafehud_angle_height "1" "height of the indicator showing the player's current angle (relative to the panel height)"
 +seta hud_panel_strafehud_angle_width "0.001" "width of the indicator showing the player's current angle (relative to the panel width)"
 +seta hud_panel_strafehud_angle_neutral_color "1 1 0" "color of the indicator showing the player's current angle if it is within the neutral zone"
 +seta hud_panel_strafehud_angle_accel_color "0 1 1" "color of the indicator showing the player's current angle if it is within the acceleration zone"
 +seta hud_panel_strafehud_angle_overturn_color "1 0 1" "color of the indicator showing the player's current angle if it is within the overturn zone"
 +seta hud_panel_strafehud_angle_arrow "1" "set the angle indicator's arrow style: 0 = none, 1 = top, 2 = bottom, 3 = both"
 +seta hud_panel_strafehud_angle_arrow_size "0.5" "size of the arrow (relative to the panel height)"
 +seta hud_panel_strafehud_bestangle "1" "set to \"1\" to enable a ghost angle indicator showing the best angle to gain maximum acceleration"
 +seta hud_panel_strafehud_bestangle_color "1 1 1" "color of the indicator showing the best angle to gain maximum acceleration"
 +seta hud_panel_strafehud_bestangle_alpha "0.5" "opacity of the indicator showing the best angle to gain maximum acceleration"
 +seta hud_panel_strafehud_switch_minspeed "-1" "minimum speed in qu/s at which switch indicator(s) which are used to aid changing strafe direction will be shown (set to -1 for dynamic minspeed)"
 +seta hud_panel_strafehud_switch_color "1 1 0" "color of the switch indicator"
 +seta hud_panel_strafehud_switch_alpha "1" "opacity of the switch indicator"
 +seta hud_panel_strafehud_switch_width "0.003" "width of the strafe angle indicator(s) (relative to the strafe bar width)"
  seta hud_panel_strafehud_direction_color "0 0.5 1" "color of the direction caps which indicate the direction the player is currently strafing towards"
 -seta hud_panel_strafehud_direction_alpha "1" "opacity of the direction caps which indicate the direction the player is currently strafing towards"
 +seta hud_panel_strafehud_direction_alpha "0" "opacity of the direction caps which indicate the direction the player is currently strafing towards"
  seta hud_panel_strafehud_direction_width "0.25" "stroke width of the direction caps which indicate the direction the player is currently strafing towards (relative to the panel height)"
  seta hud_panel_strafehud_direction_length "0.02" "length of the horizontal component of the direction caps which indicate the direction the player is currently strafing towards (relative to the panel width)"
 -seta hud_panel_strafehud_slickdetector_range "0" "range of the slick detector in qu, \"0\" to disable"
 -seta hud_panel_strafehud_slickdetector_granularity "2" "value from 0 to 4 which defines how exact the search for slick should be, higher values may yield better results but require more computation"
 +seta hud_panel_strafehud_slickdetector_range "200" "range of the slick detector in qu, \"0\" to disable"
 +seta hud_panel_strafehud_slickdetector_granularity "1" "value from 0 to 4 which defines how exact the search for slick should be, higher values may yield better results but require more computation"
  seta hud_panel_strafehud_slickdetector_color "0 1 1" "color of the slick detector indicator"
  seta hud_panel_strafehud_slickdetector_alpha "0.5" "opacity of the slick detector indicator"
  seta hud_panel_strafehud_slickdetector_height "0.125" "height of the slick detector indicator (relative to the panel height)"
 -seta hud_panel_strafehud_startspeed_fade "0" "fade time (in seconds) of the start speed text or \"0\" to disable"
 +seta hud_panel_strafehud_startspeed_fade "4" "fade time (in seconds) of the start speed text or \"0\" to disable"
  seta hud_panel_strafehud_startspeed_color "1 0.75 0" "color of the start speed text"
  seta hud_panel_strafehud_startspeed_size "1.5" "size of the start speed text (relative to the panel height)"
  seta hud_panel_strafehud_jumpheight_fade "0" "fade time (in seconds) of the jump height text or \"0\" to disable"
  seta hud_panel_strafehud_jumpheight_min "50" "minimum jump height to display in the selected unit"
  seta hud_panel_strafehud_jumpheight_color "0 1 0.75" "color of the jump height text"
  seta hud_panel_strafehud_jumpheight_size "1.5" "size of the jump height text (relative to the panel height)"
 -seta hud_panel_strafehud_timeout_air "0.1" "time (in seconds) after take off before changing to air strafe physics when not jumping (visually more consistent hud while on slick downwards ramps)"
 -seta hud_panel_strafehud_timeout_ground "0.03333333" "time (in seconds) after landing before changing to non-air strafe physics (visually more consistent hud while strafe turning when touching the floor after every hop)"
 +seta hud_panel_strafehud_timeout_ground "0.1" "time (in seconds) after take off before changing to air strafe physics when not jumping (visually more consistent hud while on slick downwards ramps)"
  seta hud_panel_strafehud_timeout_turn "0.1" "time (in seconds) after releasing the strafe keys before changing mode (visually more consistent hud while switching between left/right strafe turning)"
 -seta hud_panel_strafehud_timeout_direction "0.5" "time (in seconds) it takes until direction changes (forward or backward movement) are applied (set to zero if you intend to sideways strafe)"
  seta hud_panel_strafehud_antiflicker_angle "0.01" "how many degrees from 0° to 180° the hud ignores if it could cause visual disturbances otherwise (and to counter rounding errors)"
 -seta hud_panel_strafehud_antiflicker_speed "0.0001" "how many qu/s the hud ignores if it could cause visual disturbances otherwise (and to counter rounding errors)"
 +seta hud_panel_strafehud_fps_update "0.5" "update interval (in seconds) of the frametime to calculate the optimal angle, smaller values may cause flickering"
 +seta hud_panel_strafehud_sonar "0" "set to \"1\" to enable the strafe sonar"
 +seta hud_panel_strafehud_sonar_audio "misc/talk" "audio to play for sonar"
 +seta hud_panel_strafehud_sonar_start "0.5" "how optimal from 0 to 1 your strafing angle has to be for the strafe sonar to activate"
 +seta hud_panel_strafehud_sonar_interval "0.333333" "strafe sonar sound interval in seconds"
 +seta hud_panel_strafehud_sonar_volume_start "0.333333" "sound volume of the strafe sonar"
 +seta hud_panel_strafehud_sonar_volume_range "0.666666" "dynamic volume range of the strafe sonar as you approach the optimal angle"
 +seta hud_panel_strafehud_sonar_pitch_start "0.9" "playback speed of the strafe sonar"
 +seta hud_panel_strafehud_sonar_pitch_range "0.1" "dynamic playback speed range of the strafe sonar as you approach the optimal angle"
  
  // hud panel aliases
  alias quickmenu "cl_cmd hud quickmenu ${* ?}"
@@@ -288,3 -277,8 +290,8 @@@ seta hud_shownames_maxdistance 5000 "al
  seta hud_shownames_antioverlap 1 "if two tags overlap, fade out the one further away from you"
  seta hud_shownames_antioverlap_minalpha 0.4 "fade out overlapping tags to this alpha value"
  seta hud_shownames_offset 52 "offset (along z-axis) tag from player origin by this many units"
+ seta hud_panel_pickup_showtimer 1 "0 = hide timer, 1 = show timer, 2 = only when spectating"
+ seta hud_panel_pickup_iconsize 1.5 "icon size scale"
+ seta hud_panel_pickup_time 3 "pickup message duration (can't be higher than 5)"
+ seta hud_panel_pickup_fade_out 0.15 "how long a pickup message takes to fade out (this time is included in the message duration)"
index 6c9b9f2d7edc9efaff33e4b18160ac7a8e8e7f35,e0c17a7a128a0e623be23e7048a6afd4bec0e108..50d7b0ba57c9d5058f823ccae2defabd14ef1237
@@@ -12,10 -12,12 +12,12 @@@ const int CVAR_NOTIFY = BIT(1)
  const int CVAR_READONLY = BIT(2);
  
  // server flags
+ // NOTE: the engine doesn't clear serverflags on map change (gotomap)
  const int SERVERFLAG_ALLOW_FULLBRIGHT = BIT(0);
  const int SERVERFLAG_TEAMPLAY = BIT(1);
  const int SERVERFLAG_PLAYERSTATS = BIT(2);
  const int SERVERFLAG_PLAYERSTATS_CUSTOM = BIT(3);
+ const int SERVERFLAG_FORBID_PICKUPTIMER = BIT(4);
  
  const int SPECIES_HUMAN = 0;
  const int SPECIES_ROBOT_SOLID = 1;
@@@ -26,7 -28,7 +28,7 @@@ const int SPECIES_ROBOT_SHINY = 5
  const int SPECIES_RESERVED = 15;
  
  #ifdef GAMEQC
 -const int RANKINGS_CNT = 99;
 +const int RANKINGS_CNT = 256;
  
  ///////////////////////////
  // keys pressed
index e11cd93d0ae28f8614bec406b8c7c6004f275e76,ce8b7816bf6add167e87cdde1082450a986e3a1c..5386c8c8c1aa007218da2fb51be4086065c84fb8
@@@ -131,7 -131,7 +131,7 @@@ void ctf_CaptureRecord(entity flag, ent
                ctf_captimerecord = cap_time;
                db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
                db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
 -              write_recordmarker(player, flag.ctf_pickuptime, cap_time);
 +              write_recordmarker(player, 1, flag.ctf_pickuptime, cap_time);
        }
  
        if(autocvar_g_ctf_leaderboard && !ctf_oneflag)
@@@ -356,6 -356,7 +356,7 @@@ void ctf_Handle_Drop(entity flag, entit
        flag.angles = '0 0 0';
        SetResourceExplicit(flag, RES_HEALTH, flag.max_health);
        flag.ctf_droptime = time;
+       flag.ctf_landtime = 0;
        flag.ctf_dropper = player;
        flag.ctf_status = FLAG_DROPPED;
  
@@@ -473,6 -474,7 +474,7 @@@ void ctf_Handle_Throw(entity player, en
        flag.solid = SOLID_TRIGGER;
        flag.ctf_dropper = player;
        flag.ctf_droptime = time;
+       flag.ctf_landtime = 0;
  
        flag.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND for MOVETYPE_TOSS
  
@@@ -732,12 -734,9 +734,9 @@@ void ctf_Handle_Pickup(entity flag, ent
        if(flag.team)
                FOREACH_CLIENT(IS_PLAYER(it) && it != player, {
                        if(CTF_SAMETEAM(flag, it))
-                       {
-                               if(SAME_TEAM(player, it))
-                                       Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname);
-                               else
-                                       Send_Notification(NOTIF_ONE, it, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname);
-                       }
+                               Send_Notification(NOTIF_ONE, it, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname);
+                       else if(DIFF_TEAM(player, it))
+                               Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_NUM(flag.team, CHOICE_CTF_PICKUP_ENEMY_OTHER), Team_ColorCode(player.team), player.netname);
                });
  
        _sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE);
@@@ -949,7 -948,7 +948,7 @@@ void ctf_FlagThink(entity this
                                for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
                                        if(tmp_entity.ctf_status == FLAG_DROPPED)
                                        if(vdist(this.origin - tmp_entity.origin, <, autocvar_g_ctf_dropped_capture_radius))
-                                       if(time > tmp_entity.ctf_droptime + autocvar_g_ctf_dropped_capture_delay)
+                                       if((this.noalign || tmp_entity.ctf_landtime) && time > ((this.noalign) ? tmp_entity.ctf_droptime : tmp_entity.ctf_landtime) + autocvar_g_ctf_dropped_capture_delay)
                                                ctf_Handle_Capture(this, tmp_entity, CAPTURE_DROPPED);
                        }
                        return;
                case FLAG_DROPPED:
                {
                        this.angles = '0 0 0'; // reset flag angles in case warpzones adjust it
+                       if(IS_ONGROUND(this) && !this.ctf_landtime)
+                               this.ctf_landtime = time; // landtime is reset when thrown, and we don't want to restart the timer if the flag is pushed
  
                        if(autocvar_g_ctf_flag_dropped_floatinwater)
                        {
@@@ -1210,6 -1211,7 +1211,7 @@@ void ctf_RespawnFlag(entity flag
        flag.ctf_dropper = NULL;
        flag.ctf_pickuptime = 0;
        flag.ctf_droptime = 0;
+       flag.ctf_landtime = 0;
        flag.ctf_flagdamaged_byworld = false;
        navigation_dynamicgoal_unset(flag);
  
@@@ -1304,11 -1306,6 +1306,6 @@@ void ctf_FlagSetup(int teamnum, entity 
        flag.nextthink = time + FLAG_THINKRATE;
        flag.ctf_status = FLAG_BASE;
  
-       // set correct team colors
-       flag.glowmod = Team_ColorRGB(teamnum);
-       flag.colormap = (teamnum) ? (teamnum - 1) * 0x11 : 0x00;
-       flag.colormap |= BIT(10); // RENDER_COLORMAPPED
        // crudely force them all to 0
        if(autocvar_g_ctf_score_ignore_fields)
                flag.cnt = flag.score_assist = flag.score_team_capture = flag.score_capture = flag.score_drop = flag.score_pickup = flag.score_return = 0;
@@@ -1758,7 -1755,7 +1755,7 @@@ void havocbot_role_ctf_carrier(entity t
  
                entity goal = this.goalentity;
                if (havocbot_ctf_is_basewaypoint(goal) && vdist(goal.origin - this.origin, <, 100))
-                       this.goalentity_lock_timeout = time + ((this.bot_aimtarg) ? 2 : 3);
+                       this.goalentity_lock_timeout = time + ((this.enemy) ? 2 : 3);
  
                if (goal)
                        this.havocbot_cantfindflag = time + 10;
index 4dd747353869fc00929360907c015199eef5505e,86dd8fc929db6113253b97ddd37ae402cf31cc4a..fdeff1b6fa902d1919c0ba0e9af6697f33ddd56b
@@@ -522,9 -522,9 +522,9 @@@ void W_Crylink_Attack2(Weapon thiswep, 
  METHOD(Crylink, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
  {
      if(random() < 0.10)
-         PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(crylink, speed), 0, WEP_CVAR_PRI(crylink, middle_lifetime), false);
+         PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(crylink, speed), 0, WEP_CVAR_PRI(crylink, middle_lifetime), false, true);
      else
-         PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), false);
+         PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), false, true);
  }
  METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
  {
          thiswep.wr_reload(thiswep, actor, weaponentity);
      }
  
 -    if(fire & 1)
 +    // attack swapping is useful for emulating BFG behavior in XDF
 +    int primary_fire = autocvar_g_balance_crylink_swap_attacks ? fire & 2 : fire & 1;
 +    int secondary_fire = autocvar_g_balance_crylink_swap_attacks ? fire & 1 : fire & 2;
 +
 +    if(primary_fire)
      {
          if(actor.(weaponentity).crylink_waitrelease != 1)
          if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(crylink, refire)))
          }
      }
  
 -    if((fire & 2) && autocvar_g_balance_crylink_secondary)
 +    if((secondary_fire) && autocvar_g_balance_crylink_secondary)
      {
          if(actor.(weaponentity).crylink_waitrelease != 2)
          if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(crylink, refire)))
          }
      }
  
 -    if((actor.(weaponentity).crylink_waitrelease == 1 && !(fire & 1)) || (actor.(weaponentity).crylink_waitrelease == 2 && !(fire & 2)))
 +    if((actor.(weaponentity).crylink_waitrelease == 1 && !(primary_fire)) || (actor.(weaponentity).crylink_waitrelease == 2 && !(secondary_fire)))
      {
          if(!actor.(weaponentity).crylink_lastgroup || time > actor.(weaponentity).crylink_lastgroup.teleport_time)
          {
index 02f07784e9893fbf6429914a0feb2813017b843d,799c3426f61f9c6ac32e9f91170230b61491f650..209040f9f4dc6313d4ca05fda1a69532d0849f72
@@@ -26,7 -26,6 +26,7 @@@
  #include <server/scores_rules.qh>
  #include <server/teamplay.qh>
  #include <server/world.qh>
 +#include <lib/misc.qh>
  
  //  used by GameCommand_make_mapinfo()
  void make_mapinfo_Think(entity this)
@@@ -730,7 -729,7 +730,7 @@@ void GameCommand_gametype(int request, 
                        if (argv(1) != "")
                        {
                                string s = argv(1);
-                               Gametype t = MapInfo_Type_FromString(s, false), tsave = MapInfo_CurrentGametype();
+                               Gametype t = MapInfo_Type_FromString(s, false, false), tsave = MapInfo_CurrentGametype();
  
                                if (t)
                                {
@@@ -1151,38 -1150,6 +1151,38 @@@ void GameCommand_nospectators(int reque
        }
  }
  
 +void GameCommand_printplayer(int request, int argc)
 +{
 +      switch (request)
 +        {
 +              case CMD_REQUEST_COMMAND:
 +                {
 +                      entity player = GetIndexedEntity(argc, 1);
 +                        if (player.playerid)
 +                        {
 +                              GameLogEcho(strcat(
 +                                                   strcat(
 +                                                          ":playerinfo:", ftos(player.playerid),
 +                                                          ":", ftos(etof(player)),
 +                                                          ":", ftos(CS_CVAR(player).cvar_cl_allow_uidtracking),
 +                                                          ":", ftos(CS_CVAR(player).cvar_cl_allow_uid2name)),
 +                                                   strcat(
 +                                                          ":", ftos(CS_CVAR(player).cvar_cl_allow_uidranking),
 +                                                          ":", ((IS_REAL_CLIENT(player)) ? GameLog_ProcessIP(player.netaddress) : "bot"),
 +                                                          ":", player.crypto_idfp,
 +                                                          ":", playername(player.netname, player.team, false))));
 +                        }
 +                      return;
 +                }
 +              default:
 +                case CMD_REQUEST_USAGE:
 +                {
 +                      LOG_HELP("Usage:^3 sv_cmd printplayer <player_entity_id>");
 +                        return;
 +                }
 +        }
 +}
 +
  void GameCommand_printstats(int request)
  {
        switch (request)
@@@ -1655,36 -1622,6 +1655,36 @@@ void GameCommand_warp(int request, int 
        }
  }
  
 +void IRCSay(string msgstr)
 +{
 +      if(msgstr == "")
 +              return;
 +
 +      string prefix;
 +      if(substring(msgstr, 0, 3) == "^4*") // actions
 +              prefix = "\{3}";
 +      else
 +              prefix = "\{1}";
 +
 +      msgstr = strcat(prefix, strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
 +
 +      FOREACH_CLIENTSLOT(true,
 +      {
 +              if(!intermission_running)
 +              if((autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || game_stopped)))
 +              if(IS_PLAYER(it))
 +                      continue;
 +              if(IS_REAL_CLIENT(it))
 +                      sprint(it, msgstr);
 +      });
 +}
 +
 +void GameCommand_ircmsg(int request, int argc, string command)
 +{
 +      IRCSay(substring(command, strlen(argv(0))+1, strlen(command)));
 +        return;
 +}
 +
  /* use this when creating a new command, making sure to place it in alphabetical order... also,
  ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
  void GameCommand_(int request)
@@@ -1731,12 -1668,10 +1731,12 @@@ SERVER_COMMAND(extendmatchtime, "Increa
  SERVER_COMMAND(gametype, "Simple command to change the active gametype") { GameCommand_gametype(request, arguments); }
  SERVER_COMMAND(gettaginfo, "Get specific information about a weapon model") { GameCommand_gettaginfo(request, arguments); }
  SERVER_COMMAND(gotomap, "Simple command to switch to another map") { GameCommand_gotomap(request, arguments); }
 +SERVER_COMMAND(ircmsg, "Utility function to forward chat messages from IRC/discord/whatever") { GameCommand_ircmsg(request, arguments, command); }
  SERVER_COMMAND(lockteams, "Disable the ability for players to switch or enter teams") { GameCommand_lockteams(request); }
  SERVER_COMMAND(make_mapinfo, "Automatically rebuild mapinfo files") { GameCommand_make_mapinfo(request); }
  SERVER_COMMAND(moveplayer, "Change the team/status of a player") { GameCommand_moveplayer(request, arguments); }
  SERVER_COMMAND(nospectators, "Automatically remove spectators from a match") { GameCommand_nospectators(request); }
 +SERVER_COMMAND(printplayer, "Print information about a player") { GameCommand_printplayer(request, arguments); }
  SERVER_COMMAND(printstats, "Dump eventlog player stats and other score information") { GameCommand_printstats(request); }
  SERVER_COMMAND(radarmap, "Generate a radar image of the map") { GameCommand_radarmap(request, arguments); }
  SERVER_COMMAND(reducematchtime, "Decrease the timelimit value incrementally") { GameCommand_reducematchtime(request); }