Merge branch 'master' into terencehill/lms_updates
authorterencehill <piuntn@gmail.com>
Thu, 17 Jun 2021 12:18:15 +0000 (14:18 +0200)
committerterencehill <piuntn@gmail.com>
Thu, 17 Jun 2021 12:18:15 +0000 (14:18 +0200)
# Conflicts:
# qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc

gamemodes-server.cfg
qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc
qcsrc/common/mutators/mutator/waypoints/all.inc

index 77a6d735dc80c97fff785a4ffdbf9ecabc7d1fca..e97ae1a01a5f979c4354fe16e71fa8badd8629f2 100644 (file)
@@ -449,6 +449,18 @@ set g_lms_regenerate 0
 set g_lms_last_join 3  "if g_lms_join_anytime is false, new players can only join if the worst active player has more than (fraglimit - g_lms_last_join) lives"
 set g_lms_join_anytime 1       "if true, new players can join, but get same amount of lives as the worst player"
 set g_lms_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena"
+set g_lms_leader_wp_lives 2 "show waypoints for players leading by this number of lives"
+set g_lms_leader_wp_max_relative 0.5 "show waypoints for leaders only if they are max this fraction of total players"
+set g_lms_leader_wp_time 5 "show waypoints for leaders only for this amount of time"
+set g_lms_leader_wp_time_repeat 30 "periodically show again waypoints for leaders after this amount of time"
+set g_lms_dynamic_respawn_delay 1 "increase player respawn delay based on the number of lives less than the leader (NOTE: delay doesn't increase when only 2 players are left alive)"
+set g_lms_dynamic_respawn_delay_base 2 "base player respawn delay"
+set g_lms_dynamic_respawn_delay_increase 3 "increase base player respawn delay by this amount of time for each life less than the leader"
+set g_lms_dynamic_vampire 1 "attackers receive a fraction of health removed from enemies based on the number of lives less than the enemy"
+set g_lms_dynamic_vampire_factor_base 0.1 "base vampire factor"
+set g_lms_dynamic_vampire_factor_increase 0.1 "increase vampire factor by this fraction for each life less than the enemy"
+set g_lms_dynamic_vampire_factor_max 0.5 "max vampire factor"
+set g_lms_dynamic_vampire_min_lives_diff 2 "number of lives the attacker must have less than the enemy to receive the base fraction of health"
 
 
 // =========
index dc18b7c9072413a51d0d93d2aff0993682cb0750..1d47326929b7851dcd8e24b3676a0d22d71cb6b0 100644 (file)
@@ -10,6 +10,20 @@ int autocvar_g_lms_extra_lives;
 bool autocvar_g_lms_join_anytime;
 int autocvar_g_lms_last_join;
 bool autocvar_g_lms_regenerate;
+int autocvar_g_lms_leader_wp_lives;
+float autocvar_g_lms_leader_wp_max_relative;
+float autocvar_g_lms_leader_wp_time;
+float autocvar_g_lms_leader_wp_time_repeat;
+float autocvar_g_lms_dynamic_respawn_delay;
+float autocvar_g_lms_dynamic_respawn_delay_base;
+float autocvar_g_lms_dynamic_respawn_delay_increase;
+bool autocvar_g_lms_dynamic_vampire;
+float autocvar_g_lms_dynamic_vampire_factor_base;
+float autocvar_g_lms_dynamic_vampire_factor_increase;
+float autocvar_g_lms_dynamic_vampire_factor_max;
+int autocvar_g_lms_dynamic_vampire_min_lives_diff;
+
+.float lms_wp_time;
 
 // main functions
 int LMS_NewPlayerLives()
@@ -127,6 +141,8 @@ MUTATOR_HOOKFUNCTION(lms, reset_map_players)
                it.frags = FRAGS_PLAYER;
                GameRules_scoring_add(it, LMS_LIVES, LMS_NewPlayerLives());
                PutClientInServer(it);
