diff --git a/Mammoth/Include/TSE.h b/Mammoth/Include/TSE.h index b72a3841b..000dddbc2 100644 --- a/Mammoth/Include/TSE.h +++ b/Mammoth/Include/TSE.h @@ -654,12 +654,14 @@ class CSpaceObject // Devices - virtual bool CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot = CDeviceSystem::SSlotDesc(), InstallItemResults *retiResult = NULL, CString *retsResult = NULL, CItem *retItemToReplace = NULL); + virtual bool CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot = CDeviceSystem::SSlotDesc(), bool bForceUseOfDeviceSlot = false, InstallItemResults *retiResult = NULL, CString *retsResult = NULL, CItem *retItemToReplace = NULL); + void ClearDeviceSlotAtID (const CString id) { m_DeviceSlotsGivenID.DeleteAt(id); } virtual void DamageExternalDevice (int iDev, SDamageCtx &Ctx) { } virtual void DisableDevice (CInstalledDevice *pDevice) { } bool FindDevice (const CItem &Item, CInstalledDevice **retpDevice, CString *retsError); virtual CInstalledDevice *FindDevice (const CItem &Item) { return NULL; } virtual bool FindDeviceSlotDesc (const CItem &Item, SDeviceDesc *retDesc) { return false; } + virtual bool FindDeviceSlotDesc (const CString &ID, SDeviceDesc *retDesc) { return false; } bool FireCanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot, CString *retsResult); bool FireCanRemoveItem (const CItem &Item, int iSlot, CString *retsResult); int GetNextAutoDefenseDeviceIndex (int iDev); @@ -669,6 +671,7 @@ class CSpaceObject virtual CDeviceSystem &GetDeviceSystem (void) { return CDeviceSystem::m_Null; } virtual const CDeviceSystem &GetDeviceSystem (void) const { return CDeviceSystem::m_Null; } CItem GetItemForDevice (CInstalledDevice *pDevice); + int GetDeviceSlotAtID (const CString id) const { if (m_DeviceSlotsGivenID.Find(id)) return *m_DeviceSlotsGivenID.GetAt(id); return -1; } const CInstalledDevice *GetNamedDevice (DeviceNames iDev) const { return GetDeviceSystem().GetNamedDevice(iDev); } CInstalledDevice *GetNamedDevice (DeviceNames iDev) { return GetDeviceSystem().GetNamedDevice(iDev); } CDeviceItem GetNamedDeviceItem (DeviceNames iDev) const { return GetDeviceSystem().GetNamedDeviceItem(iDev); } @@ -676,6 +679,7 @@ class CSpaceObject virtual void OnDeviceStatus (CInstalledDevice *pDev, CDeviceClass::DeviceNotificationTypes iEvent) { } bool SetCursorAtDevice (CItemListManipulator &ItemList, int iDevSlot); bool SetCursorAtDevice (CItemListManipulator &ItemList, CInstalledDevice *pDevice); + void SetDeviceSlotAtID (const CString id, const int iDeviceSlotIndex) { if (iDeviceSlotIndex < 0 || iDeviceSlotIndex >= GetDeviceSystem().GetCount()) throw CException(ERR_FAIL); m_DeviceSlotsGivenID.SetAt(id, iDeviceSlotIndex); } void OnObjDestroyUpdateDevices (const SDestroyCtx& Ctx); // Docking @@ -1548,6 +1552,7 @@ class CSpaceObject CSpaceObjectList m_SubscribedObjs; // List of objects to notify when something happens CObjectJoint *m_pFirstJoint = NULL; // List of joints CPhysicsForceDesc m_ForceDesc; // Temporary; valid only inside Update. + TSortMap m_DeviceSlotsGivenID; // Mapping of device desc IDs to device slots where devices with those descs are installed mutable int m_iImageScale = -1; // Cached value computed in GetImageScale int m_iControlsFrozen:8 = 0; // Object will not respond to controls diff --git a/Mammoth/Include/TSEDevices.h b/Mammoth/Include/TSEDevices.h index 330bdf8e4..230e9d3bd 100644 --- a/Mammoth/Include/TSEDevices.h +++ b/Mammoth/Include/TSEDevices.h @@ -487,6 +487,7 @@ struct SDeviceDesc CEnhancementDesc Enhancements; // Slot enhancements to installed device int iSlotBonus = 0; double rShotSeparationScale = 1.; // Governs scaling of shot separation for dual etc weapons + }; class CDeviceDescList @@ -553,9 +554,15 @@ class IDeviceGenerator virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, DeviceNames iDev, SDeviceDesc *retDesc) const { return false; } virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, CSpaceObject *pSource, const CItem &Item, SDeviceDesc *retDesc) const { return false; } + virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, CSpaceObject *pObj, const CString& sID, SDeviceDesc* retDesc) const { return false; }; virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, const CDeviceDescList &DescList, const CItem &Item, SDeviceDesc *retDesc) const { return false; } virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, const CDeviceDescList &DescList, const CString &sID, SDeviceDesc *retDesc) const { return false; } - virtual bool FindDeviceSlot (const CString &sID, SDeviceDesc *retDesc = NULL, int *retiMaxCount = NULL) const { return false; } + virtual bool FindDeviceSlot(const CString& sID, SDeviceDesc* retDesc = NULL, int* retiMaxCount = NULL) const { return false; } + virtual bool ItemFitsSlot (CSpaceObject *pObj, const CItem &Item, const int iSlotIndex) const { return false; }; + virtual int GetNumberOfDescs () const { return 0; } + virtual const int GetDescIndexGivenId (const CString &sID) const { return -1; } + virtual ICCItem* GetDeviceSlotProperty (const int iSlotIndex, CCodeChain *pCC, const CString &Property, const ICCItem *pArgs) const { return pCC->CreateNil(); }; + virtual TArray GetDeviceSlotIds() const { return TArray(); } static ALERROR InitDeviceDescFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, SDeviceDesc *retDesc); }; diff --git a/Mammoth/Include/TSEShipClass.h b/Mammoth/Include/TSEShipClass.h index 1b95fa379..353c3ce71 100644 --- a/Mammoth/Include/TSEShipClass.h +++ b/Mammoth/Include/TSEShipClass.h @@ -398,6 +398,7 @@ class CShipClass : public CDesignType bool CreateWreck (CShip *pShip, CSpaceObject **retpWreck = NULL); bool FindDeviceSlotDesc (DeviceNames iDev, SDeviceDesc *retDesc) const; bool FindDeviceSlotDesc (CShip *pShip, const CItem &Item, SDeviceDesc *retDesc) const; + bool FindDeviceSlotDesc (CShip *pShip, const CString& sID, SDeviceDesc *retDesc) const; static const DWORD GDFLAG_NO_DEVICE_SLOT_SEARCH = 0x00000001; void GenerateDevices (int iLevel, CDeviceDescList &Devices, DWORD dwFlags = 0) const; diff --git a/Mammoth/Include/TSEShipSystems.h b/Mammoth/Include/TSEShipSystems.h index 7c7dc6704..25f32a0c7 100644 --- a/Mammoth/Include/TSEShipSystems.h +++ b/Mammoth/Include/TSEShipSystems.h @@ -201,10 +201,11 @@ class CDeviceSystem int GetCountByID (const CString &sID) const; CInstalledDevice &GetDevice (int iIndex) { if (iIndex < 0 || iIndex >= GetCount()) throw CException(ERR_FAIL); return *m_Devices[iIndex]; } CDeviceItem GetDeviceItem (int iIndex) const { if (iIndex < 0 || iIndex >= GetCount()) throw CException(ERR_FAIL); if (!m_Devices[iIndex]->IsEmpty()) return m_Devices[iIndex]->GetItem()->AsDeviceItem(); else return CItem().AsDeviceItem(); } + const IDeviceGenerator *GetSlots() const { return m_pSlots; } const CInstalledDevice &GetDevice (int iIndex) const { if (iIndex < 0 || iIndex >= GetCount()) throw CException(ERR_FAIL); return *m_Devices[iIndex]; } const CInstalledDevice *GetNamedDevice (DeviceNames iDev) const { return GetNamedDeviceHelper(iDev); } CInstalledDevice *GetNamedDevice (DeviceNames iDev) { return const_cast(GetNamedDeviceHelper(iDev)); } - CDeviceItem GetNamedDeviceItem (DeviceNames iDev) const { if (HasNamedDevices() && m_NamedDevices[iDev] != -1) return GetDevice(m_NamedDevices[iDev]).GetItem()->AsDeviceItem(); else return CItem().AsDeviceItem(); } + CDeviceItem GetNamedDeviceItem (DeviceNames iDev) const { if (HasNamedDevices() && m_NamedDevices[iDev] != -1 && GetDevice(m_NamedDevices[iDev]).GetItem()) return GetDevice(m_NamedDevices[iDev]).GetItem()->AsDeviceItem(); else return CItem().AsDeviceItem(); } int GetNamedIndex (DeviceNames iDev) const { return (HasNamedDevices() ? m_NamedDevices[iDev] : -1); } DWORD GetTargetTypes (void) const; bool HasShieldsUp (void) const; diff --git a/Mammoth/Include/TSESpaceObjectsImpl.h b/Mammoth/Include/TSESpaceObjectsImpl.h index 7dfd4fee4..ca370ae32 100644 --- a/Mammoth/Include/TSESpaceObjectsImpl.h +++ b/Mammoth/Include/TSESpaceObjectsImpl.h @@ -1048,6 +1048,7 @@ class CShip : public TSpaceObjectImpl bool FindDeviceAtPos (const CVector &vPos, CInstalledDevice **retpDevice); int GetAmmoForSelectedLinkedFireWeapons(CInstalledDevice *pDevice); DeviceNames GetDeviceNameForCategory (ItemCategories iCategory); + ICCItem* GetDeviceSlotProperty(CCodeChain* pCC, CCodeChainCtx& Ctx, const ICCItem* pArgs) const; int GetItemDeviceName (const CItem &Item) const; bool HasNamedDevice (DeviceNames iDev) const; void InstallItemAsDevice (CItemListManipulator &ItemList, const CDeviceSystem::SSlotDesc &RecommendedSlot = CDeviceSystem::SSlotDesc()); @@ -1142,7 +1143,7 @@ class CShip : public TSpaceObjectImpl virtual bool CanAttack (void) const override; virtual bool CanBeAttacked (void) const override { return CanAttack(); } virtual bool CanBeDestroyedBy (CSpaceObject &Attacker) const override; - virtual bool CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot = CDeviceSystem::SSlotDesc(), InstallItemResults *retiResult = NULL, CString *retsResult = NULL, CItem *retItemToReplace = NULL) override; + virtual bool CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot = CDeviceSystem::SSlotDesc(), bool bForceUseOfDeviceSlot = false, InstallItemResults *retiResult = NULL, CString *retsResult = NULL, CItem *retItemToReplace = NULL) override; virtual bool CanMove (void) const override { return true; } virtual RequestDockResults CanObjRequestDock (CSpaceObject *pObj = NULL) const override; virtual bool CanThrust (void) const override { return (GetThrust() > 0.0); } @@ -1158,6 +1159,7 @@ class CShip : public TSpaceObjectImpl virtual bool FindDataField (const CString &sField, CString *retsValue) override; virtual CInstalledDevice *FindDevice (const CItem &Item) override; virtual bool FindDeviceSlotDesc (const CItem &Item, SDeviceDesc *retDesc) override { return m_pClass->FindDeviceSlotDesc(this, Item, retDesc); } + virtual bool FindDeviceSlotDesc (const CString &ID, SDeviceDesc *retDesc) override { return m_pClass->FindDeviceSlotDesc(this, ID, retDesc); } virtual bool FollowsObjThroughGate (CSpaceObject *pLeader = NULL) override; virtual AbilityStatus GetAbility (Abilities iAbility) const override; virtual int GetAISettingInteger (const CString &sSetting) override { return m_pController->GetAISettingInteger(sSetting); } diff --git a/Mammoth/TSE/CCExtensions.cpp b/Mammoth/TSE/CCExtensions.cpp index 10b19b097..a2e6a9942 100644 --- a/Mammoth/TSE/CCExtensions.cpp +++ b/Mammoth/TSE/CCExtensions.cpp @@ -299,7 +299,7 @@ ICCItem *fnObjGetArmor (CEvalContext *pEvalCtx, ICCItem *pArguments, DWORD dwDat #define FN_SHIP_DEVICE_SLOT_AVAIL 1 #define FN_SHIP_INSTALL_AUTOPILOT 2 #define FN_SHIP_HAS_AUTOPILOT 3 -// spare +#define FN_SHIP_DEV_SLOT_PROPERTY 4 #define FN_SHIP_INSTALL_TARGETING 5 #define FN_SHIP_HAS_TARGETING 6 #define FN_SHIP_CLASS 7 @@ -663,6 +663,8 @@ ICCItem *fnXMLGet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData); #define FIELD_RADIUS_OFFSET CONSTLIT("radiusOffset") #define FIELD_REMOVE CONSTLIT("remove") #define FIELD_ROTATION CONSTLIT("rotation") +#define FIELD_SLOT_ID CONSTLIT("slotID") // TODO(heliogenesis): Remove +#define FIELD_SLOT_POS_INDEX CONSTLIT("slotPosIndex") // TODO(heliogenesis): Remove #define FIELD_SOURCE_ONLY CONSTLIT("sourceOnly") #define FIELD_TYPE CONSTLIT("type") #define FIELD_WIDTH CONSTLIT("width") @@ -1284,6 +1286,33 @@ static PRIMITIVEPROCDEF g_Extensions[] = "(shpGetClassName class flags) -> class name", "ii", 0, }, + { "shpGetDeviceSlotProperty", fnShipGet, FN_SHIP_DEV_SLOT_PROPERTY, + "(shpGetDeviceSlotProperty ship [deviceSlot] property) -> angle\n\n" + + "deviceSlot can be an int or struct with these parameters:\n\n" + " deviceSlot: device slot number\n" + " slotID: device slot ID\n" + " slotPosIndex: device slot pos index\n" + + "property:\n\n" + " 'attributes\n" + " 'criteria\n" + " 'description\n" + " 'deviceAtSlot\n" + " 'fireArc\n" + " 'hasAttribute [attribute]\n" + " 'maxMass\n" + " 'maxPower\n" + " 'maxPowerPercent\n" + " 'omnidirectional\n" + " 'pos\n" + " 'posAngle\n" + " 'posCartesian\n" + " 'posRadius\n" + " 'secondaryWeapon\n", + + "i*s", 0, }, + { "shpGetDirection", fnShipGetOld, FN_SHIP_DIRECTION, "(shpGetDirection ship) -> angle", NULL, 0, }, @@ -1362,7 +1391,12 @@ static PRIMITIVEPROCDEF g_Extensions[] = "ivi", PPFLAG_SIDEEFFECTS, }, { "shpInstallDevice", fnShipSet, FN_SHIP_INSTALL_DEVICE, - "(shpInstallDevice ship item [deviceSlot]) -> itemStruct (or Nil)", + "(shpInstallDevice ship item [deviceSlot]) -> itemStruct (or Nil)\n\n" + + "deviceSlot can be an int or struct with these parameters:\n\n" + " deviceSlot: device slot number\n" + " slotID: device slot ID\n" + " slotPosIndex: device slot pos index\n", "iv*", PPFLAG_SIDEEFFECTS, }, { "shpIsBlind", fnShipGetOld, FN_SHIP_BLINDNESS, @@ -2286,6 +2320,7 @@ static PRIMITIVEPROCDEF g_Extensions[] = "property (device)\n\n" " 'capacitor\n" + " 'slotID\n" " 'enabled\n" " 'linkedFireOptions\n" " 'pos\n" @@ -7144,11 +7179,12 @@ ICCItem *fnObjGet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData) case FN_OBJ_CAN_INSTALL_ITEM: { - CItem Item(pCtx->AsItem(pArgs->GetElement(1))); + CItem Item = (pCtx->AsItem(pArgs->GetElement(1))); if (Item.GetType() == NULL) return pCC->CreateError(CONSTLIT("Invalid item"), pArgs->GetElement(1)); CDeviceSystem::SSlotDesc Slot; + bool bForceUseOfDeviceSlot = false; if (pArgs->GetCount() > 2) { if (!CTLispConvert::AsSlotDesc(*pArgs->GetElement(2), Slot)) @@ -7167,14 +7203,18 @@ ICCItem *fnObjGet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData) return pCC->CreateError(CONSTLIT("Invalid armor segment"), pArgs->GetElement(2)); if (Item.IsDevice() && pShip - && (Slot.iIndex < 0 || Slot.iIndex >= pShip->GetDeviceCount() || pShip->GetDevice(Slot.iIndex)->IsEmpty())) + && (Slot.iIndex < 0 || Slot.iIndex >= pShip->GetDeviceCount())) return pCC->CreateError(CONSTLIT("Invalid device slot"), pArgs->GetElement(2)); + bForceUseOfDeviceSlot = pCtx->GetAPIVersion() >= 54; } if (!Slot.sID.IsBlank()) { - if (!pObj->GetDeviceSystem().FindSlotDesc(Slot.sID)) - return pCC->CreateError(CONSTLIT("Unkown slot ID"), pArgs->GetElement(2)); + CShip* pShip = pObj->AsShip(); + const bool bDeviceSlotExists = pShip->GetDeviceSystem().FindSlotDesc(Slot.sID); + if (!bDeviceSlotExists) + return pCC->CreateError(CONSTLIT("Unknown slot ID"), pArgs->GetElement(2)); + bForceUseOfDeviceSlot = pCtx->GetAPIVersion() >= 54; } } @@ -7183,7 +7223,7 @@ ICCItem *fnObjGet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData) CSpaceObject::InstallItemResults iResult; CString sResult; CItem ItemToReplace; - bool bCanInstall = pObj->CanInstallItem(Item, Slot, &iResult, &sResult, &ItemToReplace); + bool bCanInstall = pObj->CanInstallItem(Item, Slot, bForceUseOfDeviceSlot, &iResult, &sResult, &ItemToReplace); // Generate the result @@ -10928,6 +10968,11 @@ ICCItem *fnShipGet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData) } } + case FN_SHIP_DEV_SLOT_PROPERTY: + { + return pShip->GetDeviceSlotProperty(pCC, *pCtx, pArgs); + } + case FN_SHIP_DOCK_OBJ: { CSpaceObject *pObj = pShip->GetDockedObj(); @@ -11243,7 +11288,7 @@ ICCItem *fnShipSet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData) CSpaceObject::InstallItemResults iResult; CString sResult; - pShip->CanInstallItem(Item, Slot, &iResult, &sResult); + pShip->CanInstallItem(Item, Slot, false, &iResult, &sResult); if (!sResult.IsBlank()) return pCC->CreateString(sResult); @@ -11456,7 +11501,6 @@ ICCItem *fnShipSet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData) return pCC->CreateNil(); // See if we passed in a device slot - CDeviceSystem::SSlotDesc Slot; if (pArgs->GetCount() > 2) { @@ -11468,14 +11512,15 @@ ICCItem *fnShipSet (CEvalContext *pEvalCtx, ICCItem *pArgs, DWORD dwData) if (Slot.iIndex != -1) { if (Item.IsDevice() - && (Slot.iIndex < 0 || Slot.iIndex >= pShip->GetDeviceCount() || pShip->GetDevice(Slot.iIndex)->IsEmpty())) + && (Slot.iIndex < 0 || Slot.iIndex >= pShip->GetDeviceCount())) return pCC->CreateError(CONSTLIT("Invalid device slot"), pArgs->GetElement(2)); } if (!Slot.sID.IsBlank()) { - if (!pShip->GetDeviceSystem().FindSlotDesc(Slot.sID)) - return pCC->CreateError(CONSTLIT("Unkown slot ID"), pArgs->GetElement(2)); + const bool bDeviceSlotExists = pShip->GetDeviceSystem().FindSlotDesc(Slot.sID); + if (!bDeviceSlotExists) + return pCC->CreateError(CONSTLIT("Unknown slot ID"), pArgs->GetElement(2)); } } diff --git a/Mammoth/TSE/CDeviceSystem.cpp b/Mammoth/TSE/CDeviceSystem.cpp index 1c284a904..25fefc227 100644 --- a/Mammoth/TSE/CDeviceSystem.cpp +++ b/Mammoth/TSE/CDeviceSystem.cpp @@ -818,7 +818,7 @@ bool CDeviceSystem::Install (CSpaceObject *pObj, CItemListManipulator &ItemList, SDeviceDesc Desc; if (Slot.sID) { - FindSlotDesc(Slot.sID, &Desc); + pObj->FindDeviceSlotDesc(Slot.sID, &Desc); } else { diff --git a/Mammoth/TSE/CDeviceTable.cpp b/Mammoth/TSE/CDeviceTable.cpp index c7f8c4ea5..c16a00772 100644 --- a/Mammoth/TSE/CDeviceTable.cpp +++ b/Mammoth/TSE/CDeviceTable.cpp @@ -3,6 +3,9 @@ // IDeviceGenerator objects #include "PreComp.h" +#include +#include +#include #define DEVICE_TAG CONSTLIT("Device") #define DEVICES_TAG CONSTLIT("Devices") @@ -16,6 +19,7 @@ #define NULL_TAG CONSTLIT("Null") #define TABLE_TAG CONSTLIT("Table") +#define ATTRIBUTES_ATTRIB CONSTLIT("attributes") #define CANNOT_BE_EMPTY_ATTRIB CONSTLIT("cannotBeEmpty") #define CATEGORIES_ATTRIB CONSTLIT("categories") #define CHANCE_ATTRIB CONSTLIT("chance") @@ -23,6 +27,7 @@ #define COUNT_ATTRIB CONSTLIT("count") #define CRITERIA_ATTRIB CONSTLIT("criteria") #define DAMAGED_ATTRIB CONSTLIT("damaged") +#define DESCRIPTION_ATTRIB CONSTLIT("description") #define DEVICE_ID_ATTRIB CONSTLIT("deviceID") #define ENHANCED_ATTRIB CONSTLIT("enhanced") #define ENHANCEMENT_ATTRIB CONSTLIT("enhancement") @@ -41,7 +46,11 @@ #define MAX_FIRE_ARC_ATTRIB CONSTLIT("maxFireArc") #define MIN_FIRE_ARC_ATTRIB CONSTLIT("minFireArc") #define MAX_FIRE_RANGE_ATTRIB CONSTLIT("maxFireRange") +#define MAX_MASS_ATTRIB CONSTLIT("maxMass") +#define MAX_POWER_ATTRIB CONSTLIT("maxPower") +#define MAX_POWER_PERCENT_ATTRIB CONSTLIT("maxPowerPercentage") #define MISSILE_DEFENSE_ATTRIB CONSTLIT("missileDefense") +#define NAME_ATTRIB CONSTLIT("name") #define OMNIDIRECTIONAL_ATTRIB CONSTLIT("omnidirectional") #define SECONDARY_WEAPON_ATTRIB CONSTLIT("secondaryWeapon") #define SEGMENT_ID_ATTRIB CONSTLIT("segmentID") @@ -50,6 +59,24 @@ #define TABLE_ATTRIB CONSTLIT("table") #define UNID_ATTRIB CONSTLIT("unid") +#define PROPERTY_DEVICE_SLOT_ATTRIBUTES CONSTLIT("attributes") +#define PROPERTY_DEVICE_SLOT_CRITERIA CONSTLIT("criteria") +#define PROPERTY_DEVICE_SLOT_DESCRIPTION CONSTLIT("description") +#define PROPERTY_DEVICE_SLOT_FIRE_ARC CONSTLIT("fireArc") +#define PROPERTY_DEVICE_SLOT_HAS_ATTRIBUTE CONSTLIT("hasAttribute") +#define PROPERTY_DEVICE_SLOT_MAX_FIRE_ARC CONSTLIT("maxFireArc") +#define PROPERTY_DEVICE_SLOT_MAX_MASS CONSTLIT("maxMass") +#define PROPERTY_DEVICE_SLOT_MAX_POWER CONSTLIT("maxPower") +#define PROPERTY_DEVICE_SLOT_MAX_POWER_PERCENT CONSTLIT("maxPowerPercent") +#define PROPERTY_DEVICE_SLOT_MIN_FIRE_ARC CONSTLIT("minFireArc") +#define PROPERTY_DEVICE_SLOT_NAME CONSTLIT("name") +#define PROPERTY_DEVICE_SLOT_OMNIDIRECTIONAL CONSTLIT("omnidirectional") +#define PROPERTY_DEVICE_SLOT_POS CONSTLIT("pos") +#define PROPERTY_DEVICE_SLOT_POS_ANGLE CONSTLIT("posAngle") +#define PROPERTY_DEVICE_SLOT_POS_CARTESIAN CONSTLIT("posCartesian") +#define PROPERTY_DEVICE_SLOT_POS_RADIUS CONSTLIT("posRadius") +#define PROPERTY_DEVICE_SLOT_SECONDARY_WEAPON CONSTLIT("secondaryWeapon") + class CNullDevice : public IDeviceGenerator { public: @@ -183,9 +210,15 @@ class CGroupOfDeviceGenerators : public IDeviceGenerator virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, DeviceNames iDev, SDeviceDesc *retDesc) const override; virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, CSpaceObject *pObj, const CItem &Item, SDeviceDesc *retDesc) const override; + virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, CSpaceObject* pObj, const CString& sID, SDeviceDesc *retDesc) const override; virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, const CDeviceDescList &DescList, const CItem &Item, SDeviceDesc *retDesc) const override; virtual bool FindDefaultDesc (SDeviceGenerateCtx &Ctx, const CDeviceDescList &DescList, const CString &sID, SDeviceDesc *retDesc) const override; virtual bool FindDeviceSlot (const CString &sID, SDeviceDesc *retDesc = NULL, int *retiMaxCount = NULL) const override; + virtual bool ItemFitsSlot (CSpaceObject *pObj, const CItem &Item, const int iSlotIndex) const override; + virtual int GetNumberOfDescs () const override { return m_SlotDesc.GetCount(); } + virtual const int GetDescIndexGivenId (const CString &sID) const override { return m_SlotDescIndicesByID.Find(sID) ? *m_SlotDescIndicesByID.GetAt(sID) : -1; } + virtual ICCItem* GetDeviceSlotProperty (const int iSlotIndex, CCodeChain *pCC, const CString &Property, const ICCItem *pArgs) const override; + virtual TArray GetDeviceSlotIds() const override { TArray ids; for (int i = 0; i < m_SlotDescIndicesByID.GetCount(); i++) { ids.Insert(m_SlotDescIndicesByID.GetKey(i)); } return ids; } private: struct SEntry @@ -196,9 +229,15 @@ class CGroupOfDeviceGenerators : public IDeviceGenerator struct SSlotDesc { + TSortMap Attributes; CItemCriteria Criteria; SDeviceDesc DefaultDesc; int iMaxCount; + int iMaxMass = -1; + CString Description; + CString Name; + int iMaxPower = -1; + Metric fMaxPowerPercent = -1.0; }; const SSlotDesc *FindSlotDesc (CSpaceObject *pObj, const CItem &Item) const; @@ -207,6 +246,7 @@ class CGroupOfDeviceGenerators : public IDeviceGenerator TArray m_Table; TArray m_SlotDesc; + TSortMap m_SlotDescIndicesByID; }; const CNullDevice CNullDevice::m_Null; @@ -1210,10 +1250,10 @@ bool CGroupOfDeviceGenerators::FindDefaultDesc (SDeviceGenerateCtx &Ctx, CSpaceO { // Skip if this slot does not meet criteria - if (!Item.MatchesCriteria(m_SlotDesc[i].Criteria)) + if (!ItemFitsSlot(pObj, Item, i)) continue; - // If this slot has an ID and maximum counts and if we've already + // If this slot has an ID and maximum counts and if we've already // exceeded those counts, then skip. if (m_SlotDesc[i].iMaxCount != -1 @@ -1247,6 +1287,44 @@ bool CGroupOfDeviceGenerators::FindDefaultDesc (SDeviceGenerateCtx &Ctx, CSpaceO return true; } +bool CGroupOfDeviceGenerators::FindDefaultDesc (SDeviceGenerateCtx& Ctx, CSpaceObject* pObj, const CString& sID, SDeviceDesc* retDesc) const + +// FindDefaultDesc +// +// Looks for a slot descriptor that matches the given ID and returns it. + + { + int i; + + // Look for a matching slot + + for (i = 0; i < m_SlotDesc.GetCount(); i++) + { + // Skip if not the desired id + + if (!strEquals(m_SlotDesc[i].DefaultDesc.sID, sID)) + continue; + + // If this slot has an ID and maximum counts and if we've already + // exceeded those counts, then skip. + + if (m_SlotDesc[i].iMaxCount != -1 + && !m_SlotDesc[i].DefaultDesc.sID.IsBlank() + && pObj + && pObj->GetDeviceSystem().GetCountByID(m_SlotDesc[i].DefaultDesc.sID) >= m_SlotDesc[i].iMaxCount) + continue; + + // If we get this far, then this is a valid slot. + + *retDesc = m_SlotDesc[i].DefaultDesc; + return true; + } + + // Not found + + return false; + } + bool CGroupOfDeviceGenerators::FindDefaultDesc (SDeviceGenerateCtx &Ctx, const CDeviceDescList &DescList, const CItem &Item, SDeviceDesc *retDesc) const // FindDefaultDesc @@ -1378,12 +1456,107 @@ const CGroupOfDeviceGenerators::SSlotDesc *CGroupOfDeviceGenerators::FindSlotDes int i; for (i = 0; i < m_SlotDesc.GetCount(); i++) - if (Item.MatchesCriteria(m_SlotDesc[i].Criteria)) + if (ItemFitsSlot(pObj, Item, i)) return &m_SlotDesc[i]; return NULL; } +ICCItem* CGroupOfDeviceGenerators::GetDeviceSlotProperty (const int iSlotIndex, CCodeChain *pCC, const CString &Property, const ICCItem *pArgs) const + +// GetDeviceSlotProperty +// +// Return the value of the property requested +// TODO(heliogenesis): Complete unimplemented fields + { + const SSlotDesc &Slot = m_SlotDesc[iSlotIndex]; + const SDeviceDesc &DefaultDesc = Slot.DefaultDesc; + if (Property == PROPERTY_DEVICE_SLOT_ATTRIBUTES) + { + ICCItem* pResult = pCC->CreateLinkedList(); + for (int i = 0; i < Slot.Attributes.GetCount(); i++) + { + pResult->Append(pCC->CreateString(Slot.Attributes.GetKey(i))); + } + return pResult; + } + else if (Property == PROPERTY_DEVICE_SLOT_CRITERIA) + return pCC->CreateString(Slot.Criteria.AsString()); + else if (Property == PROPERTY_DEVICE_SLOT_DESCRIPTION) + return pCC->CreateString(Slot.Description); + else if (Property == PROPERTY_DEVICE_SLOT_FIRE_ARC) + { + if (DefaultDesc.bOmnidirectional) + return pCC->CreateString(OMNIDIRECTIONAL_ATTRIB); + else if (DefaultDesc.iMaxFireArc != DefaultDesc.iMinFireArc) + { + ICCItem* pResult = pCC->CreateLinkedList(); + pResult->Append(pCC->CreateInteger(DefaultDesc.iMinFireArc)); + pResult->Append(pCC->CreateInteger(DefaultDesc.iMaxFireArc)); + return pResult; + } + else + return (DefaultDesc.iMinFireArc == 0) ? pCC->CreateNil() : pCC->CreateInteger(DefaultDesc.iMinFireArc); + } + else if (Property == PROPERTY_DEVICE_SLOT_HAS_ATTRIBUTE) + return pArgs->GetCount() >= 4 ? pCC->CreateBool(Slot.Attributes.Find(pArgs->GetElement(3)->GetStringValue())) : pCC->CreateError("Insufficient arguments"); + else if (Property == PROPERTY_DEVICE_SLOT_MAX_FIRE_ARC) + return pCC->CreateInteger(DefaultDesc.iMaxFireArc); + else if (Property == PROPERTY_DEVICE_SLOT_MAX_MASS) + return pCC->CreateInteger(Slot.iMaxMass); + else if (Property == PROPERTY_DEVICE_SLOT_MAX_POWER) + return pCC->CreateInteger(Slot.iMaxPower); + else if (Property == PROPERTY_DEVICE_SLOT_MAX_POWER_PERCENT) + return pCC->CreateDouble(Slot.fMaxPowerPercent); + else if (Property == PROPERTY_DEVICE_SLOT_MIN_FIRE_ARC) + return pCC->CreateInteger(DefaultDesc.iMinFireArc); + else if (Property == PROPERTY_DEVICE_SLOT_NAME) + return pCC->CreateString(Slot.Name); + else if (Property == PROPERTY_DEVICE_SLOT_OMNIDIRECTIONAL) + return pCC->CreateBool(DefaultDesc.bOmnidirectional); + else if (Property == PROPERTY_DEVICE_SLOT_POS) + { + ICCItem* pResult = pCC->CreateLinkedList(); + pResult->Append(pCC->CreateInteger(DefaultDesc.iPosAngle)); + pResult->Append(pCC->CreateInteger(DefaultDesc.iPosRadius)); + return pResult; + } + else if (Property == PROPERTY_DEVICE_SLOT_POS_ANGLE) + return pCC->CreateInteger(DefaultDesc.iPosAngle); + else if (Property == PROPERTY_DEVICE_SLOT_POS_CARTESIAN) + { + ICCItem* pResult = pCC->CreateLinkedList(); + pResult->Append(pCC->CreateInteger(int(sin((DefaultDesc.iPosAngle + 90) * PI / 2.0) * DefaultDesc.iPosRadius))); + pResult->Append(pCC->CreateInteger(int(cos((DefaultDesc.iPosAngle + 90) * PI / 2.0) * DefaultDesc.iPosRadius))); + return pResult; + } + else if (Property == PROPERTY_DEVICE_SLOT_POS_RADIUS) + return pCC->CreateInteger(DefaultDesc.iPosRadius); + else if (Property == PROPERTY_DEVICE_SLOT_SECONDARY_WEAPON) + return pCC->CreateBool(DefaultDesc.bSecondary); + else + return pCC->CreateError("Unknown device slot property", pCC->CreateString(Property)); + } + +bool CGroupOfDeviceGenerators::ItemFitsSlot (CSpaceObject *pObj, const CItem &Item, const int iSlotIndex) const + +// ItemFitsSlot +// +// Returns TRUE if the item fits the slot specified by iSlotIndex, otherwise FALSE + + { + CItemCtx ItemCtx(&Item); + int iPowerUse = Item.IsDevice() ? Item.AsDeviceItem().GetDeviceClass().GetPowerRating(ItemCtx) : 0; // TODO: Move this outside of this function; this is a slow operation + int iMass = Item.GetMassKg(); + const SSlotDesc Slot = m_SlotDesc[iSlotIndex]; + bool bMatchesCriteria = Item.MatchesCriteria(Slot.Criteria); + bool bMeetsPowerLimits = Slot.iMaxPower > 0 ? Slot.iMaxPower >= iPowerUse : true; + bool bMeetsPowerPercentLimits = Slot.fMaxPowerPercent > 0.0 ? (pObj->GetMaxPower() * Slot.fMaxPowerPercent) >= iPowerUse : true; + bool bMeetsMassLimits = Slot.iMaxMass > 0 ? Slot.iMaxMass >= iMass : true; + + return (bMatchesCriteria && bMeetsPowerLimits && bMeetsPowerPercentLimits && bMeetsMassLimits); + } + bool CGroupOfDeviceGenerators::HasItemAttribute (const CString &sAttrib) const // HasItemAttribute @@ -1445,6 +1618,23 @@ ALERROR CGroupOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement SSlotDesc *pSlotDesc = m_SlotDesc.Insert(); pSlotDesc->Criteria.Init(pEntry->GetAttribute(CRITERIA_ATTRIB)); + pSlotDesc->Description = pEntry->GetAttribute(DESCRIPTION_ATTRIB); + pSlotDesc->Name = pEntry->GetAttribute(NAME_ATTRIB); + pSlotDesc->iMaxPower = pEntry->GetAttributeInteger(MAX_POWER_ATTRIB); + pSlotDesc->fMaxPowerPercent = pEntry->GetAttributeFloat(MAX_POWER_PERCENT_ATTRIB); + pSlotDesc->iMaxMass = pEntry->GetAttributeInteger(MAX_MASS_ATTRIB); + + const std::string sAttributes = std::string(pEntry->GetAttribute(ATTRIBUTES_ATTRIB)); + const char cDelimiter = ';'; + + std::istringstream iss (sAttributes); + std::string item; + while (std::getline(iss, item, cDelimiter)) + { + item.erase(std::remove(item.begin(), item.end(), ' '), item.end()); + if (!item.empty()) + pSlotDesc->Attributes.Insert(CString(item.c_str())); + } if (error = IDeviceGenerator::InitDeviceDescFromXML(Ctx, pEntry, &pSlotDesc->DefaultDesc)) return error; @@ -1455,6 +1645,11 @@ ALERROR CGroupOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement if (pSlotDesc->iMaxCount == -1 && !pSlotDesc->DefaultDesc.sID.IsBlank()) pSlotDesc->iMaxCount = 1; + + if (!pSlotDesc->DefaultDesc.sID.IsBlank()) + { + m_SlotDescIndicesByID.Insert(pSlotDesc->DefaultDesc.sID, m_SlotDesc.GetCount() - 1); + } } else { diff --git a/Mammoth/TSE/CInstalledDevice.cpp b/Mammoth/TSE/CInstalledDevice.cpp index 3e37f42b7..c85b658b5 100644 --- a/Mammoth/TSE/CInstalledDevice.cpp +++ b/Mammoth/TSE/CInstalledDevice.cpp @@ -374,6 +374,8 @@ void CInstalledDevice::Install (CSpaceObject &Source, CItemListManipulator &Item // definitions from the previous device). InitFromDesc(Desc); + if (!m_sID.IsBlank() && iDeviceSlot != -1) + m_pSource->SetDeviceSlotAtID(m_sID, iDeviceSlot); // Call the class @@ -782,6 +784,10 @@ void CInstalledDevice::ReadFromStream (CSpaceObject &Source, SLoadCtx &Ctx) } } + if (m_pSource != NULL && !m_sID.IsBlank()) { + m_pSource->SetDeviceSlotAtID(m_sID, m_iDeviceSlot); + } + // In previous versions we automatically offset weapon positions. // In later versions we explicitly set the position, so we have // to do so here. @@ -1243,6 +1249,7 @@ void CInstalledDevice::Uninstall (CSpaceObject *pObj, CItemListManipulator &Item CShip *pShip = pObj->AsShip(); if (pShip) pShip->GetController()->OnItemUninstalled(theItem); + pObj->ClearDeviceSlotAtID(m_sID); // We need to refresh the cursor because OnUninstall might // have done something diff --git a/Mammoth/TSE/CShip.cpp b/Mammoth/TSE/CShip.cpp index 5b6fe36bf..46c5555b3 100644 --- a/Mammoth/TSE/CShip.cpp +++ b/Mammoth/TSE/CShip.cpp @@ -429,7 +429,9 @@ void CShip::CalcDeviceBonus (void) for (CDeviceItem DeviceItem : GetDeviceSystem()) { - CInstalledDevice &Device = *DeviceItem.GetInstalledDevice(); + CInstalledDevice *pDevice = DeviceItem.GetInstalledDevice(); + if (!pDevice) continue; + CInstalledDevice &Device = *pDevice; CItemCtx ItemCtx(this, &Device); // Keep track of device types to see if we have duplicates @@ -460,7 +462,9 @@ void CShip::CalcDeviceBonus (void) for (CDeviceItem OtherDevItem : GetDeviceSystem()) { - CInstalledDevice &OtherDev = *OtherDevItem.GetInstalledDevice(); + CInstalledDevice *pOtherDev = OtherDevItem.GetInstalledDevice(); + if (!pOtherDev) continue; + CInstalledDevice &OtherDev = *pOtherDev; if (OtherDev.GetDeviceSlot() != Device.GetDeviceSlot()) { // See if this device enhances us @@ -510,7 +514,9 @@ void CShip::CalcDeviceBonus (void) for (CDeviceItem DeviceItem : GetDeviceSystem()) { - CInstalledDevice &Device = *DeviceItem.GetInstalledDevice(); + CInstalledDevice *pDevice = DeviceItem.GetInstalledDevice(); + if (!pDevice) continue; + CInstalledDevice &Device = *pDevice; int *pCount = DeviceTypes.GetAt(Device.GetClass()->GetUNID()); Device.SetDuplicate(*pCount > 1); @@ -1029,7 +1035,7 @@ bool CShip::CanBeDestroyedBy (CSpaceObject &Attacker) const return false; } -bool CShip::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot, InstallItemResults *retiResult, CString *retsResult, CItem *retItemToReplace) +bool CShip::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot, bool bForceUseOfDeviceSlot, InstallItemResults *retiResult, CString *retsResult, CItem *retItemToReplace) // CanInstallItem // @@ -1041,6 +1047,7 @@ bool CShip::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &S CString sResult; CItem ItemToReplace; const CHullDesc &Hull = m_pClass->GetHullDesc(); + const IDeviceGenerator *pDevSlots = GetDeviceSystem().GetSlots(); int iSlot = Slot.iIndex; @@ -1116,6 +1123,7 @@ bool CShip::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &S else if (Item.IsDevice()) { CItemCtx ItemCtx(&Item); + int iSlotFromId = pDevSlots->GetDescIndexGivenId(Slot.sID); // Get the item type @@ -1127,6 +1135,11 @@ bool CShip::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &S if (!Item.MatchesCriteria(Hull.GetDeviceCriteria())) iResult = insNotCompatible; + // If we force use of a device slot, and that device slot doesn't fit, then we cannot install + + if (bForceUseOfDeviceSlot && !pDevSlots->ItemFitsSlot(this, Item, iSlotFromId != -1 ? iSlotFromId : iSlot)) + iResult = insNotCompatible; + // Ask the object if we can install this item else if (!FireCanInstallItem(Item, Slot, &sResult)) diff --git a/Mammoth/TSE/CShipClass.cpp b/Mammoth/TSE/CShipClass.cpp index 8319c1c26..89a1632a2 100644 --- a/Mammoth/TSE/CShipClass.cpp +++ b/Mammoth/TSE/CShipClass.cpp @@ -2095,6 +2095,30 @@ bool CShipClass::FindDeviceSlotDesc (CShip *pShip, const CItem &Item, SDeviceDes return false; } +bool CShipClass::FindDeviceSlotDesc (CShip *pShip, const CString& sID, SDeviceDesc *retDesc) const + +// FindDeviceSlotDesc +// +// Looks for a device slot descriptor + + { + SDeviceGenerateCtx Ctx(GetUniverse()); + + // If we have a dedicated device slot object, then use that. + + if (m_pDeviceSlots) + return m_pDeviceSlots->FindDefaultDesc(Ctx, pShip, sID, retDesc); + + // Otherwise, for backwards compatibility we check the device generator. + + else if (m_pDevices) + return m_pDevices->FindDefaultDesc(Ctx, pShip, sID, retDesc); + + // Otherwise, not found + + return false; + } + void CShipClass::GenerateDevices (int iLevel, CDeviceDescList &Devices, DWORD dwFlags) const // GenerateDevices diff --git a/Mammoth/TSE/CSpaceObject.cpp b/Mammoth/TSE/CSpaceObject.cpp index 639e74cd8..a3c10d851 100644 --- a/Mammoth/TSE/CSpaceObject.cpp +++ b/Mammoth/TSE/CSpaceObject.cpp @@ -964,7 +964,7 @@ bool CSpaceObject::CanFireOnObjHelper (CSpaceObject *pObj) const ); } -bool CSpaceObject::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot, InstallItemResults *retiResult, CString *retsResult, CItem *retItemToReplace) +bool CSpaceObject::CanInstallItem (const CItem &Item, const CDeviceSystem::SSlotDesc &Slot, bool bForceUseOfDeviceSlot, InstallItemResults *retiResult, CString *retsResult, CItem *retItemToReplace) // CanInstallItem // @@ -2032,6 +2032,11 @@ bool CSpaceObject::FireCanInstallItem (const CItem &Item, const CDeviceSystem::S const CItem &ItemToReplace = ItemList.GetItemAtCursor(); Ctx.DefineItem(CONSTLIT("aItemToReplace"), ItemToReplace); } + if (AsShip() ? GetDeviceSlotAtID(Slot.sID) : false) + { + const CItem & ItemToReplace = ItemList.GetItemAtCursor(); + Ctx.DefineItem(CONSTLIT("aItemToReplace"), ItemToReplace); + } else Ctx.DefineNil(CONSTLIT("aItemToReplace")); diff --git a/Mammoth/TSE/ShipProperties.cpp b/Mammoth/TSE/ShipProperties.cpp index 5a648f7ab..7796636fa 100644 --- a/Mammoth/TSE/ShipProperties.cpp +++ b/Mammoth/TSE/ShipProperties.cpp @@ -4,6 +4,10 @@ // Copyright (c) 2021 Kronosaur Productions, LLC. All Rights Reserved. #include "PreComp.h" +#include + +#define FIELD_DEVICE_SLOT CONSTLIT("deviceSlot") +#define FIELD_SLOT_ID CONSTLIT("slotID") #define PROPERTY_ALWAYS_LEAVE_WRECK CONSTLIT("alwaysLeaveWreck") #define PROPERTY_ARMOR_COUNT CONSTLIT("armorCount") @@ -25,6 +29,8 @@ #define PROPERTY_CHARACTER_NAME CONSTLIT("characterName") #define PROPERTY_DEVICE_DAMAGE_IMMUNE CONSTLIT("deviceDamageImmune") #define PROPERTY_DEVICE_DISRUPT_IMMUNE CONSTLIT("deviceDisruptImmune") +#define PROPERTY_DEVICE_AT_SLOT CONSTLIT("deviceAtSlot") +#define PROPERTY_DEVICE_SLOT_IDS CONSTLIT("deviceSlotIDs") #define PROPERTY_DISINTEGRATION_IMMUNE CONSTLIT("disintegrationImmune") #define PROPERTY_DOCKED_AT_ID CONSTLIT("dockedAtID") #define PROPERTY_DOCKING_ENABLED CONSTLIT("dockingEnabled") @@ -260,6 +266,68 @@ ICCItemPtr CShip::OnFindProperty (CCodeChainCtx &CCX, const CString &sProperty) return ICCItemPtr(); } +ICCItem* CShip::GetDeviceSlotProperty(CCodeChain* pCC, CCodeChainCtx& Ctx, const ICCItem* pArgs) const + +// GetDeviceSlotProperty +// +// Returns a device slot property + + { + int iDeviceSlot = -1; + if (pArgs->GetCount() >= 3) + { + CString sProperty = pArgs->GetElement(2)->GetStringValue(); + const IDeviceGenerator *pDevSlots = this->GetDeviceSystem().GetSlots(); + int numSlots = pDevSlots->GetNumberOfDescs(); + ICCItem *pOptions = pArgs->GetElement(1); + if (pOptions->IsInteger()) + iDeviceSlot = pOptions->GetIntegerValue(); + else + { + ICCItem *pDeviceSlot = pOptions->GetElement(FIELD_DEVICE_SLOT); + ICCItem *pDeviceSlotID = pOptions->GetElement(FIELD_SLOT_ID); + if (pDeviceSlot && !pDeviceSlot->IsNil()) + iDeviceSlot = pDeviceSlot->GetIntegerValue(); + + if (pDeviceSlotID && !pDeviceSlotID->IsNil()) + { + CString sDeviceSlotID = pDeviceSlotID->GetStringValue(); + iDeviceSlot = pDevSlots->GetDescIndexGivenId(sDeviceSlotID); + if (iDeviceSlot == -1) + return pCC->CreateError(CONSTLIT("Invalid device slot ID"), pDeviceSlotID); + } + } + if (iDeviceSlot != -1) + { + if (iDeviceSlot < 0 || iDeviceSlot >= numSlots) + return pCC->CreateError(CONSTLIT("Invalid device slot"), pOptions); + } + + if (iDeviceSlot < 0) + return pCC->CreateError(CONSTLIT("Invalid device slot"), pOptions); + + if (strEquals(sProperty, PROPERTY_DEVICE_AT_SLOT)) + { + ICCItem* pDeviceSlotID = pOptions->GetElement(FIELD_SLOT_ID); + if (pDeviceSlotID && !pDeviceSlotID->IsNil()) + { + int iDeviceIndex = GetDeviceSlotAtID(pDeviceSlotID->GetStringValue()); + if (iDeviceIndex != -1) + return CreateListFromItem(m_Devices.GetDevice(iDeviceIndex).GetDeviceItem()); + else + return pCC->CreateNil(); + } + else + return pCC->CreateError(CONSTLIT("deviceAtSlot requires slotID to be defined"), pOptions); + } + + return pDevSlots->GetDeviceSlotProperty(iDeviceSlot, pCC, sProperty, pArgs); + } + else + return pCC->CreateError(CONSTLIT("Insufficient arguments")); + + } + ICCItem *CShip::GetPropertyCompatible (CCodeChainCtx &Ctx, const CString &sName) const // GetProperty @@ -270,6 +338,8 @@ ICCItem *CShip::GetPropertyCompatible (CCodeChainCtx &Ctx, const CString &sName) CCodeChain &CC = GetUniverse().GetCC(); ICCItem *pResult; + // Device slot properties + if (strEquals(sName, PROPERTY_ALWAYS_LEAVE_WRECK)) return CC.CreateBool(m_fAlwaysLeaveWreck || m_pClass->GetWreckChance() >= 100); @@ -335,7 +405,7 @@ ICCItem *CShip::GetPropertyCompatible (CCodeChainCtx &Ctx, const CString &sName) else if (strEquals(sName, PROPERTY_CHALLENGE_RATING)) return CC.CreateInteger(CChallengeRatingCalculator::CalcChallengeRating(*this)); - + else if (strEquals(sName, PROPERTY_CHARACTER)) return (m_pCharacter ? CC.CreateInteger(m_pCharacter->GetUNID()) : CC.CreateNil()); @@ -348,6 +418,15 @@ ICCItem *CShip::GetPropertyCompatible (CCodeChainCtx &Ctx, const CString &sName) else if (strEquals(sName, PROPERTY_DEVICE_DISRUPT_IMMUNE)) return CC.CreateBool(m_Armor.IsImmune(specialDeviceDisrupt)); + else if (strEquals(sName, PROPERTY_DEVICE_SLOT_IDS)) + { + TArray ids = GetDeviceSystem().GetSlots()->GetDeviceSlotIds(); + ICCItem* pIDs = CC.CreateLinkedList(); + for (int i = 0; i < ids.GetCount(); i++) + pIDs->AppendString(ids[i]); + return pIDs; + } + else if (strEquals(sName, PROPERTY_DISINTEGRATION_IMMUNE)) return CC.CreateBool(m_Armor.IsImmune(specialDisintegration)); diff --git a/Mammoth/TSUI/CWeaponHUDDefault.cpp b/Mammoth/TSUI/CWeaponHUDDefault.cpp index f48ca5b95..0e14811b3 100644 --- a/Mammoth/TSUI/CWeaponHUDDefault.cpp +++ b/Mammoth/TSUI/CWeaponHUDDefault.cpp @@ -149,7 +149,7 @@ void CWeaponHUDDefault::PaintDeviceStatus (CShip *pShip, DeviceNames iDev, int x const CG16bitFont &LargeBoldFont = VI.GetFont(fontLargeBold); CInstalledDevice *pDevice = pShip->GetNamedDevice(iDev); - if (pDevice) + if (pDevice && pDevice->GetClass()) { CItemCtx ItemCtx(pShip, pDevice); CDeviceClass *pClass = pDevice->GetClass(); diff --git a/Transcendence/TransCore/RPGCode.xml b/Transcendence/TransCore/RPGCode.xml index f99689f80..2ec3b6c57 100644 --- a/Transcendence/TransCore/RPGCode.xml +++ b/Transcendence/TransCore/RPGCode.xml @@ -1860,6 +1860,7 @@ ; noCargoCheck: If True, then we don't check to see if the old device fits in cargo ; replaceItem: If not Nil, we try to replace this device. ; showActualItem: Show true item, even if unknown to player. + ; slotID: If not Nil, we try to install in the device slot with the specified ID. ; targetObj: Object on which to install (if Nil, we assume player ship) ; techCriteria: If not Nil, the item must match this criteria ; techCriteriaOverride: If not Nil, and item matches this criteria, install even if above maxTechLevel @@ -1953,6 +1954,9 @@ ) (recommendedSlot (switch + (@ data 'slotID) + {slotID : (@ data 'slotID)} + (not (@ data 'replaceItem)) -1