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 ======================================
10 //======================= Global variables ====================================
14 /// \brief Classnames to replace %s with.
15 /// string autocvar_g_random_items_replace_%s;
17 // Map probability cvars
19 /// \brief Probability of random %s spawning in the map.
20 /// float autocvar_g_random_items_%s_probability;
22 /// \brief Probability of random %s spawning in the map during overkill.
23 /// float autocvar_g_random_items_overkill_%s_probability;
27 float autocvar_g_random_loot_min; ///< Minimum amount of loot items.
28 float autocvar_g_random_loot_max; ///< Maximum amount of loot items.
29 float autocvar_g_random_loot_time; ///< Amount of time the loot will stay.
30 float autocvar_g_random_loot_spread; ///< How far can loot be thrown.
32 // Loot probability cvars
34 /// \brief Probability of random %s spawning as loot.
35 /// float autocvar_g_random_loot_%s_probability;
37 /// \brief Probability of random %s spawning as loot during overkill.
38 /// float autocvar_g_random_loot_overkill_%s_probability;
40 /// \brief Holds whether random item is spawning. Used to prevent infinite
42 bool random_items_is_spawning = false;
44 //====================== Forward declarations =================================
46 /// \brief Returns a random classname of the item with specific property.
47 /// \param[in] prefix Prefix of the cvars that hold probabilities.
48 /// \return Random classname of the item.
49 string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
52 //=========================== Public API ======================================
54 string RandomItems_GetRandomItemClassName(string prefix)
56 if (MUTATOR_IS_ENABLED(mutator_instagib))
58 return RandomItems_GetRandomInstagibItemClassName(prefix);
60 if (MUTATOR_IS_ENABLED(ok))
62 return RandomItems_GetRandomOverkillItemClassName(prefix);
64 return RandomItems_GetRandomVanillaItemClassName(prefix,
65 RANDOM_ITEM_TYPE_ALL);
68 string RandomItems_GetRandomVanillaItemClassName(string prefix, int types)
77 RandomSelection_Init();
78 if (types & RANDOM_ITEM_TYPE_HEALTH)
80 cvar_name = sprintf("g_%s_health_probability", prefix);
81 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
83 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
87 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_HEALTH,
91 if (types & RANDOM_ITEM_TYPE_ARMOR)
93 cvar_name = sprintf("g_%s_armor_probability", prefix);
94 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
96 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
100 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_ARMOR,
104 if (types & RANDOM_ITEM_TYPE_RESOURCE)
106 cvar_name = sprintf("g_%s_resource_probability", prefix);
107 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
109 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
113 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_RESOURCE,
117 if (types & RANDOM_ITEM_TYPE_WEAPON)
119 cvar_name = sprintf("g_%s_weapon_probability", prefix);
120 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
122 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
126 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_WEAPON, cvar(cvar_name), 1);
129 if (types & RANDOM_ITEM_TYPE_POWERUP)
131 cvar_name = sprintf("g_%s_powerup_probability", prefix);
132 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
134 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
138 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_POWERUP, cvar(cvar_name), 1);
141 int item_type = RandomSelection_chosen_float;
142 string class_name = "";
145 case RANDOM_ITEM_TYPE_HEALTH:
147 class_name = RandomItems_GetRandomItemClassNameWithProperty(
148 prefix, instanceOfHealth);
151 case RANDOM_ITEM_TYPE_ARMOR:
153 class_name = RandomItems_GetRandomItemClassNameWithProperty(
154 prefix, instanceOfArmor);
157 case RANDOM_ITEM_TYPE_RESOURCE:
159 class_name = RandomItems_GetRandomItemClassNameWithProperty(
160 prefix, instanceOfAmmo);
163 case RANDOM_ITEM_TYPE_WEAPON:
165 RandomSelection_Init();
166 FOREACH(Weapons, it != WEP_Null &&
167 !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
169 cvar_name = sprintf("g_%s_%s_probability", prefix,
170 it.m_canonical_spawnfunc);
171 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
173 LOG_WARNF("Random items: cvar %s doesn't exist.",
177 RandomSelection_AddString(it.m_canonical_spawnfunc,
180 class_name = RandomSelection_chosen_string;
183 case RANDOM_ITEM_TYPE_POWERUP:
185 class_name = RandomItems_GetRandomItemClassNameWithProperty(
186 prefix, instanceOfPowerup);
190 if (class_name != "")
199 string RandomItems_GetRandomInstagibItemClassName(string prefix)
201 RandomSelection_Init();
202 FOREACH(Items, it.spawnflags & ITEM_FLAG_INSTAGIB &&
203 Item_IsDefinitionAllowed(it),
205 string cvar_name = sprintf("g_%s_%s_probability", prefix,
206 it.m_canonical_spawnfunc);
207 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
209 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
212 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
214 return RandomSelection_chosen_string;
217 string RandomItems_GetRandomOverkillItemClassName(string prefix)
219 RandomSelection_Init();
220 FOREACH(Items, (it.spawnflags & ITEM_FLAG_OVERKILL) &&
221 !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED) &&
222 Item_IsDefinitionAllowed(it),
224 string cvar_name = sprintf("g_%s_overkill_%s_probability", prefix,
225 it.m_canonical_spawnfunc);
226 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
228 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
231 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
233 string cvar_name = sprintf("g_%s_overkill_weapon_okhmg_probability", prefix);
234 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
236 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
240 RandomSelection_AddString("weapon_okhmg", cvar(cvar_name), 1);
242 cvar_name = sprintf("g_%s_overkill_weapon_okrpc_probability", prefix);
243 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
245 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
249 RandomSelection_AddString("weapon_okrpc", cvar(cvar_name), 1);
251 return RandomSelection_chosen_string;
254 //========================= Free functions ====================================
256 /// \brief Returns list of classnames to replace a map item with.
257 /// \param[in] item Item to inspect.
258 /// \return List of classnames to replace a map item with.
259 string RandomItems_GetItemReplacementClassNames(entity item)
261 string cvar_name = sprintf("g_random_items_replace_%s", item.classname);
262 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
264 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
267 return cvar_string(cvar_name);
270 string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
273 RandomSelection_Init();
274 FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL) &&
275 Item_IsDefinitionAllowed(it),
277 string cvar_name = sprintf("g_%s_%s_probability", prefix,
278 it.m_canonical_spawnfunc);
279 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
281 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
284 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
286 return RandomSelection_chosen_string;
289 /// \brief Replaces a map item.
290 /// \param[in] item Item to replace.
291 /// \return Spawned item on success, NULL otherwise.
292 entity RandomItems_ReplaceMapItem(entity item)
294 //PrintToChatAll(strcat("Replacing ", item.classname));
295 string new_classnames = RandomItems_GetItemReplacementClassNames(item);
296 if (new_classnames == "")
300 string new_classname;
301 if (new_classnames == "random")
303 new_classname = RandomItems_GetRandomItemClassName("random_items");
304 if (new_classname == "")
311 int num_new_classnames = tokenize_console(new_classnames);
312 if (num_new_classnames == 1)
314 new_classname = new_classnames;
318 int classname_index = floor(random() * num_new_classnames);
319 new_classname = argv(classname_index);
322 //PrintToChatAll(strcat("Replacing with ", new_classname));
323 if (new_classname == item.classname)
327 random_items_is_spawning = true;
329 if (!MUTATOR_IS_ENABLED(ok))
331 new_item = Item_Create(strzone(new_classname), item.origin,
332 Item_ShouldKeepPosition(item));
333 random_items_is_spawning = false;
334 if (new_item == NULL)
342 new_item.classname = strzone(new_classname);
343 new_item.spawnfunc_checked = true;
344 new_item.noalign = Item_ShouldKeepPosition(item);
345 new_item.ok_item = true;
346 Item_Initialize(new_item, new_classname);
347 random_items_is_spawning = false;
348 if (wasfreed(new_item))
352 setorigin(new_item, item.origin);
356 new_item.team = item.team;
361 /// \brief Spawns a random loot item.
362 /// \param[in] position Position of the item.
363 /// \return No return.
364 void RandomItems_SpawnLootItem(vector position)
366 string class_name = RandomItems_GetRandomItemClassName("random_loot");
367 if (class_name == "")
371 vector spread = '0 0 0';
372 spread.z = autocvar_g_random_loot_spread / 2;
373 spread += randomvec() * autocvar_g_random_loot_spread;
374 random_items_is_spawning = true;
375 if (!MUTATOR_IS_ENABLED(ok))
377 Item_CreateLoot(class_name, position, spread,
378 autocvar_g_random_loot_time);
382 entity item = spawn();
384 item.classname = class_name;
385 Item_InitializeLoot(item, class_name, position, spread,
386 autocvar_g_random_loot_time);
388 random_items_is_spawning = false;
391 //============================= Hooks ========================================
393 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsString)
395 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":random_items");
398 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
400 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random items");
403 /// \brief Hook that is called when an item is about to spawn.
404 MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
406 //PrintToChatAll("FilterItem");
407 if (!autocvar_g_random_items)
411 if (random_items_is_spawning == true)
415 entity item = M_ARGV(0, entity);
416 if (Item_IsLoot(item))
420 if (RandomItems_ReplaceMapItem(item) == NULL)
427 /// \brief Hook that is called after the player has touched an item.
428 MUTATOR_HOOKFUNCTION(random_items, ItemTouched, CBC_ORDER_LAST)
430 //PrintToChatAll("ItemTouched");
431 if (!autocvar_g_random_items)
435 entity item = M_ARGV(0, entity);
436 if (Item_IsLoot(item))
440 entity new_item = RandomItems_ReplaceMapItem(item);
441 if (new_item == NULL)
445 Item_ScheduleRespawn(new_item);
449 /// \brief Hook which is called when the player dies.
450 MUTATOR_HOOKFUNCTION(random_items, PlayerDies)
452 //PrintToChatAll("PlayerDies");
453 if (!autocvar_g_random_loot)
457 entity victim = M_ARGV(2, entity);
458 vector loot_position = victim.origin + '0 0 32';
459 int num_loot_items = floor(autocvar_g_random_loot_min + random() *
460 autocvar_g_random_loot_max);
461 for (int item_index = 0; item_index < num_loot_items; ++item_index)
463 RandomItems_SpawnLootItem(loot_position);