Skip to content

Commit

Permalink
Allowed soldier groups per craft
Browse files Browse the repository at this point in the history
  • Loading branch information
MeridianOXC committed Feb 10, 2024
1 parent 4d58a92 commit 3743d8e
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 22 deletions.
2 changes: 2 additions & 0 deletions bin/common/Language/OXCE/en-GB.yml
Expand Up @@ -52,6 +52,8 @@ en-GB:
STR_BOLD: "Bold (+50%)"
STR_VERY_BOLD: "Very bold (+100%)"
#CraftSoldiersState.cpp
STR_SOLDIER_GROUP_NOT_ALLOWED: "This soldier type is not allowed onboard."
STR_SOLDIER_GROUP_NOT_SAME: "This soldier type is not compatible with other soldiers onboard."
STR_CRAFT_DEPLOYMENT_PREVIEW: "Preview"
STR_CRAFT_DEPLOYMENT_PREVIEW_SAVED: "Preview *"
STR_CRAFT_DEPLOYMENT_PREVIEW_BRIEFING: "This feature allows you to preview and customise the deployment of your units at the beginning of a battle. You have unlimited movement. Press and hold 'Alt' to highlight valid unit positions. Click on the 'Abort Mission' icon to save a custom deployment. Click on the 'End Turn' icon to exit the preview.{NEWLINE}Note that adding any new soldiers or vehicles to the craft automatically invalidates the saved custom deployment. Presence of a custom deployment is indicated by an asterisk on the 'Preview' button."
Expand Down
2 changes: 2 additions & 0 deletions bin/common/Language/OXCE/en-US.yml
Expand Up @@ -52,6 +52,8 @@ en-US:
STR_BOLD: "Bold (+50%)"
STR_VERY_BOLD: "Very bold (+100%)"
#CraftSoldiersState.cpp
STR_SOLDIER_GROUP_NOT_ALLOWED: "This soldier type is not allowed onboard."
STR_SOLDIER_GROUP_NOT_SAME: "This soldier type is not compatible with other soldiers onboard."
STR_CRAFT_DEPLOYMENT_PREVIEW: "Preview"
STR_CRAFT_DEPLOYMENT_PREVIEW_SAVED: "Preview *"
STR_CRAFT_DEPLOYMENT_PREVIEW_BRIEFING: "This feature allows you to preview and customize the deployment of your units at the beginning of a battle. You have unlimited movement. Press and hold 'Alt' to highlight valid unit positions. Click on the 'Abort Mission' icon to save a custom deployment. Click on the 'End Turn' icon to exit the preview.{NEWLINE}Note that adding any new soldiers or vehicles to the craft automatically invalidates the saved custom deployment. Presence of a custom deployment is indicated by an asterisk on the 'Preview' button."
Expand Down
2 changes: 2 additions & 0 deletions bin/common/Language/Technical/en-GB.yml
Expand Up @@ -411,6 +411,8 @@ en-GB:
maxUnitsLimit: "Max cargo space"
pilots: "Cockpit size"
vehicles: "HWP capacity"
onlyOneSoldierGroupAllowed: "Only one soldier group allowed?"
allowedSoldierGroups: "Allowed soldier groups"
maxHWPUnitsLimit: "Max HWP capacity"
maxSmallSoldiers: "Max small soldiers"
maxLargeSoldiers: "Max large soldiers"
Expand Down
2 changes: 2 additions & 0 deletions bin/common/Language/Technical/en-US.yml
Expand Up @@ -411,6 +411,8 @@ en-US:
maxUnitsLimit: "Max cargo space"
pilots: "Cockpit size"
vehicles: "HWP capacity"
onlyOneSoldierGroupAllowed: "Only one soldier group allowed?"
allowedSoldierGroups: "Allowed soldier groups"
maxHWPUnitsLimit: "Max HWP capacity"
maxSmallSoldiers: "Max small soldiers"
maxLargeSoldiers: "Max large soldiers"
Expand Down
11 changes: 10 additions & 1 deletion src/Basescape/CraftArmorState.cpp
Expand Up @@ -472,12 +472,21 @@ void CraftArmorState::lstSoldiersClick(Action *action)
else if (s->hasFullHealth())
{
int space = c->getSpaceAvailable();
if (c->validateAddingSoldier(space, s))
CraftPlacementErrors err = c->validateAddingSoldier(space, s);
if (err == CPE_None)
{
s->setCraftAndMoveEquipment(c, _base, _game->getSavedGame()->getMonthsPassed() == -1, true);
_lstSoldiers->setCellText(_lstSoldiers->getSelectedRow(), 1, c->getName(_game->getLanguage()));
_lstSoldiers->setRowColor(_lstSoldiers->getSelectedRow(), _lstSoldiers->getSecondaryColor());
}
else if (err == CPE_SoldierGroupNotAllowed)
{
_game->pushState(new ErrorMessageState(tr("STR_SOLDIER_GROUP_NOT_ALLOWED"), _palette, _game->getMod()->getInterface("soldierInfo")->getElement("errorMessage")->color, "BACK01.SCR", _game->getMod()->getInterface("soldierInfo")->getElement("errorPalette")->color));
}
else if (err == CPE_SoldierGroupNotSame)
{
_game->pushState(new ErrorMessageState(tr("STR_SOLDIER_GROUP_NOT_SAME"), _palette, _game->getMod()->getInterface("soldierInfo")->getElement("errorMessage")->color, "BACK01.SCR", _game->getMod()->getInterface("soldierInfo")->getElement("errorPalette")->color));
}
else if (space > 0)
{
_game->pushState(new ErrorMessageState(tr("STR_NOT_ENOUGH_CRAFT_SPACE"), _palette, _game->getMod()->getInterface("soldierInfo")->getElement("errorMessage")->color, "BACK01.SCR", _game->getMod()->getInterface("soldierInfo")->getElement("errorPalette")->color));
Expand Down
11 changes: 10 additions & 1 deletion src/Basescape/CraftSoldiersState.cpp
Expand Up @@ -525,7 +525,8 @@ void CraftSoldiersState::lstSoldiersClick(Action *action)
else if (s->hasFullHealth())
{
int space = c->getSpaceAvailable();
if (c->validateAddingSoldier(space, s))
CraftPlacementErrors err = c->validateAddingSoldier(space, s);
if (err == CPE_None)
{
s->setCraftAndMoveEquipment(c, _base, _game->getSavedGame()->getMonthsPassed() == -1, true);
_lstSoldiers->setCellText(row, 2, c->getName(_game->getLanguage()));
Expand All @@ -534,6 +535,14 @@ void CraftSoldiersState::lstSoldiersClick(Action *action)
// update the label to indicate absence of a saved craft deployment
_btnPreview->setText(tr("STR_CRAFT_DEPLOYMENT_PREVIEW"));
}
else if (err == CPE_SoldierGroupNotAllowed)
{
_game->pushState(new ErrorMessageState(tr("STR_SOLDIER_GROUP_NOT_ALLOWED"), _palette, _game->getMod()->getInterface("soldierInfo")->getElement("errorMessage")->color, "BACK01.SCR", _game->getMod()->getInterface("soldierInfo")->getElement("errorPalette")->color));
}
else if (err == CPE_SoldierGroupNotSame)
{
_game->pushState(new ErrorMessageState(tr("STR_SOLDIER_GROUP_NOT_SAME"), _palette, _game->getMod()->getInterface("soldierInfo")->getElement("errorMessage")->color, "BACK01.SCR", _game->getMod()->getInterface("soldierInfo")->getElement("errorPalette")->color));
}
else if (space > 0)
{
_game->pushState(new ErrorMessageState(tr("STR_NOT_ENOUGH_CRAFT_SPACE"), _palette, _game->getMod()->getInterface("soldierInfo")->getElement("errorMessage")->color, "BACK01.SCR", _game->getMod()->getInterface("soldierInfo")->getElement("errorPalette")->color));
Expand Down
8 changes: 5 additions & 3 deletions src/Mod/Mod.cpp
Expand Up @@ -3849,12 +3849,13 @@ SavedGame *Mod::newSave(GameDifficulty diff) const
Craft *found = 0;
for (auto* craft : *base->getCrafts())
{
if (!found && craft->getRules()->getAllowLanding() && craft->getSpaceAvailable() > 0)
CraftPlacementErrors err = craft->validateAddingSoldier(craft->getSpaceAvailable(), soldier);
if (!found && craft->getRules()->getAllowLanding() && err == CPE_None)
{
// Remember transporter as fall-back, but search further for interceptors
found = craft;
}
if (!craft->getRules()->getAllowLanding() && craft->getSpaceUsed() < craft->getRules()->getPilots())
if (!craft->getRules()->getAllowLanding() && err == CPE_None && craft->getSpaceUsed() < craft->getRules()->getPilots())
{
// Fill interceptors with minimum amount of pilots necessary
found = craft;
Expand All @@ -3867,7 +3868,8 @@ SavedGame *Mod::newSave(GameDifficulty diff) const
Craft *found = 0;
for (auto* craft : *base->getCrafts())
{
if (craft->getRules()->getAllowLanding() && craft->getSpaceAvailable() > 0)
CraftPlacementErrors err = craft->validateAddingSoldier(craft->getSpaceAvailable(), soldier);
if (craft->getRules()->getAllowLanding() && err == CPE_None)
{
// First available transporter will do
found = craft;
Expand Down
9 changes: 4 additions & 5 deletions src/Mod/RuleCraft.cpp
Expand Up @@ -40,7 +40,7 @@ RuleCraft::RuleCraft(const std::string &type, int listOrder) :
_monthlyBuyLimit(0), _costBuy(0), _costRent(0), _costSell(0), _repairRate(1), _refuelRate(1),
_transferTime(24), _score(0), _battlescapeTerrainData(0), _maxSkinIndex(0),
_keepCraftAfterFailedMission(false), _allowLanding(true), _spacecraft(false), _notifyWhenRefueled(false), _autoPatrol(false), _undetectable(false),
_listOrder(listOrder), _maxAltitude(-1), _defaultAltitude("STR_VERY_LOW"), _stats(),
_listOrder(listOrder), _maxAltitude(-1), _defaultAltitude("STR_VERY_LOW"), _onlyOneSoldierGroupAllowed(false), _stats(),
_shieldRechargeAtBase(1000),
_mapVisible(true), _forceShowInMonthlyCosts(false), _useAllStartTiles(false)
{
Expand Down Expand Up @@ -141,10 +141,9 @@ void RuleCraft::load(const YAML::Node &node, Mod *mod, const ModScript &parsers)
{
_craftInventoryTile = craftInventoryTile.as<std::vector<int> >(_craftInventoryTile);
}
if (const YAML::Node& craftGroups = node["groups"])
{
_groups = craftGroups.as<std::vector<int> >(_groups);
}
mod->loadUnorderedInts(_type, _groups, node["groups"]);
mod->loadUnorderedInts(_type, _allowedSoldierGroups, node["allowedSoldierGroups"]);
_onlyOneSoldierGroupAllowed = node["onlyOneSoldierGroupAllowed"].as<bool>(_onlyOneSoldierGroupAllowed);
_maxSkinIndex = node["maxSkinIndex"].as<int>(_maxSkinIndex);
_deployment = node["deployment"].as< RuleCraftDeployment >(_deployment);
_keepCraftAfterFailedMission = node["keepCraftAfterFailedMission"].as<bool>(_keepCraftAfterFailedMission);
Expand Down
6 changes: 6 additions & 0 deletions src/Mod/RuleCraft.h
Expand Up @@ -210,6 +210,8 @@ class RuleCraft
RuleCraftDeployment _deployment;
std::vector<int> _craftInventoryTile;
std::vector<int> _groups;
std::vector<int> _allowedSoldierGroups;
bool _onlyOneSoldierGroupAllowed;
RuleCraftStats _stats;
int _shieldRechargeAtBase;
bool _mapVisible, _forceShowInMonthlyCosts;
Expand Down Expand Up @@ -333,6 +335,10 @@ class RuleCraft
const std::vector<int> &getCraftInventoryTile() const;
/// Gets the craft groups (used in map scripts).
const std::vector<int>& getGroups() const { return _groups; }
/// Gets the list of allowed soldier groups.
const std::vector<int>& getAllowedSoldierGroups() const { return _allowedSoldierGroups; }
/// Does this craft allow soldiers of the same group only?
bool isOnlyOneSoldierGroupAllowed() const { return _onlyOneSoldierGroupAllowed; }
/// Gets the item limit for this craft.
int getMaxItems() const;
/// Gets the item storage space limit for this craft.
Expand Down
3 changes: 2 additions & 1 deletion src/Mod/RuleSoldier.cpp
Expand Up @@ -38,7 +38,7 @@ namespace OpenXcom
* type of soldier.
* @param type String defining the type.
*/
RuleSoldier::RuleSoldier(const std::string &type, int listOrder) : _type(type), _listOrder(listOrder), _armor(nullptr), _specWeapon(nullptr),
RuleSoldier::RuleSoldier(const std::string &type, int listOrder) : _type(type), _group(0), _listOrder(listOrder), _armor(nullptr), _specWeapon(nullptr),
_monthlyBuyLimit(0), _costBuy(0), _costSalary(0),
_costSalarySquaddie(0), _costSalarySergeant(0), _costSalaryCaptain(0), _costSalaryColonel(0), _costSalaryCommander(0),
_standHeight(0), _kneelHeight(0), _floatHeight(0), _femaleFrequency(50), _value(20), _transferTime(0), _moraleLossWhenKilled(100),
Expand Down Expand Up @@ -182,6 +182,7 @@ void RuleSoldier::load(const YAML::Node &node, Mod *mod, const ModScript &parser

mod->loadNames(_type, _skillNames, node["skills"]);

_group = node["group"].as<int>(_group);
_listOrder = node["listOrder"].as<int>(_listOrder);

_scriptValues.load(node, parsers.getShared());
Expand Down
3 changes: 3 additions & 0 deletions src/Mod/RuleSoldier.h
Expand Up @@ -63,6 +63,7 @@ class RuleSoldier

private:
std::string _type;
int _group;
int _listOrder;
std::vector<std::string> _requires;
RuleBaseFacilityFunctions _requiresBuyBaseFunc;
Expand Down Expand Up @@ -109,6 +110,8 @@ class RuleSoldier
void afterLoad(const Mod* mod);
/// Gets the soldier's type.
const std::string& getType() const;
/// Gets the soldier type group.
int getGroup() const { return _group; }
/// Gets whether or not the soldier type should be displayed in the inventory.
bool getShowTypeInInventory() const { return _showTypeInInventory; }
/// Gets the list/sort order of the soldier's type.
Expand Down
43 changes: 33 additions & 10 deletions src/Savegame/Craft.cpp
Expand Up @@ -2175,45 +2175,68 @@ bool Craft::validateArmorChange(int sizeFrom, int sizeTo) const

/**
* Validates craft space and craft constraints on adding soldier to a craft.
* @return True, if adding a soldier is allowed.
*/
bool Craft::validateAddingSoldier(int space, const Soldier* s) const
CraftPlacementErrors Craft::validateAddingSoldier(int space, const Soldier* s) const
{
if (space < s->getArmor()->getTotalSize())
{
return false;
return CPE_NotEnoughSpace;
}
if (_rules->getMaxSoldiers() > -1 && getNumTotalSoldiers() >= _rules->getMaxSoldiers())
{
return false;
return CPE_TooManySoldiers;
}
if (s->getArmor()->getSize() == 1)
{
if (_rules->getMaxSmallSoldiers() > -1 && getNumSmallSoldiers() >= _rules->getMaxSmallSoldiers())
{
return false;
return CPE_TooManySmallSoldiers;
}
if (_rules->getMaxSmallUnits() > -1 && getNumSmallUnits() >= _rules->getMaxSmallUnits())
{
return false;
return CPE_TooManySmallUnits;
}
}
else // armorSize > 1
{
if (getNumVehiclesAndLargeSoldiers() >= getMaxVehiclesAndLargeSoldiersClamped())
{
return false;
return CPE_TooManyVehiclesAndLargeSoldiers;
}
if (_rules->getMaxLargeSoldiers() > -1 && getNumLargeSoldiers() >= _rules->getMaxLargeSoldiers())
{
return false;
return CPE_TooManyLargeSoldiers;
}
if (_rules->getMaxLargeUnits() > -1 && getNumLargeUnits() >= _rules->getMaxLargeUnits())
{
return false;
return CPE_TooManyLargeUnits;
}
}
return true;
auto& allowedSoldierGroups = _rules->getAllowedSoldierGroups();
if (!allowedSoldierGroups.empty())
{
if (std::find(allowedSoldierGroups.begin(), allowedSoldierGroups.end(), s->getRules()->getGroup()) == allowedSoldierGroups.end())
{
return CPE_SoldierGroupNotAllowed;
}
}
if (_rules->isOnlyOneSoldierGroupAllowed() && getNumTotalSoldiers() > 0)
{
int currentGroup = -1;
for (const auto* tmpSoldier : *_base->getSoldiers())
{
if (tmpSoldier->getCraft() == this)
{
currentGroup = tmpSoldier->getRules()->getGroup();
break;
}
}
if (s->getRules()->getGroup() != currentGroup)
{
return CPE_SoldierGroupNotSame;
}
}
return CPE_None;
}

/**
Expand Down
15 changes: 14 additions & 1 deletion src/Savegame/Craft.h
Expand Up @@ -45,6 +45,19 @@ class ScriptParserBase;
class ScriptGlobal;

enum UfoDetection : int;
enum CraftPlacementErrors : int
{
CPE_None = 0,
CPE_NotEnoughSpace = 1,
CPE_TooManySoldiers = 2,
CPE_TooManySmallSoldiers = 3,
CPE_TooManySmallUnits = 4,
CPE_TooManyVehiclesAndLargeSoldiers = 5,
CPE_TooManyLargeSoldiers = 6,
CPE_TooManyLargeUnits = 7,
CPE_SoldierGroupNotAllowed = 8,
CPE_SoldierGroupNotSame = 9,
};

typedef std::pair<Position, int> SoldierDeploymentData;

Expand Down Expand Up @@ -352,7 +365,7 @@ class Craft : public MovingTarget
/// Validates craft space and craft constraints on soldier armor change.
bool validateArmorChange(int sizeFrom, int sizeTo) const;
/// Validates craft space and craft constraints on adding soldier to a craft.
bool validateAddingSoldier(int space, const Soldier* s) const;
CraftPlacementErrors validateAddingSoldier(int space, const Soldier* s) const;
/// Validates craft space and craft constraints on adding vehicles to a craft.
int validateAddingVehicles(int totalSize) const;
};
Expand Down
3 changes: 3 additions & 0 deletions src/Ufopaedia/StatsForNerdsState.cpp
Expand Up @@ -3220,6 +3220,9 @@ void StatsForNerdsState::initCraftList()
addInteger(ss, craftRule->getMaxVehiclesAndLargeSoldiers(), "vehicles");
addInteger(ss, craftRule->getMaxVehiclesAndLargeSoldiersLimit(), "maxHWPUnitsLimit", craftRule->getMaxVehiclesAndLargeSoldiers());

addBoolean(ss, craftRule->isOnlyOneSoldierGroupAllowed(), "onlyOneSoldierGroupAllowed");
addVectorOfIntegers(ss, craftRule->getAllowedSoldierGroups(), "allowedSoldierGroups");

addInteger(ss, craftRule->getMaxSmallSoldiers(), "maxSmallSoldiers", -1);
addInteger(ss, craftRule->getMaxLargeSoldiers(), "maxLargeSoldiers", -1);
addInteger(ss, craftRule->getMaxSmallVehicles(), "maxSmallVehicles", -1);
Expand Down

0 comments on commit 3743d8e

Please sign in to comment.