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 // TODO: doesn't copy many fields from items
273 new_item = Item_Create(strzone(new_classname), item.origin,
274 Item_ShouldKeepPosition(item));
275 random_items_is_spawning = false;
276 if (new_item == NULL)
284 Item_CopyFields(item, new_item);
285 new_item.classname = strzone(new_classname);
286 new_item.ok_item = true;
287 Item_Initialize(new_item, new_classname);
288 random_items_is_spawning = false;
289 if (wasfreed(new_item))
296 new_item.team = item.team;
301 /// \brief Spawns a random loot item.
302 /// \param[in] position Position of the item.
303 /// \return No return.
304 void RandomItems_SpawnLootItem(vector position)
306 string class_name = RandomItems_GetRandomItemClassName("random_loot");
307 if (class_name == "")
311 vector spread = '0 0 0';
312 spread.z = autocvar_g_random_loot_spread / 2;
313 spread += randomvec() * autocvar_g_random_loot_spread;
314 random_items_is_spawning = true;
315 if (!MUTATOR_IS_ENABLED(ok))
317 Item_CreateLoot(class_name, position, spread,
318 autocvar_g_random_loot_time);
322 entity item = spawn();
324 item.classname = class_name;
325 Item_InitializeLoot(item, class_name, position, spread,
326 autocvar_g_random_loot_time);
328 random_items_is_spawning = false;
331 //============================= Hooks ========================================
333 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsString)
335 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":random_items");
338 MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
340 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random items");
343 /// \brief Hook that is called when an item is about to spawn.
344 MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
346 //PrintToChatAll("FilterItem");
347 if (!autocvar_g_random_items)
351 if (random_items_is_spawning == true)
355 entity item = M_ARGV(0, entity);
356 if (Item_IsLoot(item))
360 if (RandomItems_ReplaceMapItem(item) == NULL)
367 /// \brief Hook that is called after the player has touched an item.
368 MUTATOR_HOOKFUNCTION(random_items, ItemTouched, CBC_ORDER_LAST)
370 //PrintToChatAll("ItemTouched");
371 if (!autocvar_g_random_items)
375 entity item = M_ARGV(0, entity);
376 if (Item_IsLoot(item))
380 entity new_item = RandomItems_ReplaceMapItem(item);
381 if (new_item == NULL)
385 Item_ScheduleRespawn(new_item);
389 /// \brief Hook which is called when the player dies.
390 MUTATOR_HOOKFUNCTION(random_items, PlayerDies)
392 //PrintToChatAll("PlayerDies");
393 if (!autocvar_g_random_loot)
397 entity victim = M_ARGV(2, entity);
398 vector loot_position = victim.origin + '0 0 32';
399 int num_loot_items = floor(autocvar_g_random_loot_min + random() *
400 autocvar_g_random_loot_max);
401 for (int item_index = 0; item_index < num_loot_items; ++item_index)
403 RandomItems_SpawnLootItem(loot_position);