1 #include "sv_random_items.qh"
4 /// \brief Source file that contains implementation of the random items mutator.
6 /// \copyright GNU GPLv2 or any later version.
8 //============================ Constants ======================================
12 RANDOM_ITEM_TYPE_HEALTH = 1,
13 RANDOM_ITEM_TYPE_ARMOR,
14 RANDOM_ITEM_TYPE_RESOURCE,
15 RANDOM_ITEM_TYPE_WEAPON,
16 RANDOM_ITEM_TYPE_POWERUP
19 //======================= Global variables ====================================
23 /// \brief Classnames to replace %s with.
24 /// string autocvar_g_random_items_replace_%s;
26 // Map probability cvars
28 /// \brief Probability of random %s spawning in the map.
29 /// float autocvar_g_random_items_%s_probability;
31 /// \brief Probability of random %s spawning in the map during overkill.
32 /// float autocvar_g_random_items_overkill_%s_probability;
36 bool autocvar_g_random_loot; ///< Whether to enable random loot.
38 float autocvar_g_random_loot_min; ///< Minimum amount of loot items.
39 float autocvar_g_random_loot_max; ///< Maximum amount of loot items.
40 float autocvar_g_random_loot_time; ///< Amount of time the loot will stay.
41 float autocvar_g_random_loot_spread; ///< How far can loot be thrown.
43 // Loot probability cvars
45 /// \brief Probability of random %s spawning as loot.
46 /// float autocvar_g_random_loot_%s_probability;
48 /// \brief Probability of random %s spawning as loot during overkill.
49 /// float autocvar_g_random_loot_overkill_%s_probability;
51 /// \brief Holds whether random item is spawning. Used to prevent infinite
53 bool random_items_is_spawning = false;
55 //====================== Forward declarations =================================
57 /// \brief Returns a random classname of the item with specific property.
58 /// \param[in] prefix Prefix of the cvars that hold probabilities.
59 /// \return Random classname of the item.
60 string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
63 //=========================== Public API ======================================
65 string RandomItems_GetRandomItemClassName(string prefix)
67 if (autocvar_g_instagib)
69 return RandomItems_GetRandomInstagibItemClassName(prefix);
71 if (expr_evaluate(autocvar_g_overkill))
73 return RandomItems_GetRandomOverkillItemClassName(prefix);
75 return RandomItems_GetRandomVanillaItemClassName(prefix);
78 string RandomItems_GetRandomVanillaItemClassName(string prefix)
80 RandomSelection_Init();
81 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_HEALTH,
82 cvar(sprintf("g_%s_health_probability", prefix)), 1);
83 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_ARMOR,
84 cvar(sprintf("g_%s_armor_probability", prefix)), 1);
85 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_RESOURCE,
86 cvar(sprintf("g_%s_resource_probability", prefix)), 1);
87 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_WEAPON,
88 cvar(sprintf("g_%s_weapon_probability", prefix)), 1);
89 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_POWERUP,
90 cvar(sprintf("g_%s_powerup_probability", prefix)), 1);
91 int item_type = RandomSelection_chosen_float;
94 case RANDOM_ITEM_TYPE_HEALTH:
96 return RandomItems_GetRandomItemClassNameWithProperty(prefix,
99 case RANDOM_ITEM_TYPE_ARMOR:
101 return RandomItems_GetRandomItemClassNameWithProperty(prefix,
104 case RANDOM_ITEM_TYPE_RESOURCE:
106 return RandomItems_GetRandomItemClassNameWithProperty(prefix,
109 case RANDOM_ITEM_TYPE_WEAPON:
111 RandomSelection_Init();
112 FOREACH(Weapons, it != WEP_Null &&
113 !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
115 string cvar_name = sprintf("g_%s_%s_probability", prefix,
116 it.m_canonical_spawnfunc);
117 RandomSelection_AddString(it.m_canonical_spawnfunc,
120 return RandomSelection_chosen_string;
122 case RANDOM_ITEM_TYPE_POWERUP:
124 return RandomItems_GetRandomItemClassNameWithProperty(prefix,
131 string RandomItems_GetRandomInstagibItemClassName(string prefix)
133 RandomSelection_Init();
134 FOREACH(Items, it.spawnflags & ITEM_FLAG_INSTAGIB,
136 RandomSelection_AddString(it.m_canonical_spawnfunc,
137 cvar(sprintf("g_%s_%s_probability", prefix,
138 it.m_canonical_spawnfunc)), 1);
140 return RandomSelection_chosen_string;
143 string RandomItems_GetRandomOverkillItemClassName(string prefix)
145 RandomSelection_Init();
146 FOREACH(Items, (it.spawnflags & ITEM_FLAG_OVERKILL) &&
147 !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED),
149 RandomSelection_AddString(it.m_canonical_spawnfunc,
150 cvar(sprintf("g_%s_overkill_%s_probability", prefix,
151 it.m_canonical_spawnfunc)), 1);
153 RandomSelection_AddString("weapon_hmg",
154 cvar(sprintf("g_%s_overkill_weapon_hmg_probability", prefix)), 1);
155 RandomSelection_AddString("weapon_rpc",
156 cvar(sprintf("g_%s_overkill_weapon_rpc_probability", prefix)), 1);
157 return RandomSelection_chosen_string;
160 //========================= Free functions ====================================
162 /// \brief Returns list of classnames to replace a map item with.
163 /// \param[in] item Item to inspect.
164 /// \return List of classnames to replace a map item with.
165 string RandomItems_GetItemReplacementClassNames(entity item)
167 return cvar_string(sprintf("g_random_items_replace_%s", item.classname));
170 string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
173 RandomSelection_Init();
174 FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL),
176 RandomSelection_AddString(it.m_canonical_spawnfunc,
177 cvar(sprintf("g_%s_%s_probability", prefix,
178 it.m_canonical_spawnfunc)), 1);
180 return RandomSelection_chosen_string;
183 /// \brief Replaces a map item.
184 /// \param[in] item Item to replace.
185 /// \return Spawned item on success, NULL otherwise.
186 entity RandomItems_ReplaceMapItem(entity item)
188 //PrintToChatAll(strcat("Replacing ", item.classname));
189 string new_classnames = RandomItems_GetItemReplacementClassNames(item);
190 if (new_classnames == "")
194 string new_classname;
195 if (new_classnames == "random")
197 new_classname = RandomItems_GetRandomItemClassName("random_items");
198 if (new_classname == "")
205 int num_new_classnames = tokenize_console(new_classnames);
206 if (num_new_classnames == 1)
208 new_classname = new_classnames;
212 int classname_index = floor(random() * num_new_classnames);
213 new_classname = argv(classname_index);
216 //PrintToChatAll(strcat("Replacing with ", new_classname));
217 if (new_classname == item.classname)
221 random_items_is_spawning = true;
223 if (!expr_evaluate(autocvar_g_overkill))
225 new_item = Item_Create(strzone(new_classname), item.origin, true);
226 random_items_is_spawning = false;
227 if (new_item == NULL)
235 new_item.classname = strzone(new_classname);
236 new_item.spawnfunc_checked = true;
237 new_item.ok_item = true;
238 Item_Initialize(new_item, new_classname);
239 random_items_is_spawning = false;
240 if (wasfreed(new_item))
244 setorigin(new_item, item.origin);
248 new_item.team = item.team;
253 /// \brief Spawns a random loot item.
254 /// \param[in] position Position of the item.
255 /// \return No return.
256 void RandomItems_SpawnLootItem(vector position)
258 string class_name = RandomItems_GetRandomItemClassName("random_loot");
259 if (class_name == "")
263 vector spread = '0 0 0';
264 spread.z = autocvar_g_random_loot_spread / 2;
265 spread += randomvec() * autocvar_g_random_loot_spread;
266 random_items_is_spawning = true;
267 if (!expr_evaluate(autocvar_g_overkill))
269 Item_CreateLoot(class_name, position, spread,
270 autocvar_g_random_loot_time);
274 entity item = spawn();
276 item.classname = class_name;
277 Item_InitializeLoot(item, class_name, position, spread,
278 autocvar_g_random_loot_time);
280 random_items_is_spawning = false;
283 //============================= Hooks ========================================
285 REGISTER_MUTATOR(random_items, (autocvar_g_random_items ||
286 autocvar_g_random_loot));
288 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsString)
290 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":random_items");
293 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
295 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random items");
298 /// \brief Hook that is called when an item is about to spawn.
299 MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
301 //PrintToChatAll("FilterItem");
302 if (!autocvar_g_random_items)
306 if (random_items_is_spawning == true)
310 entity item = M_ARGV(0, entity);
311 if (Item_IsLoot(item))
315 if (RandomItems_ReplaceMapItem(item) == NULL)
322 /// \brief Hook that is called after the player has touched an item.
323 MUTATOR_HOOKFUNCTION(random_items, ItemTouched, CBC_ORDER_LAST)
325 //PrintToChatAll("ItemTouched");
326 if (!autocvar_g_random_items)
330 entity item = M_ARGV(0, entity);
331 if (Item_IsLoot(item))
335 entity new_item = RandomItems_ReplaceMapItem(item);
336 if (new_item == NULL)
340 Item_ScheduleRespawn(new_item);
344 /// \brief Hook which is called when the player dies.
345 MUTATOR_HOOKFUNCTION(random_items, PlayerDies)
347 //PrintToChatAll("PlayerDies");
348 if (!autocvar_g_random_loot)
352 entity victim = M_ARGV(2, entity);
353 vector loot_position = victim.origin + '0 0 32';
354 int num_loot_items = floor(autocvar_g_random_loot_min + random() *
355 autocvar_g_random_loot_max);
356 for (int item_index = 0; item_index < num_loot_items; ++item_index)
358 RandomItems_SpawnLootItem(loot_position);