+               if (it.waypointsprite_attachedforcarrier)
+                       WaypointSprite_Kill(it.waypointsprite_attachedforcarrier);
        });
 }
 
@@ -156,6 +172,33 @@ MUTATOR_HOOKFUNCTION(lms, PutClientInServer)
        }
 }
 
+MUTATOR_HOOKFUNCTION(lms, CalculateRespawnTime)
+{
+       entity player = M_ARGV(0, entity);
+       player.respawn_flags |= RESPAWN_FORCE;
+
+       if (autocvar_g_lms_dynamic_respawn_delay <= 0)
+               return false;
+
+       int pl_lives = GameRules_scoring_add(player, LMS_LIVES, 0);
+       int max_lives = 0;
+       int pl_cnt = 0;
+       FOREACH_CLIENT(it != player && IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
+               int lives = GameRules_scoring_add(it, LMS_LIVES, 0);
+               if (lives > max_lives)
+                       max_lives = lives;
+               pl_cnt++;
+       });
+
+       // min delay with only 2 players
+       if (pl_cnt == 1) // player wasn't counted
+               max_lives = 0;
+
+       player.respawn_time = time + autocvar_g_lms_dynamic_respawn_delay_base +
+               autocvar_g_lms_dynamic_respawn_delay_increase * max(0, max_lives - pl_lives);
+       return true;
+}
+
 MUTATOR_HOOKFUNCTION(lms, ForbidSpawn)
 {
        entity player = M_ARGV(0, entity);
@@ -170,20 +213,6 @@ MUTATOR_HOOKFUNCTION(lms, ForbidSpawn)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(lms, PlayerDies)
-{
-       entity frag_target = M_ARGV(2, entity);
-
-       float tl = GameRules_scoring_add(frag_target, LMS_LIVES, 0);
-       if (tl <= 0)
-       {
-               frag_target.respawn_flags = RESPAWN_SILENT;
-               // prevent unwanted sudden rejoin as spectator and movement of spectator camera
-               frag_target.respawn_time = time + 2;
-       }
-       frag_target.respawn_flags |= RESPAWN_FORCE;
-}
-
 void lms_RemovePlayer(entity player)
 {
        static int quitters = 0;
@@ -296,6 +325,109 @@ MUTATOR_HOOKFUNCTION(lms, ForbidThrowCurrentWeapon)
        return true;
 }
 
+MUTATOR_HOOKFUNCTION(lms, Damage_Calculate)
+{
+       if (!autocvar_g_lms_dynamic_vampire)
+               return;
+
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float frag_damage = M_ARGV(4, float);
+
+       if (IS_PLAYER(frag_attacker) && !IS_DEAD(frag_target) && frag_attacker != frag_target)
+       {
+               float vampire_factor = 0;
+
+               int frag_attacker_lives = GameRules_scoring_add(frag_attacker, LMS_LIVES, 0);
+               int frag_target_lives = GameRules_scoring_add(frag_target, LMS_LIVES, 0);
+               int diff = frag_target_lives - frag_attacker_lives - autocvar_g_lms_dynamic_vampire_min_lives_diff;
+
+               if (diff >= 0)
+                       vampire_factor = autocvar_g_lms_dynamic_vampire_factor_base + diff * autocvar_g_lms_dynamic_vampire_factor_increase;
+               if (vampire_factor > 0)
+               {
+                       vampire_factor = min(vampire_factor, autocvar_g_lms_dynamic_vampire_factor_max);
+                       SetResourceExplicit(frag_attacker, RES_HEALTH,
+                               min(GetResource(frag_attacker, RES_HEALTH) + frag_damage * vampire_factor, start_health));
+               }
+       }
+}
+
+bool lms_waypointsprite_visible_for_player(entity this, entity player, entity view) // runs on waypoints which are attached to ballcarriers, updates once per frame
+{
+       if(view.lms_wp_time)
+               if(IS_SPEC(player))
+                       return false; // we don't want spectators of leaders to see the attached waypoint on the top of their screen
+
+       float leader_time = autocvar_g_lms_leader_wp_time;
+       float leader_repeat_time = leader_time + autocvar_g_lms_leader_wp_time_repeat;
+       float wp_time = this.owner.lms_wp_time;
+       if (wp_time && (time - wp_time) % leader_repeat_time > leader_time)
+               return false;
+
+       return true;
+}
+
+void lms_UpdateWaypoints()
+{
+       int max_lives = 0;
+       int pl_cnt = 0;
+       FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
+               int lives = GameRules_scoring_add(it, LMS_LIVES, 0);
+               if (lives > max_lives)
+                       max_lives = lives;
+               pl_cnt++;
+       });
+
+       int second_max_lives = 0;
+       int pl_cnt_with_max_lives = 0;
+       FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
+               int lives = GameRules_scoring_add(it, LMS_LIVES, 0);
+               if (lives == max_lives)
+                       pl_cnt_with_max_lives++;
+               else if (lives > second_max_lives)
+                       second_max_lives = lives;
+       });
+
+       int lives_diff = autocvar_g_lms_leader_wp_lives;
+       if (max_lives - second_max_lives >= lives_diff && pl_cnt_with_max_lives <= pl_cnt * autocvar_g_lms_leader_wp_max_relative)
+               FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
+                       int lives = GameRules_scoring_add(it, LMS_LIVES, 0);
+                       if (lives == max_lives)
+                       {
+                               if (!it.waypointsprite_attachedforcarrier)
+                               {
+                                       WaypointSprite_AttachCarrier(WP_LmsLeader, it, RADARICON_FLAGCARRIER);
+                                       it.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = lms_waypointsprite_visible_for_player;
+                                       WaypointSprite_UpdateRule(it.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
+                                       vector pl_color = colormapPaletteColor(it.clientcolors & 0x0F, false);
+                                       WaypointSprite_UpdateTeamRadar(it.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, pl_color);
+                                       WaypointSprite_Ping(it.waypointsprite_attachedforcarrier);
+                               }
+                               if (!it.lms_wp_time)
+                                       it.lms_wp_time = time;
+                       }
+                       else
+                       {
+                               if (it.waypointsprite_attachedforcarrier)
+                                       WaypointSprite_Kill(it.waypointsprite_attachedforcarrier);
+                               it.lms_wp_time = 0;
+                       }
+               });
+       else
+               FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, {
+                       if (it.waypointsprite_attachedforcarrier)
+                               WaypointSprite_Kill(it.waypointsprite_attachedforcarrier);
+                       it.lms_wp_time = 0;
+               });
+}
+
+MUTATOR_HOOKFUNCTION(lms, PlayerDied)
+{
+       if (!warmup_stage && autocvar_g_lms_leader_wp_lives > 0)
+               lms_UpdateWaypoints();
+}
+
 MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill)
 {
        entity frag_target = M_ARGV(1, entity);
index daa5af4d51dff6d099a53f1cc13d299c94e899df..aa0ca250fdaede1d595fa6fd5f4113a022bba0d8 100644 (file)
@@ -48,6 +48,8 @@ REGISTER_WAYPOINT(KeyCarrierPink, _("Key carrier"), "kh_pink_carrying", '0 1 1',
 REGISTER_WAYPOINT(KaBall, _("Ball"), "notify_ballpickedup", '0 1 1', 1);
 REGISTER_WAYPOINT(KaBallCarrier, _("Ball carrier"), "keepawayball_carrying", '1 0 0', 1);
 
+REGISTER_WAYPOINT(LmsLeader, _("Leader"), "", '0 1 1', 1);
+
 REGISTER_WAYPOINT(NbBall, _("Ball"), "", '0.91 0.85 0.62', 1);
 REGISTER_WAYPOINT(NbGoal, _("Goal"), "", '1 0.5 0', 1);