#include "pch.h" #include "consoledata.h" namespace { class InventoryImage : public WrapImage, public TrekClientEventSink, public IKeyboardInput { // a search key for finding identical inventory slots class InventorySlotDesc { EquipmentType et; Mount mount; IpartIGC* ppart; IshipIGC* pgunner; const char* szKey; public: InventorySlotDesc() {}; InventorySlotDesc(EquipmentType etN, Mount mountN, IpartIGC* partN, IshipIGC* pgunnerN, const char* szKeyN) : et(etN), mount(mountN), ppart(partN), pgunner(pgunnerN), szKey(szKeyN) {} bool operator == (const InventorySlotDesc& desc) const { return (et == desc.et) && (mount == desc.mount) && (ppart == desc.ppart) && (pgunner == desc.pgunner) && (szKey == desc.szKey); } bool operator < (const InventorySlotDesc& desc) const { if (et != desc.et) return et < desc.et; else if (mount != desc.mount) return mount < desc.mount; else if (ppart != desc.ppart) return ppart < desc.ppart; else if (pgunner != desc.pgunner) return pgunner < desc.pgunner; else return szKey < desc.szKey; } bool operator > (const InventorySlotDesc& desc) const { return desc < (*this); } bool operator >= (const InventorySlotDesc& desc) const { return !((*this) < desc); } }; typedef TMap > LineMDLCache; LineMDLCache cache1, cache2; LineMDLCache *pcacheCurrent, *pcachePrevious; public: InventoryImage() : WrapImage(Image::GetEmpty()), pcacheCurrent(&cache1), pcachePrevious(&cache2) { Rebuild(); }; TRef CreatePartPane(const ZString& strNamespace, IpartIGC* ppart, const char* szKey, IshipIGC* gunner = NULL, const char* szLocation = "") { TRef ppartWrapper = new PartWrapper(); ppartWrapper->SetPart(ppart); TRef pmodelDataGunner = new ModelData(); pmodelDataGunner->SetModel(gunner); TRef ppane; TRef pnsInventoryPart = GetModeler()->CreateNameSpace("inventorypart", GetModeler()->GetNameSpace("console")); pnsInventoryPart->AddMember("part", ppartWrapper); pnsInventoryPart->AddMember("gunner", pmodelDataGunner); pnsInventoryPart->AddMember("partkey", new StringValue(szKey)); pnsInventoryPart->AddMember("partlocation", new StringValue(szLocation)); TRef pns = GetModeler()->GetNameSpace(strNamespace); CastTo(ppane, pns->FindMember("InventoryLine")); GetModeler()->UnloadNameSpace(strNamespace); GetModeler()->UnloadNameSpace("inventorypart"); return ppane; }; bool ShipHasPartForMount(IshipIGC* pship, EquipmentType et, Mount mount) { if (mount < 0) return false; for (const PartLinkIGC* ppartLink = pship->GetParts()->first(); ppartLink; ppartLink = ppartLink->next()) { if (ppartLink->data()->GetEquipmentType() == et && pship->GetHullType()->CanMount(ppartLink->data()->GetPartType(), mount)) return true; } return false; } void ProcessPart(Pane* paneParent, IshipIGC* pship, EquipmentType et, Mount mount, const char* szKey, IshipIGC* gunner = NULL, const char* szLocation = "") { const IhullTypeIGC* pht = pship->GetHullType(); // if the ship can mount the part in question (or if it's in inventory)... if (pht && (mount < 0 || pht->GetPartMask(et, mount) != 0)) { // create/find an entry for this part. ZString strNamespace; IpartIGC* ppart = pship->GetMountedPart(et, mount); if (ppart || ShipHasPartForMount(pship, et, mount)) { TRef ppane; InventorySlotDesc desc(et, mount, ppart, gunner, szKey); if (pcachePrevious->Find(desc, ppane)) { // reuse the existing pane } else if (ppart) { strNamespace = ppart->GetPartType()->GetInventoryLineMDLName(); ppane = CreatePartPane(strNamespace, ppart, szKey, gunner, szLocation); } else { strNamespace = ZString("invEmpty") + IpartTypeIGC::GetEquipmentTypeName(et); ppane = CreatePartPane(strNamespace, ppart, szKey, gunner, szLocation); } pcacheCurrent->Set(desc, ppane); paneParent->InsertAtBottom(ppane); } }; }; void ProcessObservers(Pane* paneParent, IshipIGC* pship, const char* szKey) { const IhullTypeIGC* pht = pship->GetHullType(); int nObservers = 0; for (ShipLinkIGC* psl = pship->GetChildShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if (pship->GetTurretID() == NA) { nObservers++; } } if (nObservers > 0) { TRef ppane; InventorySlotDesc desc(NA, NA, NULL, pship, szKey); if (!pcachePrevious->Find(desc, ppane)) { TRef pns = GetModeler()->GetNameSpace("invObserver"); CastTo(ppane, pns->FindMember("InventoryLine")); GetModeler()->UnloadNameSpace("invObserver"); } pcacheCurrent->Set(desc, ppane); paneParent->InsertAtBottom(ppane); } }; bool ShowCargoSlot(IshipIGC* pship, Mount mount) { const IhullTypeIGC* pht = pship->GetHullType(); if (!pht || (pht->GetCapabilities() & c_habmLifepod)) return false; IpartIGC* ppart = pship->GetMountedPart(NA, mount); IpartTypeIGC *ppartType = ppart ? ppart->GetPartType() : NULL; // If this is ammo/missiles/etc, only show the first instance of this part ObjectType ot = ppart ? ppart->GetObjectType() : NA; if (ppart == NULL || ot == OT_pack || ot == OT_magazine || ot == OT_dispenser) { for (Mount mountPrev = mount + 1; mountPrev < 0; ++mountPrev) { IpartIGC *ppartPrev = pship->GetMountedPart(NA, mountPrev); IpartTypeIGC *ppartTypePrev = ppartPrev ? ppartPrev->GetPartType() : NULL; if (ppartType == ppartTypePrev) { return false; break; } } } return true; }; void ProcessCargo(Pane* paneParent, IshipIGC* pship, Mount mount, const char* szKey) { if (ShowCargoSlot(pship, mount)) { IpartIGC* ppart = pship->GetMountedPart(NA, mount); // create an empty slot TRef ppane; InventorySlotDesc desc(NA, NA, ppart, NULL, szKey); if (pcachePrevious->Find(desc, ppane)) { // reuse the existing pane } else if (ppart) { ppane = CreatePartPane( ppart->GetPartType()->GetInventoryLineMDLName(), ppart, szKey, NULL, "Cargo" ); } else { ppane = CreatePartPane("invEmptyCargo", ppart, szKey, NULL, "Cargo"); } pcacheCurrent->Set(desc, ppane); paneParent->InsertAtBottom(ppane); } }; /* void ProcessCargo(Pane* paneParent, IshipIGC* pship, const char* szKey) { IpartIGC* ppart = trekClient.GetCargoPart(); TRef ppane; InventorySlotDesc desc(NA, NA, ppart, NULL); if (pcachePrevious->Find(desc, ppane)) { // reuse the existing pane } else if (ppart) { ppane = CreatePartPane( ppart->GetPartType()->GetInventoryLineMDLName(), ppart, szKey, NULL, "Cargo" ); } else { ppane = CreatePartPane("invEmptyCargo", ppart, szKey, NULL, "Cargo"); } pcacheCurrent->Set(desc, ppane); paneParent->InsertAtBottom(ppane); }; */ void ProcessTurret(Pane* ppaneParent, IshipIGC* pship, Mount mount, const char* szKey) { IshipIGC* pshipGunner = pship->GetGunner(mount); IpartIGC* ppart = pship->GetMountedPart(ET_Weapon, mount); const IhullTypeIGC* pht = pship->GetHullType(); ProcessPart(ppaneParent, pship, ET_Weapon, mount, szKey, pshipGunner, pht->GetHardpointData(mount).locationAbreviation); if (pshipGunner != NULL) { TRef ppaneTurret; InventorySlotDesc desc(NA, mount, ppart, pshipGunner, szKey); if (!pcachePrevious->Find(desc, ppaneTurret)) { ppaneTurret = CreatePartPane(pshipGunner ? "invturret" : "invemptyturret", ppart, "", pshipGunner, pht->GetHardpointData(mount).locationAbreviation); } pcacheCurrent->Set(desc, ppaneTurret); ppaneParent->InsertAtBottom(ppaneTurret); } } void Rebuild() { IshipIGC* pship = trekClient.GetShip()->GetSourceShip(); const IhullTypeIGC* pht = pship->GetHullType(); bool bIsPilot = trekClient.GetShip()->GetParentShip() == NULL; // swap the old and the new cache LineMDLCache *pcacheTemp = pcachePrevious; pcachePrevious = pcacheCurrent; pcacheCurrent = pcacheTemp; TRef pColumnPane = new ColumnPane(); //TRef pRowPane = new RowPane(); //TRef pTab = new ImagePane(GetModeler()->LoadImage( // "inventorytabbmp", false)); TRef pTop = new ImagePane(GetModeler()->LoadImage( "inventorytopbmp", false)); TRef pBottom = new ImagePane(GetModeler()->LoadImage( "inventorybottombmp", false)); //pRowPane->InsertAtBottom(pTab); pColumnPane->InsertAtBottom(pTop); static const char* const vszWeaponHotKeys[] = { "1.", "2.", "3.", "4." }; static const char* const vszTurretHotKeys[] = { "Y.", "U.", "I.", "O.", "", "", "", "" }; ZAssert(c_maxUnmannedWeapons <= sizeof(vszWeaponHotKeys)/sizeof(const char* const)); ZAssert(c_maxMountedWeapons <= sizeof(vszTurretHotKeys)/sizeof(const char* const)); Mount mount; int nFixedWeapons = pht->GetMaxFixedWeapons(); for (mount = 0; mount < nFixedWeapons; mount++) { ProcessPart(pColumnPane, pship, ET_Weapon, mount, bIsPilot ? vszWeaponHotKeys[mount] : ""); }; ProcessObservers(pColumnPane, pship, ""); for (; mount < pht->GetMaxWeapons(); mount++) { ProcessTurret(pColumnPane, pship, mount, bIsPilot ? vszTurretHotKeys[mount - nFixedWeapons] : ""); }; ProcessPart(pColumnPane, pship, ET_Magazine, 0, "5."); ProcessPart(pColumnPane, pship, ET_ChaffLauncher, 0, "6."); ProcessPart(pColumnPane, pship, ET_Shield, 0, "7."); ProcessPart(pColumnPane, pship, ET_Cloak, 0, "8."); ProcessPart(pColumnPane, pship, ET_Afterburner, 0, "9."); ProcessPart(pColumnPane, pship, ET_Dispenser, 0, "0."); ProcessCargo(pColumnPane, pship, trekClient.GetSelectedCargoMount(), "S."); for (int nCargoOffset = 1; nCargoOffset < c_maxCargo; nCargoOffset++) { Mount mountCargo = trekClient.GetSelectedCargoMount() - nCargoOffset; if (mountCargo < -c_maxCargo) mountCargo += c_maxCargo; ProcessCargo(pColumnPane, pship, mountCargo, ""); } //ProcessCargo(pColumnPane, pship, "S."); SetImage(CreatePaneImage(GetEngine(), SurfaceType3D(), true, pColumnPane)); pColumnPane->InsertAtBottom(pBottom); //pRowPane->InsertAtBottom(pColumnPane); pcachePrevious->SetEmpty(); } void OnLoadoutChanged(IpartIGC* ppart, LoadoutChange lc) { switch (lc) { case c_lcMounted: case c_lcDismounted: case c_lcAdded: case c_lcRemoved: case c_lcHullChange: case c_lcAddPassenger: case c_lcRemovePassenger: case c_lcPassengerMove: case c_lcTurretChange: case c_lcCargoSelectionChanged: // don't bother updating the inventory while in the hangar if (trekClient.GetShip()->GetCluster()) { Rebuild(); } else { // we do need to make sure we don't hang on to parts that // have changed, however, so destroy the inventory pane. SetImage(Image::GetEmpty()); pcacheCurrent->SetEmpty(); } break; case c_lcQuantityChange: case c_lcDisembark: // don't try to handle this break; default: ZError("Unknown loadout change"); } } void OnClusterChanged(IclusterIGC* pcluster) { if (pcluster != NULL) { // this should be safe even if we are just flying from aleph // to aleph because nothing should have changed. Rebuild(); } } }; } TRef InventoryImageFactory::Apply(ObjectStack& stack) { return (Value*)(new InventoryImage()); };