/*
* 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 "Craft.h"
#include "../fmath.h"
#include "../Engine/Language.h"
#include "../Engine/RNG.h"
#include "../Mod/RuleCraft.h"
#include "CraftWeapon.h"
#include "../Mod/RuleCraftWeapon.h"
#include "../Mod/Mod.h"
#include "SavedGame.h"
#include "ItemContainer.h"
#include "Soldier.h"
#include "Base.h"
#include "Ufo.h"
#include "Waypoint.h"
#include "MissionSite.h"
#include "AlienBase.h"
#include "Vehicle.h"
#include "../Mod/RuleItem.h"
#include "../Mod/AlienDeployment.h"
#include "../Engine/Logger.h"
namespace OpenXcom
{
/**
* Initializes a craft of the specified type and
* assigns it the latest craft ID available.
* @param rules Pointer to ruleset.
* @param base Pointer to base of origin.
* @param id ID to assign to the craft (0 to not assign).
*/
Craft::Craft(RuleCraft *rules, Base *base, int id) : MovingTarget(), _rules(rules), _base(base), _fuel(0), _damage(0), _interceptionOrder(0), _takeoff(0), _status("STR_READY"), _lowFuel(false), _mission(false), _inBattlescape(false), _inDogfight(false)
{
_items = new ItemContainer();
if (id != 0)
{
_id = id;
}
for (unsigned int i = 0; i < _rules->getWeapons(); ++i)
{
_weapons.push_back(0);
}
if (base != 0)
{
setBase(base);
}
_speedMaxRadian = calculateRadianSpeed(_rules->getMaxSpeed()) * 120;
}
/**
* Delete the contents of the craft from memory.
*/
Craft::~Craft()
{
for (std::vector<CraftWeapon*>::iterator i = _weapons.begin(); i != _weapons.end(); ++i)
{
delete *i;
}
delete _items;
for (std::vector<Vehicle*>::iterator i = _vehicles.begin(); i != _vehicles.end(); ++i)
{
delete *i;
}
}
/**
* Loads the craft from a YAML file.
* @param node YAML node.
* @param mod Mod for the saved game.
* @param save Pointer to the saved game.
*/
void Craft::load(const YAML::Node &node, const Mod *mod, SavedGame *save)
{
MovingTarget::load(node);
_fuel = node["fuel"].as<int>(_fuel);
_damage = node["damage"].as<int>(_damage);
size_t j = 0;
for (YAML::const_iterator i = node["weapons"].begin(); i != node["weapons"].end(); ++i)
{
if (_rules->getWeapons() > j)
{
std::string type = (*i)["type"].as<std::string>();
if (type != "0" && mod->getCraftWeapon(type))
{
CraftWeapon *w = new CraftWeapon(mod->getCraftWeapon(type), 0);
w->load(*i);
_weapons[j] = w;
}
else
{
_weapons[j] = 0;
if (type != "0")
{
Log(LOG_ERROR) << "Failed to load craft weapon " << type;
}
}
j++;
}
}
_items->load(node["items"]);
// Some old saves have bad items, better get rid of them to avoid further bugs
for (std::map<std::string, int>::iterator i = _items->getContents()->begin(); i != _items->getContents()->end();)
{
if (mod->getItem(i->first) == 0)
{
Log(LOG_ERROR) << "Failed to load item " << i->first;
_items->getContents()->erase(i++);
}
else
{
++i;
}
}
for (YAML::const_iterator i = node["vehicles"].begin(); i != node["vehicles"].end(); ++i)
{
std::string type = (*i)["type"].as<std::string>();
if (mod->getItem(type))
{
Vehicle *v = new Vehicle(mod->getItem(type), 0, 4);
v->load(*i);
_vehicles.push_back(v);
}
else
{
Log(LOG_ERROR) << "Failed to load item " << type;
}
}
_status = node["status"].as<std::string>(_status);
_lowFuel = node["lowFuel"].as<bool>(_lowFuel);
_mission = node["mission"].as<bool>(_mission);
_interceptionOrder = node["interceptionOrder"].as<int>(_interceptionOrder);
if (const YAML::Node &dest = node["dest"])
{
std::string type = dest["type"].as<std::string>();
int id = dest["id"].as<int>();
if (type == "STR_BASE")
{
returnToBase();
}
else if (type == "STR_UFO")
{
for (std::vector<Ufo*>::iterator i = save->getUfos()->begin(); i != save->getUfos()->end(); ++i)
{
if ((*i)->getId() == id)
{
setDestination(*i);
break;
}
}
}
else if (type == "STR_WAY_POINT")
{
for (std::vector<Waypoint*>::iterator i = save->getWaypoints()->begin(); i != save->getWaypoints()->end(); ++i)
{
if ((*i)->getId() == id)
{
setDestination(*i);
break;
}
}
}
else
{
// Backwards compatibility
if (type == "STR_ALIEN_TERROR")
type = "STR_TERROR_SITE";
bool found = false;
for (std::vector<MissionSite*>::iterator i = save->getMissionSites()->begin(); i != save->getMissionSites()->end() && !found; ++i)
{
if ((*i)->getId() == id && (*i)->getDeployment()->getMarkerName() == type)
{
setDestination(*i);
found = true;
}
}
for (std::vector<AlienBase*>::iterator i = save->getAlienBases()->begin(); i != save->getAlienBases()->end() && !found; ++i)
{
if ((*i)->getId() == id && (*i)->getDeployment()->getMarkerName() == type)
{
setDestination(*i);
found = true;
}
}
}
}
_takeoff = node["takeoff"].as<int>(_takeoff);
_inBattlescape = node["inBattlescape"].as<bool>(_inBattlescape);
if (_inBattlescape)
setSpeed(0);
}
/**
* Saves the craft to a YAML file.
* @return YAML node.
*/
YAML::Node Craft::save() const
{
YAML::Node node = MovingTarget::save();
node["type"] = _rules->getType();
node["fuel"] = _fuel;
node["damage"] = _damage;
for (std::vector<CraftWeapon*>::const_iterator i = _weapons.begin(); i != _weapons.end(); ++i)
{
YAML::Node subnode;
if (*i != 0)
{
subnode = (*i)->save();
}
else
{
subnode["type"] = "0";
}
node["weapons"].push_back(subnode);
}
node["items"] = _items->save();
for (std::vector<Vehicle*>::const_iterator i = _vehicles.begin(); i != _vehicles.end(); ++i)
{
node["vehicles"].push_back((*i)->save());
}
node["status"] = _status;
if (_lowFuel)
node["lowFuel"] = _lowFuel;
if (_mission)
node["mission"] = _mission;
if (_inBattlescape)
node["inBattlescape"] = _inBattlescape;
if (_interceptionOrder != 0)
node["interceptionOrder"] = _interceptionOrder;
if (_takeoff != 0)
node["takeoff"] = _takeoff;
return node;
}
/**
* Loads a craft unique identifier from a YAML file.
* @param node YAML node.
* @return Unique craft id.
*/
CraftId Craft::loadId(const YAML::Node &node)
{
return std::make_pair(node["type"].as<std::string>(), node["id"].as<int>());
}
/**
* Returns the craft's unique type used for
* savegame purposes.
* @return ID.
*/
std::string Craft::getType() const
{
return _rules->getType();
}
/**
* Returns the ruleset for the craft's type.
* @return Pointer to ruleset.
*/
RuleCraft *Craft::getRules() const
{
return _rules;
}
/**
* Changes the ruleset for the craft's type.
* @param rules Pointer to ruleset.
* @warning ONLY FOR NEW BATTLE USE!
*/
void Craft::changeRules(RuleCraft *rules)
{
_rules = rules;
_weapons.clear();
for (unsigned int i = 0; i < _rules->getWeapons(); ++i)
{
_weapons.push_back(0);
}
}
/**
* Returns the craft's unique default name.
* @param lang Language to get strings from.
* @return Full name.
*/
std::string Craft::getDefaultName(Language *lang) const
{
return lang->getString("STR_CRAFTNAME").arg(lang->getString(getType())).arg(_id);
}
/**
* Returns the globe marker for the craft.
* @return Marker sprite, -1 if none.
*/
int Craft::getMarker() const
{
if (_status != "STR_OUT")
return -1;
else if (_rules->getMarker() == -1)
return 1;
return _rules->getMarker();
}
/**
* Returns the base the craft belongs to.
* @return Pointer to base.
*/
Base *Craft::getBase() const
{
return _base;
}
/**
* Changes the base the craft belongs to.
* @param base Pointer to base.
* @param move Move the craft to the base coordinates.
*/
void Craft::setBase(Base *base, bool move)
{
_base = base;
if (move)
{
_lon = base->getLongitude();
_lat = base->getLatitude();
}
}
/**
* Returns the current status of the craft.
* @return Status string.
*/
std::string Craft::getStatus() const
{
return _status;
}
/**
* Changes the current status of the craft.
* @param status Status string.
*/
void Craft::setStatus(const std::string &status)
{
_status = status;
}
/**
* Returns the current altitude of the craft.
* @return Altitude.
*/
std::string Craft::getAltitude() const
{
Ufo *u = dynamic_cast<Ufo*>(_dest);
if (u && u->getAltitude() != "STR_GROUND")
{
return u->getAltitude();
}
else
{
return "STR_VERY_LOW";
}
}
/**
* Changes the destination the craft is heading to.
* @param dest Pointer to new destination.
*/
void Craft::setDestination(Target *dest)
{
if (_status != "STR_OUT")
{
_takeoff = 60;
}
if (dest == 0)
setSpeed(_rules->getMaxSpeed()/2);
else
setSpeed(_rules->getMaxSpeed());
MovingTarget::setDestination(dest);
}
/**
* Returns the amount of weapons currently
* equipped on this craft.
* @return Number of weapons.
*/
int Craft::getNumWeapons() const
{
if (_rules->getWeapons() == 0)
{
return 0;
}
int total = 0;
for (std::vector<CraftWeapon*>::const_iterator i = _weapons.begin(); i != _weapons.end(); ++i)
{
if ((*i) != 0)
{
total++;
}
}
return total;
}
/**
* Returns the amount of soldiers from a list
* that are currently attached to this craft.
* @return Number of soldiers.
*/
int Craft::getNumSoldiers() const
{
if (_rules->getSoldiers() == 0)
return 0;
int total = 0;
for (std::vector<Soldier*>::iterator i = _base->getSoldiers()->begin(); i != _base->getSoldiers()->end(); ++i)
{
if ((*i)->getCraft() == this)
total++;
}
return total;
}
/**
* Returns the amount of equipment currently
* equipped on this craft.
* @return Number of items.
*/
int Craft::getNumEquipment() const
{
return _items->getTotalQuantity();
}
/**
* Returns the amount of vehicles currently
* contained in this craft.
* @return Number of vehicles.
*/
int Craft::getNumVehicles() const
{
return _vehicles.size();
}
/**
* Returns the list of weapons currently equipped
* in the craft.
* @return Pointer to weapon list.
*/
std::vector<CraftWeapon*> *Craft::getWeapons()
{
return &_weapons;
}
/**
* Returns the list of items in the craft.
* @return Pointer to the item list.
*/
ItemContainer *Craft::getItems()
{
return _items;
}
/**
* Returns the list of vehicles currently equipped
* in the craft.
* @return Pointer to vehicle list.
*/
std::vector<Vehicle*> *Craft::getVehicles()
{
return &_vehicles;
}
/**
* Returns the amount of fuel currently contained
* in this craft.
* @return Amount of fuel.
*/
int Craft::getFuel() const
{
return _fuel;
}
/**
* Changes the amount of fuel currently contained
* in this craft.
* @param fuel Amount of fuel.
*/
void Craft::setFuel(int fuel)
{
_fuel = fuel;
if (_fuel > _rules->getMaxFuel())
{
_fuel = _rules->getMaxFuel();
}
else if (_fuel < 0)
{
_fuel = 0;
}
}
/**
* Returns the ratio between the amount of fuel currently
* contained in this craft and the total it can carry.
* @return Percentage of fuel.
*/
int Craft::getFuelPercentage() const
{
return (int)floor((double)_fuel / _rules->getMaxFuel() * 100.0);
}
/**
* Returns the amount of damage this craft has taken.
* @return Amount of damage.
*/
int Craft::getDamage() const
{
return _damage;
}
/**
* Changes the amount of damage this craft has taken.
* @param damage Amount of damage.
*/
void Craft::setDamage(int damage)
{
_damage = damage;
if (_damage < 0)
{
_damage = 0;
}
}
/**
* Returns the ratio between the amount of damage this
* craft can take and the total it can take before it's
* destroyed.
* @return Percentage of damage.
*/
int Craft::getDamagePercentage() const
{
return (int)floor((double)_damage / _rules->getMaxDamage() * 100);
}
/**
* Returns whether the craft is currently low on fuel
* (only has enough to head back to base).
* @return True if it's low, false otherwise.
*/
bool Craft::getLowFuel() const
{
return _lowFuel;
}
/**
* Changes whether the craft is currently low on fuel
* (only has enough to head back to base).
* @param low True if it's low, false otherwise.
*/
void Craft::setLowFuel(bool low)
{
_lowFuel = low;
}
/**
* Returns whether the craft has just done a ground mission,
* and is forced to return to base.
* @return True if it's returning, false otherwise.
*/
bool Craft::getMissionComplete() const
{
return _mission;
}
/**
* Changes whether the craft has just done a ground mission,
* and is forced to return to base.
* @param mission True if it's returning, false otherwise.
*/
void Craft::setMissionComplete(bool mission)
{
_mission = mission;
}
/**
* Returns the current distance between the craft
* and the base it belongs to.
* @return Distance in radian.
*/
double Craft::getDistanceFromBase() const
{
return getDistance(_base);
}
/**
* Returns the amount of fuel the craft uses up
* while it's on the air, based on its current speed.
* @return Fuel amount.
*/
int Craft::getFuelConsumption() const
{
return getFuelConsumption(_speed);
}
/**
* Returns the amount of fuel the craft uses up
* while it's on the air.
* @param speed Craft speed for estimation.
* @return Fuel amount.
*/
int Craft::getFuelConsumption(int speed) const
{
if (!_rules->getRefuelItem().empty())
return 1;
return (int)floor(speed / 100.0);
}
/**
* Returns the minimum required fuel for the
* craft to make it back to base.
* @return Fuel amount.
*/
int Craft::getFuelLimit() const
{
return getFuelLimit(_base);
}
/**
* Returns the minimum required fuel for the
* craft to go to a base.
* @param base Pointer to target base.
* @return Fuel amount.
*/
int Craft::getFuelLimit(Base *base) const
{
return (int)floor(getFuelConsumption(_rules->getMaxSpeed()) * getDistance(base) / _speedMaxRadian);
}
/**
* Returns the maximum range the craft can travel
* from its origin base on its current fuel.
* @return Range in radians.
*/
double Craft::getBaseRange() const
{
return _fuel / 2.0 / getFuelConsumption(_rules->getMaxSpeed()) * _speedMaxRadian;
}
/**
* Sends the craft back to its origin base.
*/
void Craft::returnToBase()
{
setDestination(_base);
}
/**
* Moves the craft to its destination.
*/
void Craft::think()
{
if (_takeoff == 0)
{
move();
}
else
{
_takeoff--;
resetMeetPoint();
}
if (reachedDestination() && _dest == (Target*)_base)
{
setInterceptionOrder(0); // just to be sure
checkup();
setDestination(0);
setSpeed(0);
_lowFuel = false;
_mission = false;
_takeoff = 0;
}
}
/**
* Checks the condition of all the craft's systems
* to define its new status (eg. when arriving at base).
*/
void Craft::checkup()
{
int available = 0, full = 0;
for (std::vector<CraftWeapon*>::iterator i = _weapons.begin(); i != _weapons.end(); ++i)
{
if ((*i) == 0)
continue;
available++;
if ((*i)->getAmmo() >= (*i)->getRules()->getAmmoMax())
{
full++;
}
else
{
(*i)->setRearming(true);
}
}
if (_damage > 0)
{
_status = "STR_REPAIRS";
}
else if (available != full)
{
_status = "STR_REARMING";
}
else
{
_status = "STR_REFUELLING";
}
}
/**
* Returns if a certain target is detected by the craft's
* radar, taking in account the range and chance.
* @param target Pointer to target to compare.
* @return True if it's detected, False otherwise.
*/
bool Craft::detect(Target *target) const
{
if (_rules->getRadarRange() == 0 || !insideRadarRange(target))
return false;
// backward compatibility with vanilla
if (_rules->getRadarChance() == 100)
return true;
Ufo *u = dynamic_cast<Ufo*>(target);
int chance = _rules->getRadarChance() * (100 + u->getVisibility()) / 100;
return RNG::percent(chance);
}
/**
* Returns if a certain target is inside the craft's
* radar range, taking in account the positions of both.
* @param target Pointer to target to compare.
* @return True if inside radar range.
*/
bool Craft::insideRadarRange(Target *target) const
{
double range = Nautical(_rules->getRadarRange());
return (getDistance(target) <= range);
}
/**
* Consumes the craft's fuel every 10 minutes
* while it's on the air.
*/
void Craft::consumeFuel()
{
setFuel(_fuel - getFuelConsumption());
}
/**
* Repairs the craft's damage every hour
* while it's docked in the base.
*/
void Craft::repair()
{
setDamage(_damage - _rules->getRepairRate());
if (_damage <= 0)
{
_status = "STR_REARMING";
}
}
/**
* Refuels the craft every 30 minutes
* while it's docked in the base.
* @return The item ID missing for refuelling, or "" if none.
*/
std::string Craft::refuel()
{
std::string fuel;
if (_fuel < _rules->getMaxFuel())
{
std::string item = _rules->getRefuelItem();
if (item.empty())
{
setFuel(_fuel + _rules->getRefuelRate());
}
else
{
if (_base->getStorageItems()->getItem(item) > 0)
{
_base->getStorageItems()->removeItem(item);
setFuel(_fuel + _rules->getRefuelRate());
_lowFuel = false;
}
else if (!_lowFuel)
{
fuel = item;
if (_fuel > 0)
{
_status = "STR_READY";
}
else
{
_lowFuel = true;
}
}
}
}
if (_fuel >= _rules->getMaxFuel())
{
_status = "STR_READY";
for (std::vector<CraftWeapon*>::iterator i = _weapons.begin(); i != _weapons.end(); ++i)
{
if (*i && (*i)->isRearming())
{
_status = "STR_REARMING";
break;
}
}
}
return fuel;
}
/**
* Rearms the craft's weapons by adding ammo every hour
* while it's docked in the base.
* @param mod Pointer to mod.
* @return The ammo ID missing for rearming, or "" if none.
*/
std::string Craft::rearm(const Mod *mod)
{
std::string ammo;
for (std::vector<CraftWeapon*>::iterator i = _weapons.begin(); ; ++i)
{
if (i == _weapons.end())
{
_status = "STR_REFUELLING";
break;
}
if (*i != 0 && (*i)->isRearming())
{
std::string clip = (*i)->getRules()->getClipItem();
int available = _base->getStorageItems()->getItem(clip);
if (clip.empty())
{
(*i)->rearm(0, 0);
}
else if (available > 0)
{
int used = (*i)->rearm(available, mod->getItem(clip)->getClipSize());
if (used == available && (*i)->isRearming())
{
ammo = clip;
(*i)->setRearming(false);
}
_base->getStorageItems()->removeItem(clip, used);
}
else
{
ammo = clip;
(*i)->setRearming(false);
}
break;
}
}
return ammo;
}
/**
* Returns the craft's battlescape status.
* @return Is the craft currently in battle?
*/
bool Craft::isInBattlescape() const
{
return _inBattlescape;
}
/**
* Changes the craft's battlescape status.
* @param inbattle True if it's in battle, False otherwise.
*/
void Craft::setInBattlescape(bool inbattle)
{
if (inbattle)
setSpeed(0);
_inBattlescape = inbattle;
}
/// Returns the craft destroyed status.
/**
* If the amount of damage the craft take
* is more than it's health it will be
* destroyed.
* @return Is the craft destroyed?
*/
bool Craft::isDestroyed() const
{
return (_damage >= _rules->getMaxDamage());
}
/**
* Returns the amount of space available for
* soldiers and vehicles.
* @return Space available.
*/
int Craft::getSpaceAvailable() const
{
return _rules->getSoldiers() - getSpaceUsed();
}
/**
* Returns the amount of space in use by
* soldiers and vehicles.
* @return Space used.
*/
int Craft::getSpaceUsed() const
{
int vehicleSpaceUsed = 0;
for (std::vector<Vehicle*>::const_iterator i = _vehicles.begin(); i != _vehicles.end(); ++i)
{
vehicleSpaceUsed += (*i)->getSize();
}
return getNumSoldiers() + vehicleSpaceUsed;
}
/**
* Returns the total amount of vehicles of
* a certain type stored in the craft.
* @param vehicle Vehicle type.
* @return Number of vehicles.
*/
int Craft::getVehicleCount(const std::string &vehicle) const
{
int total = 0;
for (std::vector<Vehicle*>::const_iterator i = _vehicles.begin(); i != _vehicles.end(); ++i)
{
if ((*i)->getRules()->getType() == vehicle)
{
total++;
}
}
return total;
}
/**
* Returns the craft's dogfight status.
* @return Is the craft ion a dogfight?
*/
bool Craft::isInDogfight() const
{
return _inDogfight;
}
/**
* Changes the craft's dogfight status.
* @param inDogfight True if it's in dogfight, False otherwise.
*/
void Craft::setInDogfight(bool inDogfight)
{
_inDogfight = inDogfight;
}
/**
* Sets interception order (first craft to leave the base gets 1, second 2, etc.).
* @param order Interception order.
*/
void Craft::setInterceptionOrder(const int order)
{
_interceptionOrder = order;
}
/**
* Gets interception order.
* @return Interception order.
*/
int Craft::getInterceptionOrder() const
{
return _interceptionOrder;
}
/**
* Gets the craft's unique id.
* @return A tuple of the craft's type and per-type id.
*/
CraftId Craft::getUniqueId() const
{
return std::make_pair(_rules->getType(), _id);
}
/**
* Unloads all the craft contents to the base.
* @param mod Pointer to mod.
*/
void Craft::unload(const Mod *mod)
{
// Remove weapons
for (std::vector<CraftWeapon*>::iterator w = _weapons.begin(); w != _weapons.end(); ++w)
{
if ((*w) != 0)
{
_base->getStorageItems()->addItem((*w)->getRules()->getLauncherItem());
_base->getStorageItems()->addItem((*w)->getRules()->getClipItem(), (*w)->getClipsLoaded(mod));
delete (*w);
(*w) = 0;
}
}
// Remove items
for (std::map<std::string, int>::iterator it = _items->getContents()->begin(); it != _items->getContents()->end(); ++it)
{
_base->getStorageItems()->addItem(it->first, it->second);
}
// Remove vehicles
for (std::vector<Vehicle*>::iterator v = _vehicles.begin(); v != _vehicles.end(); ++v)
{
_base->getStorageItems()->addItem((*v)->getRules()->getType());
if (!(*v)->getRules()->getCompatibleAmmo()->empty())
{
_base->getStorageItems()->addItem((*v)->getRules()->getCompatibleAmmo()->front(), (*v)->getAmmo());
}
delete (*v);
(*v) = 0;
}
_vehicles.clear();
// Remove soldiers
for (std::vector<Soldier*>::iterator s = _base->getSoldiers()->begin(); s != _base->getSoldiers()->end(); ++s)
{
if ((*s)->getCraft() == this)
{
(*s)->setCraft(0);
}
}
}
/**
* Checks if an item can be reused by the craft and
* updates its status appropriately.
* @param item Item ID.
*/
void Craft::reuseItem(const std::string& item)
{
if (_status != "STR_READY")
return;
// Check if it's ammo to reload the craft
for (std::vector<CraftWeapon*>::iterator w = _weapons.begin(); w != _weapons.end(); ++w)
{
if ((*w) != 0 && item == (*w)->getRules()->getClipItem() && (*w)->getAmmo() < (*w)->getRules()->getAmmoMax())
{
(*w)->setRearming(true);
_status = "STR_REARMING";
}
}
// Check if it's fuel to refuel the craft
if (item == _rules->getRefuelItem() && _fuel < _rules->getMaxFuel())
_status = "STR_REFUELLING";
}
}
↑ V820 The 'item' variable is not used after copying. Copying can be replaced with move/swap for optimization.
↑ V820 The 'clip' variable is not used after copying. Copying can be replaced with move/swap for optimization.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the '_items->getContents()' expression repeatedly.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the '_base->getStorageItems()' expression repeatedly.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the '(* v)->getRules()' expression repeatedly.