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 string cvar_name = sprintf("g_%s_health_probability", prefix);
82 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
84 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
88 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_HEALTH, cvar(cvar_name), 1);
90 cvar_name = sprintf("g_%s_armor_probability", prefix);
91 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
93 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
97 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_ARMOR, cvar(cvar_name), 1);
99 cvar_name = sprintf("g_%s_resource_probability", prefix);
100 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
102 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
106 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_RESOURCE, cvar(cvar_name), 1);
108 cvar_name = sprintf("g_%s_weapon_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_WEAPON, cvar(cvar_name), 1);
117 cvar_name = sprintf("g_%s_powerup_probability", prefix);
118 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
120 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
124 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_POWERUP, cvar(cvar_name), 1);
126 int item_type = RandomSelection_chosen_float;
129 case RANDOM_ITEM_TYPE_HEALTH:
131 return RandomItems_GetRandomItemClassNameWithProperty(prefix,
134 case RANDOM_ITEM_TYPE_ARMOR:
136 return RandomItems_GetRandomItemClassNameWithProperty(prefix,
139 case RANDOM_ITEM_TYPE_RESOURCE:
141 return RandomItems_GetRandomItemClassNameWithProperty(prefix,
144 case RANDOM_ITEM_TYPE_WEAPON:
146 RandomSelection_Init();
147 FOREACH(Weapons, it != WEP_Null &&
148 !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
150 cvar_name = sprintf("g_%s_%s_probability", prefix,
151 it.m_canonical_spawnfunc);
152 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
154 LOG_WARNF("Random items: cvar %s doesn't exist.",
158 RandomSelection_AddString(it.m_canonical_spawnfunc,
161 return RandomSelection_chosen_string;
163 case RANDOM_ITEM_TYPE_POWERUP:
165 return RandomItems_GetRandomItemClassNameWithProperty(prefix,
172 string RandomItems_GetRandomInstagibItemClassName(string prefix)
174 RandomSelection_Init();
175 FOREACH(Items, it.spawnflags & ITEM_FLAG_INSTAGIB &&
176 Item_IsDefinitionAllowed(it),
178 string cvar_name = sprintf("g_%s_%s_probability", prefix,
179 it.m_canonical_spawnfunc);
180 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
182 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
185 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
187 return RandomSelection_chosen_string;
190 string RandomItems_GetRandomOverkillItemClassName(string prefix)
192 RandomSelection_Init();
193 FOREACH(Items, (it.spawnflags & ITEM_FLAG_OVERKILL) &&
194 !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED) &&
195 Item_IsDefinitionAllowed(it),
197 string cvar_name = sprintf("g_%s_overkill_%s_probability", prefix,
198 it.m_canonical_spawnfunc);
199 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
201 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
204 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
206 string cvar_name = sprintf("g_%s_overkill_weapon_hmg_probability", prefix);
207 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
209 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
213 RandomSelection_AddString("weapon_hmg", cvar(cvar_name), 1);
215 cvar_name = sprintf("g_%s_overkill_weapon_rpc_probability", prefix);
216 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
218 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
222 RandomSelection_AddString("weapon_rpc", cvar(cvar_name), 1);
224 return RandomSelection_chosen_string;
227 //========================= Free functions ====================================
229 /// \brief Returns list of classnames to replace a map item with.
230 /// \param[in] item Item to inspect.
231 /// \return List of classnames to replace a map item with.
232 string RandomItems_GetItemReplacementClassNames(entity item)
234 string cvar_name = sprintf("g_random_items_replace_%s", item.classname);
235 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
237 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
240 return cvar_string(cvar_name);
243 string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
246 RandomSelection_Init();
247 FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL) &&
248 Item_IsDefinitionAllowed(it),
250 string cvar_name = sprintf("g_%s_%s_probability", prefix,
251 it.m_canonical_spawnfunc);
252 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
254 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
257 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
259 return RandomSelection_chosen_string;
262 /// \brief Replaces a map item.
263 /// \param[in] item Item to replace.
264 /// \return Spawned item on success, NULL otherwise.
265 entity RandomItems_ReplaceMapItem(entity item)
267 //PrintToChatAll(strcat("Replacing ", item.classname));
268 string new_classnames = RandomItems_GetItemReplacementClassNames(item);
269 if (new_classnames == "")
273 string new_classname;
274 if (new_classnames == "random")
276 new_classname = RandomItems_GetRandomItemClassName("random_items");
277 if (new_classname == "")
284 int num_new_classnames = tokenize_console(new_classnames);
285 if (num_new_classnames == 1)
287 new_classname = new_classnames;
291 int classname_index = floor(random() * num_new_classnames);
292 new_classname = argv(classname_index);
295 //PrintToChatAll(strcat("Replacing with ", new_classname));
296 if (new_classname == item.classname)
300 random_items_is_spawning = true;
302 if (!expr_evaluate(autocvar_g_overkill))
304 new_item = Item_Create(strzone(new_classname), item.origin,
305 Item_ShouldKeepPosition(item));
306 random_items_is_spawning = false;
307 if (new_item == NULL)
315 new_item.classname = strzone(new_classname);
316 new_item.spawnfunc_checked = true;
317 new_item.noalign = Item_ShouldKeepPosition(item);
318 new_item.ok_item = true;
319 Item_Initialize(new_item, new_classname);
320 random_items_is_spawning = false;
321 if (wasfreed(new_item))
325 setorigin(new_item, item.origin);
329 new_item.team = item.team;
334 /// \brief Spawns a random loot item.
335 /// \param[in] position Position of the item.
336 /// \return No return.
337 void RandomItems_SpawnLootItem(vector position)
339 string class_name = RandomItems_GetRandomItemClassName("random_loot");
340 if (class_name == "")
344 vector spread = '0 0 0';
345 spread.z = autocvar_g_random_loot_spread / 2;
346 spread += randomvec() * autocvar_g_random_loot_spread;
347 random_items_is_spawning = true;
348 if (!expr_evaluate(autocvar_g_overkill))
350 Item_CreateLoot(class_name, position, spread,
351 autocvar_g_random_loot_time);
355 entity item = spawn();
357 item.classname = class_name;
358 Item_InitializeLoot(item, class_name, position, spread,
359 autocvar_g_random_loot_time);
361 random_items_is_spawning = false;
364 //============================= Hooks ========================================
366 REGISTER_MUTATOR(random_items, (autocvar_g_random_items ||
367 autocvar_g_random_loot));
369 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsString)
371 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":random_items");
374 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
376 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random items");
379 /// \brief Hook that is called when an item is about to spawn.
380 MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
382 //PrintToChatAll("FilterItem");
383 if (!autocvar_g_random_items)
387 if (random_items_is_spawning == true)
391 entity item = M_ARGV(0, entity);
392 if (Item_IsLoot(item))
396 if (RandomItems_ReplaceMapItem(item) == NULL)
403 /// \brief Hook that is called after the player has touched an item.
404 MUTATOR_HOOKFUNCTION(random_items, ItemTouched, CBC_ORDER_LAST)
406 //PrintToChatAll("ItemTouched");
407 if (!autocvar_g_random_items)
411 entity item = M_ARGV(0, entity);
412 if (Item_IsLoot(item))
416 entity new_item = RandomItems_ReplaceMapItem(item);
417 if (new_item == NULL)
421 Item_ScheduleRespawn(new_item);
425 /// \brief Hook which is called when the player dies.
426 MUTATOR_HOOKFUNCTION(random_items, PlayerDies)
428 //PrintToChatAll("PlayerDies");
429 if (!autocvar_g_random_loot)
433 entity victim = M_ARGV(2, entity);
434 vector loot_position = victim.origin + '0 0 32';
435 int num_loot_items = floor(autocvar_g_random_loot_min + random() *
436 autocvar_g_random_loot_max);
437 for (int item_index = 0; item_index < num_loot_items; ++item_index)
439 RandomItems_SpawnLootItem(loot_position);