diff --git a/Mammoth/Include/TSE.h b/Mammoth/Include/TSE.h index ba0f60eed..be2cc8b17 100644 --- a/Mammoth/Include/TSE.h +++ b/Mammoth/Include/TSE.h @@ -1467,6 +1467,7 @@ class CSpaceObject void AddEffect (IEffectPainter *pPainter, int xOffset, int yOffset, int iTick = 0, int iRotation = 0); void CalcInsideBarrier (void); Metric CalculateItemMass (Metric *retrCargoMass = NULL) const; + Metric CalculateItemVolume (Metric *retrCargoVolume = NULL) const; bool CanFireOnObjHelper (CSpaceObject *pObj) const; void ClearCannotBeHit (void) { m_fCannotBeHit = false; } void ClearInDamageCode (void) { m_fInDamage = false; } diff --git a/Mammoth/Include/TSEAdventureDesc.h b/Mammoth/Include/TSEAdventureDesc.h index 609f985e2..9077baedc 100644 --- a/Mammoth/Include/TSEAdventureDesc.h +++ b/Mammoth/Include/TSEAdventureDesc.h @@ -38,6 +38,8 @@ class CEngineOptions Metric GetDamageMethodAdjStationHullArmor (EDamageMethod iMethod) const { return GetDamageMethodAdj(m_DamageMethodStationAdj.Hull.Armor, iMethod); }; Metric GetDamageMethodMinDamage () const { return m_rDamageMethodAdjMinDamage; } EDamageMethodSystem GetDamageMethodSystem () const { return m_iDamageMethodSystem; } + Metric GetItemDefaultDensity () const { return m_rDefaultItemDensity; } + Metric GetItemXMLMassToVolumeRatio () const { return m_rDefaultItemMassToVolume; } const CMiningDamageLevelDesc* GetMiningMaxOreLevels () const { return &m_MiningDamageMaxOreLevels; } const CDamageAdjDesc* GetShieldDamageAdj (int iLevel) const { if (iLevel < 1 || iLevel > MAX_ITEM_LEVEL) throw CException(ERR_FAIL); return &m_ShieldDamageAdj[iLevel - 1]; } Metric GetShieldIdlePowerRatio () const { return m_rDefaultShieldIdlePowerRatio; } @@ -169,6 +171,11 @@ class CEngineOptions int m_iDefaultInteraction = -1; int m_iDefaultShotHP = -1; + // Default item legacy mass-volume conversions + + Metric m_rDefaultItemDensity = 1.0; + Metric m_rDefaultItemMassToVolume = 1.0; + // Default power consumption Metric m_rDefaultShieldIdlePowerRatio = 0.125; diff --git a/Mammoth/Include/TSEArmor.h b/Mammoth/Include/TSEArmor.h index 7b90e3153..e45317c0e 100644 --- a/Mammoth/Include/TSEArmor.h +++ b/Mammoth/Include/TSEArmor.h @@ -132,7 +132,7 @@ class CArmorClass int CalcBalance (const CArmorItem &ArmorItem) const { CArmorItem::SBalance Balance; return CalcBalance(ArmorItem, Balance); } Metric CalcBalanceDamageAdj (const CArmorItem &ArmorItem, const SScalableStats &Stats) const; Metric CalcBalanceDamageEffectAdj (const CArmorItem &ArmorItem, const SScalableStats &Stats) const; - Metric CalcBalanceMass (const CArmorItem &ArmorItem, const SScalableStats &Stats, Metric *retrStdMass) const; + Metric CalcBalanceSize (const CArmorItem &ArmorItem, const SScalableStats &Stats, Metric *retrStdSize) const; Metric CalcBalancePower (const CArmorItem &ArmorItem, const SScalableStats &Stats) const; Metric CalcBalanceRegen (const CArmorItem &ArmorItem, const SScalableStats &Stats) const; Metric CalcBalanceRepair (const CArmorItem &ArmorItem, const SScalableStats &Stats) const; diff --git a/Mammoth/Include/TSEDesign.h b/Mammoth/Include/TSEDesign.h index c4f1cda2c..a4162c1d3 100644 --- a/Mammoth/Include/TSEDesign.h +++ b/Mammoth/Include/TSEDesign.h @@ -356,7 +356,7 @@ class CDesignType size_t GetAllocMemoryUsage (void) const; DWORD GetAPIVersion (void) const { return m_dwVersion; } const CAchievementDataBlock &GetAchievementDefinitions () const { return (m_pExtra ? m_pExtra->Achievements : CAchievementDataBlock::Null()); } - const CArmorMassDefinitions &GetArmorMassDefinitions (void) const { return (m_pExtra ? m_pExtra->ArmorDefinitions : CArmorMassDefinitions::Null); } + const CArmorClassDefinitions &GetArmorMassDefinitions (void) const { return (m_pExtra ? m_pExtra->ArmorDefinitions : CArmorClassDefinitions::Null); } const CString &GetAttributes (void) const { return m_sAttributes; } TArray GetDataKeys (const EDesignDataTypes iDataType ); CString GetDataField (const CString &sField) const { CString sValue; FindDataField(sField, &sValue); return sValue; } @@ -489,7 +489,7 @@ class CDesignType CAttributeDataBlock InitGlobalData; // Initial global data CLanguageDataBlock Language; // Language data CXMLElement *pLocalScreens = NULL; // Local dock screen - CArmorMassDefinitions ArmorDefinitions; // Armor mass definitions + CArmorClassDefinitions ArmorDefinitions; // Armor mass definitions CDisplayAttributeDefinitions DisplayAttribs; // Display attribute definitions CItemEncounterDefinitions ItemEncounterDefinitions; // Item encounter definitions CAchievementDataBlock Achievements; // Achievements defined by this type @@ -1451,8 +1451,8 @@ class CDesignCollection CAdventureDesc &GetAdventureDesc (void) { return (m_pAdventureDesc ? *m_pAdventureDesc : m_EmptyAdventure); } DWORD GetAdventureUNID (void) const { return (m_pAdventureExtension ? m_pAdventureExtension->GetUNID() : 0); } DWORD GetAPIVersion (void) const { return m_dwMinAPIVersion; } - CArmorMassDefinitions &GetArmorMassDefinitions (void) { return m_ArmorDefinitions; } - const CArmorMassDefinitions &GetArmorMassDefinitions (void) const { return m_ArmorDefinitions; } + CArmorClassDefinitions &GetArmorMassDefinitions (void) { return m_ArmorDefinitions; } + const CArmorClassDefinitions &GetArmorMassDefinitions (void) const { return m_ArmorDefinitions; } int GetCount (void) const { return m_AllTypes.GetCount(); } int GetCount (DesignTypes iType) const { return m_ByType[iType].GetCount(); } const CDisplayAttributeDefinitions &GetDisplayAttributes (void) const { return m_DisplayAttribs; } @@ -1517,7 +1517,7 @@ class CDesignCollection CAdventureDesc *m_pAdventureDesc = NULL; TSortMap m_EconomyIndex; CAchievementDefinitions m_AchievementDefinitions; - CArmorMassDefinitions m_ArmorDefinitions; + CArmorClassDefinitions m_ArmorDefinitions; CDisplayAttributeDefinitions m_DisplayAttribs; CItemEncounterDefinitions m_ItemEncounterDefinitions; CGlobalEventCache *m_EventsCache[evtCount]; diff --git a/Mammoth/Include/TSEEngineDefs.h b/Mammoth/Include/TSEEngineDefs.h index 508d932c3..b127fe8a7 100644 --- a/Mammoth/Include/TSEEngineDefs.h +++ b/Mammoth/Include/TSEEngineDefs.h @@ -323,6 +323,49 @@ #define PROPERTY_CORE_HIDE_RADIATION_IMMUNE CONSTLIT("core.hideRadiationImmune") #define PROPERTY_CORE_HIDE_SHATTER_IMMUNE CONSTLIT("core.hideShatterImmune") +// core.item.xmlMassToRealVolume +// +// Adventure Property: Evaluated at bind time. This property exists for adventures +// that balance cargo/mass divergently from pre-2.0 Stars of the Pilgrim, but still +// wish to convert items from old mods/extensions. +// This is relevant because in legacy Stars of the Pilgrim, cargo holds assumed items +// have a density much less than water, so the max mass of the cargo holds was divided +// by approximately 4. This computation converts the xml mass +// from kg to tons BEFORE applying this ratio. +// +// Notice: if this is not the inverse of core.item.defaultDensity, you will need to +// update your armor mass classes accordingly or switch to using armor size classes +// Notice: it is strongly recommended to switch to armor size classes instead of +// using the legacy armor mass classes +// +// Example values: +// 1.0 = volume is set to the mass provided in the XML +// 1.5 = volume is set to 1.5x the mass provided in the XML +// +// Default: 1.0 +// +#define PROPERTY_CORE_ITEM_LEGACY_MASS_TO_VOLUME CONSTLIT("core.item.xmlMassToRealVolume") + +// core.item.defaultDensity +// +// Adventure Property: Evaluated at bind time. This property allows adventures to +// assume a specific density for items that do not provide a volume. This calculation +// Is applied after core.item.legacyMassToValue to compute the actual mass the item +// should be converted to in this adventure. +// +// Notice: if this is not the inverse of core.item.defaultDensity, you will need to +// update your armor mass classes accordingly or switch to using armor size classes +// Notice: it is strongly recommended to switch to armor size classes instead of +// using the legacy armor mass classes +// +// Example values: +// 1.0 = mass is set to the computed volume (equal density to water) +// 0.125 = mass is set to one quarter the computed volume +// +// Default: 1.0 +// +#define PROPERTY_CORE_ITEM_DEFAULT_DENSITY CONSTLIT("core.item.defaultDensity") + // core.item.shield.idlePowerAdj // // Adventure Property: Evaluated at bind time. This property controls the diff --git a/Mammoth/Include/TSEItemDefs.h b/Mammoth/Include/TSEItemDefs.h index aa6b48111..aea8b7ed1 100644 --- a/Mammoth/Include/TSEItemDefs.h +++ b/Mammoth/Include/TSEItemDefs.h @@ -74,6 +74,7 @@ class CItemCriteria bool MatchesItemCategory (const CItemType &ItemType) const; bool MatchesLauncherMissilesOnly (void) const { return m_bLauncherMissileOnly; } bool MatchesLevel (int iLevel) const { return m_LevelRange.Matches(iLevel); } + bool MatchesSize (Metric rSize) const { return m_SizeRange.Matches(rSize); } bool MatchesMass (int iMassKg) const { return m_MassRange.Matches(iMassKg); } bool MatchesNotInstalledOnly (void) const { return m_bNotInstalledOnly; } bool MatchesPrice (CurrencyValue iValue) const { return m_PriceRange.Matches((int)iValue); } @@ -110,8 +111,10 @@ class CItemCriteria CIntegerRangeCriteria m_LevelRange; // Only items of this level CIntegerRangeCriteria m_PriceRange; // Only items of this price + CDoubleRangeCriteria m_SizeRange; // Only items of this volume (in cubic meters (CBM)) CIntegerRangeCriteria m_MassRange; // Only items of this mass (in kg) CIntegerRangeCriteria m_RepairLevelRange; // Only items of this repair level + CString m_sLookup; // Look up a shared criteria ICCItemPtr m_pFilter; // Filter returns Nil for excluded items @@ -316,48 +319,51 @@ class CDisplayAttributeDefinitions TArray m_ItemAttribs; }; -class CArmorMassDefinitions +class CArmorClassDefinitions { public: - void Append (const CArmorMassDefinitions &Src); + void Append (const CArmorClassDefinitions &Src); void DeleteAll (void) { m_Definitions.DeleteAll(); InvalidateIDIndex(); } - bool FindPreviousMassClass (const CString &sID, CString *retsPrevID = NULL, int *retiPrevMass = NULL) const; + bool FindPreviousArmorClass (const CString &sID, CString *retsPrevID = NULL, Metric *retrPrevSize = NULL) const; Metric GetFrequencyMax (const CString &sID) const; - const CString &GetMassClassID (const CItem &Item) const; - const CString &GetMassClassLabel (const CString &sID) const; - int GetMassClassMass (const CString &sID) const; + const CString &GetArmorClassID (const CItem &Item) const; + const CString &GetArmorClassLabel (const CString &sID) const; + Metric GetArmorClassSize (const CString &sID) const; ALERROR InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc); bool IsEmpty (void) const { return (m_Definitions.GetCount() == 0); } - void OnBindArmor (SDesignLoadCtx &Ctx, const CItem &Item, CString *retsMassClass = NULL); + void OnBindArmor (SDesignLoadCtx &Ctx, const CItem &Item, CString *retsArmorClass = NULL); void OnInitDone (void); - static const CArmorMassDefinitions Null; + static const CArmorClassDefinitions Null; private: - struct SArmorMassEntry + struct SArmorClassEntry { CString sDefinition; // Index to m_Definitions CString sID; // Required ID - int iMaxMass = 0; // Maximum mass (kg) + Metric rMaxSize = 0; // Maximum size (cubic meters (CBM)) CString sText; // Text to display on item + CString sTextShort; // Text to display in size-constrained UIs - int iCount = 0; // Number of armor types for this mass + int iCount = 0; // Number of armor types for this class }; - struct SArmorMassDefinition + struct SArmorClassDefinition { CItemCriteria Criteria; // Criteria for armor - TSortMap Classes; + TSortMap Classes; + TSortMap Ids; }; - const SArmorMassEntry *FindMassEntry (const CItem &Item) const { return const_cast(this)->FindMassEntryActual(Item); } - SArmorMassEntry *FindMassEntryActual (const CItem &Item); - void InvalidateIDIndex (void) { m_ByID.DeleteAll(); } + const SArmorClassEntry *FindClassEntry (const CItem &Item) const { return const_cast(this)->FindClassEntryActual(Item); } + SArmorClassEntry *FindClassEntryActual (const CItem &Item); + void InvalidateIDIndex () { m_ByID.DeleteAll(); } + void CleanupDefinitions () { m_Definitions.DeleteAll(); } - TSortMap m_Definitions; - TSortMap m_ByID; + TSortMap m_Definitions; + TSortMap m_ByID; }; // CItemEncounterDefinitions -------------------------------------------------- diff --git a/Mammoth/Include/TSEItemInlines.h b/Mammoth/Include/TSEItemInlines.h index 63648dc99..91c51ea0a 100644 --- a/Mammoth/Include/TSEItemInlines.h +++ b/Mammoth/Include/TSEItemInlines.h @@ -1,3 +1,4 @@ +#include "TSEItems.h" // TSEItemInlines.h // // Inline functions for various item classes. @@ -27,6 +28,16 @@ inline const CObjectImageArray &CItem::GetImage (void) const return (m_pItemType ? m_pItemType->GetImage() : CObjectImageArray::Null()); } +// GetVolume +// +// Get the volume in cubic meters for a single instance of this item +// +inline Metric CItem::GetVolume() const + { + CItemCtx ItemCtx(*this); + return m_pItemType->GetVolume(ItemCtx); + } + inline bool CItem::HasAttribute (const CString &sAttrib) const { return (m_pItemType ? m_pItemType->HasLiteralAttribute(sAttrib): false); @@ -71,12 +82,12 @@ inline bool CDifferentiatedItem::AccumulateEnhancementDisplayAttributes (TArray< return m_Item.AccumulateEnhancementDisplayAttributes(retList); } -inline ItemCategories CDifferentiatedItem::GetCategory (void) const +inline ItemCategories CDifferentiatedItem::GetCategory () const { return m_Item.GetCategory(); } -inline int CDifferentiatedItem::GetCharges (void) const +inline int CDifferentiatedItem::GetCharges () const { return m_Item.GetCharges(); } @@ -87,7 +98,7 @@ inline CCurrencyAndValue CDifferentiatedItem::GetCurrencyAndValue (bool bActual) return GetType().GetCurrencyAndValue(ItemCtx, bActual); } -inline const CEconomyType &CDifferentiatedItem::GetCurrencyType (void) const +inline const CEconomyType &CDifferentiatedItem::GetCurrencyType () const { const CEconomyType *pCurrency = GetType().GetCurrencyType(); if (pCurrency) @@ -96,22 +107,27 @@ inline const CEconomyType &CDifferentiatedItem::GetCurrencyType (void) const return GetType().GetUniverse().GetCreditCurrency(); } -inline const CObjectImageArray &CDifferentiatedItem::GetImage (void) const +inline const CObjectImageArray &CDifferentiatedItem::GetImage () const { return m_Item.GetImage(); } -inline int CDifferentiatedItem::GetLevel (void) const +inline int CDifferentiatedItem::GetLevel () const { return m_Item.GetLevel(); } -inline int CDifferentiatedItem::GetMassKg (void) const +inline int CDifferentiatedItem::GetMassKg () const { return m_Item.GetMassKg(); } -inline int CDifferentiatedItem::GetMinLevel (void) const +inline Metric CDifferentiatedItem::GetVolume () const + { + return m_Item.GetVolume(); + } + +inline int CDifferentiatedItem::GetMinLevel () const { return GetType().GetMinLevel(); } @@ -139,17 +155,17 @@ inline ICCItemPtr CDifferentiatedItem::GetPropertyKeys () const return m_Item.GetItemPropertyKeys(CCX, ItemCtx, false); } -inline int CDifferentiatedItem::GetVariantNumber (void) const +inline int CDifferentiatedItem::GetVariantNumber () const { return m_Item.GetVariantNumber(); } -inline const CItemType &CDifferentiatedItem::GetType (void) const +inline const CItemType &CDifferentiatedItem::GetType () const { return *m_Item.GetType(); } -inline CItemType &CDifferentiatedItem::GetType (void) +inline CItemType &CDifferentiatedItem::GetType () { return *m_Item.GetType(); } @@ -159,22 +175,22 @@ inline bool CDifferentiatedItem::IsDamaged (int *retiDamagedHP) const return m_Item.IsDamaged(retiDamagedHP); } -inline bool CDifferentiatedItem::IsDisrupted (void) const +inline bool CDifferentiatedItem::IsDisrupted () const { return m_Item.IsDisrupted(); } -inline bool CDifferentiatedItem::IsEnhanced (void) const +inline bool CDifferentiatedItem::IsEnhanced () const { return m_Item.IsEnhanced(); } -inline bool CDifferentiatedItem::IsLauncher (void) const +inline bool CDifferentiatedItem::IsLauncher () const { return m_Item.IsLauncher(); } -inline bool CDifferentiatedItem::IsWeapon (void) const +inline bool CDifferentiatedItem::IsWeapon () const { return m_Item.IsWeapon(); } @@ -191,12 +207,12 @@ inline int CArmorClass::GetInstallCost (const CArmorItem &ArmorItem) const const SScalableStats &Stats = GetScaledStats(ArmorItem); return (int)m_pItemType->GetCurrencyType()->Exchange(Stats.InstallCost); } -inline CString CArmorClass::GetName (void) const +inline CString CArmorClass::GetName () const { return m_pItemType->GetNounPhrase(); } -inline DWORD CArmorClass::GetUNID (void) +inline DWORD CArmorClass::GetUNID () { return m_pItemType->GetUNID(); } diff --git a/Mammoth/Include/TSEItemType.h b/Mammoth/Include/TSEItemType.h index 282dd6ad6..d97e8061f 100644 --- a/Mammoth/Include/TSEItemType.h +++ b/Mammoth/Include/TSEItemType.h @@ -118,6 +118,7 @@ class CItemType : public CDesignType bool GetUseDesc (SUseDesc *retDesc = NULL) const; int GetValue (CItemCtx &Ctx, bool bActual = false) const { return (int)GetCurrencyAndValue(Ctx, bActual).GetValue(); } int GetValueBonusPerCharge (void) const { return m_iExtraValuePerCharge; } + Metric GetVolume (CItemCtx &Ctx) const; CWeaponFireDesc *GetWeaponFireDesc (CItemCtx &Ctx, CString *retsError = NULL) const; bool HasOnRefuelCode (void) const { return FindEventHandlerItemType(evtOnRefuel); } bool HasOnInstallCode (void) const { return FindEventHandlerItemType(evtOnInstall); } @@ -195,6 +196,7 @@ class CItemType : public CDesignType int m_iMaxLevel; // Max level, for scalable items CCurrencyAndValue m_iValue; // Value in some currency int m_iMass; // Mass in kilograms + Metric m_rVolume; // Volume in Cubic meters FrequencyTypes m_Frequency; // Frequency DiceRange m_NumberAppearing; // Number appearing @@ -207,6 +209,7 @@ class CItemType : public CDesignType int m_iExtraMassPerCharge; // Extra mass per charge (in kilos) int m_iExtraValuePerCharge; // Extra value per charge (may be negative) + Metric m_rExtraVolumePerCharge; // Extra volume per charge (in cubic meters) // Components IItemGenerator *m_pComponents; // Table to generate item components (may be NULL) diff --git a/Mammoth/Include/TSEItems.h b/Mammoth/Include/TSEItems.h index b5a8fdb12..58c498097 100644 --- a/Mammoth/Include/TSEItems.h +++ b/Mammoth/Include/TSEItems.h @@ -53,25 +53,26 @@ class CDifferentiatedItem { public: inline bool AccumulateEnhancementDisplayAttributes (TArray &retList) const; - inline ItemCategories GetCategory (void) const; - inline int GetCharges (void) const; + inline ItemCategories GetCategory () const; + inline int GetCharges () const; inline CCurrencyAndValue GetCurrencyAndValue (bool bActual = false) const; - inline const CEconomyType &GetCurrencyType (void) const; - inline const CObjectImageArray &GetImage (void) const; - inline int GetLevel (void) const; - inline int GetMassKg (void) const; - inline int GetMinLevel (void) const; + inline const CEconomyType &GetCurrencyType () const; + inline const CObjectImageArray &GetImage () const; + inline int GetLevel () const; + inline int GetMassKg () const; + inline Metric GetVolume () const; + inline int GetMinLevel () const; inline CString GetNounPhrase (DWORD dwFlags = 0) const; inline ICCItemPtr GetProperty (const CString &sProperty) const; inline ICCItemPtr GetPropertyKeys () const; - inline const CItemType &GetType (void) const; - inline CItemType &GetType (void); - inline int GetVariantNumber (void) const; + inline const CItemType &GetType () const; + inline CItemType &GetType (); + inline int GetVariantNumber () const; inline bool IsDamaged (int *retiDamagedHP = NULL) const; - inline bool IsDisrupted (void) const; - inline bool IsEnhanced (void) const; - inline bool IsLauncher (void) const; - inline bool IsWeapon (void) const; + inline bool IsDisrupted () const; + inline bool IsEnhanced () const; + inline bool IsLauncher () const; + inline bool IsWeapon () const; void ReportEventError (const CSpaceObject *pSource, const CString &sEvent, const ICCItem &ErrorItem) const; protected: @@ -356,8 +357,8 @@ class CItem int GetItemPropertyInteger (CCodeChainCtx &CCCtx, CItemCtx &Ctx, const CString &sProperty) const; CString GetItemPropertyString (CCodeChainCtx &CCCtx, CItemCtx &Ctx, const CString &sProperty) const; int GetLevel (void) const; - Metric GetMass (void) const { return GetMassKg() / 1000.0; } - int GetMassKg (void) const; + Metric GetMass () const { return GetMassKg() / 1000.0; } + int GetMassKg () const; int GetMaxCharges (void) const; const CItemEnhancement &GetMods (void) const { return (m_pExtra ? m_pExtra->m_Mods : m_NullMod); } static const CItem &GetNullItem (void) { return m_NullItem; } @@ -378,6 +379,7 @@ class CItem CItemType *GetUnknownType (void) const; CItemType *GetUnknownTypeIfUnknown (bool bActual = false) const; int GetVariantNumber (void) const { return (m_pExtra ? (int)m_pExtra->m_dwVariantCounter : 0); } + Metric GetVolume () const; inline bool HasAttribute (const CString &sAttrib) const; bool HasComponents (void) const; bool HasMods (void) const { return (m_pExtra && m_pExtra->m_Mods.IsNotEmpty()); } diff --git a/Mammoth/Include/TSELanguage.h b/Mammoth/Include/TSELanguage.h index dec357e89..3368f9191 100644 --- a/Mammoth/Include/TSELanguage.h +++ b/Mammoth/Include/TSELanguage.h @@ -69,6 +69,9 @@ class CLanguage numberMetric, // 105 M or 10.5 k or 105 u (for adding a unit abbreviation after) numberMetricFull, // 105 Mega or 10.5 kilo or 105 micro (for adding a unit after) numberMetricUnitless, // 105M or 10.5k or 105u (for unitless numbers) + numberCBM, // Formats CBM (cubic meters) with SI prefixes + numberCBMBasic, // Formats CBM like mass (CBM if >= 1.0, mCBM if 0.001-.999) + numberCBMInt, // Formats whole CBM only, no SI prefixes }; enum EVerbFlags diff --git a/Mammoth/Include/TSEShipClass.h b/Mammoth/Include/TSEShipClass.h index 1b95fa379..76943124e 100644 --- a/Mammoth/Include/TSEShipClass.h +++ b/Mammoth/Include/TSEShipClass.h @@ -65,14 +65,14 @@ class CArmorLimits enum EResults { resultOK, - resultTooHeavy, + resultTooLarge, resultIncompatible, }; struct SSummary { - int iMaxArmorMass = 0; // Max armor mass that can be installed - int iStdArmorMass = 0; // Max armor mass that has no penalty + Metric rMaxArmorSize = 0; // Max armor size that can be installed + Metric rStdArmorSize = 0; // Max armor size that has no penalty int iMaxSpeedBonus = 0; // Largest speed bonus (i.e., at min armor) int iMaxSpeedPenalty = 0; // Largest speed penalty (negative value, at max armor) @@ -84,15 +84,15 @@ class CArmorLimits int CalcArmorSpeedBonus (const TArray &Armor) const; bool CalcArmorSpeedBonus (CItemCtx &ArmorItem, int iSegmentCount, int *retiBonus = NULL) const; bool CalcArmorSpeedBonus (const CString &sArmorClassID, int iSegmentCount, int *retiBonus = NULL) const; - ICCItemPtr CalcMaxSpeedByArmorMass (CCodeChainCtx &Ctx, int iStdSpeed) const; - void CalcSummary (const CArmorMassDefinitions &Defs, SSummary &Summary) const; + ICCItemPtr CalcMaxSpeedByArmorSize (CCodeChainCtx &Ctx, int iStdSpeed) const; + void CalcSummary (const CArmorClassDefinitions &Defs, SSummary &Summary) const; EResults CanInstallArmor (const CItem &Item) const; const CString &GetMaxArmorClass (void) const { return m_sMaxArmorClass; } - int GetMaxArmorMass (void) const { return m_iMaxArmorMass; } + Metric GetMaxArmorSize (void) const { return m_rMaxArmorSize; } int GetMaxArmorSpeedPenalty (void) const { return m_iMaxArmorSpeedPenalty; } int GetMinArmorSpeedBonus (void) const { return m_iMinArmorSpeedBonus; } const CString &GetStdArmorClass (void) const { return m_sStdArmorClass; } - int GetStdArmorMass (void) const { return m_iStdArmorMass; } + Metric GetStdArmorSize (void) const { return m_rStdArmorSize; } bool HasArmorLimits (void) const { return (m_iType != typeNone); } void InitDefaultArmorLimits (int iMass, int iMaxSpeed, Metric rThrustRatio); ALERROR InitArmorLimitsFromXML (SDesignLoadCtx &Ctx, CXMLElement *pLimits); @@ -112,15 +112,15 @@ class CArmorLimits struct SArmorLimits { CString sClass; - int iMass = 0; // Limit mass, kg (if sClass is blank) + Metric rSize = 0.0; // Limit size, in cubic meters (CBM) (if sClass is blank) TUniquePtr pCriteria; int iSpeedAdj = 0; // Change to speed for this armor class }; - int CalcArmorMass (const CItemCtx &ArmorItem) const; - int CalcArmorSpeedBonus (int iSegmentCount, int iTotalArmorMass) const; - int CalcMinArmorMassForSpeed (int iSpeed, int iStdSpeed) const; + Metric CalcArmorSize (const CItemCtx &ArmorItem) const; + int CalcArmorSpeedBonus (int iSegmentCount, Metric iTotalArmorSize) const; + Metric CalcMinArmorSizeForSpeed (int iSpeed, int iStdSpeed) const; bool FindArmorLimits (const CItemCtx &ItemCtx, const SArmorLimits **retpLimits = NULL, bool *retbClassFound = NULL) const; ETypes m_iType = typeNone; @@ -128,8 +128,8 @@ class CArmorLimits CString m_sStdArmorClass; CString m_sMaxArmorClass; - int m_iStdArmorMass = 0; // No penalty at this armor mass - int m_iMaxArmorMass = 0; // Max mass of single armor segment + Metric m_rStdArmorSize = 0; // No penalty at this armor size + Metric m_rMaxArmorSize = 0; // Max size of single armor segment int m_iMaxArmorSpeedPenalty = 0; // Change to speed at max armor mass (1/100th light-speed) int m_iMinArmorSpeedBonus = 0; // Change to speed at 1/2 std armor mass @@ -175,7 +175,7 @@ class CHullDesc ALERROR InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, int iMaxSpeed); bool IsTimeStopImmune (void) const { return m_bTimeStopImmune; } #ifdef APPLY_DEFAULT_ARMOR_LIMITS - bool NeedsDefaultArmorLimits (void) const { return (m_ArmorLimits.GetMaxArmorMass() == 0 && GetMass() > 0 && GetMass() < 1000); } + bool NeedsDefaultArmorLimits (void) const { return (m_ArmorLimits.GetMaxArmorSize() == 0 && GetMass() > 0 && GetMass() < 1000); } #else bool NeedsDefaultArmorLimits (void) const { return false; } #endif @@ -188,7 +188,7 @@ class CHullDesc int m_iSize = 0; // Length of ship in meters int m_iMass = 0; // Empty hull mass (tons) CCurrencyAndValue m_Value; // Value of hull alone (excluding any devices/armor) - int m_iCargoSpace = 0; // Default cargo space (tons) + int m_iCargoSpace = 0; // Default cargo space (cubic meters) int m_iCounterIncrementRate = 0; // Value by which temperature/capacitor counter is updated every tick int m_iStealthAdj = 0; // Stealth value of the ship at zero heat to add to armor/nebula stealth value int m_iStealthAdjAtMaxHeat = 0; // Stealth value of the ship at max heat to add to armor/nebula stealth value @@ -197,7 +197,7 @@ class CHullDesc CItemCriteria m_DeviceCriteria; // Allowable devices CArmorLimits m_ArmorLimits; // Adjustments based on armor - int m_iMaxCargoSpace = 0; // Max amount of cargo space with expansion (tons) + int m_iMaxCargoSpace = 0; // Max amount of cargo space with expansion (cubic meters) int m_iMaxCounter = 0; // Max value of counter (used for temperature or capacitor) int m_iMaxReactorPower = 0; // Max compatible reactor power int m_iMaxDevices = 0; // Max number of devices diff --git a/Mammoth/Include/TSEShipSystems.h b/Mammoth/Include/TSEShipSystems.h index f3e8ada54..dc0a0d2cb 100644 --- a/Mammoth/Include/TSEShipSystems.h +++ b/Mammoth/Include/TSEShipSystems.h @@ -654,7 +654,7 @@ class CCargoDesc void ValidateCargoSpace (int iMaxCargoSpace); private: - int m_iCargoSpace; // Cargo space in tons + int m_iCargoSpace; // Cargo space in cubic meters bool m_bUninitialized; }; diff --git a/Mammoth/Include/TSESpaceObjectsImpl.h b/Mammoth/Include/TSESpaceObjectsImpl.h index 750108765..62f292eda 100644 --- a/Mammoth/Include/TSESpaceObjectsImpl.h +++ b/Mammoth/Include/TSESpaceObjectsImpl.h @@ -1367,11 +1367,14 @@ class CShip : public TSpaceObjectImpl int FindNextDevice (int iStart, ItemCategories Category, int iDir = 1); int FindRandomDevice (bool bEnabledOnly = false); void FinishCreation (SShipGeneratorCtx *pCtx = NULL, SSystemCreateCtx *pSysCreateCtx = NULL); - Metric GetCargoMass (void) const; + Metric GetCargoMass () const; + Metric GetCargoVolume () const; CCurrencyAndValue GetHullValue (void) const; - Metric GetItemMass (void) const; + Metric GetItemMass () const; + Metric GetItemVolume () const; int GetTotalArmorHP (int *retiMaxHP = NULL) const; - void InvalidateItemMass (void) const { m_fRecalcItemMass = true; } + void InvalidateItemMass () const { m_fRecalcItemMass = true; } + void InvalidateItemVolume () const { m_fRecalcItemVolume = true; } bool IsSingletonDevice (ItemCategories iItemCat); void PaintMapShipCompartments (CG32bitImage &Dest, int x, int y, CMapViewportCtx &Ctx); void PaintShipCompartments (CG32bitImage &Dest, SViewportPaintCtx &Ctx); @@ -1431,6 +1434,8 @@ class CShip : public TSpaceObjectImpl mutable Metric m_rItemMass = 0.0; // Total mass of all items (including installed) mutable Metric m_rCargoMass = 0.0; // Mass of cargo items (not including installed) + mutable Metric m_rItemVolume = 0.0; // Total volume of all items (including installed) + mutable Metric m_rCargoVolume = 0.0; // Volume of cargo items (not including installed) int m_iCounterValue = 0; // Heat/capacitor counter value CSpaceObject *m_pDocked = NULL; // If not NULL, object we are docked to. @@ -1450,7 +1455,7 @@ class CShip : public TSpaceObjectImpl DWORD m_fIdentified:1 = false; // TRUE if player can see ship class, etc. DWORD m_fManualSuspended:1 = false; // TRUE if ship is suspended - mutable DWORD m_fRecalcItemMass:1 = true; // TRUE if we need to recalculate m_rImageMass + mutable DWORD m_fRecalcItemMass:1 = true; // TRUE if we need to recalculate m_rItemMass DWORD m_fDockingDisabled:1 = false; // TRUE if docking is disabled DWORD m_fControllerDisabled:1 = false; // TRUE if we want to disable controller DWORD m_fRecalcRotationAccel:1 = false; // TRUE if we need to recalc rotation acceleration @@ -1463,7 +1468,7 @@ class CShip : public TSpaceObjectImpl DWORD m_fHasShipCompartments:1 = false; // TRUE if we have ship compartment objects attached DWORD m_fNameBlanked:1 = false; // TRUE if name has been blanked; show generic name DWORD m_fShowMapLabel:1 = false; // TRUE if we should show a map label - DWORD m_fSpare6:1; + mutable DWORD m_fRecalcItemVolume:1 = false;// TRUE if we need to recalculate m_rItemVolume DWORD m_fSpare7:1; DWORD m_fSpare8:1; diff --git a/Mammoth/Include/TSEUtil.h b/Mammoth/Include/TSEUtil.h index 4aafb316d..0b2fff456 100644 --- a/Mammoth/Include/TSEUtil.h +++ b/Mammoth/Include/TSEUtil.h @@ -373,11 +373,11 @@ class CIntegerRangeCriteria { public: CString AsString (char chModifier = '\0') const; - int GetEqualToValue (void) const { return m_iEqualToValue; } - int GetGreaterThanValue (void) const { return m_iGreaterThanValue; } - int GetLessThanValue (void) const { return m_iLessThanValue; } + int GetEqualToValue () const { return m_iEqualToValue; } + int GetGreaterThanValue () const { return m_iGreaterThanValue; } + int GetLessThanValue () const { return m_iLessThanValue; } bool GetRange (int *retiMin = NULL, int *retiMax = NULL) const; - bool IsEmpty (void) const { return (m_iEqualToValue == -1 && m_iGreaterThanValue == -1 && m_iLessThanValue == -1); } + bool IsEmpty () const { return (m_iEqualToValue == -1 && m_iGreaterThanValue == -1 && m_iLessThanValue == -1); } bool Matches (int iValue) const; bool Parse (const char *pPos, const char **retpPos = NULL, char *retchModifier = NULL); @@ -387,6 +387,24 @@ class CIntegerRangeCriteria int m_iLessThanValue = -1; // If not -1, match only less than this value }; +class CDoubleRangeCriteria + { + public: + CString AsString (char chModifier = '\0') const; + Metric GetEqualToValue () const { return m_rEqualToValue; } + Metric GetGreaterThanValue () const { return m_rGreaterThanValue; } + Metric GetLessThanValue () const { return m_rLessThanValue; } + bool GetRange (Metric *retrMin = NULL, Metric *retrMax = NULL) const; + bool IsEmpty () const { return (IS_NAN(m_rEqualToValue) && IS_NAN(m_rGreaterThanValue) && IS_NAN(m_rLessThanValue)); } + bool Matches (Metric rValue) const; + bool Parse (const char *pPos, const char **retpPos = NULL, char *retchModifier = NULL); + + private: + Metric m_rEqualToValue = R_NAN; // If not -inf, match only this value + Metric m_rGreaterThanValue = R_NAN; // If not -inf, match only greater than this value + Metric m_rLessThanValue = R_NAN; // If not -inf, match only less than this value + }; + class DiceRange { public: diff --git a/Mammoth/Include/TSEVersions.h b/Mammoth/Include/TSEVersions.h index e889bd171..e30837d4d 100644 --- a/Mammoth/Include/TSEVersions.h +++ b/Mammoth/Include/TSEVersions.h @@ -7,7 +7,7 @@ constexpr DWORD API_VERSION = 59; constexpr DWORD UNIVERSE_SAVE_VERSION = 41; -constexpr DWORD SYSTEM_SAVE_VERSION = 218; +constexpr DWORD SYSTEM_SAVE_VERSION = 219; // Uncomment out the following define when building a stable release @@ -985,9 +985,93 @@ constexpr DWORD SYSTEM_SAVE_VERSION = 218; // tlisp: // (fmtNumber [format] number) // Updated to now accept the following additional formats: +// 'CBM 32.5 kCBM - cubic meters with SI prefixes +// 'CBMBasic 32500 CBM - cubic meters, or mCBM if 0.001<=val<1.0, or 0 - mirrors massKg except input in CBM +// 'CBMInt 32500 CBM - cubic meters, or 0 if <1.0 - mirrors massTons // 'metric 10.5 G // 'metricFull 10.5 Giga // 'metricUnitless 10.5G +// (itm@ itm [object] property) +// (itmFind ...) +// See criteria update in (itmMatches) +// (itmGetMassKg item) +// Returns item mass in kg +// (itmGetMass item) +// DEPRECATED: (synonym for itemGetVolume, for actually getting the mass use itmGetMassKg instead) +// (itmGetVolume item) +// Returns volume in CBM (real) +// (itmMatches criteria item) +// Supports volume matching "@" with <,=,> and range operators +// (obj@ obj property) +// New and updated properties: +// 'cargoSpace Available cargo space in cubic meters (previously tons) +// 'cargoSpaceFree Available cargo space in cubic meters, double +// 'cargoSpaceFreeLiters Available cargo space in liters, int +// this is purely for supporting legacy math operations that expected kg +// 'cargoSpaceFreeKg DEPRECATED: Available cargo space in liters, int (NOT kg) +// this is purely for supporting legacy cargo math that needs it in liters to check if items will fit +// 'cargoSpaceUsed Used cargo space in cubic meters, double +// 'cargoSpaceUsedLiters Used cargo space in liters, int +// this is purely for supporting legacy math operations that expected kg +// 'cargoSpaceUsedKg DEPRECATED: Used cargo space in liters, int (NOT kg) +// this is purely for supporting legacy cargo math that needs it in liters to check if items will fit +// 'maxCargoSpace Total cargo space in cubic meters (previously tons) +// (objGetCargoSpaceLeft obj) +// DEPRECATED: returns cargo space in liters, for compatibility math. Name implies returning in cubic meters though. +// (typGetDataField type field) +// New and updated fields +// ;;ItemType +// 'volume returns the volume in CBM as a string +// ;;ShipClass +// 'maxArmorSize returns max armor size in CBM as a string +// 'maxArmorMass DEPRECATED: use maxArmorSize instead (returns same value multiplied by 1000) +// +// +// 1.0 +// Conversion ratio of tons->cubic meters for legacy items that specify only mass +// accepts a double +// Default: 1.0 +// 1.0 +// Default density in tons per cubic meter to apply to legacy items that specify only mass +// accepts a double +// Default: 1.0 +// +// +// +// New name for ArmorMass tag +// volume: armor item volume in CBM (cubic meters) +// mass: Autoconverts legacy mass= values (mass in kg) to CBM based on adventure properties +// size: max size armor for this armor class (in CBM) +// label: display name of armor class (unchanged from MassClass) +// shortLabel: new string field for displaying mass classes when the mass class names are too long +// +// New feature to allow compatibility support for old armor class names (ex, aliasing Dreadnought -> SuperMassive) +// id: name of the ArmorClass to alias +// idAlias: name of the alias +// +// Deprecated tag name, use ArmorClass instead (Functions identically) +// <[Any Other Name]> +// Deprecated tag name. APIs <59 allowed any named tags instead of just MassClass and they were interpreted as MassClass. +// Unsupported in API59+ (will cause a load error). This is to allow for additional tag types in the future. +// +// Deprecated tag name, use ArmorClassDesc instead (Functions identically) +// +// density: (double) Species a specific density ratio in metric tons per cubic meter (aka, grams per cubic centimeter) +// mass: (int) If present without volume, this value is auto-converted using adventure properties to a volume, and the +// actual mass is computed from that volume back into mass using another adventure property. This is still an int +// for legacy support reasons. A future API may support doubles. +// volume: (double) volume of the item in cubic meters. Values <1 liter (0.001CBM) are allowed +// +// cargoSpace: now specifies CBM instead of tons +// If +// cargoSpace: now specifies CBM instead of tons +// If // @@ -1791,3 +1875,8 @@ constexpr DWORD SYSTEM_SAVE_VERSION = 218; // Add DamageMethodPierceAdj to SExtraDamage in DamageDesc // Add DamageMethodShredAdj to SExtraDamage in DamageDesc // +// 219: 2.0 Alpha 9 +// Add m_fRecalcItemMass to CShip +// Add m_rItemVolume to CShip +// Add m_rCargoVolume to CShip +// diff --git a/Mammoth/Include/TSEWeaponClassImpl.h b/Mammoth/Include/TSEWeaponClassImpl.h index 13f2b1002..9e382c92f 100644 --- a/Mammoth/Include/TSEWeaponClassImpl.h +++ b/Mammoth/Include/TSEWeaponClassImpl.h @@ -12,6 +12,7 @@ class CWeaponClass : public CDeviceClass static constexpr Metric HP_ARMOR_RATIO = 0.1; // Ammo HP per standard armor HP of same level static constexpr Metric STD_AMMO_MASS = 10.0; // Standard ammo mass (in kg) + static constexpr Metric STD_AMMO_VOLUME = 0.01; // Standard ammo volume (TODO: come up with better standard value and mass for ammo in 2.0) static constexpr Metric DEFAULT_HP_DAMAGE_RATIO = 0.5; // Used to compute default HP. // See: CWeaponFireDesc::CalcDefaultHitPoints @@ -63,7 +64,7 @@ class CWeaponClass : public CDeviceClass Metric rDamageType = 0.0; // Damage type balance contribution Metric rStdAmmoCost = 0.0; // Standard ammo cost (for level and fire rate) - Metric rStdAmmoMass = 0.0; // Standard ammo mass (for level and fire rate) + Metric rStdAmmoSize = 0.0; // Standard ammo mass (for level and fire rate) Metric rAmmo = 0.0; // Ammo contribution Metric rOmni = 0.0; // Omni and swivel component Metric rTracking = 0.0; // Tracking component diff --git a/Mammoth/TSE/CArmorClass.cpp b/Mammoth/TSE/CArmorClass.cpp index 4f9a0c5fc..1932f3872 100644 --- a/Mammoth/TSE/CArmorClass.cpp +++ b/Mammoth/TSE/CArmorClass.cpp @@ -134,14 +134,14 @@ const Metric DECAY_BALANCE_ADJ = -3.0; const Metric DIST_BALANCE_ADJ = 2.0; const Metric MAX_REGEN_BALANCE_BONUS = 500.0; -const Metric MASS_BALANCE_STD_MASS = 3.5; -const Metric MASS_BALANCE_K0 = 1.284; -const Metric MASS_BALANCE_K1 = -0.47; -const Metric MASS_BALANCE_K2 = 0.014; -const Metric MASS_BALANCE_ADJ = 60.0; // Linear relationship between curve and mass balance -const Metric MASS_BALANCE_LIMIT = 16.0; // Above this mass (in tons) we don't get any additional bonus -const Metric MASS_STD_MASS = (-MASS_BALANCE_K1 - sqrt(MASS_BALANCE_K1 * MASS_BALANCE_K1 - 4.0 * MASS_BALANCE_K2 * MASS_BALANCE_K0)) / (2.0 * MASS_BALANCE_K2); -const Metric MASS_COST_POWER = 0.5; +const Metric ARMOR_CLASS_BALANCE_STD_SIZE = 3.5; +const Metric ARMOR_CLASS_BALANCE_K0 = 1.284; +const Metric ARMOR_CLASS_BALANCE_K1 = -0.47; +const Metric ARMOR_CLASS_BALANCE_K2 = 0.014; +const Metric ARMOR_CLASS_BALANCE_ADJ = 60.0; // Linear relationship between curve and mass balance +const Metric ARMOR_CLASS_BALANCE_LIMIT = 16.0; // Above this size (in CBM) we don't get any additional bonus +const Metric ARMOR_CLASS_STD_SIZE = (-ARMOR_CLASS_BALANCE_K1 - sqrt(ARMOR_CLASS_BALANCE_K1 * ARMOR_CLASS_BALANCE_K1 - 4.0 * ARMOR_CLASS_BALANCE_K2 * ARMOR_CLASS_BALANCE_K0)) / (2.0 * ARMOR_CLASS_BALANCE_K2); +const Metric ARMOR_CLASS_COST_POWER = 0.5; const Metric BALANCE_COST_RATIO = -0.5; // Each percent of cost above standard is a 0.5% const Metric BALANCE_MAX_DAMAGE_ADJ = 400.0; // Max change in balance due to a single damage type @@ -987,7 +987,7 @@ int CArmorClass::CalcBalance (const CArmorItem &ArmorItem, CArmorItem::SBalance // Mass - retBalance.rMass = CalcBalanceMass(ArmorItem, Stats, &retBalance.rStdMass); + retBalance.rMass = CalcBalanceSize(ArmorItem, Stats, &retBalance.rStdMass); retBalance.rBalance += retBalance.rMass; // Compute standard hit point for the given mass @@ -996,7 +996,7 @@ int CArmorClass::CalcBalance (const CArmorItem &ArmorItem, CArmorItem::SBalance // Standard cost depends on mass - retBalance.rStdCost = Max(1.0, StdStats.iCost * mathRound(10.0 * pow(ArmorItem.GetMassKg() / (1000.0 * retBalance.rStdMass), MASS_COST_POWER)) / 10.0); + retBalance.rStdCost = Max(1.0, StdStats.iCost * mathRound(10.0 * pow(ArmorItem.GetVolume() / (retBalance.rStdMass), ARMOR_CLASS_COST_POWER)) / 10.0); // Cost @@ -1132,38 +1132,38 @@ Metric CArmorClass::CalcBalanceDamageEffectAdj (const CArmorItem &ArmorItem, con return rBalance; } -Metric CArmorClass::CalcBalanceMass (const CArmorItem &ArmorItem, const SScalableStats &Stats, Metric *retrStdMass) const - -// CalcBalanceMass +// CalcBalanceSize +// +// Calculate balance from armor size // -// Calculate balance from armor mass +Metric CArmorClass::CalcBalanceSize (const CArmorItem &ArmorItem, const SScalableStats &Stats, Metric *retrStdSize) const { // Mass in metric tons. - Metric rMass = CItem(m_pItemType, 1).GetMass(); - if (rMass == 0.0) + Metric rSize = CItem(m_pItemType, 1).GetMass(); + if (rSize == 0.0) return 0.0; - // Adjust based on the standard armor mass + // Adjust based on the standard armor class - Metric rStdMass = GetUniverse().GetDesignCollection().GetArmorMassDefinitions().GetMassClassMass(MASS_CLASS_STANDARD_ID) / 1000.0; - Metric rAdj = (rStdMass > 0.0 ? MASS_BALANCE_STD_MASS / rStdMass : 1.0); + Metric rStdClass = GetUniverse().GetDesignCollection().GetArmorMassDefinitions().GetArmorClassSize(MASS_CLASS_STANDARD_ID) / 1000.0; + Metric rAdj = (rStdClass > 0.0 ? ARMOR_CLASS_BALANCE_STD_SIZE / rStdClass : 1.0); // Need to account for everything up to the maximum defined mass class. // Anything beyond that is considered bespoke. - Metric rMaxMass = GetUniverse().GetDesignCollection().GetArmorMassDefinitions().GetMassClassMass(MASS_CLASS_MAX_ID); - rMass = Min(rAdj * rMass, rMaxMass > 0.0 ? rMaxMass : MASS_BALANCE_LIMIT); + Metric rMaxSize = GetUniverse().GetDesignCollection().GetArmorMassDefinitions().GetArmorClassSize(MASS_CLASS_MAX_ID); + rSize = Min(rAdj * rSize, rMaxSize > 0.0 ? rMaxSize : ARMOR_CLASS_BALANCE_LIMIT); // Compute the standard mass that results in 0 balance. - if (retrStdMass) - *retrStdMass = MASS_STD_MASS / (rAdj > 0.0 ? rAdj : 1.0); + if (retrStdSize) + *retrStdSize = ARMOR_CLASS_STD_SIZE / (rAdj > 0.0 ? rAdj : 1.0); // This polynomial generates a balance based on mass. - return MASS_BALANCE_ADJ * ((MASS_BALANCE_K2 * rMass * rMass) + MASS_BALANCE_K1 * rMass + MASS_BALANCE_K0); + return ARMOR_CLASS_BALANCE_ADJ * ((ARMOR_CLASS_BALANCE_K2 * rSize * rSize) + ARMOR_CLASS_BALANCE_K1 * rSize + ARMOR_CLASS_BALANCE_K0); } Metric CArmorClass::CalcBalancePower (const CArmorItem &ArmorItem, const SScalableStats &Stats) const @@ -2328,16 +2328,11 @@ CString CArmorClass::GetReference (CItemCtx &Ctx) if (iPower) AppendReferenceString(&sReference, CLanguage::ComposeNumber(CLanguage::numberPower, iPower * 100.0)); - // Mass - - int iMassKg = m_pItemType->GetMassKg(Ctx); - AppendReferenceString(&sReference, CLanguage::ComposeNumber(CLanguage::numberMass, iMassKg)); - // Mass classification - CString sMassClass = GetUniverse().GetDesignCollection().GetArmorMassDefinitions().GetMassClassLabel(m_sMassClass); - if (!sMassClass.IsBlank()) - AppendReferenceString(&sReference, sMassClass); + CString sArmorClass = GetUniverse().GetDesignCollection().GetArmorMassDefinitions().GetArmorClassLabel(m_sMassClass); + if (!sArmorClass.IsBlank()) + AppendReferenceString(&sReference, sArmorClass); // Regeneration diff --git a/Mammoth/TSE/CArmorLimits.cpp b/Mammoth/TSE/CArmorLimits.cpp index 08aaf90f2..ee598e99f 100644 --- a/Mammoth/TSE/CArmorLimits.cpp +++ b/Mammoth/TSE/CArmorLimits.cpp @@ -14,6 +14,8 @@ #define MAX_ARMOR_SPEED_PENALTY_ATTRIB CONSTLIT("maxArmorSpeedAdj") #define MIN_ARMOR_SPEED_ATTRIB CONSTLIT("minArmorSpeed") #define MIN_ARMOR_SPEED_BONUS_ATTRIB CONSTLIT("minArmorSpeedAdj") +#define ARMOR_CLASS_ATTRIB CONSTLIT("armorClass") +#define SIZE_ATTRIB CONSTLIT("size") #define SPEED_ADJ_ATTRIB CONSTLIT("speedAdj") #define STD_ARMOR_ATTRIB CONSTLIT("stdArmor") @@ -46,37 +48,37 @@ ALERROR CArmorLimits::Bind (SDesignLoadCtx &Ctx) if (!m_sMaxArmorClass.IsBlank()) { - m_iMaxArmorMass = Ctx.pDesign->GetArmorMassDefinitions().GetMassClassMass(m_sMaxArmorClass); - if (m_iMaxArmorMass == 0) + m_rMaxArmorSize = Ctx.pDesign->GetArmorMassDefinitions().GetArmorClassSize(m_sMaxArmorClass); + if (m_rMaxArmorSize == 0) { - Ctx.sError = strPatternSubst(CONSTLIT("Invalid armor mass class: %s."), m_sMaxArmorClass); + Ctx.sError = strPatternSubst(CONSTLIT("Invalid armor class: %s."), m_sMaxArmorClass); return ERR_FAIL; } } if (!m_sStdArmorClass.IsBlank()) { - m_iStdArmorMass = Ctx.pDesign->GetArmorMassDefinitions().GetMassClassMass(m_sStdArmorClass); - if (m_iStdArmorMass == 0) + m_rStdArmorSize = Ctx.pDesign->GetArmorMassDefinitions().GetArmorClassSize(m_sStdArmorClass); + if (m_rStdArmorSize == 0) { - Ctx.sError = strPatternSubst(CONSTLIT("Invalid armor mass class: %s."), m_sStdArmorClass); + Ctx.sError = strPatternSubst(CONSTLIT("Invalid armor class: %s."), m_sStdArmorClass); return ERR_FAIL; } } // Make sure values are in range - if (m_iMaxArmorMass <= 0) + if (m_rMaxArmorSize <= 0) { m_iType = typeNone; - m_iStdArmorMass = 0; + m_rStdArmorSize = 0; } - else if (m_iStdArmorMass <= 0) - m_iStdArmorMass = m_iMaxArmorMass / 2; + else if (m_rStdArmorSize <= 0) + m_rStdArmorSize = m_rMaxArmorSize / 2; - else if (m_iStdArmorMass > m_iMaxArmorMass) - m_iStdArmorMass = m_iMaxArmorMass; + else if (m_rStdArmorSize > m_rMaxArmorSize) + m_rStdArmorSize = m_rMaxArmorSize; break; } @@ -85,21 +87,21 @@ ALERROR CArmorLimits::Bind (SDesignLoadCtx &Ctx) { // Calculate and cache armor mass limits - m_iMaxArmorMass = 0; - m_iStdArmorMass = 0; + m_rMaxArmorSize = 0; + m_rStdArmorSize = 0; m_iMaxArmorSpeedPenalty = 0; m_iMinArmorSpeedBonus = 0; for (int i = 0; i < m_ArmorLimits.GetCount(); i++) { - int iMass = m_ArmorLimits[i].iMass; - if (iMass == 0) - iMass = Ctx.pDesign->GetArmorMassDefinitions().GetMassClassMass(m_ArmorLimits[i].sClass); + Metric rSize = m_ArmorLimits[i].rSize; + if (rSize == 0) + rSize = Ctx.pDesign->GetArmorMassDefinitions().GetArmorClassSize(m_ArmorLimits[i].sClass); // Max armor mass that we can support. - if (iMass > m_iMaxArmorMass) + if (rSize > m_rMaxArmorSize) { - m_iMaxArmorMass = iMass; + m_rMaxArmorSize = rSize; m_sMaxArmorClass = m_ArmorLimits[i].sClass; m_pMaxArmorLimits = &m_ArmorLimits[i]; } @@ -109,9 +111,9 @@ ALERROR CArmorLimits::Bind (SDesignLoadCtx &Ctx) if (m_ArmorLimits[i].iSpeedAdj == 0) { - if (iMass > m_iStdArmorMass) + if (rSize > m_rStdArmorSize) { - m_iStdArmorMass = iMass; + m_rStdArmorSize = rSize; m_sStdArmorClass = m_ArmorLimits[i].sClass; m_pStdArmorLimits = &m_ArmorLimits[i]; } @@ -143,9 +145,9 @@ ALERROR CArmorLimits::Bind (SDesignLoadCtx &Ctx) return NOERROR; } -int CArmorLimits::CalcArmorMass (const CItemCtx &ArmorItem) const +Metric CArmorLimits::CalcArmorSize (const CItemCtx &ArmorItem) const -// CalcArmorMass +// CalcArmorSize // // Returns mass of the given armor item for purposes of calculating speed // adjustment. @@ -164,15 +166,15 @@ int CArmorLimits::CalcArmorMass (const CItemCtx &ArmorItem) const return 0; CString sID = pClass->GetMassClass(ArmorItem); - int iArmorMass = g_pUniverse->GetDesignCollection().GetArmorMassDefinitions().GetMassClassMass(sID); - if (iArmorMass == 0) - return ArmorItem.GetItem().GetMassKg(); + Metric rArmorSize = g_pUniverse->GetDesignCollection().GetArmorMassDefinitions().GetArmorClassSize(sID); + if (rArmorSize < g_Epsilon) + return ArmorItem.GetItem().GetVolume(); - return iArmorMass; + return rArmorSize; } default: - return ArmorItem.GetItem().GetMassKg(); + return ArmorItem.GetItem().GetVolume(); } } @@ -196,21 +198,21 @@ bool CArmorLimits::CalcArmorSpeedBonus (CItemCtx &ArmorItem, int iSegmentCount, case typeAutoSpeedAdj: case typeCompatible: { - int iArmorMass = CalcArmorMass(ArmorItem); + Metric rArmorSize = CalcArmorSize(ArmorItem); // Too heavy? - if (iArmorMass > GetMaxArmorMass()) + if (rArmorSize > GetMaxArmorSize()) return false; // Add up the total armor mass - int iTotalArmorMass = iSegmentCount * iArmorMass; + Metric rTotalArmorSize = iSegmentCount * rArmorSize; // Calculate speed bonus if (retiBonus) - *retiBonus = CalcArmorSpeedBonus(iSegmentCount, iTotalArmorMass); + *retiBonus = CalcArmorSpeedBonus(iSegmentCount, rTotalArmorSize); return true; } @@ -259,32 +261,32 @@ bool CArmorLimits::CalcArmorSpeedBonus (const CString &sArmorClassID, int iSegme case typeAutoSpeedAdj: case typeCompatible: { - const CArmorMassDefinitions &Def = g_pUniverse->GetDesignCollection().GetArmorMassDefinitions(); - int iArmorMass = Def.GetMassClassMass(sArmorClassID); - if (iArmorMass == 0) + const CArmorClassDefinitions &Def = g_pUniverse->GetDesignCollection().GetArmorMassDefinitions(); + Metric rArmorSize = Def.GetArmorClassSize(sArmorClassID); + if (rArmorSize == 0) return false; // Too heavy? - if (iArmorMass > GetMaxArmorMass()) + if (rArmorSize > GetMaxArmorSize()) return false; // Add up the total armor mass - int iTotalArmorMass = iSegmentCount * iArmorMass; + Metric rTotalArmorSize = iSegmentCount * rArmorSize; // Calculate speed bonus if (retiBonus) - *retiBonus = CalcArmorSpeedBonus(iSegmentCount, iTotalArmorMass); + *retiBonus = CalcArmorSpeedBonus(iSegmentCount, rTotalArmorSize); return true; } case typeTable: { - const CArmorMassDefinitions &Def = g_pUniverse->GetDesignCollection().GetArmorMassDefinitions(); - int iArmorMass = Def.GetMassClassMass(sArmorClassID); + const CArmorClassDefinitions &Def = g_pUniverse->GetDesignCollection().GetArmorMassDefinitions(); + Metric rArmorSize = Def.GetArmorClassSize(sArmorClassID); // Search @@ -294,9 +296,9 @@ bool CArmorLimits::CalcArmorSpeedBonus (const CString &sArmorClassID, int iSegme // Skip if the wrong mass class - if (pLimits->iMass) + if (pLimits->rSize) { - if (iArmorMass > pLimits->iMass) + if (rArmorSize > pLimits->rSize) continue; } else @@ -349,13 +351,13 @@ int CArmorLimits::CalcArmorSpeedBonus (const TArray &Armor) const { // Add up the armor mass - int iTotalArmorMass = 0; + Metric rTotalArmorSize = 0; for (int i = 0; i < Armor.GetCount(); i++) - iTotalArmorMass += CalcArmorMass(Armor[i]); + rTotalArmorSize += CalcArmorSize(Armor[i]); // Calculate speed bonus - return CalcArmorSpeedBonus(Armor.GetCount(), iTotalArmorMass); + return CalcArmorSpeedBonus(Armor.GetCount(), rTotalArmorSize); } case typeTable: @@ -391,7 +393,7 @@ int CArmorLimits::CalcArmorSpeedBonus (const TArray &Armor) const } } -int CArmorLimits::CalcArmorSpeedBonus (int iSegmentCount, int iTotalArmorMass) const +int CArmorLimits::CalcArmorSpeedBonus (int iSegmentCount, Metric rTotalArmorSize) const // CalcArmorSpeedBonus // @@ -411,42 +413,42 @@ int CArmorLimits::CalcArmorSpeedBonus (int iSegmentCount, int iTotalArmorMass) c ASSERT(m_iHullMass > 0); - // Compute the armor mass delta from the standard mass value as a - // fraction of the hull mass. The delta is positive if the total - // armor mass is lighter than standard and negative if heavier than - // standard. + // Compute the armor size delta from the standard size value as a + // fraction of the hull mass * massToSizeRatio. + // The delta is positive if the total armor mass is lighter than + // standard and negative if heavier than standard. - int iMassDelta = (m_iStdArmorMass * iSegmentCount) - iTotalArmorMass; - Metric rMassFrac = Absolute(iMassDelta) / (1000.0 * m_iHullMass); + Metric rSizeDelta = (m_rStdArmorSize * iSegmentCount) - rTotalArmorSize; + Metric rSizeFrac = Absolute(rSizeDelta) / (m_iHullMass * g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio()); // Scale the mass fraction to a speed adjustment value. - int iSpeedAdj = mathRound(MASS_TO_SPEED_ADJ_KX * pow(rMassFrac, MASS_TO_SPEED_ADJ_KE)); + int iSpeedAdj = mathRound(MASS_TO_SPEED_ADJ_KX * pow(rSizeFrac, MASS_TO_SPEED_ADJ_KE)); // Penalty or bonus - return (iMassDelta < 0 ? -iSpeedAdj : iSpeedAdj); + return (rSizeDelta < 0 ? -iSpeedAdj : iSpeedAdj); } case typeCompatible: { - int iStdTotalArmorMass = m_iStdArmorMass * iSegmentCount; + Metric rStdTotalArmorSize = m_rStdArmorSize * iSegmentCount; // Speed pentalty - if (iTotalArmorMass >= iStdTotalArmorMass) + if (rTotalArmorSize >= rStdTotalArmorSize) { if (m_iMaxArmorSpeedPenalty < 0 - && m_iMaxArmorMass > m_iStdArmorMass) + && m_rMaxArmorSize > m_rStdArmorSize) { - int iMaxTotalArmorMass = m_iMaxArmorMass * iSegmentCount; - int iRange = iMaxTotalArmorMass - iStdTotalArmorMass; - int iMassPerTick = iRange / (1 - m_iMaxArmorSpeedPenalty); - if (iMassPerTick <= 0) + Metric rMaxTotalArmorSize = m_rMaxArmorSize * iSegmentCount; + Metric rRange = rMaxTotalArmorSize - rStdTotalArmorSize; + Metric rSizePerInc = rRange / (1 - m_iMaxArmorSpeedPenalty); + if (rSizePerInc <= 0) return 0; - int iTicks = (iTotalArmorMass - iStdTotalArmorMass) / iMassPerTick; - return Max(-iTicks, m_iMaxArmorSpeedPenalty); + int iIncrements = (int)((rTotalArmorSize - rStdTotalArmorSize) / rSizePerInc); + return Max(-iIncrements, m_iMaxArmorSpeedPenalty); } else return 0; @@ -458,14 +460,14 @@ int CArmorLimits::CalcArmorSpeedBonus (int iSegmentCount, int iTotalArmorMass) c { if (m_iMinArmorSpeedBonus > 0) { - int iMinTotalArmorMass = m_iStdArmorMass * iSegmentCount / 2; - int iRange = iStdTotalArmorMass - iMinTotalArmorMass; - int iMassPerTick = iRange / m_iMinArmorSpeedBonus; - if (iMassPerTick <= 0) + Metric rMinTotalArmorSize = m_rStdArmorSize * iSegmentCount / 2; + Metric rRange = rStdTotalArmorSize - rMinTotalArmorSize; + Metric rSizePerInc = rRange / m_iMinArmorSpeedBonus; + if (rSizePerInc <= 0) return 0; - int iTicks = (iStdTotalArmorMass - iTotalArmorMass) / iMassPerTick; - return Min(iTicks, m_iMinArmorSpeedBonus); + int iIncrements = (int)((rStdTotalArmorSize - rTotalArmorSize) / rSizePerInc); + return Min(iIncrements, m_iMinArmorSpeedBonus); } else return 0; @@ -485,14 +487,14 @@ int CArmorLimits::CalcArmorSpeedBonus (int iSegmentCount, int iTotalArmorMass) c } } -ICCItemPtr CArmorLimits::CalcMaxSpeedByArmorMass (CCodeChainCtx &Ctx, int iStdSpeed) const - -// CalcMaxSpeedByArmorMass +// CalcMaxSpeedByArmorSize // // Returns a struct with entries for each value of max speed. Each entry has the // smallest armor mass which results in the given speed. // // If there is no variation in speed, we return a single speed value. +// +ICCItemPtr CArmorLimits::CalcMaxSpeedByArmorSize (CCodeChainCtx &Ctx, int iStdSpeed) const { ICCItemPtr pResult(ICCItem::SymbolTable); @@ -515,18 +517,18 @@ ICCItemPtr CArmorLimits::CalcMaxSpeedByArmorMass (CCodeChainCtx &Ctx, int iStdSp CString sLine; if (i == iMinSpeed) - sLine = strPatternSubst(CONSTLIT("%d-%d"), CalcMinArmorMassForSpeed(i, iStdSpeed), m_iMaxArmorMass); + sLine = strPatternSubst(CONSTLIT("%r-%r"), CalcMinArmorSizeForSpeed(i, iStdSpeed), m_rMaxArmorSize); else if (i == iMaxSpeed) { if (i == iStdSpeed && i > iMinSpeed) - sLine = strPatternSubst(CONSTLIT("0-%d"), CalcMinArmorMassForSpeed(i - 1, iStdSpeed) - 1); + sLine = strPatternSubst(CONSTLIT("0-%r"), CalcMinArmorSizeForSpeed(i - 1, iStdSpeed) - 1); else - sLine = strPatternSubst(CONSTLIT("0-%d"), CalcMinArmorMassForSpeed(i, iStdSpeed)); + sLine = strPatternSubst(CONSTLIT("0-%r"), CalcMinArmorSizeForSpeed(i, iStdSpeed)); } else if (i > iStdSpeed) - sLine = strPatternSubst(CONSTLIT("%d-%d"), CalcMinArmorMassForSpeed(i + 1, iStdSpeed) + 1, CalcMinArmorMassForSpeed(i, iStdSpeed)); + sLine = strPatternSubst(CONSTLIT("%r-%r"), CalcMinArmorSizeForSpeed(i + 1, iStdSpeed) + 1, CalcMinArmorSizeForSpeed(i, iStdSpeed)); else - sLine = strPatternSubst(CONSTLIT("%d-%d"), CalcMinArmorMassForSpeed(i, iStdSpeed), CalcMinArmorMassForSpeed(i - 1, iStdSpeed) - 1); + sLine = strPatternSubst(CONSTLIT("%r-%r"), CalcMinArmorSizeForSpeed(i, iStdSpeed), CalcMinArmorSizeForSpeed(i - 1, iStdSpeed) - 1); pResult->SetStringAt(strFromInt(i), sLine); } @@ -541,7 +543,7 @@ ICCItemPtr CArmorLimits::CalcMaxSpeedByArmorMass (CCodeChainCtx &Ctx, int iStdSp if (!m_ArmorLimits[i].sClass.IsBlank()) pResult->SetStringAt(strFromInt(iSpeed), m_ArmorLimits[i].sClass); else - pResult->SetStringAt(strFromInt(iSpeed), strFromInt(m_ArmorLimits[i].iMass)); + pResult->SetStringAt(strFromInt(iSpeed), strFromDouble(m_ArmorLimits[i].rSize)); } break; } @@ -554,54 +556,54 @@ ICCItemPtr CArmorLimits::CalcMaxSpeedByArmorMass (CCodeChainCtx &Ctx, int iStdSp return pResult; } -int CArmorLimits::CalcMinArmorMassForSpeed (int iSpeed, int iStdSpeed) const - -// CalcMinArmorMassForSpeed +// CalcMinArmorSizeForSpeed // -// Returns the smallest armor mass that is compatible with the given speed. +// Returns the smallest armor size that is compatible with the given speed. +// +Metric CArmorLimits::CalcMinArmorSizeForSpeed (int iSpeed, int iStdSpeed) const { int iMinSpeed = iStdSpeed + m_iMaxArmorSpeedPenalty; int iMaxSpeed = iStdSpeed + m_iMinArmorSpeedBonus; - int iPenaltyRange = m_iMaxArmorMass - m_iStdArmorMass; - int iPenaltyMassPerPoint = iPenaltyRange / (1 - m_iMaxArmorSpeedPenalty); + Metric rPenaltyRange = m_rMaxArmorSize - m_rStdArmorSize; + Metric rPenaltySizePerPoint = rPenaltyRange / (1 - m_iMaxArmorSpeedPenalty); - int iMinArmorMass = m_iStdArmorMass / 2; - int iBonusRange = m_iStdArmorMass - iMinArmorMass; - int iBonusMassPerPoint = (m_iMinArmorSpeedBonus > 0 ? iBonusRange / m_iMinArmorSpeedBonus : 0); + Metric rMinArmorSize = m_rStdArmorSize / 2; + Metric rBonusRange = m_rStdArmorSize - rMinArmorSize; + Metric rBonusSizePerPoint = (m_iMinArmorSpeedBonus > 0 ? rBonusRange / m_iMinArmorSpeedBonus : 0); if (iSpeed < iStdSpeed) { int iDiff = iStdSpeed - iSpeed; - return m_iStdArmorMass + (iPenaltyMassPerPoint * iDiff); + return m_rStdArmorSize + (rPenaltySizePerPoint * iDiff); } else if (iSpeed == iStdSpeed) { if (iMinSpeed == iMaxSpeed) - return m_iStdArmorMass; + return m_rStdArmorSize; else - return (m_iStdArmorMass - iBonusMassPerPoint) + 1; + return (m_rStdArmorSize - rBonusSizePerPoint) + 1; } else { int iDiff = iSpeed - iStdSpeed; - return m_iStdArmorMass - (iBonusMassPerPoint * iDiff); + return m_rStdArmorSize - (rBonusSizePerPoint * iDiff); } } -void CArmorLimits::CalcSummary (const CArmorMassDefinitions &Defs, SSummary &Summary) const - // CalcSummary // // Calculate some summary values about the limits. +// +void CArmorLimits::CalcSummary (const CArmorClassDefinitions &Defs, SSummary &Summary) const { switch (m_iType) { case typeNone: - Summary.iMaxArmorMass = 100000; - Summary.iStdArmorMass = 100000; + Summary.rMaxArmorSize = 100000; + Summary.rStdArmorSize = 100000; Summary.iMaxSpeedBonus = 0; Summary.iMaxSpeedPenalty = 0; Summary.rMaxArmorFrequency = 1.0; @@ -612,8 +614,8 @@ void CArmorLimits::CalcSummary (const CArmorMassDefinitions &Defs, SSummary &Sum case typeAutoSpeedAdj: case typeTable: { - Summary.iMaxArmorMass = GetMaxArmorMass(); - Summary.iStdArmorMass = GetStdArmorMass(); + Summary.rMaxArmorSize = GetMaxArmorSize(); + Summary.rStdArmorSize = GetStdArmorSize(); Summary.iMaxSpeedBonus = GetMinArmorSpeedBonus(); Summary.iMaxSpeedPenalty = GetMaxArmorSpeedPenalty(); Summary.rMaxArmorFrequency = Defs.GetFrequencyMax(m_sMaxArmorClass); @@ -623,8 +625,8 @@ void CArmorLimits::CalcSummary (const CArmorMassDefinitions &Defs, SSummary &Sum case typeCompatible: { - Summary.iMaxArmorMass = GetMaxArmorMass(); - Summary.iStdArmorMass = GetStdArmorMass(); + Summary.rMaxArmorSize = GetMaxArmorSize(); + Summary.rStdArmorSize = GetStdArmorSize(); Summary.iMaxSpeedBonus = GetMinArmorSpeedBonus(); Summary.iMaxSpeedPenalty = GetMaxArmorSpeedPenalty(); @@ -638,13 +640,13 @@ void CArmorLimits::CalcSummary (const CArmorMassDefinitions &Defs, SSummary &Sum continue; CItem Item(pItemType, 1); - int iMass = Item.GetMassKg(); + Metric rSize = Item.GetVolume(); iTotalArmor++; - if (iMass <= Summary.iStdArmorMass) + if (rSize <= Summary.rStdArmorSize) iTotalStdArmor++; - if (iMass <= Summary.iMaxArmorMass) + if (rSize <= Summary.rMaxArmorSize) iTotalMaxArmor++; } @@ -690,9 +692,9 @@ CArmorLimits::EResults CArmorLimits::CanInstallArmor (const CItem &Item) const case typeAutoSpeedAdj: case typeCompatible: { - int iArmorMass = CalcArmorMass(Item); - if (iArmorMass > GetMaxArmorMass()) - return resultTooHeavy; + Metric rArmorSize = CalcArmorSize(Item); + if (rArmorSize > GetMaxArmorSize()) + return resultTooLarge; return resultOK; } @@ -715,7 +717,7 @@ CArmorLimits::EResults CArmorLimits::CanInstallArmor (const CItem &Item) const // Otherwise, it means that we're too heavy. else - return resultTooHeavy; + return resultTooLarge; } return resultOK; @@ -727,11 +729,11 @@ CArmorLimits::EResults CArmorLimits::CanInstallArmor (const CItem &Item) const } } -bool CArmorLimits::FindArmorLimits (const CItemCtx &ItemCtx, const SArmorLimits **retpLimits, bool *retbClassFound) const - // FindArmorLimits // // Finds the armor limit descriptor for this armor item. +// +bool CArmorLimits::FindArmorLimits (const CItemCtx &ItemCtx, const SArmorLimits **retpLimits, bool *retbClassFound) const { if (retbClassFound) *retbClassFound = false; @@ -742,17 +744,17 @@ bool CArmorLimits::FindArmorLimits (const CItemCtx &ItemCtx, const SArmorLimits return false; const CString &sMassClass = pArmor->GetMassClass(ItemCtx); - int iMassKg = ArmorItem.GetMassKg(); + Metric rSize = ArmorItem.GetVolume(); for (int i = 0; i < m_ArmorLimits.GetCount(); i++) { const SArmorLimits *pLimits = &m_ArmorLimits[i]; - // Skip if the wrong mass class + // Skip if the wrong class - if (pLimits->iMass) + if (pLimits->rSize) { - if (iMassKg > pLimits->iMass) + if (rSize > pLimits->rSize) continue; } else @@ -785,12 +787,12 @@ bool CArmorLimits::FindArmorLimits (const CItemCtx &ItemCtx, const SArmorLimits return false; } -void CArmorLimits::InitDefaultArmorLimits (int iMass, int iMaxSpeed, Metric rThrustRatio) - // InitDefaultArmorLimits // // If no armor limits are specified, we initialize them here based on mass, // speed, and thrust +// +void CArmorLimits::InitDefaultArmorLimits (int iMass, int iMaxSpeed, Metric rThrustRatio) { // If we're 1000 tons or more, then no limits @@ -803,18 +805,17 @@ void CArmorLimits::InitDefaultArmorLimits (int iMass, int iMaxSpeed, Metric rThr const Metric MAX_ARMOR_POWER = 0.7; const Metric MAX_ARMOR_FACTOR = 0.6; const Metric STD_THRUST_RATIO = 7.0; - const int MAX_ARMOR_MAX = 50; + const Metric MAX_ARMOR_MAX = 50; + Metric rHullSize = iMass * g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio(); - int iMaxArmorTons = Min(MAX_ARMOR_MAX, mathRound(MAX_ARMOR_FACTOR * pow((Metric)iMass, MAX_ARMOR_POWER) * Max(1.0, rThrustRatio / STD_THRUST_RATIO))); - m_iMaxArmorMass = 1000 * iMaxArmorTons; + m_rMaxArmorSize = Min(MAX_ARMOR_MAX, MAX_ARMOR_FACTOR * pow(rHullSize, MAX_ARMOR_POWER) * Max(1.0, rThrustRatio / STD_THRUST_RATIO)); // Compute the mass of standard armor const Metric STD_ARMOR_POWER = 0.8; const Metric STD_ARMOR_FACTOR = 0.8; - int iStdArmorTons = mathRound(STD_ARMOR_FACTOR * pow((Metric)iMaxArmorTons, STD_ARMOR_POWER)); - m_iStdArmorMass = 1000 * iStdArmorTons; + m_rStdArmorSize = STD_ARMOR_FACTOR * pow(m_rMaxArmorSize, STD_ARMOR_POWER); // Compute the max speed at maximum armor @@ -843,14 +844,22 @@ ALERROR CArmorLimits::InitArmorLimitsFromXML (SDesignLoadCtx &Ctx, CXMLElement * // element might have other sub-elements that we don't understand. { - CString sMassClass = pLimits->GetAttribute(MASS_CLASS_ATTRIB); - int iMass = pLimits->GetAttributeIntegerBounded(MASS_ATTRIB, 1, -1, 0); + CString sArmorClass = pLimits->GetAttribute(MASS_CLASS_ATTRIB); + Metric rSize = pLimits->GetAttributeDoubleBounded(SIZE_ATTRIB, 1, -1, 0); + + // If the new size value isnt loaded or we are on an old API, try using mass + + if (rSize < g_Epsilon || Ctx.GetAPIVersion() < 59) + { + int iMass = pLimits->GetAttributeIntegerBounded(MASS_ATTRIB, 1, -1, 0); + rSize = iMass * g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio(); + } // Either massClass or mass must be defined. - if (sMassClass.IsBlank() && iMass == 0) + if (sArmorClass.IsBlank() && rSize < g_Epsilon) { - Ctx.sError = strPatternSubst(CONSTLIT("Invalid mass class: %s"), sMassClass); + Ctx.sError = strPatternSubst(CONSTLIT("Invalid armor class: %s"), sArmorClass); return ERR_FAIL; } @@ -865,8 +874,8 @@ ALERROR CArmorLimits::InitArmorLimitsFromXML (SDesignLoadCtx &Ctx, CXMLElement * // Now that we have all elements, create the entry. SArmorLimits &NewLimits = *m_ArmorLimits.Insert(); - NewLimits.sClass = sMassClass; - NewLimits.iMass = (sMassClass.IsBlank() ? iMass : 0); + NewLimits.sClass = sArmorClass; + NewLimits.rSize = (sArmorClass.IsBlank() ? rSize : 0); if (!sCriteria.IsBlank()) { @@ -919,21 +928,21 @@ ALERROR CArmorLimits::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, int CString sValue; if (pDesc->FindAttribute(MAX_ARMOR_ATTRIB, &sValue)) { - m_iMaxArmorMass = strToInt(sValue, -1); - if (m_iMaxArmorMass < 0) + m_rMaxArmorSize = strToDouble(sValue, -1.0); + if (m_rMaxArmorSize < 0 || IS_NAN(m_rMaxArmorSize)) m_sMaxArmorClass = sValue; } else - m_iMaxArmorMass = 0; + m_rMaxArmorSize = 0; if (pDesc->FindAttribute(STD_ARMOR_ATTRIB, &sValue)) { - m_iStdArmorMass = strToInt(sValue, -1); - if (m_iStdArmorMass < 0) + m_rStdArmorSize = strToDouble(sValue, -1.0); + if (m_rStdArmorSize < 0 || IS_NAN(m_rMaxArmorSize)) m_sStdArmorClass = sValue; } else - m_iStdArmorMass = 0; + m_rStdArmorSize = 0; // Speed bonus/penalty @@ -953,7 +962,7 @@ ALERROR CArmorLimits::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, int if (m_iMaxArmorSpeedPenalty || m_iMinArmorSpeedBonus) m_iType = typeCompatible; - else if (m_iMaxArmorMass || !m_sMaxArmorClass.IsBlank()) + else if (m_rMaxArmorSize || !m_sMaxArmorClass.IsBlank()) { if (m_iHullMass == 0) { diff --git a/Mammoth/TSE/CArmorMassDefinitions.cpp b/Mammoth/TSE/CArmorMassDefinitions.cpp index 5b7cf7a28..7fbaf7fad 100644 --- a/Mammoth/TSE/CArmorMassDefinitions.cpp +++ b/Mammoth/TSE/CArmorMassDefinitions.cpp @@ -7,12 +7,19 @@ #define CRITERIA_ATTRIB CONSTLIT("criteria") #define ID_ATTRIB CONSTLIT("id") +#define ID_ALIAS_ATTRIB CONSTLIT("idAlias") #define LABEL_ATTRIB CONSTLIT("label") +#define LABEL_SHORT_ATTRIB CONSTLIT("shortLabel") #define MASS_ATTRIB CONSTLIT("mass") +#define VOLUME_ATTRIB CONSTLIT("volume") -const CArmorMassDefinitions CArmorMassDefinitions::Null; +#define ARMOR_CLASS_ELEMENT CONSTLIT("ArmorClass") +#define ARMOR_CLASS_ELEMENT_LEGACY CONSTLIT("ArmorMass") +#define ARMOR_CLASS_ALIAS CONSTLIT("ArmorClassAlias") -void CArmorMassDefinitions::Append (const CArmorMassDefinitions &Src) +const CArmorClassDefinitions CArmorClassDefinitions::Null; + +void CArmorClassDefinitions::Append (const CArmorClassDefinitions &Src) // Append // @@ -25,14 +32,14 @@ void CArmorMassDefinitions::Append (const CArmorMassDefinitions &Src) InvalidateIDIndex(); } -CArmorMassDefinitions::SArmorMassEntry *CArmorMassDefinitions::FindMassEntryActual (const CItem &Item) +CArmorClassDefinitions::SArmorClassEntry *CArmorClassDefinitions::FindClassEntryActual (const CItem &Item) -// FindMassEntryActual +// FindClassEntryActual // // Returns the entry for the given item. { - int iMass = Item.GetMassKg(); + Metric rSize = Item.GetVolume(); // First look for any definitions with an explicit criteria. @@ -41,60 +48,60 @@ CArmorMassDefinitions::SArmorMassEntry *CArmorMassDefinitions::FindMassEntryActu if (m_Definitions.GetKey(i).IsBlank()) continue; - SArmorMassDefinition &Def = m_Definitions[i]; + SArmorClassDefinition &Def = m_Definitions[i]; if (!Item.MatchesCriteria(Def.Criteria)) continue; for (int j = 0; j < Def.Classes.GetCount(); j++) - if (iMass <= Def.Classes[j].iMaxMass) + if (rSize <= Def.Classes[j].rMaxSize) return &Def.Classes[j]; } // If not found, look for the default definition - SArmorMassDefinition *pDef = m_Definitions.GetAt(NULL_STR); + SArmorClassDefinition *pDef = m_Definitions.GetAt(NULL_STR); if (pDef == NULL) return NULL; for (int j = 0; j < pDef->Classes.GetCount(); j++) - if (iMass <= pDef->Classes[j].iMaxMass) + if (rSize <= pDef->Classes[j].rMaxSize) return &pDef->Classes[j]; return NULL; } -bool CArmorMassDefinitions::FindPreviousMassClass (const CString &sID, CString *retsPrevID, int *retiPrevMass) const - -// FindPreviousMassClass +// FindPreviousArmorClass // -// Finds the largest mass definition that is smaller than the given mass class. +// Finds the largest armor class definition that is smaller than the given armor class. +// +bool CArmorClassDefinitions::FindPreviousArmorClass (const CString &sID, CString *retsPrevID, Metric *retrPrevSize) const { // Find the armor class by ID - SArmorMassEntry *pMax; + SArmorClassEntry *pMax; if (!m_ByID.Find(sID, &pMax)) return false; // Now find the definition where this came from - const SArmorMassDefinition *pDef = m_Definitions.GetAt(pMax->sDefinition); + const SArmorClassDefinition *pDef = m_Definitions.GetAt(pMax->sDefinition); if (pDef == NULL) return false; // Look through the definition for the previous class. int iBest = -1; - int iBestMass = 0; + Metric rBestSize = 0; for (int i = 0; i < pDef->Classes.GetCount(); i++) { - const SArmorMassEntry &Entry = pDef->Classes[i]; + const SArmorClassEntry &Entry = pDef->Classes[i]; - if (Entry.iMaxMass < pMax->iMaxMass - && (iBest == -1 || Entry.iMaxMass > iBestMass)) + if (Entry.rMaxSize < pMax->rMaxSize + && (iBest == -1 || Entry.rMaxSize > rBestSize)) { iBest = i; - iBestMass = Entry.iMaxMass; + rBestSize = Entry.rMaxSize; } } @@ -106,38 +113,38 @@ bool CArmorMassDefinitions::FindPreviousMassClass (const CString &sID, CString * if (retsPrevID) *retsPrevID = pDef->Classes[iBest].sID; - if (retiPrevMass) - *retiPrevMass = pDef->Classes[iBest].iMaxMass; + if (retrPrevSize) + *retrPrevSize = pDef->Classes[iBest].rMaxSize; return true; } -Metric CArmorMassDefinitions::GetFrequencyMax (const CString &sID) const - // GetFrequencyMax // -// Returns the percent of all armor types that are at or below the given mass +// Returns the percent of all armor types that are at or below the given armor // class. This is used to determine what percent of armor types are usable by -// a ship class with a given armor mass limit. +// a ship class with a given armor size limit. // // E.g., if sID == "heavy" then we return the percent of all armor types that // are either ultraLight, light, medium, or heavy. // // NOTE: This call is only valid after all armor types have been bound. You may // call this after BindDesign +// +Metric CArmorClassDefinitions::GetFrequencyMax (const CString &sID) const { // Find the armor class by ID - SArmorMassEntry *pMax; + SArmorClassEntry *pMax; if (!m_ByID.Find(sID, &pMax)) return 0.0; - int iMaxMass = pMax->iMaxMass; + Metric rMaxSize = pMax->rMaxSize; // Now find the definition where this came from - const SArmorMassDefinition *pDef = m_Definitions.GetAt(pMax->sDefinition); + const SArmorClassDefinition *pDef = m_Definitions.GetAt(pMax->sDefinition); if (pDef == NULL) return 0.0; @@ -147,10 +154,10 @@ Metric CArmorMassDefinitions::GetFrequencyMax (const CString &sID) const int iTotal = 0; for (int i = 0; i < pDef->Classes.GetCount(); i++) { - const SArmorMassEntry &Entry = pDef->Classes[i]; + const SArmorClassEntry &Entry = pDef->Classes[i]; iTotal += Entry.iCount; - if (Entry.iMaxMass <= iMaxMass) + if (Entry.rMaxSize <= rMaxSize) iCount += Entry.iCount; } @@ -162,53 +169,53 @@ Metric CArmorMassDefinitions::GetFrequencyMax (const CString &sID) const return (Metric)iCount / (Metric)iTotal; } -const CString &CArmorMassDefinitions::GetMassClassID (const CItem &Item) const - -// GetMassClassID +// GetArmorClassID +// +// Finds the item and returns the armor class ID (or NULL_STR if not found). // -// Finds the item and returns the mass class ID (or NULL_STR if not found). +const CString &CArmorClassDefinitions::GetArmorClassID (const CItem &Item) const { - const SArmorMassEntry *pEntry = FindMassEntry(Item); + const SArmorClassEntry *pEntry = FindClassEntry(Item); if (pEntry == NULL) return NULL_STR; return pEntry->sID; } -const CString &CArmorMassDefinitions::GetMassClassLabel (const CString &sID) const - -// GetMassClassLabel +// GetArmorClassLabel // // Returns the text. +// +const CString &CArmorClassDefinitions::GetArmorClassLabel (const CString &sID) const { - SArmorMassEntry *pEntry; + SArmorClassEntry *pEntry; if (!m_ByID.Find(sID, &pEntry)) return NULL_STR; return pEntry->sText; } -int CArmorMassDefinitions::GetMassClassMass (const CString &sID) const - -// GetMassClassMass +// GetArmorClassSize // -// Returns the mass for the given mass class. +// Returns the size for the given armor class. +// +Metric CArmorClassDefinitions::GetArmorClassSize (const CString &sID) const { - SArmorMassEntry *pEntry; + SArmorClassEntry *pEntry; if (!m_ByID.Find(sID, &pEntry)) return 0; - return pEntry->iMaxMass; + return pEntry->rMaxSize; } -ALERROR CArmorMassDefinitions::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) - // InitFromXML // // Initialize from XML +// +ALERROR CArmorClassDefinitions::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) { // Read the criteria @@ -217,7 +224,7 @@ ALERROR CArmorMassDefinitions::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pD // Add a new definitions, for this criteria - SArmorMassDefinition *pDef = m_Definitions.SetAt(sCriteria); + SArmorClassDefinition *pDef = m_Definitions.SetAt(sCriteria); // Parse the criteria @@ -225,25 +232,105 @@ ALERROR CArmorMassDefinitions::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pD // Now read all the mass definitions + int iArmorClass = 0; + TSortMap Aliases; + for (int i = 0; i < pDesc->GetContentElementCount(); i++) { CXMLElement *pEntry = pDesc->GetContentElement(i); - int iMass = pEntry->GetAttributeIntegerBounded(MASS_ATTRIB, 1, -1, 0); - if (iMass == 0) + CString sTag = pEntry->GetTag(); + + // Prior to API59 subelements of ArmorClassDesc (ArmorMassDesc at the time) could be named anything + + if (strEquals(sTag, ARMOR_CLASS_ELEMENT) || strEquals(sTag, ARMOR_CLASS_ELEMENT_LEGACY) || Ctx.GetAPIVersion() < 59) + { + + int iMass = pEntry->GetAttributeIntegerBounded(MASS_ATTRIB, 1, -1, 0); + Metric rVolume = pEntry->GetAttributeDoubleBounded(VOLUME_ATTRIB, g_Epsilon, -1.0, 0.0); + + if (iMass && !rVolume) + { + Metric rDefaultMassToVolume = g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio(); + rVolume = rDefaultMassToVolume * iMass / 1000.0; + } + + if (rVolume < g_Epsilon) + { + Ctx.sError = CONSTLIT("Expected volume or mass attributes."); + CleanupDefinitions(); + return ERR_FAIL; + } + + // Insert + + SArmorClassEntry *pClass = pDef->Classes.SetAt(iArmorClass); + pClass->sDefinition = sCriteria; + pClass->sID = pEntry->GetAttribute(ID_ATTRIB); + pClass->sText = pEntry->GetAttribute(LABEL_ATTRIB); + pClass->sTextShort = pEntry->GetAttribute(LABEL_SHORT_ATTRIB); + if (pClass->sTextShort.IsBlank()) + pClass->sTextShort = pClass->sText; + pClass->rMaxSize = rVolume; + + pDef->Ids.Insert(pClass->sID, iArmorClass); + + // Prepare for next loop + + iArmorClass++; + } + else if (strEquals(sTag, ARMOR_CLASS_ALIAS)) { - Ctx.sError = CONSTLIT("Expected mass attributes."); - m_Definitions.DeleteAll(); + CString sId = pEntry->GetAttribute(ID_ATTRIB); + CString sAlias = pEntry->GetAttribute(ID_ALIAS_ATTRIB); + + if (sId.IsBlank() || sAlias.IsBlank()) + { + Ctx.sError = strPatternSubst(CONSTLIT("Armor class aliases require both id and idAlias to be defined and non-empty.")); + CleanupDefinitions(); + return ERR_FAIL; + } + + Aliases.Insert(sAlias, sId); + } + else + { + Ctx.sError = strPatternSubst(CONSTLIT("Expected ArmorClass or ArmorClassAlias but got %s instead."), sTag); + CleanupDefinitions(); return ERR_FAIL; } - // Insert + } + + for (int i = 0; i < Aliases.GetCount(); i++) + { + CString sAlias = Aliases.GetKey(i); + CString sId = Aliases.GetValue(i); - SArmorMassEntry *pMass = pDef->Classes.SetAt(iMass); - pMass->sDefinition = sCriteria; - pMass->sID = pEntry->GetAttribute(ID_ATTRIB); - pMass->sText = pEntry->GetAttribute(LABEL_ATTRIB); - pMass->iMaxMass = iMass; + if (Aliases.Find(sId)) + { + Ctx.sError = strPatternSubst(CONSTLIT("Attempted to add an alias (%s) that aliases another alias: %s"), sAlias, sId); + CleanupDefinitions(); + return ERR_FAIL; + } + + if (pDef->Ids.Find(sAlias)) + { + Ctx.sError = strPatternSubst(CONSTLIT("Attempted to add an alias (%s) that already exists as an Id"), sAlias); + CleanupDefinitions(); + return ERR_FAIL; + } + + int iClass; + + if (!pDef->Ids.Find(sId, &iClass)) + { + Ctx.sError = strPatternSubst(CONSTLIT("Attempted to add an alias (%s) for an id that does not exist (%s)"), sAlias, sId); + CleanupDefinitions(); + return ERR_FAIL; + } + + pDef->Ids.Insert(sAlias, iClass); } InvalidateIDIndex(); @@ -251,18 +338,18 @@ ALERROR CArmorMassDefinitions::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pD return NOERROR; } -void CArmorMassDefinitions::OnBindArmor (SDesignLoadCtx &Ctx, const CItem &Item, CString *retsMassClass) - // OnBindArmor // // This is called when we bind an armor type so that we can keep track of how // many armor types for each class. +// +void CArmorClassDefinitions::OnBindArmor (SDesignLoadCtx &Ctx, const CItem &Item, CString *retsArmorClass) { - SArmorMassEntry *pEntry = FindMassEntryActual(Item); + SArmorClassEntry *pEntry = FindClassEntryActual(Item); if (pEntry == NULL) { - if (retsMassClass) *retsMassClass = NULL_STR; + if (retsArmorClass) *retsArmorClass = NULL_STR; return; } @@ -272,15 +359,15 @@ void CArmorMassDefinitions::OnBindArmor (SDesignLoadCtx &Ctx, const CItem &Item, // Return the mass class - if (retsMassClass) - *retsMassClass = pEntry->sID; + if (retsArmorClass) + *retsArmorClass = pEntry->sID; } -void CArmorMassDefinitions::OnInitDone (void) - // OnInitDone // // This is called just before we start calling Bind on an armor type. +// +void CArmorClassDefinitions::OnInitDone (void) { // Initialize the index. @@ -289,10 +376,13 @@ void CArmorMassDefinitions::OnInitDone (void) for (int i = 0; i < m_Definitions.GetCount(); i++) { - SArmorMassDefinition &Def = m_Definitions[i]; - for (int j = 0; j < Def.Classes.GetCount(); j++) + SArmorClassDefinition &Def = m_Definitions[i]; + for (int j = 0; j < Def.Ids.GetCount(); j++) { - SArmorMassEntry &Entry = Def.Classes[j]; + CString sName = Def.Ids.GetKey(j); + int iIdx = Def.Ids.GetValue(j); + + SArmorClassEntry &Entry = Def.Classes[iIdx]; // While we're here, we initialize the counts, since we will soon // get called at OnBindArmor. @@ -301,7 +391,7 @@ void CArmorMassDefinitions::OnInitDone (void) // Add to the index. - m_ByID.SetAt(Entry.sID, &Entry); + m_ByID.SetAt(sName, &Entry); } } } diff --git a/Mammoth/TSE/CCExtensions.cpp b/Mammoth/TSE/CCExtensions.cpp index 08eacf5c1..76f0540da 100644 --- a/Mammoth/TSE/CCExtensions.cpp +++ b/Mammoth/TSE/CCExtensions.cpp @@ -87,6 +87,8 @@ ICCItem *fnFormat (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData); #define FN_ITEM_GET_STATIC_DATA_KEYS 40 #define FN_ITEM_GET_TYPE_DATA_KEYS 41 #define FN_ITEM_PROPERTY_KEYS 42 +#define FN_ITEM_VOLUME 43 +#define FN_ITEM_VOLUME_COMPAT 44 ICCItem *fnItemGetTypes (CEvalContext *pEvalCtx, ICCItem *pArguments, DWORD dwData); ICCItem *fnItemGet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData); @@ -880,8 +882,12 @@ static PRIMITIVEPROCDEF g_Extensions[] = "(itmGetLevel item|type) -> level", "v", 0, }, - { "itmGetMass", fnItemGet, FN_ITEM_MASS, + { "itmGetMassKg", fnItemGet, FN_ITEM_MASS, "(itmGetMass item|type) -> mass of single item in kg", + "v", 0, }, + + { "itmGetMass", fnItemGet, FN_ITEM_VOLUME_COMPAT, + "DEPRECATED: use (itmGetVolume item|type) instead. For Mass use (itmGetMassKg item|type)", "v", 0, }, { "itmGetMaxAppearing", fnItemGet, FN_ITEM_MAX_APPEARING, @@ -916,6 +922,10 @@ static PRIMITIVEPROCDEF g_Extensions[] = "(itmGetPrice item|type [currency]) -> price of a single item", "v*", 0, }, + { "itmGetVolume", fnItemGet, FN_ITEM_VOLUME, + "(itmGetVolume item|type) -> size of single item in CBM (real)", + "v", 0, }, + { "itm@Keys", fnItemGet, FN_ITEM_PROPERTY_KEYS, "(itm@Keys item|type) -> list of property keys", "v", 0, }, @@ -1181,7 +1191,10 @@ static PRIMITIVEPROCDEF g_Extensions[] = "(fmtNumber [type] value) -> string\n\n" "type:\n\n" - + + " 'CBM\n" + " 'CBMBasic\n" + " 'CBMInt\n" " 'integer\n" " 'massKg\n" " 'massTons\n" @@ -1910,7 +1923,10 @@ static PRIMITIVEPROCDEF g_Extensions[] = "il*", 0, }, { "objGetCargoSpaceLeft", fnObjGetOld, FN_OBJ_CARGO_SPACE_LEFT, - "(objGetCargoSpaceLeft obj) -> space left in kg", + "DEPRECATED: use (obj@ obj 'cargoSpaceFree) -> space left in cubic meters instead\n" + + " (objGetCargoSpaceLeft obj) -> space left in liters\n", + NULL, 0, }, { "objGetCharacterData", fnObjGet, FN_OBJ_GET_CHARACTER_DATA, @@ -2178,7 +2194,10 @@ static PRIMITIVEPROCDEF g_Extensions[] = " 'availableNonWeaponSlots\n" " 'availableWeaponSlots\n" " 'blindingImmune\n" - " 'cargoSpace -> in tons\n" + " 'cargoMassKg -> in kg\n" + " 'cargoSpace -> in CBM\n" + " 'cargoSpaceFree -> in CBM\n" + " 'cargoSpaceUsed -> in CBM\n" " 'counterIncrementRate\n" " 'counterValue\n" " 'character\n" @@ -3860,7 +3879,8 @@ static PRIMITIVEPROCDEF g_Extensions[] = " 'slotCategory\n" " 'treasureValue\n" " 'unknownType\n" - " 'useKey\n\n" + " 'useKey\n" + " 'volume\n\n" "field (player ships):\n\n" " 'dockServicesScreen UNID of dock services screen\n" @@ -5922,6 +5942,14 @@ ICCItem *fnItemGet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData) pResult = pCC->CreateInteger(Item.GetMassKg()); break; + case FN_ITEM_VOLUME: + pResult = pCC->CreateDouble(Item.GetVolume()); + break; + + case FN_ITEM_VOLUME_COMPAT: + pResult = pCC->CreateDouble(Item.GetVolume() * 1000); + break; + case FN_ITEM_MAX_APPEARING: pResult = pCC->CreateInteger(pType->GetNumberAppearing().GetMaxValue()); break; @@ -8399,7 +8427,7 @@ ICCItem *fnObjGetOld (CEvalContext *pEvalCtx, ICCItem *pArguments, DWORD dwData) pResult = pCC->CreateBool(pObj->CanAttack()); break; - case FN_OBJ_CARGO_SPACE_LEFT: + case FN_OBJ_CARGO_SPACE_LEFT: // This is for compatibility pResult = pCC->CreateInteger((int)(pObj->GetCargoSpaceLeft() * 1000.0)); break; diff --git a/Mammoth/TSE/CCargoDesc.cpp b/Mammoth/TSE/CCargoDesc.cpp index 4b47f846a..509a8775f 100644 --- a/Mammoth/TSE/CCargoDesc.cpp +++ b/Mammoth/TSE/CCargoDesc.cpp @@ -18,6 +18,16 @@ ALERROR CCargoDesc::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) // up more cargo space than we provide. m_iCargoSpace = pDesc->GetAttributeInteger(CARGO_SPACE_ATTRIB); + + // If we are from before cargo was switched to volume, we need to convert + // to that system instead based on the conversion ratio in engine options + + if (Ctx.GetAPIVersion() < 59) + { + Metric rXMLMassToVolume = g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio(); + m_iCargoSpace = mathRound(rXMLMassToVolume * m_iCargoSpace); + } + m_bUninitialized = false; return NOERROR; diff --git a/Mammoth/TSE/CCargoSpaceClass.cpp b/Mammoth/TSE/CCargoSpaceClass.cpp index a46251e84..7f1660749 100644 --- a/Mammoth/TSE/CCargoSpaceClass.cpp +++ b/Mammoth/TSE/CCargoSpaceClass.cpp @@ -245,13 +245,13 @@ CString CCargoSpaceClass::OnGetReference (CItemCtx &Ctx, const CItem &Ammo, DWOR int iCargoInc = Min(pDesc->GetCargoSpace(), Hull.GetMaxCargoSpace() - Hull.GetCargoSpace()); if (iCargoInc > 0) - sReference = strPatternSubst(CONSTLIT("+%d ton capacity"), iCargoInc); + sReference = strPatternSubst(CONSTLIT("+%d CBM capacity"), iCargoInc); } // Otherwise, describe the full amount else - sReference = strPatternSubst(CONSTLIT("%d ton capacity"), pDesc->GetCargoSpace()); + sReference = strPatternSubst(CONSTLIT("%d CBM capacity"), pDesc->GetCargoSpace()); // Done diff --git a/Mammoth/TSE/CDesignCollection.cpp b/Mammoth/TSE/CDesignCollection.cpp index 2412db08f..10dc7f584 100644 --- a/Mammoth/TSE/CDesignCollection.cpp +++ b/Mammoth/TSE/CDesignCollection.cpp @@ -324,7 +324,7 @@ ALERROR CDesignCollection::BindDesign (CUniverse &Universe, const TArrayGetArmorMassDefinitions(); + const CArmorClassDefinitions &ArmorDefinitions = pEntry->GetArmorMassDefinitions(); if (!ArmorDefinitions.IsEmpty()) m_ArmorDefinitions.Append(ArmorDefinitions); diff --git a/Mammoth/TSE/CDesignType.cpp b/Mammoth/TSE/CDesignType.cpp index 305a046ae..ced9beaf5 100644 --- a/Mammoth/TSE/CDesignType.cpp +++ b/Mammoth/TSE/CDesignType.cpp @@ -8,7 +8,8 @@ #define ACHIEVEMENTS_TAG CONSTLIT("Achievements") #define ADVENTURE_DESC_TAG CONSTLIT("AdventureDesc") -#define ARMOR_MASS_DESC_TAG CONSTLIT("ArmorMassDesc") +#define ARMOR_CLASS_LEGACY_DESC_TAG CONSTLIT("ArmorMassDesc") +#define ARMOR_CLASS_DESC_TAG CONSTLIT("ArmorClassDesc") #define ATTRIBUTE_DESC_TAG CONSTLIT("AttributeDesc") #define DATA_TAG CONSTLIT("Data") #define DISPLAY_ATTRIBUTES_TAG CONSTLIT("DisplayAttributes") @@ -3034,7 +3035,7 @@ ALERROR CDesignType::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, bool return ComposeLoadError(Ctx, Ctx.sError); } } - else if (strEquals(pItem->GetTag(), ARMOR_MASS_DESC_TAG)) + else if (strEquals(pItem->GetTag(), ARMOR_CLASS_DESC_TAG) || strEquals(pItem->GetTag(), ARMOR_CLASS_LEGACY_DESC_TAG)) { if (error = SetExtra()->ArmorDefinitions.InitFromXML(Ctx, pItem)) { diff --git a/Mammoth/TSE/CDoubleRangeCriteria.cpp b/Mammoth/TSE/CDoubleRangeCriteria.cpp new file mode 100644 index 000000000..50d15ff23 --- /dev/null +++ b/Mammoth/TSE/CDoubleRangeCriteria.cpp @@ -0,0 +1,207 @@ +// CDoubleRangeCriteria.cpp +// +// CDoubleRangeCriteria class +// Copryight (c) 2026 Kronosaur Productions, LLC. All Rights Reserved. + +#include "PreComp.h" + +// AsString +// +// Returns criteria as a string. +// +CString CDoubleRangeCriteria::AsString (char chModifier) const + + { + CString sModifier; + if (chModifier != '\0') + sModifier = CString(&chModifier, 1); + + if (IsEmpty()) + return NULL_STR; + else if (!IS_NAN(m_rEqualToValue)) + return strPatternSubst(CONSTLIT(" =%s%d;"), sModifier, m_rEqualToValue); + else if (IS_NAN(m_rLessThanValue)) + return strPatternSubst(CONSTLIT(" >%s%d;"), sModifier, m_rGreaterThanValue); + else if (IS_NAN(m_rGreaterThanValue)) + return strPatternSubst(CONSTLIT(" <%s%d;"), sModifier, m_rLessThanValue); + else + return strPatternSubst(CONSTLIT(" >%s%d; <%s%d;"), sModifier, m_rGreaterThanValue, sModifier, m_rLessThanValue); + } + +// GetRange +// +// Returns the match range. +// +bool CDoubleRangeCriteria::GetRange (Metric *retrMin, Metric *retrMax) const + + { + if (!IS_NAN(m_rEqualToValue)) + { + if (retrMin) *retrMin = m_rEqualToValue; + if (retrMax) *retrMax = m_rEqualToValue; + return true; + } + else if (IS_NAN(m_rLessThanValue) && IS_NAN(m_rGreaterThanValue)) + { + if (retrMin) *retrMin = R_NAN; + if (retrMax) *retrMax = R_NAN; + return false; + } + else + { + if (retrMax) + { + if (!IS_NAN(m_rLessThanValue)) + *retrMax = m_rLessThanValue - 1; + else + *retrMax = -1; + } + + if (retrMin) + { + if (!IS_NAN(m_rGreaterThanValue)) + *retrMin = m_rGreaterThanValue + 1; + else + *retrMin = -1; + } + + return true; + } + } + +// Matches +// +// Returns TRUE if we match the given value. +// +bool CDoubleRangeCriteria::Matches (Metric rValue) const + + { + if (!IS_NAN(m_rEqualToValue) && rValue != m_rEqualToValue) + return false; + + if (!IS_NAN(m_rGreaterThanValue) && rValue <= m_rGreaterThanValue) + return false; + + if (!IS_NAN(m_rLessThanValue) && rValue >= m_rLessThanValue) + return false; + + return true; + } + +// Parse +// +// Parses a criteria. We accept the following syntax: +// +// =n +// >n +// =n +// <=n +// +// =Mn +// >Mn +// =Mn +// <=Mn +// +// M:n +// M:n-m +// +// Where M is a modifier character and n is a non-negative integer. +// +bool CDoubleRangeCriteria::Parse (const char *pPos, const char **retpPos, char *retchModifier) + + { + // Initialize to defaults and short-circuit. + + *this = CDoubleRangeCriteria(); + if (pPos == NULL || *pPos == '\0') + return true; + + // Parse + + char chModifier = '\0'; + switch (*pPos) + { + case '=': + case '>': + case '<': + { + char chOp = *pPos++; + Metric rEqualAdj = 0; + + if (*pPos == '\0') + return false; + + if (*pPos == '=') + { + rEqualAdj = g_Epsilon; + pPos++; + } + + if (!(*pPos >= '0' && *pPos <= '9') && *pPos != '-' && *pPos != '+') + chModifier = *pPos++; + + bool bFailed; + Metric rValue = strParseDouble(pPos, 0.0, &pPos, &bFailed); + if (bFailed) + return false; + + if (chOp == '=') + m_rEqualToValue = rValue; + else if (chOp == '>') + m_rGreaterThanValue = rValue - rEqualAdj; + else if (chOp == '<') + m_rLessThanValue = rValue + rEqualAdj; + + if (retchModifier) + *retchModifier = chModifier; + + break; + } + + default: + { + chModifier = *pPos++; + if (*pPos != ':') + return false; + + pPos++; + + bool bFailed; + Metric rLowValue = strParseDouble(pPos, 0.0, &pPos, &bFailed); + if (bFailed) + return false; + + Metric rHighValue = R_NAN; + if (*pPos == '-') + { + pPos++; + + rHighValue = strParseDouble(pPos, 0.0, &pPos, &bFailed); + if (bFailed) + rHighValue = R_INF; + } + + if (IS_NAN(rHighValue)) + m_rEqualToValue = rLowValue; + else if (IS_P_INF(rHighValue)) + m_rGreaterThanValue = rLowValue - g_Epsilon; + else + { + m_rGreaterThanValue = rLowValue - g_Epsilon; + m_rLessThanValue = rHighValue + g_Epsilon; + } + + break; + } + } + + if (retpPos) + *retpPos = pPos; + + if (retchModifier) + *retchModifier = chModifier; + + return true; + } diff --git a/Mammoth/TSE/CEngineOptions.cpp b/Mammoth/TSE/CEngineOptions.cpp index b26655f9c..249aa619b 100644 --- a/Mammoth/TSE/CEngineOptions.cpp +++ b/Mammoth/TSE/CEngineOptions.cpp @@ -905,6 +905,14 @@ bool CEngineOptions::InitFromProperties (SDesignLoadCtx &Ctx, const CDesignType m_bHideRadiationImmune = !Type.GetProperty(CCX, PROPERTY_CORE_HIDE_RADIATION_IMMUNE)->IsNil(); m_bHideShatterImmune = !Type.GetProperty(CCX, PROPERTY_CORE_HIDE_SHATTER_IMMUNE)->IsNil(); + // Handle item mass-volume conversion + + pValue = Type.GetProperty(CCX, PROPERTY_CORE_ITEM_DEFAULT_DENSITY); + m_rDefaultItemDensity = (pValue->IsNil() ? 1.0 : pValue->GetDoubleValue()); + + pValue = Type.GetProperty(CCX, PROPERTY_CORE_ITEM_LEGACY_MASS_TO_VOLUME); + m_rDefaultItemMassToVolume = (pValue->IsNil() ? 1.0 : pValue->GetDoubleValue()); + // Handle shield power idle consumption pValue = Type.GetProperty(CCX, PROPERTY_CORE_ITEM_SHIELD_IDLE_POWER_ADJ); diff --git a/Mammoth/TSE/CHullDesc.cpp b/Mammoth/TSE/CHullDesc.cpp index 78374c854..91c2d90bc 100644 --- a/Mammoth/TSE/CHullDesc.cpp +++ b/Mammoth/TSE/CHullDesc.cpp @@ -76,6 +76,16 @@ ALERROR CHullDesc::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, int iMa m_iMass = pHull->GetAttributeInteger(MASS_ATTRIB); m_iSize = pHull->GetAttributeIntegerBounded(SIZE_ATTRIB, 1, -1, 0); m_iCargoSpace = pHull->GetAttributeIntegerBounded(CARGO_SPACE_ATTRIB, 0, -1, 0); + + // If we are from before cargo was switched to volume, we need to convert + // to that system instead based on the conversion ratio in engine options + + if (Ctx.GetAPIVersion() < 59) + { + Metric rXMLMassToVolume = g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio(); + m_iCargoSpace = mathRound(rXMLMassToVolume * m_iCargoSpace); + } + m_iLifeSupportPowerUse = pHull->GetAttributeIntegerBounded(LIFE_SUPPORT_POWER_USER_ATTRIB, 0, -1, CPowerConsumption::DEFAULT_LIFESUPPORT_POWER_USE); // Hull value @@ -99,6 +109,16 @@ ALERROR CHullDesc::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, int iMa // Maximums m_iMaxCargoSpace = pHull->GetAttributeIntegerBounded(MAX_CARGO_SPACE_ATTRIB, m_iCargoSpace, -1, m_iCargoSpace); + + // If we are from before cargo was switched to volume, we need to convert + // to that system instead based on the conversion ratio in engine options + + if (Ctx.GetAPIVersion() < 59) + { + Metric rXMLMassToVolume = g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio(); + m_iMaxCargoSpace = mathRound(rXMLMassToVolume * m_iMaxCargoSpace); + } + m_iMaxReactorPower = pHull->GetAttributeInteger(MAX_REACTOR_POWER_ATTRIB); // Device limits diff --git a/Mammoth/TSE/CItem.cpp b/Mammoth/TSE/CItem.cpp index 1d59c2e30..abd992d80 100644 --- a/Mammoth/TSE/CItem.cpp +++ b/Mammoth/TSE/CItem.cpp @@ -1517,11 +1517,11 @@ int CItem::GetLevel (void) const return GetType()->GetMinLevel(); } -int CItem::GetMassKg (void) const - // GetMass // // Returns the mass of a single unit of the item type. +// +int CItem::GetMassKg (void) const { CItemCtx ItemCtx(*this); @@ -2449,6 +2449,11 @@ bool CItem::MatchesCriteria (const CItemCriteria &Criteria) const if (!Criteria.MatchesMass(GetMassKg())) return false; + // Check for volume modifiers + + if (!Criteria.MatchesSize(GetVolume())) + return false; + // Check for repair level if (!Criteria.MatchesRepairLevel(GetRepairLevel())) diff --git a/Mammoth/TSE/CItemCriteria.cpp b/Mammoth/TSE/CItemCriteria.cpp index f6c714c88..d67202ec8 100644 --- a/Mammoth/TSE/CItemCriteria.cpp +++ b/Mammoth/TSE/CItemCriteria.cpp @@ -436,14 +436,14 @@ bool CItemCriteria::MatchesItemCategory (const CItemType &ItemType) const return true; } -void CItemCriteria::ParseSubExpression (const char *pPos, DWORD dwFlags) - // ParseSubExpression // // Parses a sub-expression and returns the first character after the end of the // sub-expression. // // NOTE: We assume that we are currently in the default state. +// +void CItemCriteria::ParseSubExpression (const char *pPos, DWORD dwFlags) { // Skip leading whitespace @@ -678,6 +678,9 @@ void CItemCriteria::ParseSubExpression (const char *pPos, DWORD dwFlags) m_bExcludeVirtual = false; break; + case '@': + m_SizeRange.Parse(pPos, &pPos); + case '+': case '-': { @@ -742,34 +745,44 @@ void CItemCriteria::ParseSubExpression (const char *pPos, DWORD dwFlags) case '>': case '<': { - CIntegerRangeCriteria Range; + CDoubleRangeCriteria RRange; char chModifier; + const char* pcEndPos; - if (Range.Parse(pPos, &pPos, &chModifier)) + if (RRange.Parse(pPos, &pcEndPos, &chModifier)) { - switch (chModifier) + if (chModifier == '@') + m_SizeRange = RRange; + else { - case '\0': - if (dwFlags & FLAG_REPAIR_LEVEL) - m_RepairLevelRange = Range; - else - m_LevelRange = Range; - break; - - case '$': - m_PriceRange = Range; - break; - - case '#': - m_MassRange = Range; - break; - - case 'R': - m_RepairLevelRange = Range; - break; + CIntegerRangeCriteria Range; + if (Range.Parse(pPos, &pcEndPos, &chModifier)) + { + switch (chModifier) + { + case '\0': + if (dwFlags & FLAG_REPAIR_LEVEL) + m_RepairLevelRange = Range; + else + m_LevelRange = Range; + break; + + case '$': + m_PriceRange = Range; + break; + + case '#': + m_MassRange = Range; + break; + + case 'R': + m_RepairLevelRange = Range; + break; + } + } } } - + pPos = pcEndPos; break; } } @@ -908,7 +921,7 @@ void CItemCriteria::WriteSubExpression (CMemoryWriteStream &Output) const if (!sTerm.IsBlank()) Output.Write(sTerm.GetPointer(), sTerm.GetLength()); - sTerm = m_MassRange.AsString('#'); + sTerm = m_SizeRange.AsString('#'); if (!sTerm.IsBlank()) Output.Write(sTerm.GetPointer(), sTerm.GetLength()); diff --git a/Mammoth/TSE/CItemType.cpp b/Mammoth/TSE/CItemType.cpp index 0efc074e5..ce24400f4 100644 --- a/Mammoth/TSE/CItemType.cpp +++ b/Mammoth/TSE/CItemType.cpp @@ -33,6 +33,7 @@ #define INSTANCE_DATA_ATTRIB CONSTLIT("charges") #define COMPLETE_ARMOR_ONLY_ATTRIB CONSTLIT("completeArmorOnly") #define DATA_ATTRIB CONSTLIT("data") +#define DENSITY_ATTRIB CONSTLIT("density") #define ENHANCEMENT_ATTRIB CONSTLIT("enhancement") #define FREQUENCY_ATTRIB CONSTLIT("frequency") #define ENABLED_ONLY_ATTRIB CONSTLIT("enabledOnly") @@ -40,6 +41,7 @@ #define KEY_ATTRIB CONSTLIT("key") #define LEVEL_ATTRIB CONSTLIT("level") #define MASS_BONUS_PER_CHARGE_ATTRIB CONSTLIT("massBonusPerCharge") +#define VOLUME_BONUS_PER_CHARGE_ATTRIB CONSTLIT("volumeBonusPerCharge") #define MAX_CHARGES_ATTRIB CONSTLIT("maxCharges") #define NO_SALE_IF_USED_ATTRIB CONSTLIT("noSaleIfUsed") #define NUMBER_APPEARING_ATTRIB CONSTLIT("numberAppearing") @@ -105,6 +107,7 @@ #define FIELD_TREASURE_VALUE CONSTLIT("treasureValue") #define FIELD_UNKNOWN_TYPE CONSTLIT("unknownType") #define FIELD_USE_KEY CONSTLIT("useKey") +#define FIELD_VOLUME CONSTLIT("volume") #define PROPERTY_CATEGORY CONSTLIT("category") #define PROPERTY_COMPONENT_PRICE CONSTLIT("componentPrice") @@ -144,6 +147,7 @@ static char g_NameAttrib[] = "name"; static char g_ObjectAttrib[] = "object"; static char g_MassAttrib[] = "mass"; +static char g_VolumeAttrib[] = "volume"; static char g_DescriptionAttrib[] = "description"; static char g_RandomDamagedAttrib[] = "randomDamaged"; @@ -391,6 +395,9 @@ bool CItemType::FindDataField (const CString &sField, CString *retsValue) const else if (strEquals(sField, FIELD_MASS)) *retsValue = strFromInt(CItem(const_cast(this), 1).GetMassKg()); + + else if (strEquals(sField, FIELD_VOLUME)) + *retsValue = strFromDouble(CItem(const_cast(this), 1).GetVolume()); else if (strEquals(sField, FIELD_SHORT_NAME)) { @@ -917,11 +924,11 @@ CString CItemType::GetItemCategory (ItemCategories iCategory) } } -int CItemType::GetMassKg (CItemCtx &Ctx) const - // GetMassKg // // Returns the mass of the item in kilograms +// +int CItemType::GetMassKg(CItemCtx &Ctx) const { if (m_iExtraMassPerCharge) @@ -935,6 +942,24 @@ int CItemType::GetMassKg (CItemCtx &Ctx) const return m_iMass; } +// GetVolume +// +// Returns the mass of the item in kilograms +// +Metric CItemType::GetVolume (CItemCtx &Ctx) const + + { + if (m_rExtraVolumePerCharge) + { + if (Ctx.IsItemNull()) + return m_rVolume + (m_InitDataValue.GetAveValue() * m_rExtraVolumePerCharge); + else + return m_rVolume + (Ctx.GetItem().GetCharges() * m_rExtraVolumePerCharge); + } + else + return m_rVolume; + } + int CItemType::GetMaxHPBonus (void) const // GetMaxHPBonus @@ -1539,6 +1564,54 @@ ALERROR CItemType::OnCreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) m_sRole = pDesc->GetAttribute(ROLE_ATTRIB); m_iMass = pDesc->GetAttributeInteger(CONSTLIT(g_MassAttrib)); + m_rVolume = pDesc->GetAttributeDouble(CONSTLIT(g_VolumeAttrib)); + Metric rDensity = pDesc->GetAttributeDouble(DENSITY_ATTRIB); + bool bUsesMassCompatibility = false; + + // Volume and density are not available in old versions + + if (Ctx.GetAPIVersion() < 59 && (m_rVolume or rDensity)) + return ComposeLoadError(Ctx, CONSTLIT("Item volume and density are not available in APIs below 59")); + + // For backwards compatibility, if something has no volume but has mass, we scale volume to mass in metric tons + // We then use engine options to determine the actual mass that the item should have + + if (!m_rVolume) + { + Metric rXMLMassToVolume = g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio(); + Metric rDefaultDensity = g_pUniverse->GetEngineOptions().GetItemDefaultDensity(); + + // If an item has been improperly converted and only specifies density, we emit a warning + // This is because volume is now the hard-balance factor, not mass + // We use the adventure default density to encourage the author to fix the XML + + if (rDensity) + kernelDebugLogPattern(CONSTLIT("Warning: Item %s (%x) specified density without specifying a volume. Using adventure default density instead."), m_sName, GetUNID()); + + m_rVolume = rXMLMassToVolume * m_iMass * 0.001; + m_iMass = mathRound(rDefaultDensity * 1000 * m_rVolume); + + // We need to remember that we are in compatibility mode + + bUsesMassCompatibility = true; + } + + // If we are missing mass we need to compute it, or we have explicit density + + else if (!m_iMass || rDensity) + { + // Emit a warning if mass is still populated + + if (m_iMass) + kernelDebugLogPattern(CONSTLIT("Warning: Item %s (%x) specified mass as well as volume and density. Mass is being ignored."), m_sName, GetUNID()); + + // if we dont have density we grab the adventure default + + if (!rDensity) + rDensity = g_pUniverse->GetEngineOptions().GetItemDefaultDensity(); + + m_iMass = mathRound(rDensity * 1000 * m_rVolume); + } if (error = m_iValue.InitFromXML(Ctx, pDesc->GetAttribute(VALUE_ATTRIB))) return ComposeLoadError(Ctx, Ctx.sError); @@ -1598,9 +1671,19 @@ ALERROR CItemType::OnCreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) m_iExtraMassPerCharge = pDesc->GetAttributeIntegerBounded(MASS_BONUS_PER_CHARGE_ATTRIB, 0, -1, 0); m_iExtraValuePerCharge = pDesc->GetAttributeInteger(VALUE_BONUS_PER_CHARGE_ATTRIB); // May be negative + m_rExtraVolumePerCharge = pDesc->GetAttributeDoubleBounded(VOLUME_BONUS_PER_CHARGE_ATTRIB, 0, -1.0, 0); m_fAmmoCharges = pDesc->GetAttributeBool(AMMO_CHARGES_ATTRIB); m_fValueCharges = pDesc->GetAttributeBool(VALUE_CHARGES_ATTRIB); + if (bUsesMassCompatibility && m_rExtraVolumePerCharge < 0) + { + Metric rXMLMassToVolume = g_pUniverse->GetEngineOptions().GetItemXMLMassToVolumeRatio(); + Metric rDefaultDensity = g_pUniverse->GetEngineOptions().GetItemDefaultDensity(); + + m_rExtraVolumePerCharge = rXMLMassToVolume * m_iExtraMassPerCharge * 0.001; + m_iExtraMassPerCharge = mathRound(m_rExtraVolumePerCharge * rDefaultDensity * 1000); + } + // Flags m_fNoSaleIfUsed = pDesc->GetAttributeBool(NO_SALE_IF_USED_ATTRIB); diff --git a/Mammoth/TSE/CLanguage.cpp b/Mammoth/TSE/CLanguage.cpp index 89ae8fce0..b0bb44e15 100644 --- a/Mammoth/TSE/CLanguage.cpp +++ b/Mammoth/TSE/CLanguage.cpp @@ -823,6 +823,24 @@ CString CLanguage::ComposeNumber (ENumberFormatTypes iFormat, Metric rNumber, SN break; } + case numberCBM: + return strCat(ComposeNumber(numberMetric, rNumber, pOptions), CONSTLIT("CBM")); + + case numberCBMBasic: + { + if (rNumber >= 1000.0) + return ComposeNumber(numberCBMInt, rNumber, pOptions); + else if (rNumber < 0.001) + return CONSTLIT("0 mCBM"); + else if (rNumber < 1.0) + return strCat(strFromInt((int)mathRound(rNumber * 1000)), CONSTLIT(" mCBM")); + else + return ComposeNumber(numberCBM, rNumber, pOptions); + } + + case numberCBMInt: + return strCat(strFromInt((int)mathRound(rNumber)), CONSTLIT(" CBM")); + // For massTons, we need to convert the number to kgs case numberMassTons: diff --git a/Mammoth/TSE/CShip.cpp b/Mammoth/TSE/CShip.cpp index 90ed9bbb1..26ee8d4ef 100644 --- a/Mammoth/TSE/CShip.cpp +++ b/Mammoth/TSE/CShip.cpp @@ -1095,7 +1095,7 @@ bool CShip::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &S // See if the armor is too heavy - else if (iCanInstall == CArmorLimits::resultTooHeavy) + else if (iCanInstall == CArmorLimits::resultTooLarge) iResult = insArmorTooHeavy; // Fire CanBeInstalled to check for custom conditions @@ -1251,7 +1251,7 @@ bool CShip::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &S && pNewCargo->GetCargoSpace() < pOldCargo->GetCargoSpace()) { OnComponentChanged(comCargo); - Metric rRequiredCargoSpace = GetCargoMass() + ItemToReplace.GetMass() - Item.GetMass(); + Metric rRequiredCargoSpace = GetCargoVolume() + ItemToReplace.GetVolume() - Item.GetVolume(); Metric rNewCargoSpace = (Metric)Hull.GetCargoSpace() + pNewCargo->GetCargoSpace(); if (rRequiredCargoSpace > rNewCargoSpace) @@ -1371,7 +1371,7 @@ CShip::RemoveDeviceStatus CShip::CanRemoveDevice (const CItem &Item, CString *re // Compute how much cargo space we need to be able to hold OnComponentChanged(comCargo); - Metric rCargoSpace = GetCargoMass() + Item.GetMass(); + Metric rCargoSpace = GetCargoVolume() + Item.GetVolume(); // If this is larger than the ship class max, then we cannot remove @@ -2683,11 +2683,11 @@ CSpaceObject *CShip::GetBase (void) const return m_pController->GetBase(); } -Metric CShip::GetCargoMass (void) const - // GetCargoMass // // Returns the total mass of all items in cargo (in metric tons). +// +Metric CShip::GetCargoMass (void) const { if (m_fRecalcItemMass) @@ -2699,11 +2699,26 @@ Metric CShip::GetCargoMass (void) const return m_rCargoMass; } -Metric CShip::GetCargoSpaceLeft (void) const +// GetCargoVolume +// +// Returns the total volume of all items in cargo (in cubic meters). +// +Metric CShip::GetCargoVolume(void) const + { + if (m_fRecalcItemVolume) + { + m_rItemVolume = CalculateItemVolume(&m_rCargoVolume); + m_fRecalcItemVolume = false; + } + + return m_rCargoVolume; + } // GetCargoSpaceLeft // -// Returns the amount of cargo space left in tons +// Returns the amount of cargo space left in CBM (cubic meters) +// +Metric CShip::GetCargoSpaceLeft () const { // Compute total cargo space. Start with the space specified @@ -2713,11 +2728,11 @@ Metric CShip::GetCargoSpaceLeft (void) const // Recompute cargo mass - InvalidateItemMass(); + InvalidateItemVolume(); // Compute space left - return Max(0.0, rCargoSpace - GetCargoMass()); + return Max(0.0, rCargoSpace - GetCargoVolume()); } int CShip::GetCombatPower (void) @@ -3003,11 +3018,11 @@ int CShip::GetItemDeviceName (const CItem &Item) const return m_Devices.FindNamedIndex(Item); } -Metric CShip::GetItemMass (void) const - // GetItemMass // // Returns the total mass of all items (in metric tons). +// +Metric CShip::GetItemMass (void) const { if (m_fRecalcItemMass) @@ -3019,6 +3034,22 @@ Metric CShip::GetItemMass (void) const return m_rItemMass; } +// GetItemVolume +// +// Returns the total volume of all items (in cubic meters). +// +Metric CShip::GetItemVolume(void) const + + { + if (m_fRecalcItemVolume) + { + m_rItemVolume = CalculateItemVolume(&m_rCargoVolume); + m_fRecalcItemVolume = false; + } + + return m_rItemVolume; + } + Metric CShip::GetInvMass (void) const // GetInvMass @@ -3041,11 +3072,11 @@ Metric CShip::GetInvMass (void) const return (1.0 / rMass); } -Metric CShip::GetMass (void) const - // GetMass // // Returns the mass of the object in metric tons +// +Metric CShip::GetMass (void) const { return m_pClass->GetHullDesc().GetMass() + GetItemMass(); @@ -4353,11 +4384,12 @@ void CShip::OnComponentChanged (ObjectComponentTypes iComponent) case comCargo: { - // Calculate new mass + // Calculate new mass and volume // // NOTE: In this case we defer recalculating performance until update. m_fRecalcItemMass = true; + m_fRecalcItemVolume = true; if (m_fTrackMass) m_fRecalcRotationAccel = true; @@ -5354,9 +5386,10 @@ void CShip::OnItemEnhanced (CItemListManipulator &ItemList) if (ItemList.GetItemAtCursor().IsInstalled()) { - // Update item mass in case the mass of this item changed + // Update item mass and volume in case the mass or volume of this item changed m_fRecalcItemMass = true; + m_fRecalcItemVolume = true; // Recalc performance @@ -5904,6 +5937,11 @@ void CShip::OnReadFromStream (SLoadCtx &Ctx) if (Ctx.dwVersion >= 2) Ctx.pStream->Read(m_rItemMass); Ctx.pStream->Read(m_rCargoMass); + if (Ctx.dwVersion >= 219) + { + Ctx.pStream->Read(m_rItemVolume); + Ctx.pStream->Read(m_rCargoVolume); + } CSystem::ReadObjRefFromStream(Ctx, &m_pDocked); CSystem::ReadObjRefFromStream(Ctx, &m_pExitGate); if (Ctx.dwVersion >= 42) @@ -5922,12 +5960,17 @@ void CShip::OnReadFromStream (SLoadCtx &Ctx) bool bBit01 = ((dwLoad & 0x00000001) ? true : false); m_fRadioactive = ((dwLoad & 0x00000002) ? true : false); - if (Ctx.dwVersion >= 155 && Ctx.dwVersion < 185) + if (Ctx.dwVersion >= 219) + { + if (dwLoad & 0x00000004) + InvalidateItemVolume(); + } + // 0x00000004 Unused as of version 185, used again in 219 for a different flag + else if (Ctx.dwVersion >= 155 && Ctx.dwVersion < 185) { if (dwLoad & 0x00000004) SetAutoCreatedPorts(true); } - // 0x00000004 Unused as of version 185 m_fDestroyInGate = ((dwLoad & 0x00000008) ? true : false); m_fHalfSpeed = ((dwLoad & 0x00000010) ? true : false); m_fNameBlanked = ((dwLoad & 0x00000020) ? true : false) && (Ctx.dwVersion >= 155); @@ -6133,11 +6176,13 @@ void CShip::OnReadFromStream (SLoadCtx &Ctx) m_pEncounterInfo = Ctx.GetUniverse().FindStationType(dwLoad); } - // Calculate item mass, if appropriate. We do this for previous versions - // that did not store item mass. + // Calculate item mass and volume, if appropriate. We do this for previous versions + // that did not store item mass or volume. if (Ctx.dwVersion < 2) m_rItemMass = CalculateItemMass(&m_rCargoMass); + if (Ctx.dwVersion < 219) + m_rItemVolume = CalculateItemVolume(&m_rCargoVolume); // Irradiation source @@ -6373,6 +6418,8 @@ void CShip::OnWriteToStream (IWriteStream *pStream) // DWORD low = m_iLRSBlindnessTimer; hi = m_iDriveDamagedTimer // Metric m_rItemMass // Metric m_rCargoMass +// Metric m_rItemVolume +// Metric m_rCargoVolume // DWORD m_pDocked (CSpaceObject ref) // DWORD m_pExitGate (CSpaceObject ref) // DWORD m_iLastFireTime @@ -6439,6 +6486,8 @@ void CShip::OnWriteToStream (IWriteStream *pStream) pStream->Write(m_rItemMass); pStream->Write(m_rCargoMass); + pStream->Write(m_rItemVolume); + pStream->Write(m_rCargoVolume); WriteObjRefToStream(m_pDocked, pStream); WriteObjRefToStream(m_pExitGate, pStream); pStream->Write(m_iLastFireTime); @@ -6447,7 +6496,7 @@ void CShip::OnWriteToStream (IWriteStream *pStream) dwSave = 0; dwSave |= (m_fLRSDisabledByNebula ? 0x00000001 : 0); dwSave |= (m_fRadioactive ? 0x00000002 : 0); - // 0x00000004 Unused as of 185 + dwSave |= (m_fRecalcItemVolume ? 0x00000004 : 0); dwSave |= (m_fDestroyInGate ? 0x00000008 : 0); dwSave |= (m_fHalfSpeed ? 0x00000010 : 0); dwSave |= (m_fNameBlanked ? 0x00000020 : 0); diff --git a/Mammoth/TSE/CShipClass.cpp b/Mammoth/TSE/CShipClass.cpp index 8319c1c26..79101dec5 100644 --- a/Mammoth/TSE/CShipClass.cpp +++ b/Mammoth/TSE/CShipClass.cpp @@ -133,7 +133,8 @@ #define FIELD_MANEUVER CONSTLIT("maneuver") #define FIELD_MANUFACTURER CONSTLIT("manufacturer") #define FIELD_MASS CONSTLIT("mass") -#define FIELD_MAX_ARMOR_MASS CONSTLIT("maxArmorMass") +#define FIELD_MAX_ARMOR_MASS CONSTLIT("maxArmorMass") // deprecated, use maxArmorSize instead +#define FIELD_MAX_ARMOR_SIZE CONSTLIT("maxArmorSize") #define FIELD_MAX_CARGO_SPACE CONSTLIT("maxCargoSpace") #define FIELD_MAX_ROTATION CONSTLIT("maxRotation") #define FIELD_MAX_SPEED CONSTLIT("maxSpeed") @@ -950,12 +951,12 @@ Metric CShipClass::CalcManeuverValue (bool bDodge) const return rDodge; } -Metric CShipClass::CalcMass (const CDeviceDescList &Devices) const - // CalcMass // // Returns the total mass of the ship class, including devices and armor // (in tons). +// +Metric CShipClass::CalcMass (const CDeviceDescList &Devices) const { int i; @@ -972,18 +973,18 @@ Metric CShipClass::CalcMass (const CDeviceDescList &Devices) const return rMass; } -ICCItemPtr CShipClass::CalcMaxSpeedByArmorMass (CCodeChainCtx &Ctx) const - -// CalcMaxSpeedByArmorMass +// CalcMaxSpeedByArmorSize // // Returns a struct with entries for each value of max speed. Each entry has the // smallest armor mass which results in the given speed. // // If there is no variation in speed, we return a single speed value. +// +ICCItemPtr CShipClass::CalcMaxSpeedByArmorMass (CCodeChainCtx &Ctx) const { int iStdSpeed = mathRound(100.0 * m_Perf.GetDriveDesc().GetMaxSpeed() / LIGHT_SPEED); - return m_Hull.GetArmorLimits().CalcMaxSpeedByArmorMass(Ctx, iStdSpeed); + return m_Hull.GetArmorLimits().CalcMaxSpeedByArmorSize(Ctx, iStdSpeed); } void CShipClass::CalcPerformance (void) @@ -1966,8 +1967,8 @@ bool CShipClass::FindDataField (const CString &sField, CString *retsValue) const else if (strEquals(sField, FIELD_GENERIC_NAME)) *retsValue = GetGenericName(); - else if (strEquals(sField, FIELD_MAX_ARMOR_MASS)) - *retsValue = strFromInt(m_Hull.GetArmorLimits().GetMaxArmorMass()); + else if (strEquals(sField, FIELD_MAX_ARMOR_SIZE) || strEquals(sField, FIELD_MAX_ARMOR_MASS)) + *retsValue = strFromDouble(m_Hull.GetArmorLimits().GetMaxArmorSize()); else if (strEquals(sField, FIELD_HULL_MASS)) *retsValue = strFromInt(m_Hull.GetMass()); diff --git a/Mammoth/TSE/CSpaceObject.cpp b/Mammoth/TSE/CSpaceObject.cpp index a239943d4..b7429b3bd 100644 --- a/Mammoth/TSE/CSpaceObject.cpp +++ b/Mammoth/TSE/CSpaceObject.cpp @@ -756,11 +756,11 @@ CSpaceObject *CSpaceObject::CalcTargetToAttack (CSpaceObject *pAttacker, CSpaceO return pAttacker; } -Metric CSpaceObject::CalculateItemMass (Metric *retrCargoMass) const - // CalculateCargoMass // // Returns the total mass of the items +// +Metric CSpaceObject::CalculateItemMass (Metric *retrCargoMass) const { int i; @@ -789,6 +789,41 @@ Metric CSpaceObject::CalculateItemMass (Metric *retrCargoMass) const return rTotal; } +// CalculateCargoVolume +// +// Returns the total volume of the items in cargo +// This number is needed if unequipping all items on a ship +// and transfering them to another inventory +// retrCargoVolume excludes installed items +// +Metric CSpaceObject::CalculateItemVolume(Metric *retrCargoVolume) const + { + int i; + Metric rTotal = 0.0; + Metric rTotalCargo = 0.0; + + for (i = 0; i < m_ItemList.GetCount(); i++) + { + const CItem &Item = m_ItemList.GetItem(i); + + Metric rVolume = Item.GetVolume() * Item.GetCount(); + + // All items count towards item mass + + rTotal += rVolume; + + // Only uninstalled items count in cargo space + + if (!Item.IsInstalled()) + rTotalCargo += rVolume; + } + + if (retrCargoVolume) + *retrCargoVolume = rTotalCargo; + + return rTotal; + } + EConditionResult CSpaceObject::CanApplyCondition (ECondition iCondition, const SApplyConditionOptions &Options) const // CanApplyCondition diff --git a/Mammoth/TSE/CStation.cpp b/Mammoth/TSE/CStation.cpp index 4e38da02e..1c50e3585 100644 --- a/Mammoth/TSE/CStation.cpp +++ b/Mammoth/TSE/CStation.cpp @@ -4698,13 +4698,13 @@ void CStation::OnUpdate (SUpdateCtx &Ctx, Metric rSecondsPerTick) { // See if all items fit. - int iTotalMassKg = 0; + Metric rTotalItemVolume = 0; const auto &ItemList = GetItemList(); for (int i = 0; i < ItemList.GetCount(); i++) - iTotalMassKg += ItemList.GetItem(i).GetMassKg() * ItemList.GetItem(i).GetCount(); + rTotalItemVolume += ItemList.GetItem(i).GetVolume() * ItemList.GetItem(i).GetCount(); - Metric rSpaceLeftInTons = Ctx.GetPlayerShip()->GetCargoSpaceLeft(); - if (iTotalMassKg <= 1000.0 * rSpaceLeftInTons) + Metric rSpaceLeft = Ctx.GetPlayerShip()->GetCargoSpaceLeft(); + if (rTotalItemVolume <= rSpaceLeft) { // Take all items diff --git a/Mammoth/TSE/CWeaponClass.cpp b/Mammoth/TSE/CWeaponClass.cpp index f4e8afcad..c21df7b8b 100644 --- a/Mammoth/TSE/CWeaponClass.cpp +++ b/Mammoth/TSE/CWeaponClass.cpp @@ -506,7 +506,7 @@ int CWeaponClass::CalcBalance (const CItem &Ammo, SBalance &retBalance) const // Adjust the standard ammo cost and standard ammo mass for fire rate. retBalance.rStdAmmoCost = Stats.rAmmoCost * rFireDelay / STD_FIRE_DELAY_TICKS; - retBalance.rStdAmmoMass = STD_AMMO_MASS * rFireDelay / STD_FIRE_DELAY_TICKS; + retBalance.rStdAmmoSize = STD_AMMO_VOLUME * rFireDelay / STD_FIRE_DELAY_TICKS; // Compute the standard ammo cost at this level and figure out the // percent cost difference. +1 = ammo is 1% more expensive than @@ -521,11 +521,11 @@ int CWeaponClass::CalcBalance (const CItem &Ammo, SBalance &retBalance) const // Compute the ammo mass bonus - Metric rAmmoMass = pAmmoType->GetMassKg(AmmoItemCtx); + Metric rAmmoVolume = pAmmoType->GetVolume(AmmoItemCtx); if (pAmmoType->AreChargesAmmo() && pAmmoType->GetMaxCharges() > 0) - rAmmoMass /= (Metric)pAmmoType->GetMaxCharges(); + rAmmoVolume /= (Metric)pAmmoType->GetMaxCharges(); - Metric rAmmoMassDelta = 100.0 * (rAmmoMass - retBalance.rStdAmmoMass) / retBalance.rStdAmmoMass; + Metric rAmmoMassDelta = 100.0 * (rAmmoVolume - retBalance.rStdAmmoSize) / retBalance.rStdAmmoSize; retBalance.rAmmo += rAmmoMassDelta * BALANCE_AMMO_MASS_RATIO; // Add up to total balance @@ -3376,7 +3376,7 @@ ICCItem *CWeaponClass::FindAmmoItemProperty (CItemCtx &Ctx, const CItem &Ammo, c if (Balance.iLevel == 0) return CC.CreateNil(); - return CC.CreateInteger(mathRound(Balance.rStdAmmoMass)); + return CC.CreateInteger(mathRound(Balance.rStdAmmoSize)); } else return CC.CreateNil(); diff --git a/Mammoth/TSE/CWeaponFireDesc.cpp b/Mammoth/TSE/CWeaponFireDesc.cpp index c04fc3ff2..ab9a2a554 100644 --- a/Mammoth/TSE/CWeaponFireDesc.cpp +++ b/Mammoth/TSE/CWeaponFireDesc.cpp @@ -367,23 +367,24 @@ int CWeaponFireDesc::CalcDefaultHitPoints (void) const else if (m_iFireType == ftBeam) return 0; - // Ammo items get hit points proportional to level and mass. + // Ammo items get hit points proportional to level and volume. + // TODO: consider density when rebalancing for 2.0 else if (m_pAmmoType) { CItem AmmoItem(m_pAmmoType, 1); Metric rStdHP = CWeaponClass::HP_ARMOR_RATIO * CArmorClass::GetStdHP(AmmoItem.GetLevel()); - Metric rMassAdj = AmmoItem.GetMassKg() / CWeaponClass::STD_AMMO_MASS; + Metric rSizeAdj = AmmoItem.GetVolume() / CWeaponClass::STD_AMMO_VOLUME; // Compute how many of these shots are created by one ammo item. Metric rShotsPerAmmoItem = CalcShotsPerAmmoItem(); if (rShotsPerAmmoItem > 0.0) - rMassAdj /= rShotsPerAmmoItem; + rSizeAdj /= rShotsPerAmmoItem; // Return hit points - return mathRound(rMassAdj * rStdHP); + return mathRound(rSizeAdj * rStdHP); } // Otherwise, compute based on damage ratio. diff --git a/Mammoth/TSE/LanguageDefs.h b/Mammoth/TSE/LanguageDefs.h index 8b8217eed..67b168cbd 100644 --- a/Mammoth/TSE/LanguageDefs.h +++ b/Mammoth/TSE/LanguageDefs.h @@ -57,7 +57,10 @@ static TStaticStringTable, 23> NOUN_FLAG_TABLE = { "tokenize", nounTokenize, }; -static TStaticStringTable, 11> NUMBER_FORMAT_TABLE = { +static TStaticStringTable, 14> NUMBER_FORMAT_TABLE = { + "CBM", CLanguage::numberCBM, + "CBMBasic", CLanguage::numberCBMBasic, + "CBMInt", CLanguage::numberCBMInt, "integer", CLanguage::numberInteger, "massKg", CLanguage::numberMass, "massTons", CLanguage::numberMassTons, diff --git a/Mammoth/TSE/ShipClassProperties.cpp b/Mammoth/TSE/ShipClassProperties.cpp index 802f30fa5..9978c54b2 100644 --- a/Mammoth/TSE/ShipClassProperties.cpp +++ b/Mammoth/TSE/ShipClassProperties.cpp @@ -296,17 +296,17 @@ ICCItemPtr CShipClass::OnGetProperty (CCodeChainCtx &Ctx, const CString &sProper const CString &sMassClass = m_Hull.GetArmorLimits().GetMaxArmorClass(); if (!sMassClass.IsBlank()) { - const CArmorMassDefinitions &MassDef = GetUniverse().GetDesignCollection().GetArmorMassDefinitions(); - return ICCItemPtr(MassDef.GetMassClassLabel(sMassClass)); + const CArmorClassDefinitions &MassDef = GetUniverse().GetDesignCollection().GetArmorMassDefinitions(); + return ICCItemPtr(MassDef.GetArmorClassLabel(sMassClass)); } - else if (m_Hull.GetArmorLimits().GetMaxArmorMass() > 0) - return ICCItemPtr(CLanguage::ComposeNumber(CLanguage::numberMass, m_Hull.GetArmorLimits().GetMaxArmorMass())); + else if (m_Hull.GetArmorLimits().GetMaxArmorSize() > 0) + return ICCItemPtr(CLanguage::ComposeNumber(CLanguage::numberMass, m_Hull.GetArmorLimits().GetMaxArmorSize())); else return ICCItemPtr(); } else if (strEquals(sProperty, PROPERTY_MAX_ARMOR_MASS)) - return (m_Hull.GetArmorLimits().GetMaxArmorMass() > 0 ? ICCItemPtr(m_Hull.GetArmorLimits().GetMaxArmorMass()) : ICCItemPtr(ICCItem::Nil)); + return (m_Hull.GetArmorLimits().GetMaxArmorSize() > 0 ? ICCItemPtr(m_Hull.GetArmorLimits().GetMaxArmorSize()) : ICCItemPtr(ICCItem::Nil)); else if (strEquals(sProperty, PROPERTY_MAX_DEVICES)) return ICCItemPtr(m_Hull.GetMaxDevices()); @@ -364,17 +364,17 @@ ICCItemPtr CShipClass::OnGetProperty (CCodeChainCtx &Ctx, const CString &sProper const CString &sMassClass = m_Hull.GetArmorLimits().GetStdArmorClass(); if (!sMassClass.IsBlank()) { - const CArmorMassDefinitions &MassDef = GetUniverse().GetDesignCollection().GetArmorMassDefinitions(); - return ICCItemPtr(MassDef.GetMassClassLabel(sMassClass)); + const CArmorClassDefinitions &MassDef = GetUniverse().GetDesignCollection().GetArmorMassDefinitions(); + return ICCItemPtr(MassDef.GetArmorClassLabel(sMassClass)); } - else if (m_Hull.GetArmorLimits().GetStdArmorMass() > 0) - return ICCItemPtr(CLanguage::ComposeNumber(CLanguage::numberMass, m_Hull.GetArmorLimits().GetStdArmorMass())); + else if (m_Hull.GetArmorLimits().GetStdArmorSize() > 0) + return ICCItemPtr(CLanguage::ComposeNumber(CLanguage::numberMass, m_Hull.GetArmorLimits().GetStdArmorSize())); else return ICCItemPtr(); } else if (strEquals(sProperty, PROPERTY_STD_ARMOR_MASS)) - return (m_Hull.GetArmorLimits().GetStdArmorMass() > 0 ? ICCItemPtr(m_Hull.GetArmorLimits().GetStdArmorMass()) : ICCItemPtr(ICCItem::Nil)); + return (m_Hull.GetArmorLimits().GetStdArmorSize() > 0 ? ICCItemPtr(m_Hull.GetArmorLimits().GetStdArmorSize()) : ICCItemPtr(ICCItem::Nil)); else if (strEquals(sProperty, PROPERTY_STEALTH)) return ICCItemPtr(m_Perf.GetStealth()); diff --git a/Mammoth/TSE/ShipProperties.cpp b/Mammoth/TSE/ShipProperties.cpp index 5a648f7ab..89f73314c 100644 --- a/Mammoth/TSE/ShipProperties.cpp +++ b/Mammoth/TSE/ShipProperties.cpp @@ -13,9 +13,14 @@ #define PROPERTY_AVAILABLE_NON_WEAPON_SLOTS CONSTLIT("availableNonWeaponSlots") #define PROPERTY_AVAILABLE_WEAPON_SLOTS CONSTLIT("availableWeaponSlots") #define PROPERTY_BLINDING_IMMUNE CONSTLIT("blindingImmune") +#define PROPERTY_CARGO_KG CONSTLIT("cargoKg") // Returns the actual cargo mass in kg #define PROPERTY_CARGO_SPACE CONSTLIT("cargoSpace") -#define PROPERTY_CARGO_SPACE_FREE_KG CONSTLIT("cargoSpaceFreeKg") -#define PROPERTY_CARGO_SPACE_USED_KG CONSTLIT("cargoSpaceUsedKg") +#define PROPERTY_CARGO_SPACE_FREE_KG CONSTLIT("cargoSpaceFreeKg") // Deprecated, returns cargo space in liters, use cargoSpaceFreeLiters +#define PROPERTY_CARGO_SPACE_USED_KG CONSTLIT("cargoSpaceUsedKg") // Deprecated, returns cargo space in liters, use cargoSpaceUsedLiters - For actual cargo mass use cargoKg instead. +#define PROPERTY_CARGO_SPACE_FREE_VOL CONSTLIT("cargoSpaceFree") +#define PROPERTY_CARGO_SPACE_USED_VOL CONSTLIT("cargoSpaceUsed") +#define PROPERTY_CARGO_SPACE_FREE_LITERS CONSTLIT("cargoSpaceFreeLiters") // For compatibility math +#define PROPERTY_CARGO_SPACE_USED_LITERS CONSTLIT("cargoSpaceUsedLiters") // For compatibility math #define PROPERTY_CONTAMINATION_TIMER CONSTLIT("contaminationTimer") #define PROPERTY_COUNTER_INCREMENT_RATE CONSTLIT("counterIncrementRate") #define PROPERTY_COUNTER_VALUE CONSTLIT("counterValue") @@ -315,10 +320,25 @@ ICCItem *CShip::GetPropertyCompatible (CCodeChainCtx &Ctx, const CString &sName) else if (strEquals(sName, PROPERTY_CARGO_SPACE)) return CC.CreateInteger(CalcMaxCargoSpace()); - else if (strEquals(sName, PROPERTY_CARGO_SPACE_FREE_KG)) - return CC.CreateInteger(mathRound(GetCargoSpaceLeft() * 1000.0)); + else if (strEquals(sName, PROPERTY_CARGO_SPACE_FREE_VOL)) + return CC.CreateDouble(mathRound(GetCargoSpaceLeft())); - else if (strEquals(sName, PROPERTY_CARGO_SPACE_USED_KG)) + else if (strEquals(sName, PROPERTY_CARGO_SPACE_FREE_LITERS) || strEquals(sName, PROPERTY_CARGO_SPACE_FREE_KG)) + return CC.CreateInteger(mathRound(GetCargoSpaceLeft() * 1000)); + + else if (strEquals(sName, PROPERTY_CARGO_SPACE_USED_VOL)) + { + InvalidateItemVolume(); + return CC.CreateDouble(mathRound(GetCargoVolume())); + } + + else if (strEquals(sName, PROPERTY_CARGO_SPACE_USED_LITERS) || strEquals(sName, PROPERTY_CARGO_SPACE_USED_KG)) + { + InvalidateItemVolume(); + return CC.CreateInteger(mathRound(GetCargoVolume() * 1000)); + } + + else if (strEquals(sName, PROPERTY_CARGO_KG)) { InvalidateItemMass(); return CC.CreateInteger(mathRound(GetCargoMass() * 1000.0)); diff --git a/Mammoth/TSE/TSE.vcxproj b/Mammoth/TSE/TSE.vcxproj index c79c67669..1896c6e41 100644 --- a/Mammoth/TSE/TSE.vcxproj +++ b/Mammoth/TSE/TSE.vcxproj @@ -1037,6 +1037,7 @@ + diff --git a/Mammoth/TSE/TSE.vcxproj.filters b/Mammoth/TSE/TSE.vcxproj.filters index 291a40d39..77258cf5b 100644 --- a/Mammoth/TSE/TSE.vcxproj.filters +++ b/Mammoth/TSE/TSE.vcxproj.filters @@ -1660,5 +1660,8 @@ Source Files\Utilities + + Source Files\Utilities + \ No newline at end of file diff --git a/Mammoth/TSUI/ClassInfoHelpers.cpp b/Mammoth/TSUI/ClassInfoHelpers.cpp index c95a5cda5..f00b8b853 100644 --- a/Mammoth/TSUI/ClassInfoHelpers.cpp +++ b/Mammoth/TSUI/ClassInfoHelpers.cpp @@ -61,28 +61,28 @@ void CUIHelper::CreateClassInfoArmor (const CShipClass &Class, int x, int y, int CString sMaxArmor; - CString sMaxArmorLimit = CLanguage::ComposeNumber(CLanguage::numberMass, Hull.GetArmorLimits().GetMaxArmorMass()); - const CString &sMaxMassClass = Hull.GetArmorLimits().GetMaxArmorClass(); - if (!sMaxMassClass.IsBlank()) + CString sMaxArmorLimit = CLanguage::ComposeNumber(CLanguage::numberCBMBasic, Hull.GetArmorLimits().GetMaxArmorSize()); + const CString &sMaxArmorClass = Hull.GetArmorLimits().GetMaxArmorClass(); + if (!sMaxArmorClass.IsBlank()) { - const CArmorMassDefinitions &MassDef = Universe.GetDesignCollection().GetArmorMassDefinitions(); - sMaxArmor = strPatternSubst(CONSTLIT("%s (%s)"), MassDef.GetMassClassLabel(sMaxMassClass), sMaxArmorLimit); + const CArmorClassDefinitions &MassDef = Universe.GetDesignCollection().GetArmorMassDefinitions(); + sMaxArmor = strPatternSubst(CONSTLIT("%s"), MassDef.GetArmorClassLabel(sMaxArmorClass)); } - else if (Hull.GetArmorLimits().GetMaxArmorMass() > 0) + else if (Hull.GetArmorLimits().GetMaxArmorSize() > 0) sMaxArmor = sMaxArmorLimit; else sMaxArmor = "unlimited"; CString sStdArmor; - CString sStdArmorLimit = CLanguage::ComposeNumber(CLanguage::numberMass, Hull.GetArmorLimits().GetStdArmorMass()); - const CString &sStdMassClass = Hull.GetArmorLimits().GetStdArmorClass(); - if (!sStdMassClass.IsBlank()) + CString sStdArmorLimit = CLanguage::ComposeNumber(CLanguage::numberCBMBasic, Hull.GetArmorLimits().GetStdArmorSize()); + const CString &sStdArmorClass = Hull.GetArmorLimits().GetStdArmorClass(); + if (!sStdArmorClass.IsBlank()) { - const CArmorMassDefinitions &MassDef = Universe.GetDesignCollection().GetArmorMassDefinitions(); - sStdArmor = strPatternSubst(CONSTLIT("%s (%s)"), MassDef.GetMassClassLabel(sStdMassClass), sStdArmorLimit); + const CArmorClassDefinitions &MassDef = Universe.GetDesignCollection().GetArmorMassDefinitions(); + sStdArmor = strPatternSubst(CONSTLIT("%s"), MassDef.GetArmorClassLabel(sStdArmorClass)); } - else if (Hull.GetArmorLimits().GetStdArmorMass() > 0) + else if (Hull.GetArmorLimits().GetStdArmorSize() > 0) sStdArmor = sStdArmorLimit; else sStdArmor = "unlimited"; @@ -123,9 +123,9 @@ void CUIHelper::CreateClassInfoCargo (const CShipClass &Class, const CDeviceDesc (COLORREF)VI.GetColor(colorTextDialogLabel), strFromInt(CargoDesc.GetCargoSpace()), (COLORREF)VI.GetColor(colorTextDialogInput), - (pCargoExpansion ? strPatternSubst(CONSTLIT("ton %s"), CTextBlock::Escape(pCargoExpansion->GetType()->GetNounPhrase(nounActual))) : CONSTLIT("ton cargo hold")), + (pCargoExpansion ? strPatternSubst(CONSTLIT("CBM %s"), CTextBlock::Escape(pCargoExpansion->GetType()->GetNounPhrase(nounActual))) : CONSTLIT("CBM cargo hold")), (COLORREF)VI.GetColor(colorTextDialogLabel), - (CargoDesc.GetCargoSpace() < Class.GetHullDesc().GetMaxCargoSpace() ? strPatternSubst(CONSTLIT("optional expansion up to %d tons"), Class.GetHullDesc().GetMaxCargoSpace()) : CONSTLIT("cargo space cannot be expanded"))); + (CargoDesc.GetCargoSpace() < Class.GetHullDesc().GetMaxCargoSpace() ? strPatternSubst(CONSTLIT("optional expansion up to %d CBM"), Class.GetHullDesc().GetMaxCargoSpace()) : CONSTLIT("cargo space cannot be expanded"))); CreateClassInfoSpecialItem(pItemIcon, sText, x, y, cxWidth, dwOptions, retcyHeight, retpInfo); } diff --git a/Transcendence/TransCore/Compatibility10.xml b/Transcendence/TransCore/Compatibility10.xml index c8ae725bc..c7555b10e 100644 --- a/Transcendence/TransCore/Compatibility10.xml +++ b/Transcendence/TransCore/Compatibility10.xml @@ -66,6 +66,10 @@ (setq desc (cat desc " (" gMaxCount " for " (multiply gCost gMaxCount) ")")) ) + (setq desc (cat desc "\nUnit size: " (fmtNumber 'CBM (itmGetVolume thisItem)))) + (if (gr gMaxCount 1) + (setq desc (cat desc " (" gMaxCount " at " (fmtNumber 'CBM (multiply (itmGetVolume thisItem) gMaxCount)) ")\n")) + ) (setq desc (cat desc "\nUnit mass: " (intMassString (itmGetMass thisItem)))) (if (gr gMaxCount 1) (setq desc (cat desc " (" gMaxCount " at " (intMassString (multiply (itmGetMass thisItem) gMaxCount)) ")")) @@ -749,6 +753,7 @@ (setq priceText (cat priceText " (" availCount " for " (multiply gCost availCount) ")")) ) + (setq priceText (cat priceText "\nUnit size: " (fmtNumber 'CBM (itmGetVolume thisItem)))) (setq priceText (cat priceText "\nUnit mass: " (intMassString (itmGetMass thisItem)))) (if (gr availCount 1) (setq priceText (cat priceText " (" availCount " at " (intMassString (multiply (itmGetMass thisItem) availCount)) ")")) diff --git a/Transcendence/TransCore/HSCompatibility.xml b/Transcendence/TransCore/HSCompatibility.xml index bcdc6b6c4..17c50dbc9 100644 --- a/Transcendence/TransCore/HSCompatibility.xml +++ b/Transcendence/TransCore/HSCompatibility.xml @@ -443,6 +443,7 @@ (setq desc (cat desc " (" gMaxCount " for " (fmtCurrency currencyUsed (multiply gCost gMaxCount)) ")")) ) + (setq desc (cat desc "\nUnit size: " (fmtNumber 'CBM (itmGetVolume thisItem)))) (setq desc (cat desc "\nUnit mass: " (fmtNumber 'massKg (itmGetMass thisItem)))) (if (gr gMaxCount 1) (setq desc (cat desc " (" gMaxCount " at " (fmtNumber 'massKg (multiply (itmGetMass thisItem) gMaxCount)) ")")) diff --git a/Transcendence/TransCore/HumanSpaceVol01.xml b/Transcendence/TransCore/HumanSpaceVol01.xml index dba850df7..c69850998 100644 --- a/Transcendence/TransCore/HumanSpaceVol01.xml +++ b/Transcendence/TransCore/HumanSpaceVol01.xml @@ -1575,7 +1575,8 @@ - + + diff --git a/Transcendence/TransCore/Mining.xml b/Transcendence/TransCore/Mining.xml index fd9a4f756..754ca0fde 100644 --- a/Transcendence/TransCore/Mining.xml +++ b/Transcendence/TransCore/Mining.xml @@ -56,6 +56,9 @@ (!= aLootedBy gPlayerShip) Nil + ; TODO: properly compute mass of ore for adventures and + ; extensions like VotG and Chronicles which have ore in non-ton + ; amounts e.g. krels or ore chunks respectively (enum aLoot itemLooted (plyIncItemStat gPlayer 'itemsMinedCount itemLooted (itmGetCount itemLooted)) ) diff --git a/Transcendence/TransCore/MiscItems.xml b/Transcendence/TransCore/MiscItems.xml index 80fd19da5..efa0c228d 100644 --- a/Transcendence/TransCore/MiscItems.xml +++ b/Transcendence/TransCore/MiscItems.xml @@ -494,7 +494,8 @@ name= "[barrel(s) of ]heavy water" level= "2" value= "50" - mass= "1000" + density= "1.11" + volume= "1" frequency= "common" numberAppearing= "1d4" attributes= "Consumable; humanTech, Res;" @@ -764,7 +765,8 @@ name= "[barrel(s) of ]Kobe spring water" level= "4" value= "100" - mass= "500" + density= "1.0" + volume= "0.5" frequency= "uncommon" numberAppearing= "1d8" attributes= "Consumable; consumerGood, Food, humanTech" @@ -1615,7 +1617,8 @@ name= "[tank(s) of ]water ice" level= "1" value= "40" - mass= "1000" + volume= "1" + density= "0.917" frequency= "common" numberAppearing= "2d8" attributes= "Consumable; humanTech, Res;" @@ -1634,7 +1637,8 @@ name= "[barrel(s) of ]Charles River sparkling water" level= "2" value= "150" - mass= "150" + density= "1.0" + volume= "0.15" frequency= "rare" numberAppearing= "1d8" attributes= "Consumable; consumerGood, Food, humanTech" @@ -1786,7 +1790,8 @@ name= "[barrel(s) of ]Eridani mineral water" level= "3" value= "75" - mass= "500" + density= "1.0" + volume= "0.5" frequency= "uncommon" numberAppearing= "1d8" attributes= "Consumable; consumerGood, Food; humanTech, Lux" diff --git a/Transcendence/TransCore/RPGAutons.xml b/Transcendence/TransCore/RPGAutons.xml index 4189ce0a3..3f091603b 100644 --- a/Transcendence/TransCore/RPGAutons.xml +++ b/Transcendence/TransCore/RPGAutons.xml @@ -1324,8 +1324,8 @@ desc: (cat "{/rtf " - "{/f:MediumBold;/c:#79828c; cargo space:} " (fmtNumber 'massTons (typ@ autonClass 'cargoSpace)) "\n" - "{/f:MediumBold;/c:#79828c; max. expansion:} " (fmtNumber 'massTons (typ@ autonClass 'maxCargoSpace)) "\n" + "{/f:MediumBold;/c:#79828c; cargo space:} " (fmtNumber 'CBMInt (typ@ autonClass 'cargoSpace)) "\n" + "{/f:MediumBold;/c:#79828c; max. expansion:} " (fmtNumber 'CBMInt (typ@ autonClass 'maxCargoSpace)) "\n" "}" ) } diff --git a/Transcendence/TransCore/RPGCommoditiesExchange.xml b/Transcendence/TransCore/RPGCommoditiesExchange.xml index be46217fa..e59ec800b 100644 --- a/Transcendence/TransCore/RPGCommoditiesExchange.xml +++ b/Transcendence/TransCore/RPGCommoditiesExchange.xml @@ -288,8 +288,10 @@ markupPercent: (abs itemMarkupPercent) max: (if (gr maxCount 1) maxCount) maxValue: (fmtCurrency currencyUsed (multiply itemCost maxCount)) - mass: (fmtNumber 'massKg (itmGetMass thisItem)) - maxMass: (fmtNumber 'massKg (multiply (itmGetMass thisItem) maxCount)) + size: (fmtNumber 'CBM (itmGetVolume thisItem)) + maxSize: (fmtNumber 'CBM (* (itmGetVolume thisItem) maxCount)) + mass: (fmtNumber 'massKg (itmGetMassKg thisItem)) + maxMass: (fmtNumber 'massKg (multiply (itmGetMassKg thisItem) maxCount)) }) ; If the player has some in their cargo hold, then say so here. @@ -571,6 +573,7 @@ (if (@ gData 'max) (cat "Unit price: " (@ gData 'value) " (" (@ gData 'max) " for " (@ gData 'maxValue) ")\n" + "Unit size: " (@ gData 'size) " (" (@ gData 'max) " for " (@ gData 'maxSize) ")\n" "Unit mass: " (@ gData 'mass) " (" (@ gData 'max) " for " (@ gData 'maxMass) ")\n" (if (@ gData 'markup) (cat "Unit markup: " (@ gData 'markup) " (" (@ gData 'markupPercent) "%)") @@ -579,6 +582,7 @@ ) (cat "Unit price: " (@ gData 'value) "\n" + "Unit size: " (@ gData 'size) "\n" "Unit mass: " (@ gData 'mass) "\n" (if (@ gData 'markup) (cat "Unit markup: " (@ gData 'markup) " (" (@ gData 'markupPercent) "%)") @@ -802,8 +806,10 @@ markupPercent: (abs itemMarkupPercent) max: (if (gr offerCount 1) offerCount) maxValue: (fmtCurrency currencyUsed (multiply offerPrice offerCount)) - mass: (fmtNumber 'massKg (itmGetMass theItem)) - maxMass: (fmtNumber 'massKg (multiply (itmGetMass theItem) offerCount)) + size: (fmtNumber 'CBM (itmGetVolume theItem)) + maxSize: (fmtNumber 'CBM (* (itmGetVolume theItem) offerCount)) + mass: (fmtNumber 'massKg (itmGetMassKg theItem)) + maxMass: (fmtNumber 'massKg (multiply (itmGetMassKg theItem) offerCount)) }) ) @@ -1105,10 +1111,12 @@ Unit price: %value% (%max% for %maxValue%)\n + Unit size: %size% (%max% for %maxSize%)\n Unit mass: %mass% (%max% for %maxMass%) Unit price: %value%\n + Unit size: %size% Unit mass: %mass% diff --git a/Transcendence/TransCore/RPGCompatibility.xml b/Transcendence/TransCore/RPGCompatibility.xml index 6a70228fc..3a2704138 100644 --- a/Transcendence/TransCore/RPGCompatibility.xml +++ b/Transcendence/TransCore/RPGCompatibility.xml @@ -131,9 +131,13 @@ (setq fitCount (objGetFitCount gSource thisItem)) (setq gMaxCount (min availCount fitCount)) - (setq desc (cat "Unit mass: " (fmtNumber 'massKg (itmGetMass thisItem)))) + (setq desc (cat "Unit size: " (fmtNumber 'CBM (itmGetVolume thisItem)))) (if (gr gMaxCount 1) - (setq desc (cat desc " (" gMaxCount " at " (fmtNumber 'massKg (multiply (itmGetMass thisItem) gMaxCount)) ")")) + (setq desc (cat desc " (" gMaxCount " at " (fmtNumber 'CBM (multiply (itmGetVolume thisItem) gMaxCount)) ")")) + ) + (setq desc (cat "\nUnit mass: " (fmtNumber 'massKg (itmGetMassKg thisItem)))) + (if (gr gMaxCount 1) + (setq desc (cat desc " (" gMaxCount " at " (fmtNumber 'massKg (multiply (itmGetMassKg thisItem) gMaxCount)) ")")) ) (if (ls (objGetCargoSpaceLeft gSource) 200000) @@ -255,7 +259,11 @@ (setq gMaxCount (min availCount fitCount)) - (setq desc (cat "Unit mass: " (fmtNumber 'massKg (itmGetMass thisItem)))) + (setq desc (cat "Unit size: " (fmtNumber 'CBM (itmGetVolume thisItem)))) + (if (gr gMaxCount 1) + (setq desc (cat desc " (" gMaxCount " at " (fmtNumber 'CBM (multiply (itmGetVolume thisItem) gMaxCount)) ")")) + ) + (setq desc (cat "\nUnit mass: " (fmtNumber 'massKg (itmGetMass thisItem)))) (if (gr gMaxCount 1) (setq desc (cat desc " (" gMaxCount " at " (fmtNumber 'massKg (multiply (itmGetMass thisItem) gMaxCount)) ")")) ) diff --git a/Transcendence/TransCore/RPGDockScreens.xml b/Transcendence/TransCore/RPGDockScreens.xml index 9abce9561..95a4c504e 100644 --- a/Transcendence/TransCore/RPGDockScreens.xml +++ b/Transcendence/TransCore/RPGDockScreens.xml @@ -489,8 +489,18 @@ (capacityLeft (if (ls (objGetCargoSpaceLeft sourceObj) 200000) (scrTranslate gScreen 'capacityLeftString { - capacity: (fmtNumber 'massKg (objGetCargoSpaceLeft sourceObj)) + capacity: (fmtNumber 'CBM (obj@ sourceObj 'cargoSpaceFree)) + }) + ) + ) + + (totalSizeString + (if (gr maxCount 1) + (scrTranslate gScreen 'totalSizeString { + quantity:maxCount + size:(fmtNumber 'CBM (* (itmGetVolume thisItem) maxCount)) }) + "" ) ) @@ -498,7 +508,7 @@ (if (gr maxCount 1) (scrTranslate gScreen 'totalMassString { quantity:maxCount - mass:(fmtNumber 'massKg (* (itmGetMass thisItem) maxCount)) + mass:(fmtNumber 'massKg (* (itmGetMassKg thisItem) maxCount)) }) "" ) @@ -506,7 +516,9 @@ (itemText (scrTranslate gScreen 'descJettisonDefault { - unitMass:(fmtNumber 'massKg (itmGetMass thisItem)) + unitSize:(fmtNumber 'CBM (itmGetVolume thisItem)) + totalSizeString:totalSizeString + unitMass:(fmtNumber 'massKg (itmGetMassKg thisItem)) totalMassString:totalMassString }) ) @@ -617,9 +629,11 @@ [J]ettison + Unit size: %unitSize% %totalSizeString% Unit mass: %unitMass% %totalMassString% (%quantity% at %mass%) + (%quantity% at %size%) Capacity Left: %capacity% How many items do you wish to jettison? @@ -706,7 +720,17 @@ (if (gr maxCount 1) (scrTranslate gScreen 'totalMassString { quantity:maxCount - mass:(fmtNumber 'massKg (* (itmGetMass thisItem) maxCount)) + mass:(fmtNumber 'massKg (* (itmGetMassKg thisItem) maxCount)) + }) + "" + ) + ) + + (totalSizeString + (if (gr maxCount 1) + (scrTranslate gScreen 'totalSizeString { + quantity:maxCount + size:(fmtNumber 'CBM (* (itmGetVolume thisItem) maxCount)) }) "" ) @@ -724,8 +748,10 @@ (itemText (scrTranslate gScreen 'descLootDefault { - unitMass:(fmtNumber 'massKg (itmGetMass thisItem)) + unitSize:(fmtNumber 'CBM (itmGetVolume thisItem)) + unitMass:(fmtNumber 'massKg (itmGetMassKg thisItem)) unitPrice:(fmtCurrency itemCurrency itemPrice) + totalSizeString:totalSizeString totalMassString:totalMassString totalPriceString:totalPriceString }) @@ -770,6 +796,9 @@ (setq itemsToLoot (scrRemoveItem gScreen 1)) (objAddItem gPlayerShip itemsToLoot) + ; TODO: properly compute mass of ore for adventures and + ; extensions like VotG and Chronicles which have ore in non-ton + ; amounts e.g. krels or ore chunks respectively (if (objHasAttribute gSource 'minedOre) (plyIncItemStat gPlayer 'itemsMinedCount itemsToLoot 1) ) @@ -830,6 +859,9 @@ (setq itemsToLoot (scrRemoveItem gScreen count)) (objAddItem gPlayerShip itemsToLoot) + ; TODO: properly compute mass of ore for adventures and + ; extensions like VotG and Chronicles which have ore in non-ton + ; amounts e.g. krels or ore chunks respectively (if (objHasAttribute gSource 'minedOre) (plyIncItemStat gPlayer 'itemsMinedCount itemsToLoot count) ) @@ -849,9 +881,11 @@ + Unit size: %unitSize% %totalSizeString%\n Unit mass: %unitMass% %totalMassString%\n Unit price: %unitPrice% %totalPriceString% + (%quantity% at %size%) (%quantity% at %mass%) (%count% for %price%) Capacity Left: %capacity% diff --git a/Transcendence/TransCore/RPGShipBroker.xml b/Transcendence/TransCore/RPGShipBroker.xml index 21a74b1af..74ab5befd 100644 --- a/Transcendence/TransCore/RPGShipBroker.xml +++ b/Transcendence/TransCore/RPGShipBroker.xml @@ -170,7 +170,7 @@ ; If we have too much cargo, then say so - (gr (obj@ gPlayerShip 'cargoSpaceUsedKg) (obj@ selObj 'cargoSpaceFreeKg)) + (gr (obj@ gPlayerShip 'cargoSpaceUsed) (obj@ selObj 'cargoSpaceFree)) (block () (scrEnableAction gScreen 'actionBuyShip Nil) (scrSetActionDesc gScreen 'actionBuyShip (scrTranslate gScreen 'descTooMuchCargo)) @@ -451,7 +451,7 @@ ; Wingman has too much cargo - (gr (obj@ oldShipObj 'cargoSpaceUsedKg) (obj@ newShipObj 'cargoSpaceFreeKg)) + (gr (obj@ oldShipObj 'cargoSpaceUsed) (obj@ newShipObj 'cargoSpaceFree)) (setq confirmDesc (scrTranslate gScreen 'descTooMuchCargo { wingmanName:(objGetName oldShipObj 0) newShipName:(objGetName newShipObj 'article) @@ -771,7 +771,7 @@ ; Wingman has too much cargo - (gr (obj@ oldShipObj 'cargoSpaceUsedKg) (obj@ newShipObj 'cargoSpaceFreeKg)) + (gr (obj@ oldShipObj 'cargoSpaceUsed) (obj@ newShipObj 'cargoSpaceFree)) (block () (scrEnableAction gScreen 'actionBuyFor Nil) (scrSetDesc gScreen introDesc @@ -1390,8 +1390,8 @@ desc: (cat "{/rtf " - "{/f:MediumBold;/c:#79828c; cargo space:} " (fmtNumber 'massTons (obj@ shipObj 'cargoSpace)) "\n" - "{/f:MediumBold;/c:#79828c; max. expansion:} " (fmtNumber 'massTons (obj@ shipObj 'maxCargoSpace)) "\n" + "{/f:MediumBold;/c:#79828c; cargo space:} " (fmtNumber 'CBMInt (obj@ shipObj 'cargoSpace)) "\n" + "{/f:MediumBold;/c:#79828c; max. expansion:} " (fmtNumber 'CBMInt (obj@ shipObj 'maxCargoSpace)) "\n" "}" ) } @@ -1598,8 +1598,8 @@ (cat "{/rtf " (if cargoItem (cat "{/f:MediumBold;/c:#79828c; cargo:} " (itmGetName cargoItem 'titleCapitalize) "\n")) - "{/f:MediumBold;/c:#79828c; cargo space:} " (fmtNumber 'massTons (obj@ shipObj 'cargoSpace)) "\n" - "{/f:MediumBold;/c:#79828c; max. expansion:} " (fmtNumber 'massTons (obj@ shipObj 'maxCargoSpace)) "\n" + "{/f:MediumBold;/c:#79828c; cargo space:} " (fmtNumber 'CBMInt (obj@ shipObj 'cargoSpace)) "\n" + "{/f:MediumBold;/c:#79828c; max. expansion:} " (fmtNumber 'CBMInt (obj@ shipObj 'maxCargoSpace)) "\n" "\n" "{/f:MediumBold;/c:#79828c; device slots:} " deviceSlots " (" availDeviceSlots " free)\n" diff --git a/Transcendence/TransCore/RPGShipScreens.xml b/Transcendence/TransCore/RPGShipScreens.xml index d47401c1d..ddcbc70be 100644 --- a/Transcendence/TransCore/RPGShipScreens.xml +++ b/Transcendence/TransCore/RPGShipScreens.xml @@ -116,7 +116,8 @@ (if thisItem (block (theCategory) - (setq desc (cat "Unit mass: " (fmtNumber 'massKg (itmGetMass thisItem)) "\n\n")) + (setq desc (cat "Unit size: " (fmtNumber 'CBM (itmGetVolume thisItem)) "\n")) + (setq desc (cat "Unit mass: " (fmtNumber 'massKg (itmGetMassKg thisItem)) "\n\n")) ; Describe the item installation (setq desc (cat desc (objGetInstalledItemDesc gPlayerShip thisItem) ".")) @@ -188,15 +189,18 @@ (itemText (if (= maxCount 1) (scrTranslate gScreen 'descCargoItem { - unitMass: (fmtNumber 'massKg (itmGetMass thisItem)) + unitSize: (fmtNumber 'CBM (itmGetVolume thisItem)) + unitMass: (fmtNumber 'massKg (itmGetMassKg thisItem)) unitPrice: (fmtCurrency displayCurrency unitPrice) }) (scrTranslate gScreen 'descCargoItems { - unitMass: (fmtNumber 'massKg (itmGetMass thisItem)) + unitSize: (fmtNumber 'CBM (itmGetVolume thisItem)) + unitMass: (fmtNumber 'massKg (itmGetMassKg thisItem)) unitPrice: (fmtCurrency displayCurrency unitPrice) count: maxCount - totalMass: (fmtNumber 'massKg (* (itmGetMass thisItem) maxCount)) + totalSize: (fmtNumber 'CBM (* (itmGetVolume thisItem) maxCount)) + totalMass: (fmtNumber 'massKg (* (itmGetMassKg thisItem) maxCount)) totalPrice: (fmtCurrency displayCurrency (* unitPrice maxCount)) }) )) @@ -360,10 +364,12 @@ You are in your ship's cargo hold. + Unit size: %unitSize%\n Unit mass: %unitMass%\n Unit price: %unitPrice% + Unid size: %unitSize% (%count% at %totalSize%)\n Unit mass: %unitMass% (%count% at %totalMass%)\n Unit price: %unitPrice% (%count% for %totalPrice%) diff --git a/Transcendence/TransCore/RPGViewers.xml b/Transcendence/TransCore/RPGViewers.xml index fa42e079e..5c47fb52d 100644 --- a/Transcendence/TransCore/RPGViewers.xml +++ b/Transcendence/TransCore/RPGViewers.xml @@ -82,7 +82,7 @@ {/f:MediumBold;/c:#79828c; thrust//mass:} %thrust%\n {/f:MediumBold;/c:#79828c; maneuverability:} %maneuver%\n - {/f:MediumBold;/c:#79828c; cargo space:} %cargoSpace% tons + {/f:MediumBold;/c:#79828c; cargo space:} %cargoSpace% CBM } diff --git a/Transcendence/TransCore/StarsOfThePilgrim.xml b/Transcendence/TransCore/StarsOfThePilgrim.xml index 5dd849f1b..b58d21efe 100644 --- a/Transcendence/TransCore/StarsOfThePilgrim.xml +++ b/Transcendence/TransCore/StarsOfThePilgrim.xml @@ -169,6 +169,15 @@ } + + + 1.0 + 1.0 + 0.125 diff --git a/Transcendence/TransCore/StdArmor.xml b/Transcendence/TransCore/StdArmor.xml index 32e0f1ffe..8b043328d 100644 --- a/Transcendence/TransCore/StdArmor.xml +++ b/Transcendence/TransCore/StdArmor.xml @@ -2,25 +2,28 @@ - - + + - - - - - - - - - + + + + + + + + + + + + diff --git a/Transcendence/TransCore/StdAutons.xml b/Transcendence/TransCore/StdAutons.xml index 35032d7ed..9a5559361 100644 --- a/Transcendence/TransCore/StdAutons.xml +++ b/Transcendence/TransCore/StdAutons.xml @@ -988,7 +988,7 @@ charges= "0" massBonusPerCharge= "1000" - description= "Dock with this auton to store supplies on its cargo platform. This auton has space for 75 tons of cargo. " + description= "Dock with this auton to store supplies on its cargo platform. This auton has space for 75 CBM of cargo. " > diff --git a/Transcendence/TransCore/StdDockScreens.xml b/Transcendence/TransCore/StdDockScreens.xml index c2d731183..326bad03b 100644 --- a/Transcendence/TransCore/StdDockScreens.xml +++ b/Transcendence/TransCore/StdDockScreens.xml @@ -497,9 +497,13 @@ (setq priceText (cat priceText " (" availCount " for " (fmtCurrency currencyUsed (multiply gCost availCount)) ")")) ) - (setq priceText (cat priceText "\nUnit mass: " (fmtNumber 'massKg (itmGetMass thisItem)))) + (setq priceText (cat priceText "\nUnit size: " (fmtNumber 'CBM (itmGetVolume thisItem)))) (if (gr availCount 1) - (setq priceText (cat priceText " (" availCount " at " (fmtNumber 'massKg (multiply (itmGetMass thisItem) availCount)) ")")) + (setq priceText (cat priceText " (" availCount " at " (fmtNumber 'CBM (multiply (itmGetVolume thisItem) availCount)) ")")) + ) + (setq priceText (cat priceText "\nUnit mass: " (fmtNumber 'massKg (itmGetMassKg thisItem)))) + (if (gr availCount 1) + (setq priceText (cat priceText " (" availCount " at " (fmtNumber 'massKg (multiply (itmGetMassKg thisItem) availCount)) ")")) ) (setq priceText (cat priceText "\n\n")) diff --git a/Transcendence/TransCore/StdPlayerShips.xml b/Transcendence/TransCore/StdPlayerShips.xml index 68829f295..2c36cbc89 100644 --- a/Transcendence/TransCore/StdPlayerShips.xml +++ b/Transcendence/TransCore/StdPlayerShips.xml @@ -15,11 +15,16 @@ subcapitalShip - this is a large ship (corvette or frigate) capitalShip - this is a very large ship (destroyer or larger) freighter - this ship is a freighter - courier - this ship is not a gunship but is restricted to less than 100 tons of cargo - lightFreighter - this ship can carry 100-999 tons of cargo - heavyFreighter - this ship can carry 10000-99999 tons of cargo - superFreighter - this ship can carry 100000-999999 tons off cargo - hyperFreighter - this ship can carry over 1000000 tons of cargo + courier - this ship is a fast freighter (0.2c+) + + Special attributes for freighters: + Note: CBM = real world shipping industry name for cubic meters. + It formats with SI prefixes better than 1 k m^3 which looks like 1km^3 + But 1km^3 is 1000000x more than 1k m^3 + lightFreighter - this freighter can fit <1 kCBM + heavyFreighter - this freighter can fit 10-99 kCBM of cargo + superFreighter - this freighter can fit 100-999 kCBM of cargo + hyperFreighter - this freighter can fit >=1 MCBM of cargo --> diff --git a/Transcendence/TransCore/Tinkers.xml b/Transcendence/TransCore/Tinkers.xml index 52d6cf306..52cda44cc 100644 --- a/Transcendence/TransCore/Tinkers.xml +++ b/Transcendence/TransCore/Tinkers.xml @@ -985,15 +985,16 @@ ; Figure out how much cargo space we need for each item (spaceNeeded (- - (* (itmGetMass theItem) (itmGetCount theItem)) + (* (itmGetVolume theItem) (itmGetCount theItem)) (map theComponents 'reduceSum theComponent - (* (itmGetMass (@ theComponent 'item)) (@ theComponent 'count)) + (* (itmGetVolume (@ theComponent 'item)) (@ theComponent 'count)) ) ) ) (maxCountSpace (if (gr spaceNeeded 0) - (int (/ (objGetCargoSpaceLeft (or theSource gPlayerShip)) spaceNeeded)) + ; Need to multiply by 1000 to get liters from cubic meters + (int (/ (objGetCargoSpaceLeft (or theSource gPlayerShip)) (* 1000 spaceNeeded))) 1000 ) ) diff --git a/Transcendence/TransData/CExportProcess.cpp b/Transcendence/TransData/CExportProcess.cpp index b3c5e4456..c1aeb4dd4 100644 --- a/Transcendence/TransData/CExportProcess.cpp +++ b/Transcendence/TransData/CExportProcess.cpp @@ -85,13 +85,13 @@ void CExportProcess::WriteShipClasses (IWriteStream& Output) // MaxFuel Float Max fuel units // // FuelConsumption Float Fuel units consumed per tick per 1/10th MW. -// CargoSpaceT Integer Cargo space in tons +// CargoSpaceT Integer Cargo space in CBM (cubic meters) // HullMassT Integer Hull mass in metric tons // HullValue String Price in some currency (e.g., "1000 credit") -// HullCargoSpaceT Integer Cargo space in tons -// MaxCargoSpaceT Integer Max cargo space in tons -// StdArmorMassT Integer Standard armor mass in tons (no penalty) -// MaxArmorMassT Integer Max armor mass in tons +// HullCargoSpaceT Integer Cargo space in CBM +// MaxCargoSpaceT Integer Max cargo space in CBM +// StdArmorMassT Integer Standard armor size in CBM (no penalty) +// MaxArmorMassT Integer Max armor size in CBM // MaxArmorSpeedAdj Integer Speed penalty (1/100th of c) at max armor // MinArmorSpeedAdj Integer Speed bonus (1/100th of c) at 1/2 std armor // @@ -161,8 +161,8 @@ void CExportProcess::WriteShipClasses (IWriteStream& Output) Class.GetHullValue(), Class.GetHullDesc().GetCargoSpace(), Class.GetHullDesc().GetMaxCargoSpace(), - Class.GetHullDesc().GetArmorLimits().GetStdArmorMass(), - Class.GetHullDesc().GetArmorLimits().GetMaxArmorMass(), + Class.GetHullDesc().GetArmorLimits().GetStdArmorSize(), + Class.GetHullDesc().GetArmorLimits().GetMaxArmorSize(), Class.GetHullDesc().GetArmorLimits().GetMaxArmorSpeedPenalty(), Class.GetHullDesc().GetArmorLimits().GetMinArmorSpeedBonus(), diff --git a/Transcendence/TransData/ExportData.cpp b/Transcendence/TransData/ExportData.cpp index 658246e63..a2ecf017d 100644 --- a/Transcendence/TransData/ExportData.cpp +++ b/Transcendence/TransData/ExportData.cpp @@ -62,14 +62,14 @@ // ReactorPower Integer Reactor power in 1/10th MW // MaxFuel Float Max fuel units // FuelConsumption Float Fuel units consumed per tick per 1/10th MW. -// CargoSpaceT Integer Cargo space in tons +// CargoSpaceT Integer Cargo space in CBM (cubic meters) // // HullMassT Integer Hull mass in metric tons // HullValue String Price in some currency (e.g., "1000 credit") -// HullCargoSpaceT Integer Cargo space in tons -// MaxCargoSpaceT Integer Max cargo space in tons -// StdArmorMassT Integer Standard armor mass in tons (no penalty) -// MaxArmorMassT Integer Max armor mass in tons +// HullCargoSpaceT Integer Cargo space in CBM +// MaxCargoSpaceT Integer Max cargo space in CBM +// StdArmorMassT Integer Standard armor mass in CBM (no penalty) +// MaxArmorMassT Integer Max armor mass in CBM // MaxArmorSpeedAdj Integer Speed penalty (1/100th of c) at max armor // MinArmorSpeedAdj Integer Speed bonus (1/100th of c) at 1/2 std armor // MaxDevices Integer Max number of devices diff --git a/Transcendence/TransData/Help.cpp b/Transcendence/TransData/Help.cpp index 6f64ccf78..d941bcaa5 100644 --- a/Transcendence/TransData/Help.cpp +++ b/Transcendence/TransData/Help.cpp @@ -304,7 +304,7 @@ void ShowHelp (CXMLElement *pCmdLine) printf(" [/armorItems] installed armor items.\n"); printf(" [/balance] stats about combat and defense.\n"); printf(" [/balanceType] designation based on combat strength.\n"); - printf(" [/cargoSpace] cargo space available (in tons).\n"); + printf(" [/cargoSpace] cargo space available (in CBM (cubic meters)).\n"); printf(" [/combatStrength] combat power (absolute).\n"); printf(" [/damage] damage done per 180 ticks.\n"); printf(" [/defenseStrength] defense strength (absolute).\n"); @@ -324,7 +324,7 @@ void ShowHelp (CXMLElement *pCmdLine) printf(" [/maneuver] time for a complete rotation.\n"); printf(" [/manufacturer] ship manufacturer.\n"); printf(" [/maxArmorMass] max mass for armor (in kilograms).\n"); - printf(" [/maxCargoSpace] max cargo space (in tons).\n"); + printf(" [/maxCargoSpace] max cargo space (in CBM (cubic meters)).\n"); printf(" [/maxSpeed] maximum speed (in %% of lightspeed).\n"); printf(" [/primaryArmor] primary armor.\n"); printf(" [/primaryWeapon] primary weapon.\n"); diff --git a/Transcendence/Transcendence/CDockScreen.cpp b/Transcendence/Transcendence/CDockScreen.cpp index 3aae3bcc3..6770701d9 100644 --- a/Transcendence/Transcendence/CDockScreen.cpp +++ b/Transcendence/Transcendence/CDockScreen.cpp @@ -2261,14 +2261,14 @@ void CDockScreen::UpdateCredits (void) // Cargo space Metric rCargoSpace = m_pPlayer->GetShip()->GetCargoSpaceLeft(); if(rCargoSpace == 1.0) - m_pCargoSpace->SetText(CONSTLIT("1 ton")); + m_pCargoSpace->SetText(CONSTLIT("1 CBM")); else { - int iCargoTons = (int) rCargoSpace; - int iCargoKg = (int) ((rCargoSpace - iCargoTons) * 1000); //The kg left after taking the tons - if(iCargoTons > 0) - m_pCargoSpace->SetText(strPatternSubst("%d.%d tons", iCargoTons, iCargoKg / 100)); //Truncate kg to one decimal + int iCargoCBM = (int) rCargoSpace; + int iCargoMilliCBM = (int) ((rCargoSpace - iCargoCBM) * 1000); //The liters (mCBM) left after taking the cubic meters (CBM) + if(iCargoCBM > 0) + m_pCargoSpace->SetText(strPatternSubst("%d.%d CBM", iCargoCBM, iCargoMilliCBM / 100)); //Truncate mCBM to one decimal else - m_pCargoSpace->SetText(strPatternSubst("%d kg", iCargoKg)); + m_pCargoSpace->SetText(strPatternSubst("%d mCBM", iCargoMilliCBM)); } }