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_CALLHOOK(RandomItems_GetRandomItemClassName, prefix))
58 return M_ARGV(1, string);
60 return RandomItems_GetRandomVanillaItemClassName(prefix,
61 RANDOM_ITEM_TYPE_ALL);
64 string RandomItems_GetRandomVanillaItemClassName(string prefix, int types)
73 RandomSelection_Init();
74 if (types & RANDOM_ITEM_TYPE_HEALTH)
76 cvar_name = sprintf("g_%s_health_probability", prefix);
77 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
79 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
83 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_HEALTH,
87 if (types & RANDOM_ITEM_TYPE_ARMOR)
89 cvar_name = sprintf("g_%s_armor_probability", prefix);
90 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
92 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
96 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_ARMOR,
100 if (types & RANDOM_ITEM_TYPE_RESOURCE)
102 cvar_name = sprintf("g_%s_resource_probability", prefix);
103 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
105 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
109 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_RESOURCE,
113 if (types & RANDOM_ITEM_TYPE_WEAPON)
115 cvar_name = sprintf("g_%s_weapon_probability", prefix);
116 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
118 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
122 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_WEAPON, cvar(cvar_name), 1);
125 if (types & RANDOM_ITEM_TYPE_POWERUP)
127 cvar_name = sprintf("g_%s_powerup_probability", prefix);
128 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
130 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
134 RandomSelection_AddFloat(RANDOM_ITEM_TYPE_POWERUP, cvar(cvar_name), 1);
137 int item_type = RandomSelection_chosen_float;
138 string class_name = "";
141 case RANDOM_ITEM_TYPE_HEALTH:
143 class_name = RandomItems_GetRandomItemClassNameWithProperty(
144 prefix, instanceOfHealth);
147 case RANDOM_ITEM_TYPE_ARMOR:
149 class_name = RandomItems_GetRandomItemClassNameWithProperty(
150 prefix, instanceOfArmor);
153 case RANDOM_ITEM_TYPE_RESOURCE:
155 class_name = RandomItems_GetRandomItemClassNameWithProperty(
156 prefix, instanceOfAmmo);
159 case RANDOM_ITEM_TYPE_WEAPON:
161 RandomSelection_Init();
162 FOREACH(Weapons, it != WEP_Null &&
163 !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
165 cvar_name = sprintf("g_%s_%s_probability", prefix,
166 it.m_canonical_spawnfunc);
167 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
169 LOG_WARNF("Random items: cvar %s doesn't exist.",
173 RandomSelection_AddString(it.m_canonical_spawnfunc,
176 class_name = RandomSelection_chosen_string;
179 case RANDOM_ITEM_TYPE_POWERUP:
181 class_name = RandomItems_GetRandomItemClassNameWithProperty(
182 prefix, instanceOfPowerup);
186 if (class_name != "")
195 //========================= Free functions ====================================
197 /// \brief Returns list of classnames to replace a map item with.
198 /// \param[in] item Item to inspect.
199 /// \return List of classnames to replace a map item with.
200 string RandomItems_GetItemReplacementClassNames(entity item)
202 string cvar_name = sprintf("g_random_items_replace_%s", item.classname);
203 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
205 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
208 return cvar_string(cvar_name);
211 string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
214 RandomSelection_Init();
215 FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL) &&
216 Item_IsDefinitionAllowed(it),
218 string cvar_name = sprintf("g_%s_%s_probability", prefix,
219 it.m_canonical_spawnfunc);
220 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
222 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
225 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
227 return RandomSelection_chosen_string;
230 /// \brief Replaces a map item.
231 /// \param[in] item Item to replace.
232 /// \return Spawned item on success, NULL otherwise.
233 entity RandomItems_ReplaceMapItem(entity item)
235 //PrintToChatAll(strcat("Replacing ", item.classname));
236 string new_classnames = RandomItems_GetItemReplacementClassNames(item);
237 if (new_classnames == "")
241 string new_classname;
242 if (new_classnames == "random")
244 new_classname = RandomItems_GetRandomItemClassName("random_items");
245 if (new_classname == "")
252 int num_new_classnames = tokenize_console(new_classnames);
253 if (num_new_classnames == 1)
255 new_classname = new_classnames;
259 int classname_index = floor(random() * num_new_classnames);
260 new_classname = argv(classname_index);
263 //PrintToChatAll(strcat("Replacing with ", new_classname));
264 if (new_classname == item.classname)
268 random_items_is_spawning = true;
270 if (!MUTATOR_IS_ENABLED(ok))
272 new_item = Item_Create(strzone(new_classname), item.origin,
273 Item_ShouldKeepPosition(item));
274 random_items_is_spawning = false;
275 if (new_item == NULL)
283 new_item.classname = strzone(new_classname);
284 new_item.spawnfunc_checked = true;
285 new_item.noalign = Item_ShouldKeepPosition(item);
286 new_item.ok_item = true;
287 Item_Initialize(new_item, new_classname);
288 random_items_is_spawning = false;
289 if (wasfreed(new_item))
293 setorigin(new_item, item.origin);
297 new_item.team = item.team;
302 /// \brief Spawns a random loot item.
303 /// \param[in] position Position of the item.
304 /// \return No return.
305 void RandomItems_SpawnLootItem(vector position)
307 string class_name = RandomItems_GetRandomItemClassName("random_loot");
308 if (class_name == "")
312 vector spread = '0 0 0';
313 spread.z = autocvar_g_random_loot_spread / 2;
314 spread += randomvec() * autocvar_g_random_loot_spread;
315 random_items_is_spawning = true;
316 if (!MUTATOR_IS_ENABLED(ok))
318 Item_CreateLoot(class_name, position, spread,
319 autocvar_g_random_loot_time);
323 entity item = spawn();
325 item.classname = class_name;
326 Item_InitializeLoot(item, class_name, position, spread,
327 autocvar_g_random_loot_time);
329 random_items_is_spawning = false;
332 //============================= Hooks ========================================
334 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsString)
336 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":random_items");
339 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
341 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random items");
344 /// \brief Hook that is called when an item is about to spawn.
345 MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
347 //PrintToChatAll("FilterItem");
348 if (!autocvar_g_random_items)
352 if (random_items_is_spawning == true)
356 entity item = M_ARGV(0, entity);
357 if (Item_IsLoot(item))
361 if (RandomItems_ReplaceMapItem(item) == NULL)
368 /// \brief Hook that is called after the player has touched an item.
369 MUTATOR_HOOKFUNCTION(random_items, ItemTouched, CBC_ORDER_LAST)
371 //PrintToChatAll("ItemTouched");
372 if (!autocvar_g_random_items)
376 entity item = M_ARGV(0, entity);
377 if (Item_IsLoot(item))
381 entity new_item = RandomItems_ReplaceMapItem(item);
382 if (new_item == NULL)
386 Item_ScheduleRespawn(new_item);
390 /// \brief Hook which is called when the player dies.
391 MUTATOR_HOOKFUNCTION(random_items, PlayerDies)
393 //PrintToChatAll("PlayerDies");
394 if (!autocvar_g_random_loot)
398 entity victim = M_ARGV(2, entity);
399 vector loot_position = victim.origin + '0 0 32';
400 int num_loot_items = floor(autocvar_g_random_loot_min + random() *
401 autocvar_g_random_loot_max);
402 for (int item_index = 0; item_index < num_loot_items; ++item_index)
404 RandomItems_SpawnLootItem(loot_position);