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 bool autocvar_g_random_loot; ///< Whether to enable random loot.
29 float autocvar_g_random_loot_min; ///< Minimum amount of loot items.
30 float autocvar_g_random_loot_max; ///< Maximum amount of loot items.
31 float autocvar_g_random_loot_time; ///< Amount of time the loot will stay.
32 float autocvar_g_random_loot_spread; ///< How far can loot be thrown.
34 // Loot probability cvars
36 /// \brief Probability of random %s spawning as loot.
37 /// float autocvar_g_random_loot_%s_probability;
39 /// \brief Probability of random %s spawning as loot during overkill.
40 /// float autocvar_g_random_loot_overkill_%s_probability;
42 /// \brief Holds whether random item is spawning. Used to prevent infinite
44 bool random_items_is_spawning = false;
46 //====================== Forward declarations =================================
48 /// \brief Returns a random classname of the item with specific property.
49 /// \param[in] prefix Prefix of the cvars that hold probabilities.
50 /// \return Random classname of the item.
51 string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
54 //=========================== Public API ======================================
56 string RandomItems_GetRandomItemClassName(string prefix)
58 if (MUTATOR_IS_ENABLED(mutator_instagib))
60 return RandomItems_GetRandomInstagibItemClassName(prefix);
62 if (MUTATOR_IS_ENABLED(ok))
64 return RandomItems_GetRandomOverkillItemClassName(prefix);
66 return RandomItems_GetRandomVanillaItemClassName(prefix,
67 RANDOM_ITEM_TYPE_ALL);
70 string RandomItems_GetRandomVanillaItemClassName(string prefix, int types)
79 RandomSelection_Init();
80 if (types & RANDOM_ITEM_TYPE_HEALTH)
82 cvar_name = sprintf("g_%s_health_probability", prefix);
83 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
85 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
89 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_HEALTH,
93 if (types & RANDOM_ITEM_TYPE_ARMOR)
95 cvar_name = sprintf("g_%s_armor_probability", prefix);
96 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
98 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
102 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_ARMOR,
106 if (types & RANDOM_ITEM_TYPE_RESOURCE)
108 cvar_name = sprintf("g_%s_resource_probability", prefix);
109 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
111 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
115 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_RESOURCE,
119 if (types & RANDOM_ITEM_TYPE_WEAPON)
121 cvar_name = sprintf("g_%s_weapon_probability", prefix);
122 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
124 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
128 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_WEAPON, cvar(cvar_name), 1);
131 if (types & RANDOM_ITEM_TYPE_POWERUP)
133 cvar_name = sprintf("g_%s_powerup_probability", prefix);
134 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
136 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
140 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_POWERUP, cvar(cvar_name), 1);
143 int item_type = RandomSelection_chosen_float;
144 string class_name = "";
147 case RANDOM_ITEM_TYPE_HEALTH:
149 class_name = RandomItems_GetRandomItemClassNameWithProperty(
150 prefix, instanceOfHealth);
153 case RANDOM_ITEM_TYPE_ARMOR:
155 class_name = RandomItems_GetRandomItemClassNameWithProperty(
156 prefix, instanceOfArmor);
159 case RANDOM_ITEM_TYPE_RESOURCE:
161 class_name = RandomItems_GetRandomItemClassNameWithProperty(
162 prefix, instanceOfAmmo);
165 case RANDOM_ITEM_TYPE_WEAPON:
167 RandomSelection_Init();
168 FOREACH(Weapons, it != WEP_Null &&
169 !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
171 cvar_name = sprintf("g_%s_%s_probability", prefix,
172 it.m_canonical_spawnfunc);
173 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
175 LOG_WARNF("Random items: cvar %s doesn't exist.",
179 RandomSelection_AddString(it.m_canonical_spawnfunc,
182 class_name = RandomSelection_chosen_string;
185 case RANDOM_ITEM_TYPE_POWERUP:
187 class_name = RandomItems_GetRandomItemClassNameWithProperty(
188 prefix, instanceOfPowerup);
192 if (class_name != "")
201 string RandomItems_GetRandomInstagibItemClassName(string prefix)
203 RandomSelection_Init();
204 FOREACH(Items, it.spawnflags & ITEM_FLAG_INSTAGIB &&
205 Item_IsDefinitionAllowed(it),
207 string cvar_name = sprintf("g_%s_%s_probability", prefix,
208 it.m_canonical_spawnfunc);
209 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
211 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
214 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
216 return RandomSelection_chosen_string;
219 string RandomItems_GetRandomOverkillItemClassName(string prefix)
221 RandomSelection_Init();
222 FOREACH(Items, (it.spawnflags & ITEM_FLAG_OVERKILL) &&
223 !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED) &&
224 Item_IsDefinitionAllowed(it),
226 string cvar_name = sprintf("g_%s_overkill_%s_probability", prefix,
227 it.m_canonical_spawnfunc);
228 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
230 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
233 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
235 string cvar_name = sprintf("g_%s_overkill_weapon_okhmg_probability", prefix);
236 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
238 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
242 RandomSelection_AddString("weapon_okhmg", cvar(cvar_name), 1);
244 cvar_name = sprintf("g_%s_overkill_weapon_okrpc_probability", prefix);
245 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
247 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
251 RandomSelection_AddString("weapon_okrpc", cvar(cvar_name), 1);
253 return RandomSelection_chosen_string;
256 //========================= Free functions ====================================
258 /// \brief Returns list of classnames to replace a map item with.
259 /// \param[in] item Item to inspect.
260 /// \return List of classnames to replace a map item with.
261 string RandomItems_GetItemReplacementClassNames(entity item)
263 string cvar_name = sprintf("g_random_items_replace_%s", item.classname);
264 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
266 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
269 return cvar_string(cvar_name);
272 string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
275 RandomSelection_Init();
276 FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL) &&
277 Item_IsDefinitionAllowed(it),
279 string cvar_name = sprintf("g_%s_%s_probability", prefix,
280 it.m_canonical_spawnfunc);
281 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
283 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
286 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
288 return RandomSelection_chosen_string;
291 /// \brief Replaces a map item.
292 /// \param[in] item Item to replace.
293 /// \return Spawned item on success, NULL otherwise.
294 entity RandomItems_ReplaceMapItem(entity item)
296 //PrintToChatAll(strcat("Replacing ", item.classname));
297 string new_classnames = RandomItems_GetItemReplacementClassNames(item);
298 if (new_classnames == "")
302 string new_classname;
303 if (new_classnames == "random")
305 new_classname = RandomItems_GetRandomItemClassName("random_items");
306 if (new_classname == "")
313 int num_new_classnames = tokenize_console(new_classnames);
314 if (num_new_classnames == 1)
316 new_classname = new_classnames;
320 int classname_index = floor(random() * num_new_classnames);
321 new_classname = argv(classname_index);
324 //PrintToChatAll(strcat("Replacing with ", new_classname));
325 if (new_classname == item.classname)
329 random_items_is_spawning = true;
331 if (!MUTATOR_IS_ENABLED(ok))
333 new_item = Item_Create(strzone(new_classname), item.origin,
334 Item_ShouldKeepPosition(item));
335 random_items_is_spawning = false;
336 if (new_item == NULL)
344 new_item.classname = strzone(new_classname);
345 new_item.spawnfunc_checked = true;
346 new_item.noalign = Item_ShouldKeepPosition(item);
347 new_item.ok_item = true;
348 Item_Initialize(new_item, new_classname);
349 random_items_is_spawning = false;
350 if (wasfreed(new_item))
354 setorigin(new_item, item.origin);
358 new_item.team = item.team;
363 /// \brief Spawns a random loot item.
364 /// \param[in] position Position of the item.
365 /// \return No return.
366 void RandomItems_SpawnLootItem(vector position)
368 string class_name = RandomItems_GetRandomItemClassName("random_loot");
369 if (class_name == "")
373 vector spread = '0 0 0';
374 spread.z = autocvar_g_random_loot_spread / 2;
375 spread += randomvec() * autocvar_g_random_loot_spread;
376 random_items_is_spawning = true;
377 if (!MUTATOR_IS_ENABLED(ok))
379 Item_CreateLoot(class_name, position, spread,
380 autocvar_g_random_loot_time);
384 entity item = spawn();
386 item.classname = class_name;
387 Item_InitializeLoot(item, class_name, position, spread,
388 autocvar_g_random_loot_time);
390 random_items_is_spawning = false;
393 //============================= Hooks ========================================
395 REGISTER_MUTATOR(random_items, (autocvar_g_random_items ||
396 autocvar_g_random_loot));
398 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsString)
400 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":random_items");
403 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
405 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random items");
408 /// \brief Hook that is called when an item is about to spawn.
409 MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
411 //PrintToChatAll("FilterItem");
412 if (!autocvar_g_random_items)
416 if (random_items_is_spawning == true)
420 entity item = M_ARGV(0, entity);
421 if (Item_IsLoot(item))
425 if (RandomItems_ReplaceMapItem(item) == NULL)
432 /// \brief Hook that is called after the player has touched an item.
433 MUTATOR_HOOKFUNCTION(random_items, ItemTouched, CBC_ORDER_LAST)
435 //PrintToChatAll("ItemTouched");
436 if (!autocvar_g_random_items)
440 entity item = M_ARGV(0, entity);
441 if (Item_IsLoot(item))
445 entity new_item = RandomItems_ReplaceMapItem(item);
446 if (new_item == NULL)
450 Item_ScheduleRespawn(new_item);
454 /// \brief Hook which is called when the player dies.
455 MUTATOR_HOOKFUNCTION(random_items, PlayerDies)
457 //PrintToChatAll("PlayerDies");
458 if (!autocvar_g_random_loot)
462 entity victim = M_ARGV(2, entity);
463 vector loot_position = victim.origin + '0 0 32';
464 int num_loot_items = floor(autocvar_g_random_loot_min + random() *
465 autocvar_g_random_loot_max);
466 for (int item_index = 0; item_index < num_loot_items; ++item_index)
468 RandomItems_SpawnLootItem(loot_position);