@@ -22,72 +22,81 @@ void CSE_ALifeObject::spawn_supplies(LPCSTR ini_string)
2222 if (!xr_strlen (ini_string))
2323 return ;
2424
25+ luabind::functor<bool > funct;
26+ if (GEnv.ScriptEngine ->functor (" ai_stalker.CSE_ALifeObject_spawn_supplies" , funct))
27+ {
28+ if (funct (this , ID, ini_string))
29+ return ;
30+ }
31+
2532#pragma warning(push)
2633#pragma warning(disable : 4238)
2734 IReader reader ((void *)ini_string, xr_strlen (ini_string));
2835 CInifile ini (&reader, FS.get_path (" $game_config$" )->m_Path );
2936#pragma warning(pop)
30-
31- // Alundaio: This will spawn a single random section listed in [spawn_loadout]
32- // No need to spawn ammo, this will automatically spawn 1 box for weapon and if ammo_type is specified it will spawn that type
33- // count is used only for ammo boxes (ie wpn_pm = 3) will spawn 3 boxes, not 3 wpn_pm
34- // Usage: to create random weapon loadouts
35- static constexpr cpcstr LOADOUT_SECTION = " spawn_loadout" ;
36- if (ini.section_exist (LOADOUT_SECTION))
37+ u8 loadoutIndex = 0 ;
38+ string32 loadoutSection = " spawn_loadout" ;
39+
40+ // Alundaio: This will spawn a single random section listed in [spawn_loadout].
41+ // No need to spawn ammo, this will automatically spawn 1 box for weapon and if ammo_type is specified it will spawn that type.
42+ // Count is used only for ammo boxes (ie wpn_pm = 3) will spawn 3 boxes, not 3 wpn_pm.
43+ // Supports few loadout options, iterates over `spawn_loadout`, `spawn_loadout2` ... `spawn_loadoutN`.
44+ while (ini.section_exist (loadoutSection))
3745 {
3846 pcstr itmSection, V;
39- xr_vector<u32 > OnlyOne ;
47+ xr_vector<u32 > spawnLoadouts ;
4048
4149 pcstr lname = ai ().game_graph ().header ().level (ai ().game_graph ().vertex (m_tGraphID)->level_id ()).name ().c_str ();
4250
43- for (u32 k = 0 ; ini.r_line (LOADOUT_SECTION , k, &itmSection, &V); k++)
51+ for (u32 k = 0 ; ini.r_line (loadoutSection , k, &itmSection, &V); k++)
4452 {
4553 // If level=<lname> then only spawn items if object on that level
4654 if (strstr (V, " level=" ) != nullptr )
4755 {
4856 if (strstr (V, lname) != nullptr )
49- OnlyOne .push_back (k);
57+ spawnLoadouts .push_back (k);
5058 }
5159 else
5260 {
53- OnlyOne .push_back (k);
61+ spawnLoadouts .push_back (k);
5462 }
5563 }
5664
57- if (!OnlyOne .empty ())
65+ if (!spawnLoadouts .empty ())
5866 {
59- s32 sel = Random.randI (0 , OnlyOne .size ());
60- if (ini.r_line (LOADOUT_SECTION, OnlyOne .at (sel), &itmSection, &V))
67+ s32 sel = Random.randI (0 , spawnLoadouts .size ());
68+ if (ini.r_line (loadoutSection, spawnLoadouts .at (sel), &itmSection, &V))
6169 {
6270 VERIFY (xr_strlen (itmSection));
6371 if (pSettings->section_exist (itmSection))
6472 {
65- u32 spawn_count = 1 ;
73+ u32 spawnCount = 1 ;
6674 bool bScope = false ;
6775 bool bSilencer = false ;
6876 bool bLauncher = false ;
69- float f_cond = 1 .0f ;
70- int i_ammo_type = 0 , n = 0 ;
77+ float fCond = 1 .0f ;
78+ int iAmmoType = 0 , n = 0 ;
7179
7280 if (V && xr_strlen (V))
7381 {
7482 n = _GetItemCount (V);
7583 if (n > 0 )
7684 {
7785 string64 tmp;
78- spawn_count = atoi (_GetItem (V, 0 , tmp)); // count
86+ spawnCount = atoi (_GetItem (V, 0 , tmp)); // count
7987 }
8088
81- if (!spawn_count) spawn_count = 1 ;
82- if (nullptr != strstr (V, " cond=" ))
83- f_cond = static_cast <float >(atof (strstr (V, " cond=" ) + 5 ));
84- bScope = nullptr != strstr (V, " scope" );
85- bSilencer = nullptr != strstr (V, " silencer" );
86- bLauncher = nullptr != strstr (V, " launcher" );
87- if (nullptr != strstr (V, " ammo_type=" ))
88- i_ammo_type = atoi (strstr (V, " ammo_type=" ) + 10 );
89- }
89+ bScope = is_spawn_supplies_flag_set (V, " scope" );
90+ bSilencer = is_spawn_supplies_flag_set (V, " silencer" );
91+ bLauncher = is_spawn_supplies_flag_set (V, " launcher" );
9092
93+ if (!spawnCount)
94+ spawnCount = 1 ;
95+ if (strstr (V, " cond=" ) != nullptr )
96+ fCond = static_cast <float >(atof (strstr (V, " cond=" ) + 5 ));
97+ if (strstr (V, " ammo_type=" ) != nullptr )
98+ iAmmoType = atoi (strstr (V, " ammo_type=" ) + 10 );
99+ }
91100
92101 CSE_Abstract* E = alife ().spawn_item (itmSection, o_Position, m_tNodeID, m_tGraphID, ID);
93102 CSE_ALifeItemWeapon* W = smart_cast<CSE_ALifeItemWeapon*>(E);
@@ -109,19 +118,29 @@ void CSE_ALifeObject::spawn_supplies(LPCSTR ini_string)
109118 {
110119 string128 tmp;
111120 ammoSec = _GetItem (ammo_class, i, tmp);
112- if (i == i_ammo_type )
121+ if (i == iAmmoType )
113122 break ;
114123 }
115124 if (xr_strlen (ammoSec) && pSettings->section_exist (ammoSec))
116- for (u32 i = 1 ; i <= spawn_count ; ++i)
125+ for (u32 i = 1 ; i <= spawnCount ; ++i)
117126 alife ().spawn_item (ammoSec, o_Position, m_tNodeID, m_tGraphID, ID);
118127 }
119128 }
129+ // If not weapon item, handle count as literal count, not ammo (useful for grenades and consumables).
130+ else
131+ {
132+ for (u32 i = 1 ; i <= spawnCount - 1 ; ++i)
133+ alife ().spawn_item (itmSection, o_Position, m_tNodeID, m_tGraphID, ID);
134+ }
135+
120136 if (const auto IItem = smart_cast<CSE_ALifeInventoryItem*>(E))
121- IItem->m_fCondition = f_cond ;
137+ IItem->m_fCondition = fCond ;
122138 }
123139 }
124140 }
141+
142+ loadoutIndex += 1 ;
143+ xr_sprintf (loadoutSection, " spawn_loadout%d" , loadoutIndex);
125144 }
126145 // -Alundaio
127146
@@ -135,7 +154,7 @@ void CSE_ALifeObject::spawn_supplies(LPCSTR ini_string)
135154
136155 if (pSettings->section_exist (N)) // Alundaio: verify item section exists!
137156 {
138- float f_cond = 1 .0f ;
157+ float fCond = 1 .0f ;
139158 bool bScope = false ;
140159 bool bSilencer = false ;
141160 bool bLauncher = false ;
@@ -150,16 +169,15 @@ void CSE_ALifeObject::spawn_supplies(LPCSTR ini_string)
150169 if (!j)
151170 j = 1 ;
152171
153- bScope = nullptr != strstr (V, " scope" );
154- bSilencer = nullptr != strstr (V, " silencer" );
155- bLauncher = nullptr != strstr (V, " launcher" );
172+ bScope = is_spawn_supplies_flag_set (V, " scope" );
173+ bSilencer = is_spawn_supplies_flag_set (V, " silencer" );
174+ bLauncher = is_spawn_supplies_flag_set (V, " launcher" );
175+
156176 // probability
157- if (nullptr != strstr (V, " prob=" ))
177+ if (strstr (V, " prob=" ) != nullptr )
158178 p = static_cast <float >(atof (strstr (V, " prob=" ) + 5 ));
159- if (fis_zero (p))
160- p = 1 .0f ;
161- if (nullptr != strstr (V, " cond=" ))
162- f_cond = static_cast <float >(atof (strstr (V, " cond=" ) + 5 ));
179+ if (strstr (V, " cond=" ) != nullptr )
180+ fCond = static_cast <float >(atof (strstr (V, " cond=" ) + 5 ));
163181 }
164182 for (u32 i = 0 ; i < j; ++i)
165183 {
@@ -179,7 +197,7 @@ void CSE_ALifeObject::spawn_supplies(LPCSTR ini_string)
179197 }
180198 CSE_ALifeInventoryItem* IItem = smart_cast<CSE_ALifeInventoryItem*>(E);
181199 if (IItem)
182- IItem->m_fCondition = f_cond ;
200+ IItem->m_fCondition = fCond ;
183201 }
184202 }
185203 }
@@ -188,3 +206,33 @@ void CSE_ALifeObject::spawn_supplies(LPCSTR ini_string)
188206}
189207
190208bool CSE_ALifeObject::keep_saved_data_anyway () const /* noexcept */ { return false ; }
209+
210+ // / Check if spawn supplies section flag was set.
211+ // / Comparing to original `section = value, scope, silencer, launcher, cond=0.5`,
212+ // / also support extended variants like `section = value, scope=true, silencer=0.5, launcher, cond=0.5`.
213+ bool CSE_ALifeObject::is_spawn_supplies_flag_set (pcstr value, pcstr flag)
214+ {
215+ pcstr flagSubstring = strstr (value, flag);
216+ int flagLength = strlen (flag);
217+
218+ if (flagSubstring != nullptr )
219+ {
220+ // Got full definition with '=' char, not simple shorthand like scope/silencer.
221+ if (*(flagSubstring + flagLength) == ' =' )
222+ {
223+ if (strncmp (flagSubstring + flagLength, " =true" , 5 ) != -1 )
224+ {
225+ return true ;
226+ }
227+
228+ return randF (1 .f ) <= static_cast <float >(atof (flagSubstring + flagLength + 1 ));
229+ }
230+ // Short variant of flag without assigned value.
231+ else
232+ {
233+ return true ;
234+ }
235+ }
236+
237+ return false ;
238+ }
0 commit comments