]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Code cleanup.
authorRudolf Polzer <divverent@xonotic.org>
Wed, 23 Oct 2013 10:52:05 +0000 (12:52 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Wed, 23 Oct 2013 10:52:05 +0000 (12:52 +0200)
qcsrc/server/item_ammo.qc

index 5ccb27e0365ff0a74b3c323d27af2e88ea6fa4c1..3bb647aa7b3a817b438af023de2ce2d09721b6b7 100644 (file)
@@ -6,8 +6,8 @@ float autocvar_g_balance_ammodistribution_nudgefactor_max = 1.4;
 float autocvar_g_balance_ammodistribution_inner_exponent = -1;
 float autocvar_g_balance_ammodistribution_outer_exponent = 2;
 float autocvar_g_balance_ammodistribution_outer_nonmatch_exponent = -2;
-float autocvar_g_balance_ammodistribution_p3distance = 64 * 3 + 1;
-float autocvar_g_balance_ammodistribution_p2distance = 1024 + 1;
+float autocvar_g_balance_ammodistribution_shortdistance = 64 * 3 + 1;
+float autocvar_g_balance_ammodistribution_mediumdistance = 1024 + 1;
 // NOTE: these also exist per weapon
 float autocvar_g_balance_ammodistribution_modifier_shells = 1;
 float autocvar_g_balance_ammodistribution_modifier_bullets = 1;
@@ -87,32 +87,33 @@ void item_ammo_findtarget()
                self.item_ammo_origin = e.origin;
 }
 
