Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_PlayerData.c
Go to the documentation of this file.
1//------------------------------------------------------------------------------------------------
3{
4 protected int m_iPlayerID;
5
6 //------------------------------------------------------------------------------------------------
10 protected ref array<float> m_aStats = {};
11
12 //temporal stats single-session based that are only tracked for analytic purposes and not stored in the backend storage
15
16 //------------------------------------------------------------------------------------------------
20 protected ref array<float> m_aPreviousStats = {};
21
22 //------------------------------------------------------------------------------------------------
26 protected ref array<float> m_aAccumulatedActions = {};
28 protected int m_iLatestActionTick;
30 protected float m_fCriminalScore;
31
32 //------------------------------------------------------------------------------------------------
36 protected int m_iTimeOut = 0;
37
38
39 //------------------------------------------------------------------------------------------------
43 protected ref array<float> m_aStatsGained = {};
44
45 //------------------------------------------------------------------------------------------------
50
51 //------------------------------------------------------------------------------------------------
55 protected ref array<float> m_aEarnt = {};
56
57 //------------------------------------------------------------------------------------------------
63
64 //------------------------------------------------------------------------------------------------
69
70 //------------------------------------------------------------------------------------------------
74 protected bool m_bIsEmptyProfile;
75
76 //------------------------------------------------------------------------------------------------
81
83
84 //------------------------------------------------------------------------------------------------
89
90 /* TODO: Finish rework of player data using StoreDataModule */
91
92 //Accumulation of stats in order to look for warcrimes on time slices
93 bool accumulationEnabled = false;
94
95 //------------------------------------------------------------------------------------------------
96 void SCR_PlayerData (int id, bool hasPlayerBeenAuditted, bool requestFromBackend = true)
97 {
98 if (!GetGame().GetBackendApi())
99 return;
100
101 RegV("m_aStats");
102
104 m_iPlayerID = id;
105
106 m_iSessionStartedTickCount = System.GetTickCount();
107
108 typename tEnum = SCR_EDataStats;
109 for (int i = tEnum.GetVariableCount(); i > 0; i--)
110 {
111 m_aAccumulatedActions.Insert(0);
112 }
113
114 if (requestFromBackend)
115 RequestLoadData(hasPlayerBeenAuditted);
116 else if (!hasPlayerBeenAuditted)
118
119 if (!GetGame().GetDataCollector())
120 return;
121
122 accumulationEnabled = GetGame().GetDataCollector().FindModule(SCR_DataCollectorCrimesModule) != null;
123
125 EInputDeviceType device = GetGame().GetInputManager().GetLastUsedInputDevice();
126 UpdateTrackingController(device, device);
127 GetGame().OnInputDeviceUserChangedInvoker().Insert(UpdateTrackingController);
128 }
129
130 //------------------------------------------------------------------------------------------------
131 static SCR_PlayerData GetPlayerData(int playerID)
132 {
133 return GetGame().GetDataCollector().GetPlayerData(playerID);
134 }
135
136 //------------------------------------------------------------------------------------------------
138 void SetTimeOut(int time)
139 {
140 m_iTimeOut = time;
141 }
142
143 //------------------------------------------------------------------------------------------------
145 {
146 return m_iTimeOut;
147 }
148
149 //------------------------------------------------------------------------------------------------
150 protected void UpdateTrackingController(EInputDeviceType oldDevice, EInputDeviceType newDevice)
151 {
152 int currentTickCount = System.GetTickCount();
153
154 int newTime = (currentTickCount - m_iLastEvaluationCurrentController) * 0.001;
155 switch (oldDevice)
156 {
157 case EInputDeviceType.GAMEPAD: case EInputDeviceType.JOYSTICK:
158 m_iSecondsAsController += newTime;
159 break;
160 case EInputDeviceType.MOUSE: case EInputDeviceType.KEYBOARD:
161 m_iSecondsAsKeyboard += newTime;
162 break;
163 }
164
165 m_iLastEvaluationCurrentController = currentTickCount;
166 }
167
168 //------------------------------------------------------------------------------------------------
171 void SetPreviousStats(array<float> previousStats)
172 {
173 m_aPreviousStats = previousStats;
174 }
175
176 //------------------------------------------------------------------------------------------------
177 array<float> GetPreviousStats()
178 {
179 return m_aPreviousStats;
180 }
181
182 //------------------------------------------------------------------------------------------------
185 void SetStats(array<float> stats)
186 {
187 m_aStats = stats;
188 }
189
190 //------------------------------------------------------------------------------------------------
191 array<float> GetStats()
192 {
193 return m_aStats;
194 }
195
196 //------------------------------------------------------------------------------------------------
197 void RequestLoadData(bool HasPlayerBeenAuditted = false)
198 {
199 BackendApi ba = GetGame().GetBackendApi();
200
201 if (!ba)
202 {
203 Print("SCR_PlayerData:RequestLoadData: Backend api is null!", LogLevel.WARNING);
204 m_bIsEmptyProfile = true;
205 return;
206 }
207
208 if (HasPlayerBeenAuditted)
209 {
210 ba.PlayerData(this, m_iPlayerID);
212 return;
213 }
214
216 {
217 Print("SCR_PlayerData:RequestLoadData: The character data callback is null", LogLevel.WARNING);
218 m_bIsEmptyProfile = true;
219 return;
220 }
221
222 if (BackendAuthenticatorApi.IsAuthenticated())
223 {
224 Print("Making menuCallback request for PlayerData", LogLevel.VERBOSE);
227 ba.PlayerCharacterGet(m_CharacterDataCallback, this, m_iPlayerID); //ID 0 refers to the local player
228
229 //The callback has a reference to this instance so it will automatically call the BackendDataReady or the LoadEmptyProfile methods
230 }
231 else
232 {
233 Print("SCR_PlayerData:RequestLoadData: Requesting data before player has been authenticated!", LogLevel.WARNING);
234 m_bIsEmptyProfile = true;
235 }
236 }
237
238 //------------------------------------------------------------------------------------------------
243
244 //------------------------------------------------------------------------------------------------
246 {
247 typename characterDataEnum = SCR_EDataStats;
248
249 if (m_aStats.Count() != characterDataEnum.GetVariableCount())
250 {
251 Print("SCR:PlayerDataStats:FillWithProfile: The CharacterData from this player seems incomplete or empty. A full profile should have "+characterDataEnum.GetVariableCount()+" stats, but this one has "+m_aStats.Count()+". Is it a new player?", LogLevel.VERBOSE);
252
253 if (!m_aStats.IsEmpty())
254 {
255 Print("SCR_PlayerData of this player is not empty, but the size is not correct either. We will override their profile with a new one, which will erase their old profile.", LogLevel.DEBUG);
256 }
257
259 return;
260 }
261
262 m_bIsEmptyProfile = false;
263 m_aPreviousStats.InsertAll(m_aStats);
264 m_OnDataReady.Invoke();
265 }
266
267 //------------------------------------------------------------------------------------------------
269 {
270 typename characterDataEnum = SCR_EDataStats;
271 int count = characterDataEnum.GetVariableCount();
272
273 m_aStats.Clear();
274
275 for (int i = 0; i < count; i++)
276 {
277 if (i == SCR_EDataStats.LEVEL_EXPERIENCE)
278 m_aStats.Insert(m_Configs.XP_NEEDED_FOR_LEVEL);
279 else if (i == SCR_EDataStats.CRIME_ACCELERATION)
280 m_aStats.Insert(m_Configs.GetMinAcceleration());
281 else
282 m_aStats.Insert(0);
283
284 }
285
286 m_bIsEmptyProfile = true;
287
288 m_aPreviousStats.InsertAll(m_aStats);
289 m_OnDataReady.Invoke();
290 }
291
292 //------------------------------------------------------------------------------------------------
293 sealed void StoreProfile()
294 {
295 if (!m_CharacterDataCallback || !m_aStats || m_aStats.IsEmpty())
296 return;
297
298 BackendApi ba = GetGame().GetBackendApi();
299 if (!ba)
300 return;
301
304
305 #ifndef WORKBENCH
306 ba.PlayerCharacterUpdateS2S(m_StoringCallback, this, m_iPlayerID);
307 #else
308 ba.PlayerDevCharacterUpdate(m_StoringCallback, this, m_iPlayerID);
309 #endif
310
311 //SCR_PlayerDataEvent dataEvent = new SCR_PlayerDataEvent();
312 //dataEvent.MetersWalked = 99999;
313 //GetGame().GetStatsApi().PlayerEvent(m_iPlayerID, dataEvent);
314 //Print("Data event sent from Store Profile!!", LogLevel.DEBUG);
315 }
316
317 //------------------------------------------------------------------------------------------------
319 {
320 if (!IsDataProgressionReady() || m_aStatsGained.IsEmpty())
321 {
322 Print("Maybe a problem: DataProgression is not ready but the analytics event was requested", LogLevel.WARNING);
324 }
325
326 EInputDeviceType device = GetGame().GetInputManager().GetLastUsedInputDevice();
327 UpdateTrackingController(device, device);
328
329 Print("Time with gamepad was " + m_iSecondsAsController, LogLevel.DEBUG);
330 Print("Time with keyboard was " + m_iSecondsAsKeyboard, LogLevel.DEBUG);
331
332 dataEvent.amt_time_mission = m_aStatsGained[SCR_EDataStats.SESSION_DURATION];
333 dataEvent.amt_distance_on_foot = m_aStatsGained[SCR_EDataStats.DISTANCE_WALKED];
334 dataEvent.amt_distance_vehicle = m_aStatsGained[SCR_EDataStats.DISTANCE_DRIVEN];
336 //dataEvent.kills_from_over_25m;
337 //dataEvent.kills_from_over_50m;
338 //dataEvent.kills_from_over_100m;
339 dataEvent.friendly_kills = m_aStatsGained[SCR_EDataStats.FRIENDLY_KILLS];
340 //dataEvent.friendly_kills_from_over_25m;
341 //dataEvent.friendly_kills_from_over_50m;
342 //dataEvent.friendly_kills_from_over_100m;
343 dataEvent.vehicle_kills = m_aStatsGained[SCR_EDataStats.ROADKILLS] + m_aStatsGained[SCR_EDataStats.AI_ROADKILLS];
344 dataEvent.friendly_vehicle_kills = m_aStatsGained[SCR_EDataStats.FRIENDLY_ROADKILLS] + m_aStatsGained[SCR_EDataStats.FRIENDLY_AI_ROADKILLS];
345 dataEvent.deaths = m_aStatsGained[SCR_EDataStats.DEATHS];
346 //dataEvent.deaths_by_bleeding_out;
347 //dataEvent.deaths_by_falling;
349 //dataEvent.hits;
350 //dataEvent.hits_from_over_25m;
351 //dataEvent.hits_from_over_50m;
352 //dataEvent.hits_from_over_100m;
353 dataEvent.healing_allies = m_aStatsGained[SCR_EDataStats.BANDAGE_FRIENDLIES]; //Careful
354 dataEvent.healing_self = m_aStatsGained[SCR_EDataStats.BANDAGE_SELF]; //Careful
355 //dataEvent.weapon1_id;
356 //dataEvent.weapon1_seconds;
357 //dataEvent.weapon1_shots;
358 //dataEvent.weapon1_hits;
359 //dataEvent.weapon2_id;
360 //dataEvent.weapon2_seconds;
361 //dataEvent.weapon2_shots;
362 //dataEvent.weapon2_hits;
363 //dataEvent.weapon3_id;
364 //dataEvent.weapon3_seconds;
365 //dataEvent.weapon3_shots;
366 //dataEvent.weapon3_hits;
367 //dataEvent.setbase_coordinates;
368 dataEvent.rank_points = m_aStatsGained[SCR_EDataStats.LEVEL_EXPERIENCE];
369 dataEvent.rank_total_points = m_aStats[SCR_EDataStats.LEVEL_EXPERIENCE];
370 dataEvent.warcrimes_points = m_aStatsGained[SCR_EDataStats.WARCRIMES];
371 dataEvent.warcrimes_total_points = m_aStats[SCR_EDataStats.WARCRIMES];
372 dataEvent.rank_level = m_aStatsGained[SCR_EDataStats.RANK];
373 dataEvent.gt1_points = m_aStatsGained[SCR_EDataStats.SPPOINTS0];
374 dataEvent.gt1_total_points = m_aStats[SCR_EDataStats.SPPOINTS0];
375 dataEvent.gt2_points = m_aStatsGained[SCR_EDataStats.SPPOINTS1];
376 dataEvent.gt2_total_points = m_aStats[SCR_EDataStats.SPPOINTS1];
377 dataEvent.gt3_points = m_aStatsGained[SCR_EDataStats.SPPOINTS2];
378 dataEvent.gt3_total_points = m_aStats[SCR_EDataStats.SPPOINTS2];
379 dataEvent.gt_global_points = (m_aStatsGained[SCR_EDataStats.SPPOINTS0] + m_aStatsGained[SCR_EDataStats.SPPOINTS1] + m_aStatsGained[SCR_EDataStats.SPPOINTS2]);
380 dataEvent.seconds_as_controller = m_iSecondsAsController;
381 dataEvent.seconds_as_keyboard = m_iSecondsAsKeyboard;
382
383 return dataEvent;
384 }
385
386 //------------------------------------------------------------------------------------------------
388 {
389 Print("SCR_PlayerData:DebugCalculateStats: This method calls calculateStatsChange providing fake stats for debugging purposes.", LogLevel.DEBUG);
390
391 AddStat(SCR_EDataStats.SESSION_DURATION, 7200);
392 AddStat(SCR_EDataStats.DISTANCE_WALKED, 20000);
393 AddStat(SCR_EDataStats.KILLS, 20);
394 AddStat(SCR_EDataStats.AI_KILLS, 20);
395 AddStat(SCR_EDataStats.SHOTS, 800);
396 AddStat(SCR_EDataStats.GRENADES_THROWN, 10);
397 AddStat(SCR_EDataStats.DISTANCE_DRIVEN, 50000);
398 AddStat(SCR_EDataStats.FRIENDLY_KILLS, 30);
399 AddStat(SCR_EDataStats.BANDAGE_SELF, 30);
400 AddStat(SCR_EDataStats.BANDAGE_FRIENDLIES, 10);
401 AddStat(SCR_EDataStats.TOURNIQUET_SELF, 30);
402 AddStat(SCR_EDataStats.TOURNIQUET_FRIENDLIES, 10);
403
406 }
407
408 //------------------------------------------------------------------------------------------------
410 {
411 //We find the differences provided by this session
412 //In order to calculate Rank, Specializations, and War Crime points
414
415 for (int i = m_aStatsGained.Count()-1; i >= 0; i--)
416 {
417 m_aEarnt.Insert(0);
418 }
419
420 if (m_aStatsGained[SCR_EDataStats.SESSION_DURATION] < 1)
421 return;
422
423 //Little assert
424 if (m_aStatsGained[SCR_EDataStats.SPPOINTS0] != 0 || m_aStatsGained[SCR_EDataStats.SPPOINTS1] != 0 || m_aStatsGained[SCR_EDataStats.SPPOINTS2] != 0|| m_aStatsGained[SCR_EDataStats.RANK] != 0 || m_aStatsGained[SCR_EDataStats.LEVEL_EXPERIENCE] != 0)
425 Print("SCR_PlayerData:CalculateStatsChange: Specialization stats, experience or rank stats were modified prematurely. The career data for this session might be compromised", LogLevel.WARNING);
426
427 //CALCULATE STATS ADDITIONS TO THE SPECIALIZATIONS, THEN WAR CRIMES, THEN SPECIALIZATION POINTS, AND THEN LEVEL OF EXPERIENCE AND RANK
428
429 //Specializations: There are three: Infantry, Logistics and Medical
430 int totalSpecializationPointsGain = 0;
431 //
432 //
433 //Infantry
434 if (m_aStats[SCR_EDataStats.SPPOINTS0] < m_Configs.SPECIALIZATION_MAX)
435 {
436 m_aEarnt[SCR_EDataStats.DISTANCE_WALKED] = m_aStatsGained[SCR_EDataStats.DISTANCE_WALKED] * m_Configs.MODIFIER_DISTANCE_WALKED;
437 m_aEarnt[SCR_EDataStats.KILLS] = m_aStatsGained[SCR_EDataStats.KILLS] * m_Configs.MODIFIER_KILLS;
438 m_aEarnt[SCR_EDataStats.AI_KILLS] = m_aStatsGained[SCR_EDataStats.AI_KILLS] * m_Configs.MODIFIER_AI_KILLS;
439
440 if (m_aStatsGained[SCR_EDataStats.SHOTS] <= 0)
441 m_aEarnt[SCR_EDataStats.SHOTS] = 0;
442 else
443 //This title is misleading: We are treating shots as "precision in killing" of sorts
445
446 /* We should not use GRENADESTHROWN yet
447 if (m_aStatsGained[SCR_EDataStats.GRENADESTHROWN] <= 0)
448 m_aEarnt[SCR_EDataStats.GRENADESTHROWN] = 0;
449 else
450 m_aEarnt[SCR_EDataStats.GRENADESTHROWN] = (m_aStatsGained[SCR_EDataStats.KILLS] / m_aStatsGained[SCR_EDataStats.GRENADESTHROWN]) * m_Configs.MODIFIERPRECISION;
451 */
452
453 m_aEarnt[SCR_EDataStats.SPPOINTS0] = m_aEarnt[SCR_EDataStats.DISTANCE_WALKED] + m_aEarnt[SCR_EDataStats.KILLS] + m_aEarnt[SCR_EDataStats.AI_KILLS] + m_aEarnt[SCR_EDataStats.SHOTS]; /*+ m_aEarnt[SCR_EDataStats.GRENADES_THROWN]; */
454 }
455 else
456 m_aEarnt[SCR_EDataStats.SPPOINTS0] = 0;
457
459 totalSpecializationPointsGain += m_aStatsGained[SCR_EDataStats.SPPOINTS0];
460
461 //
462 //
463 //Logistics
464 if (m_aStats[SCR_EDataStats.SPPOINTS1] < m_Configs.SPECIALIZATION_MAX)
465 {
466 m_aEarnt[SCR_EDataStats.DISTANCE_DRIVEN] = m_aStatsGained[SCR_EDataStats.DISTANCE_DRIVEN] * m_Configs.MODIFIER_DISTANCE_DRIVEN;
467 m_aEarnt[SCR_EDataStats.POINTS_AS_DRIVER_OF_PLAYERS] = m_aStatsGained[SCR_EDataStats.POINTS_AS_DRIVER_OF_PLAYERS];
468
469 m_aEarnt[SCR_EDataStats.PLAYERS_DIED_IN_VEHICLE] = -1 * Math.Min(m_aStatsGained[SCR_EDataStats.PLAYERS_DIED_IN_VEHICLE] * m_Configs.MODIFIER_PLAYERS_DIED_IN_VEHICLE, m_Configs.MAX_PLAYERS_DIED_IN_VEHICLE_PENALTY);
470
471 m_aEarnt[SCR_EDataStats.SPPOINTS1] = m_aEarnt[SCR_EDataStats.DISTANCE_DRIVEN] + m_aEarnt[SCR_EDataStats.POINTS_AS_DRIVER_OF_PLAYERS] + m_aEarnt[SCR_EDataStats.PLAYERS_DIED_IN_VEHICLE];
472
473 if (m_aEarnt[SCR_EDataStats.SPPOINTS1] <= 0)
474 m_aEarnt[SCR_EDataStats.SPPOINTS1] = 0;
475 }
476 else
477 m_aEarnt[SCR_EDataStats.SPPOINTS1] = 0;
478
480 totalSpecializationPointsGain += m_aStatsGained[SCR_EDataStats.SPPOINTS1];
481
482 //
483 //
484 //Medical
485 if (m_aStats[SCR_EDataStats.SPPOINTS2] < m_Configs.SPECIALIZATION_MAX)
486 {
487
488 //TODO: transform these into m_aEarnt[i] = m_aStatsGained[i] * m_Configs[i] if possible
489
490 m_aEarnt[SCR_EDataStats.BANDAGE_SELF] = m_aStatsGained[SCR_EDataStats.BANDAGE_SELF] * m_Configs.MODIFIER_BANDAGE_SELF;
491 m_aEarnt[SCR_EDataStats.BANDAGE_FRIENDLIES] = m_aStatsGained[SCR_EDataStats.BANDAGE_FRIENDLIES] * m_Configs.MODIFIER_BANDAGE_FRIENDLIES;
492 m_aEarnt[SCR_EDataStats.TOURNIQUET_SELF] = m_aStatsGained[SCR_EDataStats.TOURNIQUET_SELF] * m_Configs.MODIFIER_TOURNIQUET_SELF;
493 m_aEarnt[SCR_EDataStats.TOURNIQUET_FRIENDLIES] = m_aStatsGained[SCR_EDataStats.TOURNIQUET_FRIENDLIES] * m_Configs.MODIFIER_TOURNIQUET_FRIENDLIES;
494 m_aEarnt[SCR_EDataStats.SALINE_SELF] = m_aStatsGained[SCR_EDataStats.SALINE_SELF] * m_Configs.MODIFIER_SALINE_SELF;
495 m_aEarnt[SCR_EDataStats.SALINE_FRIENDLIES] = m_aStatsGained[SCR_EDataStats.SALINE_FRIENDLIES] * m_Configs.MODIFIER_SALINE_FRIENDLIES;
496 m_aEarnt[SCR_EDataStats.MORPHINE_SELF] = m_aStatsGained[SCR_EDataStats.MORPHINE_SELF] * m_Configs.MODIFIER_MORPHINE_SELF;
497 m_aEarnt[SCR_EDataStats.MORPHINE_FRIENDLIES] = m_aStatsGained[SCR_EDataStats.MORPHINE_FRIENDLIES] * m_Configs.MODIFIER_MORPHINE_FRIENDLIES;
498
499 m_aEarnt[SCR_EDataStats.SPPOINTS2] = m_aEarnt[SCR_EDataStats.BANDAGE_SELF] + m_aEarnt[SCR_EDataStats.BANDAGE_FRIENDLIES] + m_aEarnt[SCR_EDataStats.TOURNIQUET_SELF] + m_aEarnt[SCR_EDataStats.TOURNIQUET_FRIENDLIES]+ m_aEarnt[SCR_EDataStats.SALINE_SELF] + m_aEarnt[SCR_EDataStats.SALINE_FRIENDLIES] + m_aEarnt[SCR_EDataStats.MORPHINE_SELF] + m_aEarnt[SCR_EDataStats.MORPHINE_FRIENDLIES];
500 }
501 else
502 m_aEarnt[SCR_EDataStats.SPPOINTS2] = 0;
503
505 totalSpecializationPointsGain += m_aStatsGained[SCR_EDataStats.SPPOINTS2];
506
507 //CALCULATE QUALITYTIME RATIO
508 //qualityTimeRatio is calculated as the ratio between (ExpectedPointsPerHour * HoursPlayed) and ObtainedPoints.
509 //For expected points, I use m_fSTDPointsQualityTime as the expected growth per specialization and 2.5 as the number of specializations with it as an accurate simplification
510 float qualityTimeRatio = totalSpecializationPointsGain / (1 +(m_Configs.STD_POINTS_QUALITY_TIME * 2.5 * (m_aStatsGained[SCR_EDataStats.SESSION_DURATION] / 3600)));
511
512 //Little assert on the qualityTimeRatio to check player didn't perform abnormaly well
513 if (qualityTimeRatio > 1.5)
514 Print("SCR_PlayerData:CalculateStatsChange: totalSpecializationPointsGain is suspiciously high. That means player performed better than we expected which could point an an oversight from our side, or even a player cheating. Player with session id " + m_iPlayerID + " gained " + totalSpecializationPointsGain + " points on a session of " + (m_aStatsGained[SCR_EDataStats.SESSION_DURATION] / 60) + " minutes, ending with a qualityTimeRatio of " + qualityTimeRatio, LogLevel.WARNING);
515 else
516 Print("SCR_PlayerData:CalculateStatsChange: QualityTimeRatio (aka how well player performed) is: " + qualityTimeRatio, LogLevel.DEBUG);
517
518 //LOWER WAR CRIMES IF THERE'S ANY
519 if (m_aStats[SCR_EDataStats.WARCRIMES] > 0)
520 {
521 // WarCrimes go down by m_fWarCrimesDecreaseRatePerHour * qualityTimeRatio * time
522 int warCrimesDown = qualityTimeRatio * (m_aStatsGained[SCR_EDataStats.SESSION_DURATION]/ 3600) * m_Configs.WARCRIMES_DECREASE_PER_HOUR;
523
524 //All war crimes go down equitatively by amountToDecrease / numberOfNonZeroWarCrimes amount
525 int amountToDecrease = (m_aStats[SCR_EDataStats.WARCRIME_HARMING_FRIENDLIES]) * warCrimesDown / m_aStats[SCR_EDataStats.WARCRIMES];
526
527 //Divide equitatively all the war crimes. In this case: only 1
528 m_aStats[SCR_EDataStats.WARCRIME_HARMING_FRIENDLIES] = m_aStats[SCR_EDataStats.WARCRIME_HARMING_FRIENDLIES] - amountToDecrease / 1;
529 m_aStats[SCR_EDataStats.WARCRIMES] = m_aStats[SCR_EDataStats.WARCRIMES] - warCrimesDown;
530
531 if (m_aStats[SCR_EDataStats.WARCRIMES] > m_Configs.MAX_WARCRIMES_VALUE)
532 m_aStats[SCR_EDataStats.WARCRIMES] = m_Configs.MAX_WARCRIMES_VALUE;
533 else if (m_aStats[SCR_EDataStats.WARCRIMES] < 0)
534 m_aStats[SCR_EDataStats.WARCRIMES] = 0;
535 }
536
537 //UPDATE THE SPECIALIZATION POINTS
538 //HERE we update the specializationPoints
539 //------------------------------------------------------------------------------------------------
540
541 int i, j;
542 int currentSpPoints, gainedRawPoints;
543 for (i = 0; i < m_Configs.SPECIALIZATIONS_COUNT; i++)
544 {
545 j = 0;
546 switch (i)
547 {
548 case SCR_ECareerSp.INFANTRY:
549 gainedRawPoints = m_aStatsGained[SCR_EDataStats.SPPOINTS0];
550 currentSpPoints = m_aStats[SCR_EDataStats.SPPOINTS0];
551 break;
552 case SCR_ECareerSp.LOGISTICS:
553 gainedRawPoints = m_aStatsGained[SCR_EDataStats.SPPOINTS1];
554 currentSpPoints = m_aStats[SCR_EDataStats.SPPOINTS1];
555 break;
556 case SCR_ECareerSp.MEDICAL:
557 gainedRawPoints = m_aStatsGained[SCR_EDataStats.SPPOINTS2];
558 currentSpPoints = m_aStats[SCR_EDataStats.SPPOINTS2];
559 break;
560 default: continue;
561 }
562
563 if (currentSpPoints >= m_Configs.SPECIALIZATION_MAX)
564 continue;
565
566 //Punish the specialization growth if there are war crimes
567 if (m_aStats[SCR_EDataStats.WARCRIMES] > 0)
568 gainedRawPoints *= (1 - m_Configs.WARCRIMES_PUNISHMENT);
569
570 while (j < m_Configs.INTERVALS_COUNT && m_Configs.INTERVALS_END[j] < currentSpPoints)
571 {
572 j++;
573 }
574
575 if (j >= m_Configs.INTERVALS_COUNT)
576 {
577 Print ("SCR_PlayerData:CalculateStatsChange: currentSpPoints outside of the accepted ranges.", LogLevel.WARNING);
578 continue;
579 }
580
581 //Formula: NewSpecializationPoints = CurrentSpecializationPoints + GainedPoints * c1 * (c2 ^ CurrentSpecializationPoints)
582 //m_aStats.m_aSpPoints[i] = m_aStats.m_aSpPoints[i] + rawSpPoints[i] * m_Configs.INTERVALS_C1[j] * (Math.Pow(m_Configs.INTERVALS_C2[j], m_aStats.m_aSpPoints[i]));
583 AddPointsToSpecialization(i, gainedRawPoints * m_Configs.INTERVALS_C1[j] * (Math.Pow(m_Configs.INTERVALS_C2[j], currentSpPoints * m_Configs.XP_NEEDED_FOR_LEVEL_DIVIDER)));
584 }
585
586 //LAST BUT NOT LEAST, INCREASE THE LEVEL OF EXPERIENCE
587 //IF player has max level, skip this step
588 if (m_aStats[SCR_EDataStats.LEVEL_EXPERIENCE] >= m_Configs.MAX_EXP)
589 {
590 m_aEarnt[SCR_EDataStats.SESSION_DURATION] = 0;
591 m_aEarnt[SCR_EDataStats.LEVEL_EXPERIENCE] = 0;
592 return;
593 }
594
595 //Calculate gains in level of experience. Currently, we are only using sessionduration and specializationgains
596 m_aEarnt[SCR_EDataStats.SESSION_DURATION] = m_aStatsGained[SCR_EDataStats.SESSION_DURATION] * qualityTimeRatio;
597 m_aEarnt[SCR_EDataStats.LEVEL_EXPERIENCE] = m_aEarnt[SCR_EDataStats.SESSION_DURATION] + totalSpecializationPointsGain * m_Configs.MODIFIER_SPECIALIZATIONS;
598
599 if (m_aStats[SCR_EDataStats.WARCRIMES] > 0)
600 {
601 m_aEarnt[SCR_EDataStats.LEVEL_EXPERIENCE] = m_aEarnt[SCR_EDataStats.LEVEL_EXPERIENCE] * (1 - m_Configs.WARCRIMES_PUNISHMENT);
602 }
603 m_aStats[SCR_EDataStats.LEVEL_EXPERIENCE] = m_aStats[SCR_EDataStats.LEVEL_EXPERIENCE] + m_aEarnt[SCR_EDataStats.LEVEL_EXPERIENCE];
604 if (m_aStats[SCR_EDataStats.LEVEL_EXPERIENCE] > m_Configs.MAX_EXP)
605 m_aStats[SCR_EDataStats.LEVEL_EXPERIENCE] = m_Configs.MAX_EXP;
606
607 m_aStats[SCR_EDataStats.RANK] = m_aStats[SCR_EDataStats.LEVEL_EXPERIENCE] * m_Configs.XP_NEEDED_FOR_RANK_DIVIDER;
608
610 m_aStatsGained[SCR_EDataStats.LEVEL_EXPERIENCE] = m_aStats[SCR_EDataStats.LEVEL_EXPERIENCE] - m_aPreviousStats[SCR_EDataStats.LEVEL_EXPERIENCE];
611 }
612
613 //------------------------------------------------------------------------------------------------
614 array<float> GetArrayEarntPoints()
615 {
616 return m_aEarnt;
617 }
618
619 //------------------------------------------------------------------------------------------------
621 {
622 return !m_aStats.IsEmpty();
623 }
624
625 //------------------------------------------------------------------------------------------------
627 {
628 return m_aEarnt && !m_aEarnt.IsEmpty();
629 }
630
631 //------------------------------------------------------------------------------------------------
632 void FillArrayWithSpecializationPoints(inout array<float> SpPoints, bool newStats = true)
633 {
634 for (int i = 0; i < m_Configs.SPECIALIZATIONS_COUNT; i++)
635 {
636 SpPoints.Insert(GetSpecializationPoints(i, newStats));
637 }
638 }
639
640 //------------------------------------------------------------------------------------------------
641 sealed array<float> CalculateStatsDifference()
642 {
643 array<float> StatsToReturn = {};
644
645 int count = m_aStats.Count();
647
648 for (int i = 0; i < count; i++)
649 {
650 StatsToReturn.Insert(m_aStats[i] - m_aPreviousStats[i]);
651 }
652
653 return StatsToReturn;
654 }
655
656 //------------------------------------------------------------------------------------------------
657 float GetSpecializationPoints(int n, bool newStats = true)
658 {
659 if (newStats)
660 {
661 if (!m_aStats || m_aStats.IsEmpty())
662 return 0;
663
664 switch (n)
665 {
666 case SCR_ECareerSp.INFANTRY:
667 return m_aStats[SCR_EDataStats.SPPOINTS0];
668 case SCR_ECareerSp.LOGISTICS:
669 return m_aStats[SCR_EDataStats.SPPOINTS1];
670 case SCR_ECareerSp.MEDICAL:
671 return m_aStats[SCR_EDataStats.SPPOINTS2];
672 }
673
674 Print ("SCR_PlayerData:GetSpecializationPoints: WRONG SPECIALIZATION ID.", LogLevel.WARNING);
675 return 0;
676 }
677
678 if (!m_aPreviousStats || m_aPreviousStats.IsEmpty())
679 return 0;
680
681 switch (n)
682 {
683 case SCR_ECareerSp.INFANTRY:
684 return m_aPreviousStats[SCR_EDataStats.SPPOINTS0];
685 case SCR_ECareerSp.LOGISTICS:
686 return m_aPreviousStats[SCR_EDataStats.SPPOINTS1];
687 case SCR_ECareerSp.MEDICAL:
688 return m_aPreviousStats[SCR_EDataStats.SPPOINTS2];
689 }
690
691 Print ("SCR_PlayerData:GetSpecializationPoints: WRONG SPECIALIZATION ID.", LogLevel.WARNING);
692 return 0;
693 }
694
695 //------------------------------------------------------------------------------------------------
696 protected void AddPointsToSpecialization(int n, float value)
697 {
698 if (!m_aStats || m_aStats.IsEmpty())
699 return;
700
701 switch (n)
702 {
703 case SCR_ECareerSp.INFANTRY:
704 m_aStats[SCR_EDataStats.SPPOINTS0] = Math.Min(m_aStats[SCR_EDataStats.SPPOINTS0] + value, m_Configs.SPECIALIZATION_MAX);
705 return;
706 case SCR_ECareerSp.LOGISTICS:
707 m_aStats[SCR_EDataStats.SPPOINTS1] = Math.Min(m_aStats[SCR_EDataStats.SPPOINTS1] + value, m_Configs.SPECIALIZATION_MAX);
708 return;
709 case SCR_ECareerSp.MEDICAL:
710 m_aStats[SCR_EDataStats.SPPOINTS2] = Math.Min(m_aStats[SCR_EDataStats.SPPOINTS2] + value, m_Configs.SPECIALIZATION_MAX);
711 return;
712 }
713 Print ("SCR_PlayerData:AddPointsToSpecialization: WRONG SPECIALIZATION ID.", LogLevel.WARNING);
714 }
715
716 //------------------------------------------------------------------------------------------------
719 {
720 m_Configs.m_aSpecialization0[SCR_ESpecialization0Display.DISTANCE_WALKED].SetValue(GetStat(SCR_EDataStats.DISTANCE_WALKED) * m_Configs.MetersToKilometersConversion);
721 m_Configs.m_aSpecialization0[SCR_ESpecialization0Display.PLAYER_KILLS].SetValue(GetStat(SCR_EDataStats.KILLS));
722 m_Configs.m_aSpecialization0[SCR_ESpecialization0Display.AI_KILLS].SetValue(GetStat(SCR_EDataStats.AI_KILLS));
723 m_Configs.m_aSpecialization0[SCR_ESpecialization0Display.SHOTS].SetValue(GetStat(SCR_EDataStats.SHOTS));
724
725 m_Configs.m_aSpecialization1[SCR_ESpecialization1Display.DISTANCE_DRIVEN].SetValue(GetStat(SCR_EDataStats.DISTANCE_DRIVEN) * m_Configs.MetersToKilometersConversion);
726 m_Configs.m_aSpecialization1[SCR_ESpecialization1Display.DISTANCE_AS_OCCUPANT].SetValue(GetStat(SCR_EDataStats.DISTANCE_AS_OCCUPANT) * m_Configs.MetersToKilometersConversion);
727 m_Configs.m_aSpecialization1[SCR_ESpecialization1Display.POINTS_DRIVING_PEOPLE].SetValue(GetStat(SCR_EDataStats.POINTS_AS_DRIVER_OF_PLAYERS));
728
729 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.BANDAGE_SELF].SetValue(GetStat(SCR_EDataStats.BANDAGE_SELF));
730 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.BANDAGE_FRIENDLIES].SetValue(GetStat(SCR_EDataStats.BANDAGE_FRIENDLIES));
731 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.TOURNIQUET_SELF].SetValue(GetStat(SCR_EDataStats.TOURNIQUET_SELF));
732 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.TOURNIQUET_FRIENDLIES].SetValue(GetStat(SCR_EDataStats.TOURNIQUET_FRIENDLIES));
733 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.SALINE_SELF].SetValue(GetStat(SCR_EDataStats.SALINE_SELF));
734 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.SALINE_FRIENDLIES].SetValue(GetStat(SCR_EDataStats.SALINE_FRIENDLIES));
735 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.MORPHINE_SELF].SetValue(GetStat(SCR_EDataStats.MORPHINE_SELF));
736 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.MORPHINE_FRIENDLIES].SetValue(GetStat(SCR_EDataStats.MORPHINE_FRIENDLIES));
737 }
738
739 //------------------------------------------------------------------------------------------------
742 {
743 if (m_aStatsGained.IsEmpty())
744 {
745 Print("Requesting statsDifference in PrepareSpecializationProgressionStatsDisplay but array is empty. Recalculating it!", LogLevel.DEBUG);
747 }
748
749 m_Configs.m_aSpecialization0[SCR_ESpecialization0Display.DISTANCE_WALKED].SetValue(m_aStatsGained[SCR_EDataStats.DISTANCE_WALKED] * m_Configs.MetersToKilometersConversion);
750 m_Configs.m_aSpecialization0[SCR_ESpecialization0Display.PLAYER_KILLS].SetValue(m_aStatsGained[SCR_EDataStats.KILLS]);
751 m_Configs.m_aSpecialization0[SCR_ESpecialization0Display.AI_KILLS].SetValue(m_aStatsGained[SCR_EDataStats.AI_KILLS]);
752 m_Configs.m_aSpecialization0[SCR_ESpecialization0Display.SHOTS].SetValue(m_aStatsGained[SCR_EDataStats.SHOTS]);
753
754 m_Configs.m_aSpecialization1[SCR_ESpecialization1Display.DISTANCE_DRIVEN].SetValue(m_aStatsGained[SCR_EDataStats.DISTANCE_DRIVEN] * m_Configs.MetersToKilometersConversion);
755 m_Configs.m_aSpecialization1[SCR_ESpecialization1Display.DISTANCE_AS_OCCUPANT].SetValue(m_aStatsGained[SCR_EDataStats.DISTANCE_AS_OCCUPANT] * m_Configs.MetersToKilometersConversion);
756 m_Configs.m_aSpecialization1[SCR_ESpecialization1Display.POINTS_DRIVING_PEOPLE].SetValue(m_aStatsGained[SCR_EDataStats.POINTS_AS_DRIVER_OF_PLAYERS]);
757
758 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.BANDAGE_SELF].SetValue(m_aStatsGained[SCR_EDataStats.BANDAGE_SELF]);
759 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.BANDAGE_FRIENDLIES].SetValue(m_aStatsGained[SCR_EDataStats.BANDAGE_FRIENDLIES]);
760 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.TOURNIQUET_SELF].SetValue(m_aStatsGained[SCR_EDataStats.TOURNIQUET_SELF]);
761 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.TOURNIQUET_FRIENDLIES].SetValue(m_aStatsGained[SCR_EDataStats.TOURNIQUET_FRIENDLIES]);
762 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.SALINE_SELF].SetValue(m_aStatsGained[SCR_EDataStats.SALINE_SELF]);
763 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.SALINE_FRIENDLIES].SetValue(m_aStatsGained[SCR_EDataStats.SALINE_FRIENDLIES]);
764 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.MORPHINE_SELF].SetValue(m_aStatsGained[SCR_EDataStats.MORPHINE_SELF]);
765 m_Configs.m_aSpecialization2[SCR_ESpecialization2Display.MORPHINE_FRIENDLIES].SetValue(m_aStatsGained[SCR_EDataStats.MORPHINE_FRIENDLIES]);
766 }
767
768 //------------------------------------------------------------------------------------------------
770 {
771 return m_bIsEmptyProfile;
772 }
773
774 //m_aStats and m_aPreviousStats must not be accessed from outside of this class.
775 //That is why there are getters and setters for their attributes, but not for their instances
776 //
777 //
778 //
779 //SETTERS:
780
781 //------------------------------------------------------------------------------------------------
785 void AddStat(SCR_EDataStats stat, float amount = 1, bool temp = true)
786 {
788 temp = false;
789
790 if (temp)
791 {
792 m_iLatestActionTick = System.GetTickCount();
795
796 m_aAccumulatedActions[stat] = m_aAccumulatedActions[stat] + amount;
797 }
798 else
799 {
800 m_aStats[stat] = m_aStats[stat] + amount;
801 }
802
803 s_OnStatAdded.Invoke(m_iPlayerID, stat, amount, temp);
804 }
805
806 //------------------------------------------------------------------------------------------------
807 //USE WITH CARE AND ONLY IN SPECIAL CIRCUMSTANCES!
808 void OverrideStat(SCR_EDataStats stat, float amount = 1)
809 {
810 m_aStats[stat] = amount;
811 }
812
813 //GETTERS:
814
815 //------------------------------------------------------------------------------------------------
816 float GetStat(SCR_EDataStats stat, bool newStat = true)
817 {
818 if (newStat)
819 return m_aStats[stat];
820
821 return m_aPreviousStats[stat];
822 }
823
825 //Generic//
827
828 //------------------------------------------------------------------------------------------------
833
834 //------------------------------------------------------------------------------------------------
836 {
837 return m_iLatestActionTick;
838 }
839
844
845 //------------------------------------------------------------------------------------------------
847 {
848 return m_Configs;
849 }
850
851 //------------------------------------------------------------------------------------------------
853 {
854 array<float> copy = {};
855 copy.Copy(m_aAccumulatedActions);
856 return copy;
857 }
858
859 //------------------------------------------------------------------------------------------------
861 {
862 return m_fCriminalScore;
863 }
864
865 //------------------------------------------------------------------------------------------------
866 void SetCriminalScore(float score)
867 {
869 m_fCriminalScore = score;
870 }
871
872 //------------------------------------------------------------------------------------------------
874 {
875 for (int i = 0, count = m_aAccumulatedActions.Count(); i < count; i++)
876 {
878 }
879
881
882 }
883
884 //------------------------------------------------------------------------------------------------
886 {
887 // HOTFIX: m_aStats is empty on reconnect to end screen. This shouldn't be probably called
888 // at all, but it is to someone else to review this system. Lets just check, if the index
889 // is valid to prevent VM exception
890 if (!m_aStats.IsIndexValid(SCR_EDataStats.SESSION_DURATION))
891 {
892 return;
893 }
894
895 m_aStats[SCR_EDataStats.SESSION_DURATION] = m_aStats[SCR_EDataStats.SESSION_DURATION] + (System.GetTickCount() - m_iSessionStartedTickCount) * 0.001;
896 m_iSessionStartedTickCount = System.GetTickCount();
897 }
898
899 /*****************/
900 //Specializations//
901 /*****************/
902
903 //------------------------------------------------------------------------------------------------
905 {
906 switch (n)
907 {
908 case 0: return m_Configs.SPECIALIZATION_0_COUNT;
909 case 1: return m_Configs.SPECIALIZATION_1_COUNT;
910 case 2: return m_Configs.SPECIALIZATION_2_COUNT;
911 }
912 return -1;
913 }
914};
915
916//DO NOT CHANGE THE ORDER OF THE ITEMS OF THE ENUM. (!!!) WE USE THE INDEX AS AN ID IN THE PLAYERPROFILE'S CHARACTERDATA
917//------------------------------------------------------------------------------------------------
AddonBuildInfoTool id
ArmaReforgerScripted GetGame()
Definition game.c:1398
SCR_DataCollectorComponent GetDataCollector()
Definition game.c:110
SCR_EDataStats
@ LIGHTBAN_SESSION_DURATION
Session duration at the time player was lightbanned the last time.
@ FRIENDLY_ROADKILLS
Human friendlies killed with a vehicle.
@ SALINE_FRIENDLIES
Number of times player salined friendlies (not including themself).
@ DEATHS
Deaths.
@ KILLS
Enemies killed.
@ SPPOINTS1
LOGISTICS.
@ CRIME_ACCELERATION
Kick & Ban acceleration.
@ BANDAGE_FRIENDLIES
Number of times player bandaged friendlies (not including themself).
@ GRENADES_THROWN
Grenades thrown.
@ SHOTS
Bullets shot.
@ WARCRIMES
War criminal points.
@ PLAYERS_DIED_IN_VEHICLE
Players that died inside a vehicle while the player drives it.
@ SPPOINTS0
INFANTRY.
@ KICK_STREAK
How many times was the player kicked in a row after last kick in a short succession.
@ FRIENDLY_AI_KILLS
Friendly AI kills.
@ HEAVYBAN_STREAK
How many times was the player heavybanned in a row after the last heavyban in a short succession.
@ AI_ROADKILLS
AI enemies killed with a vehicle.
@ MORPHINE_FRIENDLIES
Number of times player morphined friendlies (not including themself).
@ SALINE_SELF
Number of times player salined themself.
@ FRIENDLY_KILLS
Friendly human kills.
@ MORPHINE_SELF
Number of times player morphined themself.
@ SESSION_DURATION
Total duration of sessions.
@ POINTS_AS_DRIVER_OF_PLAYERS
Points obtained for driving players around.
@ FRIENDLY_AI_ROADKILLS
AI friendlies killed with a vehicle.
@ KICK_SESSION_DURATION
Session duration at the time player was kicked the last time.
@ LEVEL_EXPERIENCE
XP points.
@ AI_KILLS
AI enemies killed.
@ SPPOINTS2
MEDICAL.
@ LIGHTBAN_STREAK
How many times was the player lightbanned in a row after the last lightban in a short succession.
@ HEAVYBAN_SESSION_DURATION
Session duration at the time player was heavybanned the last time.
@ ROADKILLS
Human enemies killed with a vehicle.
@ BANDAGE_SELF
Number of times player bandaged themself.
@ TOURNIQUET_SELF
Number of times player tourniquetted themself.
@ RANK
Rank of the player.
@ DISTANCE_DRIVEN
Distance driven with a vehicle.
@ DISTANCE_AS_OCCUPANT
Distance the player was driven around with a vehicle.
@ TOURNIQUET_FRIENDLIES
Number of times player tourniquetted friendlies (not including themself).
@ DISTANCE_WALKED
Distance walked on foot.
@ WARCRIME_HARMING_FRIENDLIES
Harming friendlies.
SCR_ESpecialization2Display
SCR_ESpecialization0Display
SCR_ESpecialization1Display
Backend Api instance.
Definition BackendApi.c:14
base classes for filtering in server browser
Definition Math.c:13
static SCR_PlayerDataConfigs GetInstance()
array< float > GetAccumulatedActions()
ref BackendCallback m_CharacterDataCallback
ref SCR_PlayerDataEvent dataEvent
int GetLatestCriminalScoreUpdateTick()
int m_iAccumulatedActionsTick
SCR_PlayerDataConfigs GetConfigs()
ref SCR_PlayerDataConfigs m_Configs
int GetAccumulatedActionsTick()
ref array< float > m_aPreviousStats
sealed array< float > CalculateStatsDifference()
void PrepareSpecializationProgressionStatsDisplay()
This method sets the SCR_PlayerDataSpecializationDisplay arrays from the PlayerDataConfigs....
sealed void StoreProfile()
void SetStats(array< float > stats)
ref BackendCallback m_StoringCallback
ref array< float > m_aEarnt
array< float > GetStats()
void AddStat(SCR_EDataStats stat, float amount=1, bool temp=true)
void SetTimeOut(int time)
Called manually from the SCR_DataCollectorCrimesModule.
array< float > GetArrayEarntPoints()
void CalculateSessionDuration()
float GetStat(SCR_EDataStats stat, bool newStat=true)
int m_iLatestCriminalScoreUpdateTick
void RequestLoadData(bool HasPlayerBeenAuditted=false)
ref array< float > m_aAccumulatedActions
ref array< float > m_aStats
sealed void DebugCalculateStats()
float GetSpecializationPoints(int n, bool newStats=true)
void SetPreviousStats(array< float > previousStats)
static SCR_PlayerData GetPlayerData(int playerID)
float GetCriminalScore()
int m_iLastEvaluationCurrentController
Managed GetDataEventStats()
void SCR_PlayerData(int id, bool hasPlayerBeenAuditted, bool requestFromBackend=true)
bool IsDataProgressionReady()
array< float > GetPreviousStats()
ScriptInvoker GetDataReadyInvoker()
static ref ScriptInvoker s_OnStatAdded
void FillArrayWithSpecializationPoints(inout array< float > SpPoints, bool newStats=true)
void ResetAccumulatedActions()
int m_iSessionStartedTickCount
sealed void CalculateStatsChange()
void OverrideStat(SCR_EDataStats stat, float amount=1)
ref array< float > m_aStatsGained
void AddPointsToSpecialization(int n, float value)
ref ScriptInvoker m_OnDataReady
void PrepareSpecializationStatsDisplay()
This method sets the SCR_PlayerDataSpecializationDisplay arrays from the PlayerDataConfigs....
float GetSpecializationCount(int n)
void SetCriminalScore(float score)
void UpdateTrackingController(EInputDeviceType oldDevice, EInputDeviceType newDevice)
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
ScriptInvokerBase< func > ScriptInvoker
Definition tools.c:134