#pragma once
/*
* Copyright 2010-2016 OpenXcom Developers.
*
* This file is part of OpenXcom.
*
* OpenXcom is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenXcom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string>
#include <sstream>
#include <yaml-cpp/yaml.h>
#include "BattleUnit.h"
#include "../Engine/Language.h"
namespace OpenXcom
{
/**
* Container for battle unit kills statistics.
*/
struct BattleUnitKills
{
// Variables
std::string name;
std::string type, rank, race, weapon, weaponAmmo;
UnitFaction faction;
UnitStatus status;
int mission, turn, id;
UnitSide side;
UnitBodyPart bodypart;
// Functions
/// Make turn unique across all kills
int makeTurnUnique()
{
return turn += mission * 300; // Maintains divisibility by 3 as well
}
/// Check to see if turn was on HOSTILE side
bool hostileTurn() const
{
if ((turn - 1) % 3 == 0) return true;
return false;
}
/// Make turn unique across mission
void setTurn(int unitTurn, UnitFaction unitFaction)
{
turn = unitTurn * 3 + (int)unitFaction;
}
/// Load
void load(const YAML::Node &node)
{
if (const YAML::Node n = node["name"])
{
name = n.as<std::string>();
}
type = node["type"].as<std::string>(type);
rank = node["rank"].as<std::string>(rank);
race = node["race"].as<std::string>(race);
weapon = node["weapon"].as<std::string>(weapon);
weaponAmmo = node["weaponAmmo"].as<std::string>(weaponAmmo);
status = (UnitStatus)node["status"].as<int>();
faction = (UnitFaction)node["faction"].as<int>();
mission = node["mission"].as<int>(mission);
turn = node["turn"].as<int>(turn);
side = (UnitSide)node["side"].as<int>();
bodypart = (UnitBodyPart)node["bodypart"].as<int>();
id = node["id"].as<int>(id);
}
/// Save
YAML::Node save() const
{
YAML::Node node;
if (!name.empty())
node["name"] = name;
if (!type.empty())
node["type"] = type;
node["rank"] = rank;
node["race"] = race;
node["weapon"] = weapon;
node["weaponAmmo"] = weaponAmmo;
node["status"] = (int)status;
node["faction"] = (int)faction;
node["mission"] = mission;
node["turn"] = turn;
node["side"] = (int)side;
node["bodypart"] = (int)bodypart;
node["id"] = id;
return node;
}
/// Convert kill Status to string.
std::string getKillStatusString() const
{
switch (status)
{
case STATUS_DEAD: return "STR_KILLED";
case STATUS_UNCONSCIOUS: return "STR_STUNNED";
case STATUS_PANICKING: return "STR_PANICKED";
case STATUS_TURNING: return "STR_MINDCONTROLLED";
default: return "status error";
}
}
/// Convert victim Status to string.
std::string getUnitStatusString() const
{
switch (status)
{
case STATUS_DEAD: return "STATUS_DEAD";
case STATUS_UNCONSCIOUS: return "STATUS_UNCONSCIOUS";
case STATUS_PANICKING: return "STATUS_PANICKING";
case STATUS_TURNING: return "STATUS_TURNING";
default: return "status error";
}
}
/// Convert victim Faction to string.
std::string getUnitFactionString() const
{
switch (faction)
{
case FACTION_PLAYER: return "FACTION_PLAYER";
case FACTION_HOSTILE: return "FACTION_HOSTILE";
case FACTION_NEUTRAL: return "FACTION_NEUTRAL";
default: return "faction error";
}
}
/// Convert victim Side to string.
std::string getUnitSideString() const
{
switch (side)
{
case SIDE_FRONT: return "SIDE_FRONT";
case SIDE_LEFT: return "SIDE_LEFT";
case SIDE_RIGHT: return "SIDE_RIGHT";
case SIDE_REAR: return "SIDE_REAR";
case SIDE_UNDER: return "SIDE_UNDER";
default: return "side error";
}
}
/// Convert victim Body part to string.
std::string getUnitBodyPartString() const
{
switch (bodypart)
{
case BODYPART_HEAD: return "BODYPART_HEAD";
case BODYPART_TORSO: return "BODYPART_TORSO";
case BODYPART_RIGHTARM: return "BODYPART_RIGHTARM";
case BODYPART_LEFTARM: return "BODYPART_LEFTARM";
case BODYPART_RIGHTLEG: return "BODYPART_RIGHTLEG";
case BODYPART_LEFTLEG: return "BODYPART_LEFTLEG";
default: return "body part error";
}
}
/// Get human-readable victim name.
std::string getUnitName(Language *lang) const
{
if (!name.empty())
{
return name;
}
else if (!type.empty())
{
return lang->getString(type);
}
else
{
std::ostringstream ss;
ss << lang->getString(race) << " " << lang->getString(rank);
return ss.str();
}
}
/// Decide victim name, race and rank.
void setUnitStats(BattleUnit *unit)
{
name = "";
type = "";
if (unit->getGeoscapeSoldier())
{
name = unit->getGeoscapeSoldier()->getName();
}
else
{
type = unit->getType();
}
if (unit->getOriginalFaction() == FACTION_PLAYER)
{
// Soldiers
if (unit->getGeoscapeSoldier())
{
if (!unit->getGeoscapeSoldier()->getRankString().empty())
{
rank = unit->getGeoscapeSoldier()->getRankString();
}
else
{
rank = "STR_SOLDIER";
}
if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRace().empty())
{
race = unit->getUnitRules()->getRace();
}
else
{
race = "STR_FRIENDLY";
}
}
// HWPs
else
{
if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRank().empty())
{
rank = unit->getUnitRules()->getRank();
}
else
{
rank = "STR_HWPS";
}
if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRace().empty())
{
race = unit->getUnitRules()->getRace();
}
else
{
race = "STR_FRIENDLY";
}
}
}
// Aliens
else if (unit->getOriginalFaction() == FACTION_HOSTILE)
{
if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRank().empty())
{
rank = unit->getUnitRules()->getRank();
}
else
{
rank = "STR_LIVE_SOLDIER";
}
if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRace().empty())
{
race = unit->getUnitRules()->getRace();
}
else
{
race = "STR_HOSTILE";
}
}
// Civilians
else if (unit->getOriginalFaction() == FACTION_NEUTRAL)
{
if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRank().empty())
{
rank = unit->getUnitRules()->getRank();
}
else
{
rank = "STR_CIVILIAN";
}
if (unit->getUnitRules() != 0 && !unit->getUnitRules()->getRace().empty())
{
race = unit->getUnitRules()->getRace();
}
else
{
race = "STR_NEUTRAL";
}
}
// Error
else
{
rank = "STR_UNKNOWN";
race = "STR_UNKNOWN";
}
}
BattleUnitKills(const YAML::Node& node) { load(node); }
BattleUnitKills(): faction(FACTION_HOSTILE), status(STATUS_IGNORE_ME), mission(0), turn(0), id(0), side(SIDE_FRONT), bodypart(BODYPART_HEAD) { }
~BattleUnitKills() { }
};
/**
* Container for battle unit statistics.
*/
struct BattleUnitStatistics
{
// Variables
bool wasUnconcious; ///< Tracks if the soldier fell unconcious
int shotAtCounter; ///< Tracks how many times the unit was shot at
int hitCounter; ///< Tracks how many times the unit was hit
int shotByFriendlyCounter; ///< Tracks how many times the unit was hit by a friendly
int shotFriendlyCounter; ///< Tracks how many times the unit was hit a friendly
bool loneSurvivor; ///< Tracks if the soldier was the only survivor
bool ironMan; ///< Tracks if the soldier was the only soldier on the mission
int longDistanceHitCounter; ///< Tracks how many long distance shots were landed
int lowAccuracyHitCounter; ///< Tracks how many times the unit landed a low probability shot
int shotsFiredCounter; ///< Tracks how many times a unit has shot
int shotsLandedCounter; ///< Tracks how many times a unit has hit his target
std::vector<BattleUnitKills*> kills; ///< Tracks kills
int daysWounded; ///< Tracks how many days the unit was wounded for
bool KIA; ///< Tracks if the soldier was killed in battle
bool nikeCross; ///< Tracks if a soldier killed every alien or killed and stunned every alien
bool mercyCross; ///< Tracks if a soldier stunned every alien
int woundsHealed; ///< Tracks how many times a fatal wound was healed by this unit
UnitStats delta; ///< Tracks the increase in unit stats (is not saved, only used during debriefing)
int appliedStimulant; ///< Tracks how many times this soldier applied stimulant
int appliedPainKill; ///< Tracks how many times this soldier applied pain killers
int revivedSoldier; ///< Tracks how many times this soldier revived another soldier
int revivedHostile; ///< Tracks how many times this soldier revived another hostile
int revivedNeutral; ///< Tracks how many times this soldier revived another civilian
bool MIA; ///< Tracks if the soldier was left behind :(
int martyr; ///< Tracks how many kills the soldier landed on the turn of his death
int slaveKills; ///< Tracks how many kills the soldier landed thanks to a mind controlled unit.
// Functions
/// Duplicate entry check
bool duplicateEntry(UnitStatus status, int id) const
{
for (std::vector<BattleUnitKills*>::const_iterator i = kills.begin(); i != kills.end(); ++i)
{
if ((*i)->id == id && (*i)->status == status)
{
return true;
}
}
return false;
}
/// Friendly fire check
bool hasFriendlyFired() const
{
for (std::vector<BattleUnitKills*>::const_iterator i = kills.begin(); i != kills.end(); ++i)
{
if ((*i)->faction == FACTION_PLAYER)
return true;
}
return false;
}
/// Load function
void load(const YAML::Node& node)
{
wasUnconcious = node["wasUnconcious"].as<bool>(wasUnconcious);
if (const YAML::Node &YAMLkills = node["kills"])
{
for (YAML::const_iterator i = YAMLkills.begin(); i != YAMLkills.end(); ++i)
kills.push_back(new BattleUnitKills(*i));
}
shotAtCounter = node["shotAtCounter"].as<int>(shotAtCounter);
hitCounter = node["hitCounter"].as<int>(hitCounter);
shotByFriendlyCounter = node["shotByFriendlyCounter"].as<int>(shotByFriendlyCounter);
shotFriendlyCounter = node["shotFriendlyCounter"].as<int>(shotFriendlyCounter);
loneSurvivor = node["loneSurvivor"].as<bool>(loneSurvivor);
ironMan = node["ironMan"].as<bool>(ironMan);
longDistanceHitCounter = node["longDistanceHitCounter"].as<int>(longDistanceHitCounter);
lowAccuracyHitCounter = node["lowAccuracyHitCounter"].as<int>(lowAccuracyHitCounter);
shotsFiredCounter = node["shotsFiredCounter"].as<int>(shotsFiredCounter);
shotsLandedCounter = node["shotsLandedCounter"].as<int>(shotsLandedCounter);
nikeCross = node["nikeCross"].as<bool>(nikeCross);
mercyCross = node["mercyCross"].as<bool>(mercyCross);
woundsHealed = node["woundsHealed"].as<int>(woundsHealed);
appliedStimulant = node["appliedStimulant"].as<int>(appliedStimulant);
appliedPainKill = node["appliedPainKill"].as<int>(appliedPainKill);
revivedSoldier = node["revivedSoldier"].as<int>(revivedSoldier);
revivedHostile = node["revivedHostile"].as<int>(revivedHostile);
revivedNeutral = node["revivedNeutral"].as<int>(revivedNeutral);
martyr = node["martyr"].as<int>(martyr);
slaveKills = node["slaveKills"].as<int>(slaveKills);
}
/// Save function
YAML::Node save() const
{
YAML::Node node;
node["wasUnconcious"] = wasUnconcious;
if (!kills.empty())
{
for (std::vector<BattleUnitKills*>::const_iterator i = kills.begin(); i != kills.end(); ++i)
node["kills"].push_back((*i)->save());
}
if (shotAtCounter) node["shotAtCounter"] = shotAtCounter;
if (hitCounter) node["hitCounter"] = hitCounter;
if (shotByFriendlyCounter) node["shotByFriendlyCounter"] = shotByFriendlyCounter;
if (shotFriendlyCounter) node["shotFriendlyCounter"] = shotFriendlyCounter;
if (loneSurvivor) node["loneSurvivor"] = loneSurvivor;
if (ironMan) node["ironMan"] = ironMan;
if (longDistanceHitCounter) node["longDistanceHitCounter"] = longDistanceHitCounter;
if (lowAccuracyHitCounter) node["lowAccuracyHitCounter"] = lowAccuracyHitCounter;
if (shotsFiredCounter) node["shotsFiredCounter"] = shotsFiredCounter;
if (shotsLandedCounter) node["shotsLandedCounter"] = shotsLandedCounter;
if (nikeCross) node["nikeCross"] = nikeCross;
if (mercyCross) node["mercyCross"] = mercyCross;
if (woundsHealed) node["woundsHealed"] = woundsHealed;
if (appliedStimulant) node["appliedStimulant"] = appliedStimulant;
if (appliedPainKill) node["appliedPainKill"] = appliedPainKill;
if (revivedSoldier) node["revivedSoldier"] = revivedSoldier;
if (revivedHostile) node["revivedHostile"] = revivedHostile;
if (revivedNeutral) node["revivedNeutral"] = revivedNeutral;
if (martyr) node["martyr"] = martyr;
if (slaveKills) node["slaveKills"] = slaveKills;
return node;
}
BattleUnitStatistics(const YAML::Node& node) { load(node); }
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), revivedHostile(0), revivedNeutral(0), MIA(false), martyr(0), slaveKills(0) { }
~BattleUnitStatistics() { }
};
}
↑ V832 It's better to use '= default;' syntax instead of empty destructor body.
↑ V832 It's better to use '= default;' syntax instead of empty destructor body.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the 'unit->getUnitRules()' expression repeatedly.
↑ V815 Decreased performance. Consider replacing the expression 'name = ""' with 'name.clear()'.
↑ V815 Decreased performance. Consider replacing the expression 'type = ""' with 'type.clear()'.