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