#include "sv_random_items.qh"
+
/// \file
/// \brief Source file that contains implementation of the random items mutator.
/// \author Lyberta
//============================ Constants ======================================
-enum
-{
- RANDOM_ITEM_TYPE_HEALTH = 1,
- RANDOM_ITEM_TYPE_ARMOR,
- RANDOM_ITEM_TYPE_RESOURCE,
- RANDOM_ITEM_TYPE_WEAPON,
- RANDOM_ITEM_TYPE_POWERUP
-};
-
//======================= Global variables ====================================
-bool autocvar_g_random_items; ///< Whether to enable random items.
-
// Replace cvars
/// \brief Classnames to replace %s with.
// Map probability cvars
-/// \brief Probability of random health items spawning in the map.
-float autocvar_g_random_items_health_probability;
-/// \brief Probability of random armor items spawning in the map.
-float autocvar_g_random_items_armor_probability;
-/// \brief Probability of random resource items spawning in the map.
-float autocvar_g_random_items_resource_probability;
-/// \brief Probability of random weapons spawning in the map.
-float autocvar_g_random_items_weapon_probability;
-/// \brief Probability of random powerups spawning in the map.
-float autocvar_g_random_items_powerup_probability;
-
/// \brief Probability of random %s spawning in the map.
/// float autocvar_g_random_items_%s_probability;
// Loot probability cvars
-/// \brief Probability of random health items spawning as loot.
-float autocvar_g_random_loot_health_probability;
-/// \brief Probability of random armor items spawning as loot.
-float autocvar_g_random_loot_armor_probability;
-/// \brief Probability of random resource items spawning as loot.
-float autocvar_g_random_loot_resource_probability;
-/// \brief Probability of random weapons spawning as loot.
-float autocvar_g_random_loot_weapon_probability;
-/// \brief Probability of random powerups spawning as loot.
-float autocvar_g_random_loot_powerup_probability;
-
/// \brief Probability of random %s spawning as loot.
-/// float autocvar_g_random_loot_weapon_%s_probability;
+/// float autocvar_g_random_loot_%s_probability;
/// \brief Probability of random %s spawning as loot during overkill.
/// float autocvar_g_random_loot_overkill_%s_probability;
/// recursion.
bool random_items_is_spawning = false;
-//========================= Free functions ====================================
+//====================== Forward declarations =================================
+
+/// \brief Returns a random classname of the item with specific property.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the item.
+string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
+ .bool item_property);
+
+//=========================== Public API ======================================
-string RandomItems_GetItemVarName(string class_name)
+string RandomItems_GetRandomItemClassName(string prefix)
{
- if (startsWith(class_name, "weapon_"))
+ if (MUTATOR_IS_ENABLED(mutator_instagib))
{
- FOREACH(Weapons, it.m_canonical_spawnfunc == class_name, {
- if (it.spawnflags & WEP_FLAG_MUTATORBLOCKED)
- {
- return "";
- }
- return class_name;
- });
+ return RandomItems_GetRandomInstagibItemClassName(prefix);
}
- bool is_ok = expr_evaluate(autocvar_g_overkill);
- switch (class_name)
+ if (MUTATOR_IS_ENABLED(ok))
{
- #define X(classname) case #classname: return #classname
- #define XCOND(classname, var, expr) case #classname: if (expr) return #var; else break
- X(item_health_small);
- X(item_health_medium);
- X(item_health_big);
- XCOND(item_health_mega, item_health_mega, !is_ok || !autocvar_g_overkill_filter_healthmega);
-
- X(item_armor_small);
- XCOND(item_armor_medium, item_armor_medium, !is_ok || !autocvar_g_overkill_filter_armormedium);
- XCOND(item_armor_big, item_armor_big, !is_ok || !autocvar_g_overkill_filter_armorbig);
- XCOND(item_armor_mega, item_armor_mega, !is_ok || !autocvar_g_overkill_filter_armormega);
-
- X(item_shells);
- X(item_bullets);
- X(item_rockets);
- X(item_cells);
- X(item_plasma);
- X(item_fuel);
-
- X(item_strength);
- X(item_shield);
- X(item_fuel_regen);
- X(item_jetpack);
-
- X(item_vaporizer_cells);
- X(item_invisibility);
- X(item_extralife);
- X(item_speed);
-
- #undef X
- #undef XCOND
+ return RandomItems_GetRandomOverkillItemClassName(prefix);
}
- return "";
+ return RandomItems_GetRandomVanillaItemClassName(prefix,
+ RANDOM_ITEM_TYPE_ALL);
}
-/// \brief Returns list of classnames to replace a map item with.
-/// \param[in] item Item to inspect.
-/// \return List of classnames to replace a map item with.
-string RandomItems_GetItemReplacementClassNames(entity item)
+string RandomItems_GetRandomVanillaItemClassName(string prefix, int types)
{
- string class_name = RandomItems_GetItemVarName(item.classname);
- if (class_name)
+ if (types == 0)
+ {
+ return "";
+ }
+ while (types != 0)
{
- return cvar_string(sprintf("g_random_items_replace_%s", class_name));
+ string cvar_name;
+ RandomSelection_Init();
+ if (types & RANDOM_ITEM_TYPE_HEALTH)
+ {
+ cvar_name = sprintf("g_%s_health_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_HEALTH,
+ cvar(cvar_name), 1);
+ }
+ }
+ if (types & RANDOM_ITEM_TYPE_ARMOR)
+ {
+ cvar_name = sprintf("g_%s_armor_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_ARMOR,
+ cvar(cvar_name), 1);
+ }
+ }
+ if (types & RANDOM_ITEM_TYPE_RESOURCE)
+ {
+ cvar_name = sprintf("g_%s_resource_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_RESOURCE,
+ cvar(cvar_name), 1);
+ }
+ }
+ if (types & RANDOM_ITEM_TYPE_WEAPON)
+ {
+ cvar_name = sprintf("g_%s_weapon_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_WEAPON, cvar(cvar_name), 1);
+ }
+ }
+ if (types & RANDOM_ITEM_TYPE_POWERUP)
+ {
+ cvar_name = sprintf("g_%s_powerup_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_POWERUP, cvar(cvar_name), 1);
+ }
+ }
+ int item_type = RandomSelection_chosen_float;
+ string class_name = "";
+ switch (item_type)
+ {
+ case RANDOM_ITEM_TYPE_HEALTH:
+ {
+ class_name = RandomItems_GetRandomItemClassNameWithProperty(
+ prefix, instanceOfHealth);
+ break;
+ }
+ case RANDOM_ITEM_TYPE_ARMOR:
+ {
+ class_name = RandomItems_GetRandomItemClassNameWithProperty(
+ prefix, instanceOfArmor);
+ break;
+ }
+ case RANDOM_ITEM_TYPE_RESOURCE:
+ {
+ class_name = RandomItems_GetRandomItemClassNameWithProperty(
+ prefix, instanceOfAmmo);
+ break;
+ }
+ case RANDOM_ITEM_TYPE_WEAPON:
+ {
+ RandomSelection_Init();
+ FOREACH(Weapons, it != WEP_Null &&
+ !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
+ {
+ cvar_name = sprintf("g_%s_%s_probability", prefix,
+ it.m_canonical_spawnfunc);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.",
+ cvar_name);
+ continue;
+ }
+ RandomSelection_AddString(it.m_canonical_spawnfunc,
+ cvar(cvar_name), 1);
+ });
+ class_name = RandomSelection_chosen_string;
+ break;
+ }
+ case RANDOM_ITEM_TYPE_POWERUP:
+ {
+ class_name = RandomItems_GetRandomItemClassNameWithProperty(
+ prefix, instanceOfPowerup);
+ break;
+ }
+ }
+ if (class_name != "")
+ {
+ return class_name;
+ }
+ types &= ~item_type;
}
return "";
}
-/// \brief Returns a random classname of the instagib map item.
-/// \return Random classname of the instagib map item.
-string RandomItems_GetRandomInstagibMapItemClassName()
+string RandomItems_GetRandomInstagibItemClassName(string prefix)
{
RandomSelection_Init();
- #define X(classname) \
- RandomSelection_AddString( \
- classname, \
- cvar(sprintf("g_random_items_%s_probability", RandomItems_GetItemVarName(classname))), \
- 1 \
- )
- X("item_vaporizer_cells");
- X("item_invisibility");
- X("item_extralife");
- X("item_speed");
- #undef X
+ FOREACH(Items, it.spawnflags & ITEM_FLAG_INSTAGIB &&
+ Item_IsDefinitionAllowed(it),
+ {
+ string cvar_name = sprintf("g_%s_%s_probability", prefix,
+ it.m_canonical_spawnfunc);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ continue;
+ }
+ RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+ });
return RandomSelection_chosen_string;
}
-/// \brief Returns a random classname of the overkill map item.
-/// \return Random classname of the overkill map item.
-string RandomItems_GetRandomOverkillMapItemClassName()
+string RandomItems_GetRandomOverkillItemClassName(string prefix)
{
RandomSelection_Init();
- string varname;
- #define X(classname) MACRO_BEGIN \
- if ((varname = RandomItems_GetItemVarName(classname))) \
- { \
- RandomSelection_AddString( \
- classname, \
- cvar(sprintf("g_random_items_overkill_%s_probability", varname)), \
- 1 \
- ); \
- } \
- MACRO_END
- X("item_health_mega");
- X("item_armor_small");
- X("item_armor_medium");
- X("item_armor_big");
- X("item_armor_mega");
- X("weapon_hmg");
- X("weapon_rpc");
- #undef X
+ FOREACH(Items, (it.spawnflags & ITEM_FLAG_OVERKILL) &&
+ !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED) &&
+ Item_IsDefinitionAllowed(it),
+ {
+ string cvar_name = sprintf("g_%s_overkill_%s_probability", prefix,
+ it.m_canonical_spawnfunc);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ continue;
+ }
+ RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+ });
+ string cvar_name = sprintf("g_%s_overkill_weapon_okhmg_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddString("weapon_okhmg", cvar(cvar_name), 1);
+ }
+ cvar_name = sprintf("g_%s_overkill_weapon_okrpc_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddString("weapon_okrpc", cvar(cvar_name), 1);
+ }
return RandomSelection_chosen_string;
}
-/// \brief Returns a random classname of the map item.
-/// \return Random classname of the map item.
-string RandomItems_GetRandomMapItemClassName()
+//========================= Free functions ====================================
+
+/// \brief Returns list of classnames to replace a map item with.
+/// \param[in] item Item to inspect.
+/// \return List of classnames to replace a map item with.
+string RandomItems_GetItemReplacementClassNames(entity item)
{
- if (autocvar_g_instagib)
- {
- return RandomItems_GetRandomInstagibMapItemClassName();
- }
- if (expr_evaluate(autocvar_g_overkill))
+ string cvar_name = sprintf("g_random_items_replace_%s", item.classname);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
{
- return RandomItems_GetRandomOverkillMapItemClassName();
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ return "";
}
+ return cvar_string(cvar_name);
+}
+
+string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
+ .bool item_property)
+{
RandomSelection_Init();
- #define X(type, name) \
- RandomSelection_AddFloat( \
- RANDOM_ITEM_TYPE_##type, \
- autocvar_g_random_items_##name##_probability, \
- 1 \
- )
- X(HEALTH, health);
- X(ARMOR, armor);
- X(RESOURCE, resource);
- X(WEAPON, weapon);
- X(POWERUP, powerup);
- #undef X
- int item_type = RandomSelection_chosen_float;
- switch (item_type)
+ FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL) &&
+ Item_IsDefinitionAllowed(it),
{
- case RANDOM_ITEM_TYPE_HEALTH:
- {
- RandomSelection_Init();
- FOREACH(Items, it.instanceOfHealth,
- {
- RandomSelection_AddString(it.m_canonical_spawnfunc,
- cvar(sprintf("g_random_items_%s_probability",
- it.m_canonical_spawnfunc)), 1);
- });
- return RandomSelection_chosen_string;
- }
- case RANDOM_ITEM_TYPE_ARMOR:
- {
- RandomSelection_Init();
- FOREACH(Items, it.instanceOfArmor,
- {
- RandomSelection_AddString(it.m_canonical_spawnfunc,
- cvar(sprintf("g_random_items_%s_probability",
- it.m_canonical_spawnfunc)), 1);
- });
- return RandomSelection_chosen_string;
- }
- case RANDOM_ITEM_TYPE_RESOURCE:
- {
- RandomSelection_Init();
- FOREACH(Items, it.instanceOfAmmo,
- {
- RandomSelection_AddString(it.m_canonical_spawnfunc,
- cvar(sprintf("g_random_items_%s_probability",
- it.m_canonical_spawnfunc)), 1);
- });
- return RandomSelection_chosen_string;
- }
- case RANDOM_ITEM_TYPE_WEAPON:
+ string cvar_name = sprintf("g_%s_%s_probability", prefix,
+ it.m_canonical_spawnfunc);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
{
- RandomSelection_Init();
- FOREACH(Weapons, !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
- {
- string cvar_name = sprintf("g_random_items_%s_probability",
- it.m_canonical_spawnfunc);
- if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
- {
- continue;
- }
- RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
- });
- return RandomSelection_chosen_string;
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ continue;
}
- case RANDOM_ITEM_TYPE_POWERUP:
- {
- RandomSelection_Init();
- #define X(classname) \
- RandomSelection_AddString( \
- classname, \
- cvar(sprintf("g_random_items_%s_probability", classname)), \
- 1 \
- )
- X("item_strength");
- X("item_shield");
- X("item_fuel_regen");
- X("item_jetpack");
- #undef X
- return RandomSelection_chosen_string;
- }
- }
- return "";
+ RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+ });
+ return RandomSelection_chosen_string;
}
/// \brief Replaces a map item.
string new_classname;
if (new_classnames == "random")
{
- new_classname = RandomItems_GetRandomMapItemClassName();
+ new_classname = RandomItems_GetRandomItemClassName("random_items");
if (new_classname == "")
{
return NULL;
}
random_items_is_spawning = true;
entity new_item;
- if (!expr_evaluate(autocvar_g_overkill))
+ if (!MUTATOR_IS_ENABLED(ok))
{
- new_item = Item_Create(strzone(new_classname), item.origin);
+ new_item = Item_Create(strzone(new_classname), item.origin,
+ Item_ShouldKeepPosition(item));
random_items_is_spawning = false;
if (new_item == NULL)
{
new_item = spawn();
new_item.classname = strzone(new_classname);
new_item.spawnfunc_checked = true;
+ new_item.noalign = Item_ShouldKeepPosition(item);
new_item.ok_item = true;
Item_Initialize(new_item, new_classname);
random_items_is_spawning = false;
return new_item;
}
-/// \brief Returns a random classname of the instagib loot item.
-/// \return Random classname of the instagib loot item.
-string RandomItems_GetRandomInstagibLootItemClassName()
-{
- RandomSelection_Init();
- #define X(classname) \
- RandomSelection_AddString( \
- classname, \
- cvar(sprintf("g_random_loot_%s_probability", RandomItems_GetItemVarName(classname))), \
- 1 \
- )
- X("item_vaporizer_cells");
- X("item_invisibility");
- X("item_extralife");
- X("item_speed");
- #undef X
- return RandomSelection_chosen_string;
-}
-
-/// \brief Returns a random classname of the overkill loot item.
-/// \return Random classname of the overkill loot item.
-string RandomItems_GetRandomOverkillLootItemClassName()
-{
- RandomSelection_Init();
- string varname;
- #define X(classname) MACRO_BEGIN \
- if ((varname = RandomItems_GetItemVarName(classname))) \
- { \
- RandomSelection_AddString( \
- classname, \
- cvar(sprintf("g_random_loot_overkill_%s_probability", varname)), \
- 1 \
- ); \
- } \
- MACRO_END
- X("item_health_mega");
- X("item_armor_small");
- X("item_armor_medium");
- X("item_armor_big");
- X("item_armor_mega");
- X("weapon_hmg");
- X("weapon_rpc");
- #undef X
- return RandomSelection_chosen_string;
-}
-
-/// \brief Returns a random classname of the loot item.
-/// \return Random classname of the loot item.
-string RandomItems_GetRandomLootItemClassName()
-{
- if (autocvar_g_instagib)
- {
- return RandomItems_GetRandomInstagibLootItemClassName();
- }
- if (expr_evaluate(autocvar_g_overkill))
- {
- return RandomItems_GetRandomOverkillLootItemClassName();
- }
- RandomSelection_Init();
- #define X(type, name) \
- RandomSelection_AddFloat( \
- RANDOM_ITEM_TYPE_##type, \
- autocvar_g_random_loot_##name##_probability, \
- 1 \
- )
- X(HEALTH, health);
- X(ARMOR, armor);
- X(RESOURCE, resource);
- X(WEAPON, weapon);
- X(POWERUP, powerup);
- #undef X
- int item_type = RandomSelection_chosen_float;
- switch (item_type)
- {
- case RANDOM_ITEM_TYPE_HEALTH:
- {
- RandomSelection_Init();
- FOREACH(Items, it.instanceOfHealth,
- {
- RandomSelection_AddString(it.m_canonical_spawnfunc,
- cvar(sprintf("g_random_loot_%s_probability",
- it.m_canonical_spawnfunc)), 1);
- });
- return RandomSelection_chosen_string;
- }
- case RANDOM_ITEM_TYPE_ARMOR:
- {
- RandomSelection_Init();
- FOREACH(Items, it.instanceOfArmor,
- {
- RandomSelection_AddString(it.m_canonical_spawnfunc,
- cvar(sprintf("g_random_loot_%s_probability",
- it.m_canonical_spawnfunc)), 1);
- });
- return RandomSelection_chosen_string;
- }
- case RANDOM_ITEM_TYPE_RESOURCE:
- {
- RandomSelection_Init();
- FOREACH(Items, it.instanceOfAmmo,
- {
- RandomSelection_AddString(it.m_canonical_spawnfunc,
- cvar(sprintf("g_random_loot_%s_probability",
- it.m_canonical_spawnfunc)), 1);
- });
- return RandomSelection_chosen_string;
- }
- case RANDOM_ITEM_TYPE_WEAPON:
- {
- RandomSelection_Init();
- FOREACH(Weapons, !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
- {
- string class_name = strcat("weapon_", it.netname);
- string cvar_name = sprintf(
- "g_random_loot_%s_probability", class_name);
- if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
- {
- continue;
- }
- RandomSelection_AddString(class_name, cvar(cvar_name), 1);
- });
- return RandomSelection_chosen_string;
- }
- case RANDOM_ITEM_TYPE_POWERUP:
- {
- RandomSelection_Init();
- #define X(classname) \
- RandomSelection_AddString( \
- classname, \
- cvar(sprintf("g_random_loot_%s_probability", classname)), \
- 1 \
- )
- X("item_strength");
- X("item_shield");
- X("item_jetpack");
- X("item_fuel_regen");
- #undef X
- return RandomSelection_chosen_string;
- }
- }
- return "";
-}
-
/// \brief Spawns a random loot item.
/// \param[in] position Position of the item.
/// \return No return.
void RandomItems_SpawnLootItem(vector position)
{
- string class_name = RandomItems_GetRandomLootItemClassName();
+ string class_name = RandomItems_GetRandomItemClassName("random_loot");
if (class_name == "")
{
return;
spread.z = autocvar_g_random_loot_spread / 2;
spread += randomvec() * autocvar_g_random_loot_spread;
random_items_is_spawning = true;
- if (!expr_evaluate(autocvar_g_overkill))
+ if (!MUTATOR_IS_ENABLED(ok))
{
Item_CreateLoot(class_name, position, spread,
autocvar_g_random_loot_time);