+const float ITEM_AMMO_PRIORITY_UNDEFINED = -1;
+const float ITEM_AMMO_PRIORITY_UNIQUE = 1;
+const float ITEM_AMMO_PRIORITY_FORBIDDEN = 0;
+const float ITEM_AMMO_PRIORITY_NOTTHERE = 2;
+const float ITEM_AMMO_PRIORITY_FAR = 4;
+const float ITEM_AMMO_PRIORITY_MEDIUM = 6;
+const float ITEM_AMMO_PRIORITY_SHORT = 8;
+
 float item_ammo_picked;
 void item_ammo_pick()
 {
+       entity w, a;
+       float i, j;
+
+       // Run only once.
        if (item_ammo_picked)
                return;
        item_ammo_picked = 1;
 
+       // Find the items we care about.
        entity ammolist = findchain(classname, "item_ammo");
        entity weaponlist = findchainflags(flags, FL_WEAPON);
 
-       entity w, a;
-       float i, j;
-
-       float n_w = 0;
-       for (w = weaponlist; w; w = w.chain)
-               ++n_w;
-       float n_a = 0;
+       // Count the ammo items. We'll need this in many places.
+       float n_ammo = 0;
        for (a = ammolist; a; a = a.chain)
-               ++n_a;
-
-       float n_teams = 1;
-       if (have_team_spawns > 0)
-       {
-               CheckAllowedTeams(world);
-               n_teams = (c1 >= 0) + (c2 >= 0) + (c3 >= 0) + (c4 >= 0);
-       }
+               ++n_ammo;
 
        // Find out how many we want of each item.
        for (i = 0; i < ITEM_AMMO_COUNT; ++i)
@@ -124,27 +125,47 @@ void item_ammo_pick()
                if (start_weapons & get_weaponinfo(j).weapons)
                        for (i = 0; i < ITEM_AMMO_COUNT; ++i)
                                if (get_weaponinfo(j).items & item_ammo_type(i))
-                                       item_ammo_mincount[i] = n_teams;
+                               {
+                                       item_ammo_mincount[i] = 1;
+                                       // Just 1, even in teamplay! On
+                                       // properly designed maps, these
+                                       // low-priority items will end up in an
+                                       // neutral area.
+                               }
+       float n_weapons = 0;
        for (w = weaponlist; w; w = w.chain)
        {
-               float c = 0;
+               float n_ammotypes = 0;
                for (i = 0; i < ITEM_AMMO_COUNT; ++i)
                        if (get_weaponinfo(w.weapon).items & item_ammo_type(i))
-                               ++c;
-               if (!c)
-               {
-                       // Ammoless weapon is not a weapon.
-                       --n_w;
-                       // No need to remove it from the list though - no other
-                       // loop will do anything with it.
+                               ++n_ammotypes;
+               if (!n_ammotypes)
                        continue;
+
+               // Teamed items count as 1 item in total.
+               float n_itemteams = 1;
+               if (w.team)
+               {
+                       entity w2;
+                       for(w2 = world; (w2 = findfloat(w2, team, w.team)); )
+                               if (w2 != w)
+                                       if(w2.flags & FL_WEAPON)
+                                               ++n_itemteams;
                }
-               c = 1.0 / c;
+
+               n_weapons += 1.0 / n_itemteams;
+
+               // A weapon also counts as 1 in total, no matter how many ammo
+               // types it uses.
+               float weight = 1.0 / (n_ammotypes * n_itemteams);
                for (i = 0; i < ITEM_AMMO_COUNT; ++i)
                        if (get_weaponinfo(w.weapon).items & item_ammo_type(i))
                        {
-                               item_ammo_count[i] += item_ammo_modifier(i) * item_ammo_weaponmodifier(w.weapon) * c;
-                               item_ammo_mincount[i] = 0;  // n_teams;  // Not needed, weapons serve as ammo pickups too.
+                               item_ammo_count[i] += item_ammo_modifier(i) * item_ammo_weaponmodifier(w.weapon) * weight;
+                               // Weapon exists on the map! No "bonus" for
+                               // start weapons needed any more, as the weapon
+                               // pickup serves as ammo too.
+                               item_ammo_mincount[i] = 0;
                        }
        }
        float n_count = 0;
@@ -154,21 +175,32 @@ void item_ammo_pick()
                n_count += item_ammo_count[i];
                n_mincount += item_ammo_mincount[i];
        }
+       // Scale the ratio by the numbers of available guns.
        for (i = 0; i < ITEM_AMMO_COUNT; ++i)
        {
                float c_i = item_ammo_count[i];
                float m_i = item_ammo_mincount[i];
-               item_ammo_count[i]    = m_i + max(0,  ceil(c_i * (n_a - n_mincount) / n_count * autocvar_g_balance_ammodistribution_nudgefactor_max));
+               item_ammo_count[i]    = m_i + max(0,  ceil(c_i * (n_ammo - n_mincount) / n_count * autocvar_g_balance_ammodistribution_nudgefactor_max));
                // Obviously >=
-               item_ammo_mincount[i] = m_i + max(0, floor(c_i * (n_a - n_mincount) / n_count * autocvar_g_balance_ammodistribution_nudgefactor_min));
+               item_ammo_mincount[i] = m_i + max(0, floor(c_i * (n_ammo - n_mincount) / n_count * autocvar_g_balance_ammodistribution_nudgefactor_min));
                dprint(sprintf("Item %s distribution: at least %d, at most %d, distribution input %d %f\n", Item_CounterFieldName(item_ammo_type(i)), item_ammo_mincount[i], item_ammo_count[i], m_i, c_i));
        }
+       // Recalculate minimum counts, as we just changed item_ammo_mincount[]
+       n_mincount = 0;
+       for (i = 0; i < ITEM_AMMO_COUNT; ++i)
+               n_mincount += item_ammo_mincount[i];
+
+       // Check for sane distribution.
+       if (n_ammo < n_weapons * 0.5)
+               print("^3WARNING: not enough ammo items. Expect to run out of ammo.\n");
+       if (n_ammo > n_weapons * 2.0)
+               print("^3WARNING: too many ammo items. Expect to swim in ammo.\n");
 
        // Find the weights and priorities.
        for (a = ammolist; a; a = a.chain)
        {
                float totalsum = 0;
-               float maxprio = -1;
+               float maxprio = ITEM_AMMO_PRIORITY_UNDEFINED;
                float maxprio_count = 0;
                for (i = 0; i < ITEM_AMMO_COUNT; ++i)
                {
@@ -188,20 +220,24 @@ void item_ammo_pick()
                                                mindist = dist;
                                        sum += pow(dist, autocvar_g_balance_ammodistribution_inner_exponent);
                                }
-                       if (sum == 0)
-                               a.item_ammo_weight[i] = 0;
+                       if (mindist < 0)
+                               a.item_ammo_weight[i] = weight;  // To be fixed later.
                        else
                                a.item_ammo_weight[i] = pow(sum, autocvar_g_balance_ammodistribution_outer_exponent) * weight;
-                       if (mindist < 0)
-                               a.item_ammo_priority[i] = 0;
-                       else if (forbid)
-                               a.item_ammo_priority[i] = 1;
-                       else if (mindist <= autocvar_g_balance_ammodistribution_p3distance)
-                               a.item_ammo_priority[i] = 4;
-                       else if (mindist <= autocvar_g_balance_ammodistribution_p2distance)
-                               a.item_ammo_priority[i] = 3;
+
+                       // Mapper said to not place this item here? FINE.
+                       if (forbid)
+                               a.item_ammo_priority[i] = ITEM_AMMO_PRIORITY_FORBIDDEN;
+                       // Not there? Quite low prio, then.
+                       else if (mindist < 0)
+                               a.item_ammo_priority[i] = ITEM_AMMO_PRIORITY_NOTTHERE;
+                       // Distance based decisions.
+                       else if (mindist <= autocvar_g_balance_ammodistribution_shortdistance)
+                               a.item_ammo_priority[i] = ITEM_AMMO_PRIORITY_SHORT;
+                       else if (mindist <= autocvar_g_balance_ammodistribution_mediumdistance)
+                               a.item_ammo_priority[i] = ITEM_AMMO_PRIORITY_MEDIUM;
                        else
-                               a.item_ammo_priority[i] = 2;
+                               a.item_ammo_priority[i] = ITEM_AMMO_PRIORITY_FAR;
                        if (a.item_ammo_priority[i] > maxprio)
                        {
                                maxprio = a.item_ammo_priority[i];
@@ -213,11 +249,16 @@ void item_ammo_pick()
                }
                for (i = 0; i < ITEM_AMMO_COUNT; ++i)
                {
-                       float weight = a.(Item_CounterField(item_ammo_type(i)));
-                       if (a.item_ammo_weight[i] == 0)
-                               a.item_ammo_weight[i] = pow(totalsum, autocvar_g_balance_ammodistribution_outer_nonmatch_exponent) * weight;
+                       // If item is not there at all, we could not assign a
+                       // weight. Assign a weight based on the inverse of the
+                       // other item weights (i.e. put it where it's least in
+                       // the way for other items).
+                       if (a.item_ammo_priority[i] == ITEM_AMMO_PRIORITY_NOTTHERE)
+                               a.item_ammo_weight[i] *= pow(totalsum, autocvar_g_balance_ammodistribution_outer_nonmatch_exponent);
+                       // Boost if the highest priority is unique (respect
+                       // mapper's intentions).
                        if (a.item_ammo_priority[i] == maxprio && maxprio_count == 1)
-                               a.item_ammo_priority[i] += 0.5;
+                               a.item_ammo_priority[i] |= ITEM_AMMO_PRIORITY_UNIQUE;
                }
        }
 
@@ -225,11 +266,10 @@ void item_ammo_pick()
        for (a = ammolist; a; a = a.chain)
                a.item_ammo_chosen_type = -1;
 
-       // Distribute them properly.
-       n_mincount = 0;
-       for (i = 0; i < ITEM_AMMO_COUNT; ++i)
-               n_mincount += item_ammo_mincount[i];
-       while (n_a > 0)
+       // Distribute them properly. Remember the highest unfulfilled priority
+       // for warnings.
+       float max_unfulfilled_priority = ITEM_AMMO_PRIORITY_UNDEFINED;
+       while (n_ammo > 0)
        {
                // Randomly pick one of the remaining item spawn points, and a
                // corresponding item type. Honor priorities and weights.
@@ -237,46 +277,53 @@ void item_ammo_pick()
                for (a = ammolist; a; a = a.chain)
                        if (a.item_ammo_chosen_type == -1)
                                for (i = 0; i < ITEM_AMMO_COUNT; ++i)
-                                       if (((n_a <= n_mincount) ? item_ammo_mincount[i] : item_ammo_count[i]) > 0)
+                                       if (((n_ammo <= n_mincount) ? item_ammo_mincount[i] : item_ammo_count[i]) > 0)
                                                RandomSelection_Add(a, i, string_null, a.item_ammo_weight[i], a.item_ammo_priority[i]);
                a = RandomSelection_chosen_ent;
                i = RandomSelection_chosen_float;
 
+               // Selection failed?
                if (!a)
-               {
-                       if (n_mincount >= 0)
-                       {
-                               backtrace("Nothing to select. Retrying without mincount... (BTW: FIX YOUR MAP AND/OR THIS CODE)");
-                               n_mincount = 0;
-                               continue;
-                       }
-                       error("Nothing to select.");
-               }
+                       error("Nothing to select. Probably a bug in this code.");
+
+               dprint(sprintf("%v (%v): %s -> %s\n", a.origin, a.item_ammo_origin, a.save_classname, Item_CounterFieldName(item_ammo_type(i))));
 
                // Assign the selected item type to the selected item.
                a.item_ammo_chosen_type = i;
 
-               dprint(sprintf("%v (%v): %s -> %s\n", a.origin, a.item_ammo_origin, a.save_classname, Item_CounterFieldName(item_ammo_type(i))));
+               // Test for unfulfilled priorities.
+               for (j = 0; j < ITEM_AMMO_COUNT; ++j)
+                       if (j != i)
+                               if (a.item_ammo_priority[j] >= a.item_ammo_priority[i])
+                                       if (a.item_ammo_priority[j] > max_unfulfilled_priority)
+                                               max_unfulfilled_priority = a.item_ammo_priority[j];
 
                // Decrease the count of remaining items.
                if (item_ammo_mincount[i] > 0)
                        --n_mincount;
                --item_ammo_mincount[i];
                --item_ammo_count[i];
-               --n_a;
+               --n_ammo;
        }
 
-       for (i = 0; i < ITEM_AMMO_COUNT; ++i)
+       // Report the highest unfulfilled priority.
+       switch (max_unfulfilled_priority)
        {
-               if (item_ammo_count[i] - item_ammo_mincount[i] >= 2)
-               {
-                       if (item_ammo_mincount[i] == 0)
-                               print(sprintf("WARNING: map might not have enough item_ammo near %s weapons\n", Item_CounterFieldName(item_ammo_type(i))));
-                       if (item_ammo_count[i] == 0)
-                               print(sprintf("WARNING: map might have too many item_ammo near %s weapons\n", Item_CounterFieldName(item_ammo_type(i))));
-               }
+               case ITEM_AMMO_PRIORITY_SHORT | ITEM_AMMO_PRIORITY_UNIQUE:
+                       print("^3WARNING: short-range distribution could not be fulfilled with the available item counts. Random selection has taken place.\n");
+                       break;
+               case ITEM_AMMO_PRIORITY_SHORT:
+                       print("^3WARNING: short-range distribution is ambiguous. Random selection has taken place.\n");
+                       break;
+               case ITEM_AMMO_PRIORITY_MEDIUM | ITEM_AMMO_PRIORITY_UNIQUE:
+                       print("^3WARNING: medium-range distribution could not be fulfilled with the available item counts. Random selection has taken place.\n");
+                       break;
+               case ITEM_AMMO_PRIORITY_MEDIUM:
+                       print("^3WARNING: medium-range distribution is ambiguous. Random selection has taken place.\n");
+                       break;
        }
 
+       // Recount the item counts for debugging.
        for (i = 0; i < ITEM_AMMO_COUNT; ++i)
                item_ammo_count[i] = 0;
        for (a = ammolist; a; a = a.chain)