OpenXcom  1.0
Open-source clone of the original X-Com
BattleUnitStatistics.h
1 /*
2 * Copyright 2010-2016 OpenXcom Developers.
3 *
4 * This file is part of OpenXcom.
5 *
6 * OpenXcom is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OpenXcom is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include <string>
20 #include <sstream>
21 #include <yaml-cpp/yaml.h>
22 #include "BattleUnit.h"
23 #include "../Engine/Language.h"
24 
25 namespace OpenXcom
26 {
27 
32 {
34  std::wstring name;
35  std::string type, rank, race, weapon, weaponAmmo;
36  UnitFaction faction;
37  UnitStatus status;
38  int mission, turn, id;
39  UnitSide side;
40  UnitBodyPart bodypart;
41 
43  // Make turn unique across all kills
45  {
46  return turn += mission * 300; // Maintains divisibility by 3 as well
47  }
48 
49  // Check to see if turn was on HOSTILE side
50  bool hostileTurn() const
51  {
52  if ((turn - 1) % 3 == 0) return true;
53  return false;
54  }
55 
56  // Make turn unique across mission
57  void setTurn(int unitTurn, UnitFaction unitFaction)
58  {
59  turn = unitTurn * 3 + (int)unitFaction;
60  }
61 
62  // Load
63  void load(const YAML::Node &node)
64  {
65  if (const YAML::Node n = node["name"])
66  {
67  name = Language::utf8ToWstr(n.as<std::string>());
68  }
69  type = node["type"].as<std::string>(type);
70  rank = node["rank"].as<std::string>(rank);
71  race = node["race"].as<std::string>(race);
72  weapon = node["weapon"].as<std::string>(weapon);
73  weaponAmmo = node["weaponAmmo"].as<std::string>(weaponAmmo);
74  status = (UnitStatus)node["status"].as<int>();
75  faction = (UnitFaction)node["faction"].as<int>();
76  mission = node["mission"].as<int>(mission);
77  turn = node["turn"].as<int>(turn);
78  side = (UnitSide)node["side"].as<int>();
79  bodypart = (UnitBodyPart)node["bodypart"].as<int>();
80  id = node["id"].as<int>(id);
81  }
82 
83  // Save
84  YAML::Node save() const
85  {
86  YAML::Node node;
87  if (!name.empty())
88  node["name"] = Language::wstrToUtf8(name);
89  if (!type.empty())
90  node["type"] = type;
91  node["rank"] = rank;
92  node["race"] = race;
93  node["weapon"] = weapon;
94  node["weaponAmmo"] = weaponAmmo;
95  node["status"] = (int)status;
96  node["faction"] = (int)faction;
97  node["mission"] = mission;
98  node["turn"] = turn;
99  node["side"] = (int)side;
100  node["bodypart"] = (int)bodypart;
101  node["id"] = id;
102  return node;
103  }
104 
105  // Convert kill Status to string.
106  std::string getKillStatusString() const
107  {
108  switch (status)
109  {
110  case STATUS_DEAD: return "STR_KILLED";
111  case STATUS_UNCONSCIOUS: return "STR_STUNNED";
112  case STATUS_PANICKING: return "STR_PANICKED";
113  case STATUS_TURNING: return "STR_MINDCONTROLLED";
114  default: return "status error";
115  }
116  }
117 
118  // Convert victim Status to string.
119  std::string getUnitStatusString() const
120  {
121  switch (status)
122  {
123  case STATUS_DEAD: return "STATUS_DEAD";
124  case STATUS_UNCONSCIOUS: return "STATUS_UNCONSCIOUS";
125  case STATUS_PANICKING: return "STATUS_PANICKING";
126  case STATUS_TURNING: return "STATUS_TURNING";
127  default: return "status error";
128  }
129  }
130 
131  // Convert victim Faction to string.
132  std::string getUnitFactionString() const
133  {
134  switch (faction)
135  {
136  case FACTION_PLAYER: return "FACTION_PLAYER";
137  case FACTION_HOSTILE: return "FACTION_HOSTILE";
138  case FACTION_NEUTRAL: return "FACTION_NEUTRAL";
139  default: return "faction error";
140  }
141  }
142 
143  // Convert victim Side to string.
144  std::string getUnitSideString() const
145  {
146  switch (side)
147  {
148  case SIDE_FRONT: return "SIDE_FRONT";
149  case SIDE_LEFT: return "SIDE_LEFT";
150  case SIDE_RIGHT: return "SIDE_RIGHT";
151  case SIDE_REAR: return "SIDE_REAR";
152  case SIDE_UNDER: return "SIDE_UNDER";
153  default: return "side error";
154  }
155  }
156 
157  // Convert victim Body part to string.
158  std::string getUnitBodyPartString() const
159  {
160  switch (bodypart)
161  {
162  case BODYPART_HEAD: return "BODYPART_HEAD";
163  case BODYPART_TORSO: return "BODYPART_TORSO";
164  case BODYPART_RIGHTARM: return "BODYPART_RIGHTARM";
165  case BODYPART_LEFTARM: return "BODYPART_LEFTARM";
166  case BODYPART_RIGHTLEG: return "BODYPART_RIGHTLEG";
167  case BODYPART_LEFTLEG: return "BODYPART_LEFTLEG";
168  default: return "body part error";
169  }
170  }
171 
172  // Get human-readable victim name.
173  std::wstring getUnitName(Language *lang) const
174  {
175  if (!name.empty())
176  {
177  return name;
178  }
179  else if (!type.empty())
180  {
181  return lang->getString(type);
182  }
183  else
184  {
185  std::wostringstream ss;
186  ss << lang->getString(race) << L" " << lang->getString(rank);
187  return ss.str();
188  }
189  }
190 
191  // Decide victim name, race and rank.
192  void setUnitStats(BattleUnit *unit)
193  {
194  name = L"";
195  type = "";
196  if (unit->getGeoscapeSoldier())
197  {
198  name = unit->getGeoscapeSoldier()->getName();
199  }
200  else
201  {
202  type = unit->getType();
203  }
204 
205  if (unit->getOriginalFaction() == FACTION_PLAYER)
206  {
207  // Soldiers
208  if (unit->getGeoscapeSoldier())
209  {
210  if (!unit->getGeoscapeSoldier()->getRankString().empty())
211  {
212  rank = unit->getGeoscapeSoldier()->getRankString();
213  }
214  else
215  {
216  rank = "STR_SOLDIER";
217  }
218  if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRace().empty())
219  {
220  race = unit->getUnitRules()->getRace();
221  }
222  else
223  {
224  race = "STR_FRIENDLY";
225  }
226  }
227  // HWPs
228  else
229  {
230  if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRank().empty())
231  {
232  rank = unit->getUnitRules()->getRank();
233  }
234  else
235  {
236  rank = "STR_HWPS";
237  }
238  if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRace().empty())
239  {
240  race = unit->getUnitRules()->getRace();
241  }
242  else
243  {
244  race = "STR_FRIENDLY";
245  }
246  }
247  }
248  // Aliens
249  else if (unit->getOriginalFaction() == FACTION_HOSTILE)
250  {
251  if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRank().empty())
252  {
253  rank = unit->getUnitRules()->getRank();
254  }
255  else
256  {
257  rank = "STR_LIVE_SOLDIER";
258  }
259  if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRace().empty())
260  {
261  race = unit->getUnitRules()->getRace();
262  }
263  else
264  {
265  race = "STR_HOSTILE";
266  }
267  }
268  // Civilians
269  else if (unit->getOriginalFaction() == FACTION_NEUTRAL)
270  {
271  if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRank().empty())
272  {
273  rank = unit->getUnitRules()->getRank();
274  }
275  else
276  {
277  rank = "STR_CIVILIAN";
278  }
279  if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRace().empty())
280  {
281  race = unit->getUnitRules()->getRace();
282  }
283  else
284  {
285  race = "STR_NEUTRAL";
286  }
287  }
288  // Error
289  else
290  {
291  rank = "STR_UNKNOWN";
292  race = "STR_UNKNOWN";
293  }
294  }
295 
296  BattleUnitKills(const YAML::Node& node) { load(node); }
297  BattleUnitKills(): faction(FACTION_HOSTILE), status(STATUS_IGNORE_ME), mission(0), turn(0), id(0), side(SIDE_FRONT), bodypart(BODYPART_HEAD) { }
298  ~BattleUnitKills() { }
299 };
300 
305 {
307  bool wasUnconcious; // Tracks if the soldier fell unconcious
308  int shotAtCounter; // Tracks how many times the unit was shot at
309  int hitCounter; // Tracks how many times the unit was hit
310  int shotByFriendlyCounter; // Tracks how many times the unit was hit by a friendly
311  int shotFriendlyCounter; // Tracks how many times the unit was hit a friendly
312  bool loneSurvivor; // Tracks if the soldier was the only survivor
313  bool ironMan; // Tracks if the soldier was the only soldier on the mission
314  int longDistanceHitCounter; // Tracks how many long distance shots were landed
315  int lowAccuracyHitCounter; // Tracks how many times the unit landed a low probability shot
316  int shotsFiredCounter; // Tracks how many times a unit has shot
317  int shotsLandedCounter; // Tracks how many times a unit has hit his target
318  std::vector<BattleUnitKills*> kills; // Tracks kills
319  int daysWounded; // Tracks how many days the unit was wounded for
320  bool KIA; // Tracks if the soldier was killed in battle
321  bool nikeCross; // Tracks if a soldier killed every alien
322  bool mercyCross; // Tracks if a soldier stunned every alien
323  int woundsHealed; // Tracks how many times a fatal wound was healed by this unit
324  UnitStats delta; // Tracks the increase in unit stats (is not saved, only used during debriefing)
325  int appliedStimulant; // Tracks how many times this soldier applied stimulant
326  int appliedPainKill; // Tracks how many times this soldier applied pain killers
327  int revivedSoldier; // Tracks how many times this soldier revived another unit
328  bool MIA; // Tracks if the soldier was left behind :(
329  int martyr; // Tracks how many kills the soldier landed on the turn of his death
330  int slaveKills; // Tracks how many kills the soldier landed thanks to a mind controlled unit.
331 
333  // Duplicate entry check
334  bool duplicateEntry(UnitStatus status, int id) const
335  {
336  for (std::vector<BattleUnitKills*>::const_iterator i = kills.begin(); i != kills.end(); ++i)
337  {
338  if ((*i)->id == id && (*i)->status == status)
339  {
340  return true;
341  }
342  }
343  return false;
344  }
345 
346  // Friendly fire check
347  bool hasFriendlyFired() const
348  {
349  for (std::vector<BattleUnitKills*>::const_iterator i = kills.begin(); i != kills.end(); ++i)
350  {
351  if ((*i)->faction == FACTION_PLAYER)
352  return true;
353  }
354  return false;
355  }
356 
357  // Load function
358  void load(const YAML::Node& node)
359  {
360  wasUnconcious = node["wasUnconcious"].as<bool>(wasUnconcious);
361  if (const YAML::Node &YAMLkills = node["kills"])
362  {
363  for (YAML::const_iterator i = YAMLkills.begin(); i != YAMLkills.end(); ++i)
364  kills.push_back(new BattleUnitKills(*i));
365  }
366  shotAtCounter = node["shotAtCounter"].as<int>(shotAtCounter);
367  hitCounter = node["hitCounter"].as<int>(hitCounter);
368  shotByFriendlyCounter = node["shotByFriendlyCounter"].as<int>(shotByFriendlyCounter);
369  shotFriendlyCounter = node["shotFriendlyCounter"].as<int>(shotFriendlyCounter);
370  loneSurvivor = node["loneSurvivor"].as<bool>(loneSurvivor);
371  ironMan = node["ironMan"].as<bool>(ironMan);
372  longDistanceHitCounter = node["longDistanceHitCounter"].as<int>(longDistanceHitCounter);
373  lowAccuracyHitCounter = node["lowAccuracyHitCounter"].as<int>(lowAccuracyHitCounter);
374  shotsFiredCounter = node["shotsFiredCounter"].as<int>(shotsFiredCounter);
375  shotsLandedCounter = node["shotsLandedCounter"].as<int>(shotsLandedCounter);
376  nikeCross = node["nikeCross"].as<bool>(nikeCross);
377  mercyCross = node["mercyCross"].as<bool>(mercyCross);
378  woundsHealed = node["woundsHealed"].as<int>(woundsHealed);
379  appliedStimulant = node["appliedStimulant"].as<int>(appliedStimulant);
380  appliedPainKill = node["appliedPainKill"].as<int>(appliedPainKill);
381  revivedSoldier = node["revivedSoldier"].as<int>(revivedSoldier);
382  martyr = node["martyr"].as<int>(martyr);
383  slaveKills = node["slaveKills"].as<int>(slaveKills);
384  }
385 
386  // Save function
387  YAML::Node save() const
388  {
389  YAML::Node node;
390  node["wasUnconcious"] = wasUnconcious;
391  if (!kills.empty())
392  {
393  for (std::vector<BattleUnitKills*>::const_iterator i = kills.begin(); i != kills.end(); ++i)
394  node["kills"].push_back((*i)->save());
395  }
396  if (shotAtCounter) node["shotAtCounter"] = shotAtCounter;
397  if (hitCounter) node["hitCounter"] = hitCounter;
398  if (shotByFriendlyCounter) node["shotByFriendlyCounter"] = shotByFriendlyCounter;
399  if (shotFriendlyCounter) node["shotFriendlyCounter"] = shotFriendlyCounter;
400  if (loneSurvivor) node["loneSurvivor"] = loneSurvivor;
401  if (ironMan) node["ironMan"] = ironMan;
402  if (longDistanceHitCounter) node["longDistanceHitCounter"] = longDistanceHitCounter;
403  if (lowAccuracyHitCounter) node["lowAccuracyHitCounter"] = lowAccuracyHitCounter;
404  if (shotsFiredCounter) node["shotsFiredCounter"] = shotsFiredCounter;
405  if (shotsLandedCounter) node["shotsLandedCounter"] = shotsLandedCounter;
406  if (nikeCross) node["nikeCross"] = nikeCross;
407  if (mercyCross) node["mercyCross"] = mercyCross;
408  if (woundsHealed) node["woundsHealed"] = woundsHealed;
409  if (appliedStimulant) node["appliedStimulant"] = appliedStimulant;
410  if (appliedPainKill) node["appliedPainKill"] = appliedPainKill;
411  if (revivedSoldier) node["revivedSoldier"] = revivedSoldier;
412  if (martyr) node["martyr"] = martyr;
413  if (slaveKills) node["slaveKills"] = slaveKills;
414  return node;
415  }
416 
417  BattleUnitStatistics(const YAML::Node& node) { load(node); }
418  BattleUnitStatistics() : wasUnconcious(false), shotAtCounter(0), hitCounter(0), shotByFriendlyCounter(0), shotFriendlyCounter(0), loneSurvivor(false), ironMan(false), longDistanceHitCounter(0), lowAccuracyHitCounter(0), shotsFiredCounter(0), shotsLandedCounter(0), kills(), daysWounded(0), KIA(false), nikeCross(false), mercyCross(false), woundsHealed(0), appliedStimulant(0), appliedPainKill(0), revivedSoldier(0), MIA(false), martyr(0), slaveKills(0) { }
419  ~BattleUnitStatistics() { }
420 };
421 
422 }
std::wstring getName(bool statstring=false, unsigned int maxLength=20) const
Gets the soldier&#39;s name.
Definition: Soldier.cpp:200
std::wstring name
Variables.
Definition: BattleUnitStatistics.h:34
std::string getRace() const
Gets the alien race type.
Definition: Unit.cpp:159
static std::wstring utf8ToWstr(const std::string &src)
Converts a UTF-8 string to wide-string.
Definition: Language.cpp:217
Soldier * getGeoscapeSoldier() const
Get the geoscape-soldier object.
Definition: BattleUnit.cpp:2583
Container for battle unit kills statistics.
Definition: BattleUnitStatistics.h:31
UnitFaction getOriginalFaction() const
Get this unit&#39;s original faction.
Definition: BattleUnit.cpp:2745
Contains strings used throughout the game for localization.
Definition: Language.h:39
std::string getType() const
Get unit type.
Definition: BattleUnit.cpp:2600
static std::string wstrToUtf8(const std::wstring &src)
Converts a wide-string to UTF-8.
Definition: Language.cpp:118
const LocalizedText & getString(const std::string &id) const
Get a localized text.
Definition: Language.cpp:487
int makeTurnUnique()
Functions.
Definition: BattleUnitStatistics.h:44
std::string getRankString() const
Gets a string version of the soldier&#39;s rank.
Definition: Soldier.cpp:276
This struct holds some plain unit attribute data together.
Definition: Unit.h:30
bool wasUnconcious
Variables.
Definition: BattleUnitStatistics.h:307
std::string getRank() const
Gets the alien rank.
Definition: Unit.cpp:168
Container for battle unit statistics.
Definition: BattleUnitStatistics.h:304
bool duplicateEntry(UnitStatus status, int id) const
Functions.
Definition: BattleUnitStatistics.h:334
Represents a moving unit in the battlescape, player controlled or AI controlled it holds info about i...
Definition: BattleUnit.h:59
Definition: BaseInfoState.cpp:40