Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_ProjectileWindageDataGeneratorPlugin.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2[WorkbenchPluginAttribute(name: "Generate projectile wind data", description: "", wbModules: { "WorldEditor" }, awesomeFontCode: 0xf72e)]
3class SCR_ProjectileWindageDataGeneratorPlugin : WorldEditorPlugin
4{
5 [Attribute(uiwidget: UIWidgets.ResourcePickerThumbnail, desc: "Projectiles", params: "et")]
6 protected ref TResourceNameArray m_aProjectiles;
7
8 [Attribute(uiwidget: UIWidgets.ResourceNamePicker, desc: "Override config files to which generated data will be saved.\nIf left empty, or number of entries is less than number of entries in [Projectiles] list, then system will try to fetch the config from the projectile prefab data.", params: "conf class=SCR_ProjectileWindTable")]
9 protected ref TResourceNameArray m_aConfigsOverride;
10
11 [Attribute(defvalue: "10", desc: "Wind Speeds", params: "0.01 inf")]
12 protected ref TFloatArray m_aWindSpeeds;
13
14 [Attribute(defvalue: "1", desc: "Init speed coefs for which data should be generated.\nWhen this array is left empty, then system will use init speed coef = 1, and in case of mortar shells, it will fetch their values from SCR_MortarShellGadgetComponent.ChargeRingConfig", params: "0.01 inf")]
15 protected ref TFloatArray m_aInitSpeedCoefs;
16
17 [Attribute(defvalue: "10000", desc: "Max Simulated Distance[m]", params: "1 inf 0.01")]
18 protected float m_fMaxSimulatedHorizontalDistance;
19
20 [Attribute(defvalue: "60", desc: "Maximum simulated time for the projectile’s flight. When simulated time will reach this value, then simulation will be stopped even when projectile has not reached its end of travel", params: "1 inf 0.01")]
21 protected float m_fMaxSimulationTime;
22
23 [Attribute(defvalue: "3", desc: "Peak Search Precision[m]", params: "0.1 inf 0.01")]
24 protected float m_fPeakSearchPrecision;
25
26 //------------------------------------------------------------------------------------------------
27 override void Run()
28 {
29 if (Workbench.ScriptDialog("Configuration", "Select prefabs and their coresponding configs.", this) == 0)
30 return;
31
32 if (!m_aProjectiles || m_aProjectiles.IsEmpty())
33 return;
34
35 GenerateData();
36 }
37
38 //------------------------------------------------------------------------------------------------
44 protected IEntity SpawnEntity(notnull WorldEditorAPI api, ResourceName resourceName, out IEntitySource entitySource = null)
45 {
46 Resource res = Resource.Load(resourceName);
47 if (!res.IsValid())
48 return null;
49
50 entitySource = res.GetResource().ToEntitySource();
51 if (!entitySource)
52 return null;
53
54 BaseWorld world = api.GetWorld();
55 return GetGame().SpawnEntityPrefab(res, world);
56 }
57
58 //------------------------------------------------------------------------------------------------
62 protected float GetInitSpeed(notnull IEntitySource entitySource)
63 {
64 IEntityComponentSource componentSrc;
65 for (int i, componentsCount = entitySource.GetComponentCount(); i < componentsCount; i++)
66 {
67 componentSrc = entitySource.GetComponent(i);
68 if (!componentSrc)
69 continue;
70
71 if (!componentSrc.GetClassName().ToType().IsInherited(ProjectileMoveComponent))
72 continue;
73
74 float initSpeed;
75 componentSrc.Get("InitSpeed", initSpeed);
76 return initSpeed;
77 }
78
79 return -1;
80 }
81
82 //------------------------------------------------------------------------------------------------
86 protected TFloatArray FetchInitSpeedCoefs(notnull IEntity projectile)
87 {
88 TFloatArray initSpeedCoefs = {};
89 SCR_MortarShellGadgetComponent mortarShellComp = SCR_MortarShellGadgetComponent.Cast(projectile.FindComponent(SCR_MortarShellGadgetComponent));
90 if (mortarShellComp)
91 {
92 for (int j, count = mortarShellComp.GetNumberOfChargeRingConfigurations(); j < count; j++)
93 {
94 initSpeedCoefs.Insert(mortarShellComp.GetChargeRingConfig(j)[1]);
95 }
96 }
97 else
98 {
99 initSpeedCoefs.Insert(1);
100 }
101
102 return initSpeedCoefs;
103 }
104
105 //------------------------------------------------------------------------------------------------
106 protected void GenerateData()
107 {
108 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
109 if (!worldEditor)
110 return;
111
112 WorldEditorAPI api = worldEditor.GetApi();
113 if (!api)
114 return;
115
116 TFloatArray windSpeeds = m_aWindSpeeds;
117 if (windSpeeds.IsEmpty())
118 windSpeeds = {10};
119
120 TFloatArray initSpeedCoefs = m_aInitSpeedCoefs;
121 bool initCoefsOverride = !initSpeedCoefs.IsEmpty();
122
123 TResourceNameArray configs = {};
124 configs.Copy(m_aConfigsOverride);
125 map<int, string> failedEntries = new map<int, string>();
126 float initSpeed;
127 IEntity projectile;
128 ResourceName configFile;
129 IEntitySource entitySource;
132 foreach (int i, ResourceName resourceName : m_aProjectiles)
133 {
134 if (projectile)
135 delete projectile; // delete no longer used entity
136
137 projectile = SpawnEntity(api, resourceName, entitySource);
138 if (!projectile)
139 {
140 failedEntries.Set(i, "Failed to spawn projectile");
141 continue;
142 }
143
145 if (!moveComp)
146 {
147 failedEntries.Set(i, "Failed to find some form of ProjectileMoveComponent");
148 continue;
149 }
150
151 initSpeed = GetInitSpeed(entitySource);
152 if (initSpeed <= 0)
153 {
154 failedEntries.Set(i, "Failed to find init speed");
155 continue;
156 }
157
158 if (!initCoefsOverride)
159 initSpeedCoefs = FetchInitSpeedCoefs(projectile);
160
161 if (configs.IsIndexValid(i))
162 configFile = configs[i];
163 else
164 configFile = string.Empty;
165
166 if (configFile.IsEmpty())
167 {
168 ProjectileMoveComponentClass data = ProjectileMoveComponentClass.Cast(moveComp.GetComponentData(projectile));
169 configFile = data.GetProjectileWindTable();
170 if (configFile.IsEmpty())
171 {
172 failedEntries.Set(i, "Failed to find config file");
173 continue;
174 }
175 else
176 {
177 configs.Insert(configFile);
178 }
179 }
180
181 config = GenerateWindData(initSpeedCoefs, initSpeed, windSpeeds, moveComp);
182 Resource res = BaseContainerTools.CreateContainerFromInstance(config);
183 if (res.IsValid() && !BaseContainerTools.SaveContainer(res.GetResource().ToBaseContainer(), configFile))
184 failedEntries.Set(i, "Failed save generated data");
185 }
186
187 if (projectile)
188 delete projectile; // delete the last prefab that was spawned
189
190 string failedResult;
191 foreach (int id, string reason : failedEntries)
192 {
193 failedResult += string.Format("\nID=%1 - %2 - Reason=%3", id.ToString(), FilePath.StripPath(m_aProjectiles[id]), reason);
194 }
195
196 string detailedTextAddition = ".";
197 if (!failedEntries.IsEmpty())
198 {
199 failedResult = string.Format("%1%2%3", "Generation failed for given entries:", failedResult, "\n\n\nYou can use Ctrl+C to copy the content of this dialog.");
200 detailedTextAddition = " with errors! For more inforamtion check details below."
201 }
202
203 ResourceManager resourceMgr = SCR_WorldEditorToolHelper.GetResourceManager();
204 if (resourceMgr && !configs.IsEmpty())
205 {
206 array<string> configPaths = {};
207 foreach (ResourceName resourceName : configs)
208 {
209 configPaths.Insert(resourceName.GetPath());
210 }
211
212 // reimport config files to ensure that workbench reloads them, so user doesnt have to reopen them to see the changes
213 resourceMgr.RebuildResourceFiles(configPaths, "PC");
214 }
215
216 Workbench.Dialog("Finished", "Generation of windage data has been finished" + detailedTextAddition, failedResult);
217 }
218
219 //------------------------------------------------------------------------------------------------
226 protected SCR_ProjectileWindTable GenerateWindData(notnull TFloatArray initSpeedCoefs, float initSpeed, notnull TFloatArray windSpeeds, notnull ProjectileMoveComponent projectileMoveComp)
227 {
228 const float minElevAngle = 1;
229 const float maxElevAngle = 90;
230 vector poiNoWind, poiCrosswind, poiTailwind, poiHeadwind;
231 vector tailwind, headwind, crosswind;
232 vector peakPoint;
233 vector preImpactPosition;
234
235 array<ref SCR_ProjectileWindData> data = {};
236 TVectorArray values;
237 TVectorArray firingSolutions;
238 TFloatArray windData;
239 float initSpeedWithCoef;
240 foreach (float wind : windSpeeds)
241 {
242 headwind = -vector.Forward * wind;
243 tailwind = vector.Forward * wind;
244 crosswind = vector.Right * wind;
245 foreach (float coef : initSpeedCoefs)
246 {
247 values = {};
248 firingSolutions = {};
249 initSpeedWithCoef = initSpeed * coef;
250 for (int angleDeg = minElevAngle; angleDeg <= maxElevAngle; angleDeg++)
251 {
252 poiNoWind = projectileMoveComp.GetProjectileSimulationResult(vector.Zero, initSpeedWithCoef, angleDeg, maxSimulationTime: m_fMaxSimulationTime, maxHorizontalDistance: m_fMaxSimulatedHorizontalDistance);
253 poiCrosswind = projectileMoveComp.GetProjectileSimulationResult(vector.Zero, initSpeedWithCoef, angleDeg, windSpeed: crosswind, maxSimulationTime: m_fMaxSimulationTime, maxHorizontalDistance: m_fMaxSimulatedHorizontalDistance);
254 poiHeadwind = projectileMoveComp.GetProjectileSimulationResult(vector.Zero, initSpeedWithCoef, angleDeg, windSpeed: headwind, maxSimulationTime: m_fMaxSimulationTime, maxHorizontalDistance: m_fMaxSimulatedHorizontalDistance);
255 poiTailwind = projectileMoveComp.GetProjectileSimulationResult(vector.Zero, initSpeedWithCoef, angleDeg, windSpeed: tailwind, maxSimulationTime: m_fMaxSimulationTime, maxHorizontalDistance: m_fMaxSimulatedHorizontalDistance);
256
257 float angleOfImpact;
258 if (poiNoWind[2] > 0)
259 {
260 peakPoint = FindPeakHeight(projectileMoveComp, initSpeedWithCoef, angleDeg, poiNoWind[2], m_fPeakSearchPrecision, m_fMaxSimulationTime);
261 preImpactPosition = projectileMoveComp.GetProjectileSimulationResult(vector.Zero, initSpeedWithCoef, angleDeg, maxHorizontalDistance: poiNoWind[2] - 1, maxSimulationTime: m_fMaxSimulationTime);
262 angleOfImpact = vector.Direction(poiNoWind, preImpactPosition).Normalized().VectorToAngles()[1];
263 }
264 else
265 {
266 peakPoint = vector.Zero;
267 preImpactPosition = vector.Zero;
268 }
269
270 vector fireSolValues = {
272 poiNoWind[2],
273 peakPoint[1]
274 };
275
276 vector value = {
277 SCR_Math.ConvertFromRadians(Math.Atan2(poiCrosswind[0], poiCrosswind[2]), SCR_EOpticsAngleUnits.MILLIRADIANS),
278 (Math.AbsFloat(poiHeadwind[2] - poiNoWind[2]) + Math.AbsFloat(poiTailwind[2] - poiNoWind[2])) * 0.5,
279 angleOfImpact
280 };
281
282 firingSolutions.Insert(fireSolValues);
283 values.Insert(value);
284 }
285
286 data.Insert(new SCR_ProjectileWindData(coef, wind, values, firingSolutions));
287 }
288 }
289
290 return new SCR_ProjectileWindTable(data);
291 }
292
293 //------------------------------------------------------------------------------------------------
302 protected vector FindPeakHeight(notnull ProjectileMoveComponent projectileMoveComp, float initSpeedWithCoef, float angleDeg, float maxDist, float precision = 10, float maxSimulationTime = 60)
303 {
304 if (precision <= 0)
305 precision = 10;
306
307 float minDist;
308 float midDist;
309 vector first;
310 vector second;
311
312 while (minDist < maxDist)
313 {
314 midDist = (minDist + maxDist) * 0.5;
315
316 first = projectileMoveComp.GetProjectileSimulationResult(vector.Zero, initSpeedWithCoef, angleDeg, maxHorizontalDistance: midDist - precision, maxSimulationTime: maxSimulationTime);
317 second = projectileMoveComp.GetProjectileSimulationResult(vector.Zero, initSpeedWithCoef, angleDeg, maxHorizontalDistance: midDist, maxSimulationTime: maxSimulationTime);
318
319 if (first[1] > second[1])
320 maxDist = midDist - precision;
321 else if (first[1] <= second[1])
322 minDist = midDist + precision;
323 else
324 break;
325 }
326
327 if (first[1] > second[1])
328 return first;
329
330 return second;
331 }
332
333 //------------------------------------------------------------------------------------------------
334 [ButtonAttribute("Run", true)]
335 protected int ButtonRun()
336 {
337 return 1;
338 }
339
340 //------------------------------------------------------------------------------------------------
341 [ButtonAttribute("Close")]
342 protected int ButtonClose()
343 {
344 return 0;
345 }
346}
347#endif
GenerateFlowMaps WorkbenchPlugin WorkbenchPluginAttribute("Regenerate river flow-maps", "Generate and save/overwrite river flow-maps", "", "", {"WorldEditor"}, "", 0xf773)
Definition FlowmapTool.c:59
ArmaReforgerScripted GetGame()
Definition game.c:1398
params precision
ResourceName resourceName
Definition SCR_AIGroup.c:66
IEntity SpawnEntity(ResourceName entityResourceName, notnull IEntity slotOwner)
SCR_EOpticsAngleUnits
Get all prefabs that have the spawner data
override void Run()
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
proto external Managed FindComponent(typename typeName)
Definition Math.c:13
Object holding reference to resource. In destructor release the resource.
Definition Resource.c:25
static float ConvertFromRadians(float radianAngleFrom, SCR_EOpticsAngleUnits toUnitType)
Definition SCR_Math.c:290
static float ConvertToRadians(float angleFrom, SCR_EOpticsAngleUnits fromUnitType)
Definition SCR_Math.c:323
Definition Types.c:486
SCR_FieldOfViewSettings Attribute
array< float > TFloatArray
Definition Types.c:386
proto external string ToString()
Plain C++ pointer, no weak pointers, no memory management.
array< vector > TVectorArray
Definition Types.c:392
array< ResourceName > TResourceNameArray
Definition Types.c:394