From: Rudolf Polzer Date: Wed, 23 Oct 2013 10:52:05 +0000 (+0200) Subject: Code cleanup. X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=c51fa1c55d02c2d4c6aca51d8cbd24ba0fcd4ac4;p=xonotic%2Fxonotic-data.pk3dir.git Code cleanup. --- diff --git a/qcsrc/server/item_ammo.qc b/qcsrc/server/item_ammo.qc index 5ccb27e03..3bb647aa7 100644 --- a/qcsrc/server/item_ammo.qc +++ b/qcsrc/server/item_ammo.qc @@ -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)