diff --git a/Include/TSE.h b/Include/TSE.h index 7179e7d..b211047 100644 --- a/Include/TSE.h +++ b/Include/TSE.h @@ -675,6 +675,7 @@ class CSpaceObject : public CObject insOK = 0, // OK to install insArmorTooHeavy, // Armor is too heavy for ship + insArmorTooLight, // Armor is too light for ship insCannotInstall, // Cannot install (due to custom reason) insNoDeviceSlotsLeft, // No device slots left insNoNonWeaponSlotsLeft, // No non-weapon slots left diff --git a/Include/TSEShipClass.h b/Include/TSEShipClass.h index 1e17fcf..c24112f 100644 --- a/Include/TSEShipClass.h +++ b/Include/TSEShipClass.h @@ -26,6 +26,7 @@ class CHullDesc inline int GetMaxNonWeapons (void) const { return m_iMaxNonWeapons; } inline int GetMaxReactorPower (void) const { return m_iMaxReactorPower; } inline int GetMaxWeapons (void) const { return m_iMaxWeapons; } + inline int GetMinArmorMass (void) const { return m_iMinArmorMass; } inline int GetMinArmorSpeedBonus (void) const { return m_iMinArmorSpeedBonus; } inline int GetSize (void) const { return m_iSize; } inline int GetStdArmorMass (void) const { return m_iStdArmorMass; } @@ -51,6 +52,7 @@ class CHullDesc int m_iStdArmorMass = 0; // No penalty at this armor mass int m_iMaxArmorMass = 0; // Max mass of single armor segment int m_iMaxArmorSpeedPenalty = 0; // Change to speed at max armor mass (1/100th light-speed) + int m_iMinArmorMass = 0; // Min mass of single armor segment int m_iMinArmorSpeedBonus = 0; // Change to speed at 1/2 std armor mass int m_iMaxCargoSpace = 0; // Max amount of cargo space with expansion (tons) diff --git a/TSE/CArmorClass.cpp b/TSE/CArmorClass.cpp index e35f1c4..b752937 100644 --- a/TSE/CArmorClass.cpp +++ b/TSE/CArmorClass.cpp @@ -1985,12 +1985,21 @@ bool CArmorClass::GetReferenceSpeedBonus (CItemCtx &Ctx, int *retiSpeedBonus) co int iArmorMass = m_pItemType->GetMassKg(Ctx); // If this armor is too heavy to be installed in the ship class, then - // we return TRUE, but speed bonus = 0. + // we return TRUE, but speed bonus = INT_MIN. + + // If this armor is too light to be installed in the ship class, then + // we return TRUE, but speed bonus = INT_MAX. if (iArmorMass > pShipClass->GetHullDesc().GetMaxArmorMass()) { if (retiSpeedBonus) - *retiSpeedBonus = 0; + *retiSpeedBonus = INT_MIN; + return true; + } + else if (iArmorMass < pShipClass->GetHullDesc().GetMinArmorMass()) + { + if (retiSpeedBonus) + *retiSpeedBonus = INT_MAX; return true; } diff --git a/TSE/CCExtensions.cpp b/TSE/CCExtensions.cpp index b5af5e8..d9a4f73 100644 --- a/TSE/CCExtensions.cpp +++ b/TSE/CCExtensions.cpp @@ -1426,6 +1426,7 @@ static PRIMITIVEPROCDEF g_Extensions[] = " 'ok\n" " 'armorTooHeavy\n" + " 'armorTooLight\n" " 'cannotInstall\n" " 'noDeviceSlotsLeft\n" " 'noNonWeaponSlotsLeft\n" diff --git a/TSE/CHullDesc.cpp b/TSE/CHullDesc.cpp index d7df63f..43db20d 100644 --- a/TSE/CHullDesc.cpp +++ b/TSE/CHullDesc.cpp @@ -20,6 +20,7 @@ #define MAX_NON_WEAPONS_ATTRIB CONSTLIT("maxNonWeapons") #define MAX_REACTOR_POWER_ATTRIB CONSTLIT("maxReactorPower") #define MAX_WEAPONS_ATTRIB CONSTLIT("maxWeapons") +#define MIN_ARMOR_ATTRIB CONSTLIT("minArmor") #define MIN_ARMOR_SPEED_ATTRIB CONSTLIT("minArmorSpeed") #define SIZE_ATTRIB CONSTLIT("size") #define STD_ARMOR_ATTRIB CONSTLIT("stdArmor") @@ -75,7 +76,8 @@ int CHullDesc::CalcArmorSpeedBonus (int iSegmentCount, int iTotalArmorMass) cons else { - if (m_iMinArmorSpeedBonus > 0) + if (m_iMinArmorSpeedBonus > 0 + && m_iMinArmorMass < m_iStdArmorMass) { int iMinTotalArmorMass = m_iStdArmorMass * iSegmentCount / 2; int iRange = iStdTotalArmorMass - iMinTotalArmorMass; @@ -156,8 +158,7 @@ int CHullDesc::CalcMinArmorMassForSpeed (int iSpeed, int iStdSpeed) const int iPenaltyRange = m_iMaxArmorMass - m_iStdArmorMass; int iPenaltyMassPerPoint = iPenaltyRange / (1 - m_iMaxArmorSpeedPenalty); - int iMinArmorMass = m_iStdArmorMass / 2; - int iBonusRange = m_iStdArmorMass - iMinArmorMass; + int iBonusRange = m_iStdArmorMass - m_iMinArmorMass; int iBonusMassPerPoint = (m_iMinArmorSpeedBonus > 0 ? iBonusRange / m_iMinArmorSpeedBonus : 0); if (iSpeed < iStdSpeed) @@ -210,6 +211,8 @@ void CHullDesc::InitDefaultArmorLimits (int iMaxSpeed, Metric rThrustRatio) int iStdArmorTons = mathRound(STD_ARMOR_FACTOR * pow((Metric)iMaxArmorTons, STD_ARMOR_POWER)); m_iStdArmorMass = 1000 * iStdArmorTons; + m_iMinArmorMass = m_iStdArmorMass / 2; + // Compute the max speed at maximum armor const Metric MAX_ARMOR_SPEED_ADJ = 0.1; @@ -275,7 +278,8 @@ ALERROR CHullDesc::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, int iMa // Armor limits m_iMaxArmorMass = pHull->GetAttributeInteger(MAX_ARMOR_ATTRIB); - m_iStdArmorMass = pHull->GetAttributeIntegerBounded(STD_ARMOR_ATTRIB, 0, m_iMaxArmorMass, m_iMaxArmorMass / 2); + m_iMinArmorMass = pHull->GetAttributeInteger(MIN_ARMOR_ATTRIB); + m_iStdArmorMass = pHull->GetAttributeIntegerBounded(STD_ARMOR_ATTRIB, 0, m_iMaxArmorMass, (m_iMinArmorMass + m_iMaxArmorMass) / 2); m_iMaxArmorSpeedPenalty = pHull->GetAttributeIntegerBounded(MAX_ARMOR_SPEED_ATTRIB, 0, iMaxSpeed, iMaxSpeed) - iMaxSpeed; m_iMinArmorSpeedBonus = pHull->GetAttributeIntegerBounded(MIN_ARMOR_SPEED_ATTRIB, iMaxSpeed, 100, iMaxSpeed) - iMaxSpeed; diff --git a/TSE/CItem.cpp b/TSE/CItem.cpp index 60bfa03..67b42a0 100644 --- a/TSE/CItem.cpp +++ b/TSE/CItem.cpp @@ -1569,7 +1569,10 @@ bool CItem::GetReferenceSpeedBonus (CItemCtx &Ctx, DWORD dwFlags, int *retiSpeed // ship class. // // If the armor is too heavy to be used by the ship class, we return TRUE but -// retiSpeedBonus is set to 0. +// retiSpeedBonus is set to INT_MIN. +// +// If the armor is too light to be used by the ship class, we return TRUE but +// retiSpeedBonus is set to INT_MAX { CArmorClass *pArmor; diff --git a/TSE/CShip.cpp b/TSE/CShip.cpp index 6cd5776..295d3ce 100644 --- a/TSE/CShip.cpp +++ b/TSE/CShip.cpp @@ -1000,6 +1000,7 @@ bool CShip::CanInstallItem (const CItem &Item, int iSlot, InstallItemResults *re if (Item.IsArmor()) { int iMaxArmor = Hull.GetMaxArmorMass(); + int iMinArmor = Hull.GetMinArmorMass(); // See if we are compatible @@ -1016,6 +1017,11 @@ bool CShip::CanInstallItem (const CItem &Item, int iSlot, InstallItemResults *re else if (iMaxArmor && Item.GetMassKg() > iMaxArmor) iResult = insArmorTooHeavy; + // See if the armor is too light + + else if (Item.GetMassKg() < iMinArmor) + iResult = insArmorTooLight; + // Fire CanBeInstalled to check for custom conditions else if (!Item.FireCanBeInstalled(this, iSlot, &sResult)) diff --git a/TSE/CShipClass.cpp b/TSE/CShipClass.cpp index f248dbf..025b9df 100644 --- a/TSE/CShipClass.cpp +++ b/TSE/CShipClass.cpp @@ -71,6 +71,7 @@ #define MAX_REACTOR_POWER_ATTRIB CONSTLIT("maxReactorPower") #define MAX_SPEED_ATTRIB CONSTLIT("maxSpeed") #define MAX_WEAPONS_ATTRIB CONSTLIT("maxWeapons") +#define MIN_ARMOR_ATTRIB CONSTLIT("minArmor") #define MIN_ARMOR_SPEED_ATTRIB CONSTLIT("minArmorSpeed") #define NAME_ATTRIB CONSTLIT("name") #define NAME_BREAK_WIDTH CONSTLIT("nameBreakWidth") @@ -135,6 +136,7 @@ #define FIELD_MAX_ROTATION CONSTLIT("maxRotation") #define FIELD_MAX_SPEED CONSTLIT("maxSpeed") #define FIELD_MAX_STRUCTURAL_HP CONSTLIT("maxStructuralHP") +#define FIELD_MIN_ARMOR_MASS CONSTLIT("minArmorMass") #define FIELD_NAME CONSTLIT("name") #define FIELD_PLAYER_DESC CONSTLIT("playerDesc") #define FIELD_PRIMARY_ARMOR CONSTLIT("primaryArmor") @@ -184,6 +186,7 @@ #define PROPERTY_MAX_SPEED_AT_MAX_ARMOR CONSTLIT("maxSpeedAtMaxArmor") #define PROPERTY_MAX_SPEED_AT_MIN_ARMOR CONSTLIT("maxSpeedAtMinArmor") #define PROPERTY_MAX_SPEED_BY_ARMOR_MASS CONSTLIT("maxSpeedByArmorMass") +#define PROPERTY_MIN_ARMOR_MASS CONSTLIT("minArmorMass") #define PROPERTY_POWER CONSTLIT("power") #define PROPERTY_STD_ARMOR_MASS CONSTLIT("stdArmorMass") #define PROPERTY_THRUST CONSTLIT("thrust") @@ -1951,6 +1954,8 @@ bool CShipClass::FindDataField (const CString &sField, CString *retsValue) const *retsValue = GetGenericName(); else if (strEquals(sField, FIELD_MAX_ARMOR_MASS)) *retsValue = strFromInt(m_Hull.GetMaxArmorMass()); + else if (strEquals(sField, FIELD_MIN_ARMOR_MASS)) + *retsValue = strFromInt(m_Hull.GetMinArmorMass()); else if (strEquals(sField, FIELD_HULL_MASS)) *retsValue = strFromInt(m_Hull.GetMass()); else if (strEquals(sField, FIELD_DEVICE_SLOTS)) @@ -3768,10 +3773,10 @@ ICCItemPtr CShipClass::OnGetProperty (CCodeChainCtx &Ctx, const CString &sProper if (strEquals(sProperty, PROPERTY_CURRENCY)) return ICCItemPtr(CC.CreateInteger(GetEconomyType()->GetUNID())); - + else if (strEquals(sProperty, PROPERTY_CURRENCY_NAME)) return ICCItemPtr(CC.CreateString(GetEconomyType()->GetSID())); - + else if (strEquals(sProperty, PROPERTY_DEFAULT_SOVEREIGN)) return (m_pDefaultSovereign.GetUNID() ? ICCItemPtr(CC.CreateInteger(m_pDefaultSovereign.GetUNID())) : ICCItemPtr(CC.CreateNil())); @@ -3796,6 +3801,9 @@ ICCItemPtr CShipClass::OnGetProperty (CCodeChainCtx &Ctx, const CString &sProper else if (strEquals(sProperty, PROPERTY_MAX_SPEED_BY_ARMOR_MASS)) return ICCItemPtr(CalcMaxSpeedByArmorMass(Ctx)); + else if (strEquals(sProperty, PROPERTY_MIN_ARMOR_MASS)) + return ICCItemPtr(CC.CreateInteger(m_Hull.GetMinArmorMass())); + else if (strEquals(sProperty, PROPERTY_STD_ARMOR_MASS)) return (m_Hull.GetStdArmorMass() > 0 ? ICCItemPtr(CC.CreateInteger(m_Hull.GetStdArmorMass())) : ICCItemPtr(CC.CreateNil())); diff --git a/TSE/CSpaceObject.cpp b/TSE/CSpaceObject.cpp index 200764c..aa7c958 100644 --- a/TSE/CSpaceObject.cpp +++ b/TSE/CSpaceObject.cpp @@ -195,6 +195,7 @@ SInstallItemResultsData INSTALL_ITEM_RESULTS_TABLE[] = { "ok", 0, 0, }, { "armorTooHeavy", 1, -1, }, + { "armorTooLight", 1, -1, }, { "cannotInstall", -1, -1, }, { "noDeviceSlotsLeft", -1, 2 }, { "noNonWeaponSlotsLeft", -1, 13 }, diff --git a/TSUI/CUIHelper.cpp b/TSUI/CUIHelper.cpp index 9c64746..5ac079c 100644 --- a/TSUI/CUIHelper.cpp +++ b/TSUI/CUIHelper.cpp @@ -1059,11 +1059,13 @@ void CUIHelper::PaintItemEntry (CG32bitImage &Dest, CSpaceObject *pSource, const 0, &cyHeight); - if (iSpeedBonus == 0) - Medium.DrawText(Dest, cx, rcDrawRect.top, rgbDisadvantage, CONSTLIT("too heavy"), iSpeedBonus); + if (iSpeedBonus == INT_MIN) + Medium.DrawText(Dest, cx, rcDrawRect.top, rgbDisadvantage, CONSTLIT("too heavy")); + else if (iSpeedBonus == INT_MAX) + Medium.DrawText(Dest, cx, rcDrawRect.top, rgbDisadvantage, CONSTLIT("too light")); else if (iSpeedBonus > 0) Medium.DrawText(Dest, cx, rcDrawRect.top, rgbColorRef, strPatternSubst(CONSTLIT("+.%02dc bonus"), iSpeedBonus)); - else + else if(iSpeedBonus < 0) Medium.DrawText(Dest, cx, rcDrawRect.top, rgbDisadvantage, strPatternSubst(CONSTLIT("-.%02dc penalty"), -iSpeedBonus)); rcDrawRect.top += cyHeight; diff --git a/TSUI/ClassInfoHelpers.cpp b/TSUI/ClassInfoHelpers.cpp index baa80d8..1d2e716 100644 --- a/TSUI/ClassInfoHelpers.cpp +++ b/TSUI/ClassInfoHelpers.cpp @@ -61,6 +61,14 @@ void CUIHelper::CreateClassInfoArmor (CShipClass *pClass, int x, int y, int cxWi else sMaxArmor = strPatternSubst(CONSTLIT("%d.%d"), pClass->GetHullDesc().GetMaxArmorMass() / 1000, ((pClass->GetHullDesc().GetMaxArmorMass() % 1000) + 50) / 100); + // Compute the min armor limit too + + CString sMinArmor; + if ((pClass->GetHullDesc().GetMinArmorMass() % 1000) == 0) + sMaxArmor = strPatternSubst(CONSTLIT("%d"), pClass->GetHullDesc().GetMinArmorMass() / 1000); + else + sMaxArmor = strPatternSubst(CONSTLIT("%d.%d"), pClass->GetHullDesc().GetMinArmorMass() / 1000, ((pClass->GetHullDesc().GetMinArmorMass() % 1000) + 50) / 100); + // Info CreateClassInfoItem(ArmorItem, @@ -68,7 +76,7 @@ void CUIHelper::CreateClassInfoArmor (CShipClass *pClass, int x, int y, int cxWi y, cxWidth, dwOptions, - strPatternSubst(CONSTLIT("\noptional upgrade up to %s ton segments"), sMaxArmor), + strPatternSubst(CONSTLIT("\noptional upgrade up to %s ton segments.\noptional downgrade down to %s ton segments"), sMaxArmor, sMinArmor), retcyHeight, retpInfo); }