Skip to content

Commit 95b9bbc

Browse files
authored
[COC] Support usage of multiple spawn_loadout sections in character descriptions. (OpenXRay#1590)
1 parent 4003352 commit 95b9bbc

File tree

2 files changed

+89
-40
lines changed

2 files changed

+89
-40
lines changed

src/xrGame/alife_object.cpp

Lines changed: 88 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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

190208
bool 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+
}

src/xrServerEntities/xrServer_Objects_ALife.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class CSE_ALifeObject : public CSE_Abstract, public CRandom
168168
virtual u32 ef_weapon_type() const;
169169
virtual u32 ef_detector_type() const;
170170
#ifdef XRGAME_EXPORTS
171+
virtual bool is_spawn_supplies_flag_set(pcstr value, pcstr flag);
171172
virtual void spawn_supplies(LPCSTR);
172173
virtual void spawn_supplies();
173174
CALifeSimulator& alife() const;

0 commit comments

Comments
 (0)