#include "pch.h" #include "trekmdl.h" #include "trekctrls.h" const int c_iClusterHasNoRipcord = 0; const int c_iClusterHasStationRipcord = 1; const int c_iClusterHasShipRipcord = 2; const Color c_rgColorNeutral(0.5f, 0.5f, 0.5f); static void GetClusterOwners(IclusterIGC* pcluster, SideID& sideOwner, SideID& sideSecondaryOwner) { assert (c_cSidesMax == 6); int nStations[c_cSidesMax] = {0, 0, 0, 0, 0, 0}; // count the stations by side for (StationLinkIGC* psl = pcluster->GetStations()->first(); (psl != NULL); psl = psl->next()) { if (!psl->data()->GetStationType()->HasCapability(c_sabmPedestal)) nStations[psl->data()->GetSide()->GetObjectID()]++; } // the owner is the one with the most stations int nStationsOwner = 0; int nStationsSecondaryOwner = 0; sideOwner = NA; sideSecondaryOwner = NA; SideID sideID; for (sideID = 0; sideID < c_cSidesMax; sideID++) { if (nStations[sideID] > nStationsOwner) { // demote the current top owner sideSecondaryOwner = sideOwner; nStationsSecondaryOwner = nStationsOwner; // track the new top owner sideOwner = sideID; nStationsOwner = nStations[sideID]; } else if (nStations[sideID] > nStationsSecondaryOwner) { sideSecondaryOwner = sideID; nStationsSecondaryOwner = nStations[sideID]; } } } static int GetClusterPopulation(IclusterIGC* pcluster, IsideIGC* pside) { int n = 0; SectorID sectorID = pcluster->GetObjectID(); for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if (pship->GetSide() == pside) { PlayerInfo* ppi = (PlayerInfo*)(pship->GetPrivateData()); if (trekClient.m_fm.IsConnected()) { if (ppi && (ppi->LastSeenSector() == sectorID) && (ppi->StatusIsCurrent())) { if (ppi->LastSeenState() != c_ssObserver && ppi->LastSeenState() != c_ssTurret) { IhullTypeIGC* pht = trekClient.GetCore()->GetHullType(ppi->LastSeenShipType()); if (pht != NULL && !(pht->GetCapabilities() & c_habmLifepod)) n++; } } } else { if (pship->GetCluster() == pcluster) n++; } } } return n; } ////////////////////////////////////////////////////////////////////////////// // // SectorInfoPane // ////////////////////////////////////////////////////////////////////////////// struct StoredIcon { Surface* psurfaceIcon; IsideIGC* pside; int count; int sortOrder; StoredIcon(void) : count(1) { } }; typedef Slist_utl IconList; typedef Slink_utl IconLink; static void AddIcon(IObject* psurfaceIcon, IsideIGC* pside, int sortOrder, int sideID, IconList* picons) { for (IconLink* pil = picons->first(); (pil != NULL); pil = pil->next()) { if ((pil->data().psurfaceIcon == (Surface*)psurfaceIcon) && (pil->data().pside == pside)) { pil->data().count++; return; } } IconLink* p = new IconLink; p->data().psurfaceIcon = (Surface*)psurfaceIcon; p->data().pside = pside; //Hash in the side ID p->data().sortOrder = sortOrder += ((int)sideID) * 0x10000; { //Walk the list till we find the proper spot for (IconLink* pil = picons->first(); (pil != NULL); pil = pil->next()) { if (sortOrder < pil->data().sortOrder) { pil->txen(p); return; } } } picons->last(p); } class SectorInfoPane : public Image { private: TRef m_pimageBkgnd; TRef m_pimageSmallBkgnd; TRef m_pimageLargeBkgnd; TRef m_pimageExpand; TRef m_pClusterSel; TRef m_pClusterMouseOver; public: SectorInfoPane() { m_pimageSmallBkgnd = GetModeler()->LoadImage("consectorinfobmp", true); m_pimageLargeBkgnd = GetModeler()->LoadImage("conesectorinfobmp", true); m_pimageExpand = GetModeler()->LoadImage("consectorexpandbmp", true); m_pimageBkgnd = m_pimageSmallBkgnd; } void CalcBounds() { m_bounds = m_pimageBkgnd->GetBounds(); } void ClearCluster(void) { m_pClusterSel = NULL; m_pClusterMouseOver = NULL; } void SelectCluster(IclusterIGC* pClusterSel) { m_pClusterSel = pClusterSel; Changed(); } void SelectMouseOverCluster(IclusterIGC* pClusterMouseOver) { m_pClusterMouseOver = pClusterMouseOver; Changed(); } MouseResult HitTest(IInputProvider* pprovider, const Point& point, bool bCaptured) { Surface* psurfaceBackground = m_pimageBkgnd->GetSurface(); Surface* psurfaceExpand = m_pimageExpand->GetSurface(); WinPoint pntButton = psurfaceBackground->GetSize() - psurfaceExpand->GetSize(); pntButton = pntButton - WinPoint(16, 4); if (point.X() > pntButton.X() && point.Y() > pntButton.Y() && point.X() < psurfaceBackground->GetSize().X() && point.Y() < psurfaceBackground->GetSize().Y()) { return MouseResultHit(); } return m_pimageBkgnd->HitTest(pprovider, point, bCaptured); } WinPoint GetButtonOffset() { Surface* psurfaceBackground = m_pimageBkgnd->GetSurface(); Surface* psurfaceExpand = m_pimageExpand->GetSurface(); WinPoint pntButton = psurfaceBackground->GetSize() - psurfaceExpand->GetSize(); return pntButton - WinPoint(16, 4); } MouseResult Button(IInputProvider* pprovider, const Point& point, int button, bool bCaptured, bool bInside, bool bDown) { if (button == 0 && !bDown) { WinPoint pntButton = GetButtonOffset(); if (point.X() > pntButton.X() && point.Y() > pntButton.Y()) { if (m_pimageBkgnd == m_pimageSmallBkgnd) { m_pimageBkgnd = m_pimageLargeBkgnd; } else { m_pimageBkgnd = m_pimageSmallBkgnd; } Changed(); } } return MouseResult(); } /* void DrawStationIconsOfClass(Context* pcontext, IclusterIGC* pcluster, Point& ptNext, StationClassID classID) { const StationListIGC* stationList = pcluster->GetStations(); for (StationLinkIGC* stationLink = (stationList ? stationList->first() : NULL); stationLink; stationLink=stationLink->next()) { IstationIGC* pstation = stationLink->data(); // if this station is in the correct class of station if (pstation->GetBaseStationType()->GetClassID() == classID) { // draw this station icon Surface* psurfaceIcon = (Surface*)(pstation->GetIcon()); if (ptNext.X() < GetButtonOffset().X()) { pcontext->DrawImage3D(psurfaceIcon, pstation->GetSide()->GetColor(), false, ptNext); ptNext += Point(psurfaceIcon->GetSize().X(), 0); } } } } void DrawStationIcons(Context* pcontext, IclusterIGC* pcluster, const Point& ptStations) { Point ptNext = ptStations; // draw the station icons in order of importance DrawStationIconsOfClass(pcontext, pcluster, ptNext, c_scStarbase); DrawStationIconsOfClass(pcontext, pcluster, ptNext, c_scGarrison); DrawStationIconsOfClass(pcontext, pcluster, ptNext, c_scShipyard); DrawStationIconsOfClass(pcontext, pcluster, ptNext, c_scRipcord); DrawStationIconsOfClass(pcontext, pcluster, ptNext, c_scResearch); DrawStationIconsOfClass(pcontext, pcluster, ptNext, c_scOrdinance); DrawStationIconsOfClass(pcontext, pcluster, ptNext, c_scElectronics); DrawStationIconsOfClass(pcontext, pcluster, ptNext, c_scMining); } */ void DrawAsteroidIcons(Context* pcontext, IEngineFont* pfont, IclusterIGC* pcluster, const Point& ptAsteroids) { Point ptNext = ptAsteroids; // Count the asteroids TMap mapSpecialAsteroids; TMap mapSpecialAsteroidIcons; for (AsteroidLinkIGC* asteroidLink = pcluster->GetAsteroids()->first(); asteroidLink != NULL; asteroidLink = asteroidLink->next()) { AsteroidAbilityBitMask aabm = asteroidLink->data()->GetCapabilities(); // if this asteroid has any special capabilities besides being buildable if ((aabm & ~c_aabmBuildable) != 0) { // let it stand up and be counted. int nCount = 0; mapSpecialAsteroids.Find(aabm, nCount); mapSpecialAsteroids.Set(aabm, nCount+1); mapSpecialAsteroidIcons.Set(aabm, (Surface*)asteroidLink->data()->GetIcon()); } } // draw the asteroids TMap::Iterator iterCount(mapSpecialAsteroids); TMap::Iterator iterIcon(mapSpecialAsteroidIcons); while (!iterCount.End()) { pcontext->DrawImage3D(iterIcon.Value(), Color::White(), false, ptNext); pcontext->DrawString( pfont, Color::White(), ptNext + Point(iterIcon.Value()->GetSize().X() + 4, 0), ZString(iterCount.Value()) ); ptNext = ptNext - Point(0, iterIcon.Value()->GetSize().Y()); iterCount.Next(); iterIcon.Next(); } } float GetHeliumInCluster(IclusterIGC* pcluster) { float fOre = 0; for (AsteroidLinkIGC* asteriodLink = pcluster->GetAsteroids()->first(); asteriodLink != NULL; asteriodLink = asteriodLink->next()) { AsteroidAbilityBitMask aabm = asteriodLink->data()->GetCapabilities(); // if we can mine helium at this asteroid if ((aabm & c_aabmMineHe3) != 0) { // count it. fOre += asteriodLink->data()->GetOre(); } } return fOre; } void Render(Context* pcontext) { // draw the background Surface* psurfaceBackground = m_pimageBkgnd->GetSurface(); pcontext->DrawImage(psurfaceBackground); // set font TRef pfont = TrekResources::SmallFont(); // draw the resize button Surface* psurfaceExpand = m_pimageExpand->GetSurface(); pcontext->DrawImage(psurfaceExpand, false, Point::Cast(psurfaceBackground->GetSize() - psurfaceExpand->GetSize() - WinPoint(16, 4))); if (m_pClusterSel) { const int xBorder = 4; float yTop = m_pimageBkgnd->GetBounds().GetRect().YSize(); Point ptSectorName(xBorder + 20, yTop - 13); Point ptStations(xBorder, yTop - 26); // draw sectorname pcontext->DrawString( pfont, Color::White(), ptSectorName, ZString(m_pClusterSel->GetName()) + " Sector" ); // draw station icons c_cSidesMax { IconList icons; IsideIGC* psideMe = trekClient.GetSide(); { for (StationLinkIGC* psl = m_pClusterSel->GetStations()->first(); (psl != NULL); psl = psl->next()) { if (!psl->data()->GetStationType()->HasCapability(c_sabmPedestal)) { IsideIGC* pside = psl->data()->GetSide(); AddIcon(psl->data()->GetIcon(), pside, NA, (pside == psideMe) ? (c_cSidesMax * 2) : (c_cSidesMax + pside->GetObjectID()), &icons); } } } { SectorID sid = m_pClusterSel->GetObjectID(); for (ShipLinkIGC* psl = trekClient.m_pCoreIGC->GetShips()->first(); (psl != NULL); psl = psl->next()) { PlayerInfo* ppi = (PlayerInfo*)(psl->data()->GetPrivateData()); if (ppi && ppi->StatusIsCurrent() && (ppi->LastSeenState() == c_ssFlying) && (ppi->LastSeenSector() == sid)) { HullID hid = ppi->LastSeenShipType(); IhullTypeIGC* pht = trekClient.m_pCoreIGC->GetHullType(hid); if (pht) { IsideIGC* pside = psl->data()->GetSide(); AddIcon(pht->GetIcon(), pside, hid, (pside == psideMe) ? c_cSidesMax : pside->GetObjectID(), &icons); } } } } { Point ptNext = ptStations; for (IconLink* pil = icons.first(); (pil != NULL); pil = pil->next()) { if (ptNext.X() < GetButtonOffset().X()) { const Color& color = pil->data().pside->GetColor(); pcontext->DrawImage3D(pil->data().psurfaceIcon, color, false, ptNext); ptNext += Point(pil->data().psurfaceIcon->GetSize().X(), 0); if (pil->data().count > 1) { TRef pfont = TrekResources::SmallFont(); ZString strCount = pil->data().count; WinPoint pt = pfont->GetTextExtent(strCount); pcontext->DrawString(pfont, Color::White(), ptNext + Point(0, (pil->data().psurfaceIcon->GetSize().Y() - pt.Y())/2), strCount); ptNext += Point(pt.X() + 2, 0); } } } } } //DrawStationIcons(pcontext, m_pClusterSel, ptStations); if (m_pimageBkgnd == m_pimageLargeBkgnd) { const int xSecondColumn = m_pimageSmallBkgnd->GetBounds().GetRect().XSize() + xBorder - 15; Point ptHelium(xSecondColumn, yTop - 30); Point ptAsteroids(xSecondColumn, yTop - 45); // draw the helium count pcontext->DrawString( pfont, Color::White(), ptHelium, "He3: " + ZString(int(GetHeliumInCluster(m_pClusterSel))) ); // draw asteroids DrawAsteroidIcons(pcontext, pfont, m_pClusterSel, ptAsteroids); } if (m_pClusterMouseOver) { assert(m_pClusterMouseOver == m_pClusterSel); // draw the cluster warning AssetMask am = m_pClusterMouseOver->GetClusterSite()->GetClusterAssetMask(); ClusterWarning cw = GetClusterWarning(am, trekClient.MyMission()->GetMissionParams().bInvulnerableStations); if (cw > c_cwNoThreat) { pcontext->DrawString( pfont, Color::White(), Point(0,0), GetClusterWarningText(cw) ); } } } } }; TRef SectorInfoPaneFactory::Apply(ObjectStack& stack) { TRef psectorinfo = new SectorInfoPane(); return (Value*)psectorinfo; } ////////////////////////////////////////////////////////////////////////////// // // SectorMapPane // ////////////////////////////////////////////////////////////////////////////// class SectorMapPane : public Image, public IEventSink, public TrekClientEventSink { private: TRef m_pimageSectorEmpty; TRef m_pimageOwnerHighlight; TRef m_pimageSecondaryOwnerHighlight; TRef m_pimageBkgnd; TRef m_pimageSectorHighlight; TRef m_pimageSectorTargetHighlight; TRef m_pimageSectorSel; TRef m_pimageSectorPickable; TRef m_pimageSectorPickTarget; TRef m_pimageSectorPickableStation; TRef m_pimageSectorPickStationTarget; TRef m_pimageSectorQueued; TRef m_pimageCapitalWarning; TRef m_pimageSectorEnemy; TRef m_pimageSectorMiner; TRef m_pimageSectorWarning; TRef m_pimageBomberWarning; TRef m_pimageSectorCombat; TRef m_pClusterSel; TRef m_peventSinkTimer; TRef m_pSectorInfoPane; float m_xMin; float m_xMax; float m_yMin; float m_yMax; float m_xClusterMin; float m_xClusterMax; float m_yClusterMin; float m_yClusterMax; bool m_bVisible; int m_nMaskModeActive; float m_xDrag; float m_yDrag; bool m_bDragging; bool m_bHovering; bool m_bCanDrag; Point m_pointLastDrag; WinRect m_rectMap; bool m_bFlashFrame; public: enum { c_nXBorder = 4, c_nYBorder = 4 }; SectorMapPane(SectorInfoPane* pSectorInfoPane, Number* pvalueMode, int nMaskModeActive) : Image(pvalueMode), m_nMaskModeActive(nMaskModeActive), m_pSectorInfoPane(pSectorInfoPane), m_bVisible(false), m_xDrag(0), m_yDrag(0), m_bDragging(false), m_bHovering(false), m_bCanDrag(false), m_bFlashFrame(true) { pvalueMode->Update(); Modeler* pmodeler = GetModeler(); m_pimageBkgnd = pmodeler->LoadImage("sectormapbkgndbmp", false); m_pimageSectorHighlight = pmodeler->LoadImage("sectorhighlightbmp", true); m_pimageSectorTargetHighlight = pmodeler->LoadImage("sectortargetbmp", true); m_pimageSectorSel = pmodeler->LoadImage("sectorselbmp", true); m_peventSinkTimer = IEventSink::CreateDelegate(this); //GetWindow()->GetTimer()->AddSink(m_peventSinkTimer, 1.0f); m_rectMap = WinRect::Cast(m_pimageBkgnd->GetBounds().GetRect()); m_pimageSectorEmpty = pmodeler->LoadImage("sectoremptybmp", true); m_pimageOwnerHighlight = pmodeler->LoadImage("sectorownerbmp", true); m_pimageSecondaryOwnerHighlight = pmodeler->LoadImage("sectorinvaderbmp", true); m_pimageSectorPickable = pmodeler->LoadImage("sectorripcordbmp", true); m_pimageSectorPickTarget = pmodeler->LoadImage("sectorripcordtargetbmp", true); m_pimageSectorPickableStation = pmodeler->LoadImage("sectorripcordstationbmp", true); m_pimageSectorPickStationTarget = pmodeler->LoadImage("sectorripcordstationtargetbmp", true); m_pimageSectorQueued = pmodeler->LoadImage("sectorqueuedbmp", true); m_pimageCapitalWarning = pmodeler->LoadImage("sectorcapitalbmp", true); m_pimageSectorCombat = pmodeler->LoadImage("sectorcombatbmp", true); m_pimageSectorWarning = pmodeler->LoadImage("sectorwarningbmp", true); m_pimageBomberWarning = pmodeler->LoadImage("sectorbomberbmp", true); m_pimageSectorMiner = pmodeler->LoadImage("sectorminerbmp", true); m_pimageSectorEnemy = pmodeler->LoadImage("sectorenemybmp", true); } ~SectorMapPane() { SetVisible(false); } void CalcBounds() { m_bounds = m_pimageBkgnd->GetBounds(); } MouseResult HitTest(IInputProvider* pprovider, const Point& point, bool bCaptured) { return m_pimageBkgnd->HitTest(pprovider, point, bCaptured); } void OnSessionLost(char * szReason, LPDPMSG_SESSIONLOST lpSessionLost, Time lastUpdate, Time now) { SetVisible(false); //Clear the selected cluster without sending the advise m_pClusterSel = NULL; m_pSectorInfoPane->ClearCluster(); } void OnDelPlayer(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo, QuitSideReason reason, const char* szMessageParam) { if (pPlayerInfo == trekClient.GetPlayerInfo()) { SetVisible(false); //Clear the selected cluster without sending the advise m_pClusterSel = NULL; m_pSectorInfoPane->ClearCluster(); } } void SelectCluster(IclusterIGC* pClusterSel) { if (pClusterSel == NULL) { IstationIGC* pstation = trekClient.GetShip()->GetStation(); if (pstation) pClusterSel = pstation->GetCluster(); } if (pClusterSel != m_pClusterSel) { m_pClusterSel = pClusterSel; ZAssert(m_pSectorInfoPane); m_pSectorInfoPane->SelectCluster(pClusterSel); Changed(); } } void SetVisible(bool bVisible) { if (m_bVisible != bVisible) { m_bVisible = bVisible; if (bVisible) { GetWindow()->GetTimer()->AddSink(m_peventSinkTimer, 0.2f); SelectCluster(trekClient.GetChatCluster()); m_pSectorInfoPane->SelectMouseOverCluster(NULL); } else { m_pClusterSel = NULL; ZAssert(m_pSectorInfoPane); m_pSectorInfoPane->SelectCluster(NULL); m_pSectorInfoPane->SelectMouseOverCluster(NULL); GetWindow()->GetTimer()->RemoveSink(m_peventSinkTimer); } } } bool OnEvent(IEventSource* pevent) { Changed(); const int cDivisor = 2; static int nTick = 0; ++nTick; if (nTick == cDivisor) { m_bFlashFrame = !m_bFlashFrame; nTick = 0; } return true; } void CalculateScreenMinAndMax() { // map the sector data bool bFirstCluster = true; const ClusterListIGC* clusters = trekClient.m_pCoreIGC->GetClusters(); for (ClusterLinkIGC* cLink = clusters->first(); cLink != NULL; cLink = cLink->next()) { IclusterIGC* pCluster = cLink->data(); if (pCluster->GetModels()->n() != 0) { float x = pCluster->GetScreenX(); float y = pCluster->GetScreenY(); if (bFirstCluster) { bFirstCluster = false; m_xClusterMin = m_xClusterMax = x; m_yClusterMin = m_yClusterMax = y; } else { if (x < m_xClusterMin) m_xClusterMin = x; else if (x > m_xClusterMax) m_xClusterMax = x; if (y < m_yClusterMin) m_yClusterMin = y; else if (y > m_yClusterMax) m_yClusterMax = y; } } } const float c_bfr = 0.1f * max(0.0001, max(m_xClusterMax - m_xClusterMin, m_yClusterMax - m_yClusterMin)); m_xClusterMin -= c_bfr; m_xClusterMax += c_bfr; m_yClusterMin -= c_bfr; m_yClusterMax += c_bfr; // figure out the minimum and maximum screen coordinates m_xMin = m_xClusterMin; m_xMax = m_xClusterMax; m_yMin = m_yClusterMin; m_yMax = m_yClusterMax; float fDesiredAspectRatio = float(m_rectMap.XSize() - 2*c_nXBorder)/(m_rectMap.YSize() - 2*c_nYBorder); // scale it so that a map with height 2 will fit exactly const float fMapmakerHeight = 2.2f; // 10% fudge factor const float fMaxHeight = fMapmakerHeight; const float fMaxWidth = fMaxHeight * fDesiredAspectRatio; // if the map is bigger than we want to display, clip the view to the max size if (m_xMax - m_xMin > fMaxWidth || m_yMax - m_yMin > fMaxHeight) { m_bCanDrag = true; m_xMax = m_xMin + min(m_xMax - m_xMin, fMaxWidth); m_yMax = m_yMin + min(m_yMax - m_yMin, fMaxHeight); } else { m_bCanDrag = false; } //Preserve the aspect ratio float fAspectRatio = (m_xMax - m_xMin) / (m_yMax - m_yMin); if (fAspectRatio < fDesiredAspectRatio) { // grow the X size to correct the aspect ratio float fXGrowth = (fDesiredAspectRatio / fAspectRatio - 1) * (m_xMax - m_xMin); m_xMax += fXGrowth / 2; m_xMin -= fXGrowth / 2; } else if (fAspectRatio > fDesiredAspectRatio) { // grow the Y size to correct the aspect ratio float fYGrowth = (fAspectRatio / fDesiredAspectRatio - 1) * (m_yMax - m_yMin); m_yMax += fYGrowth / 2; m_yMin -= fYGrowth / 2; } // translate by the current drag offset m_xMin += m_xDrag; m_xMax += m_xDrag; m_yMin += m_yDrag; m_yMax += m_yDrag; } int ClusterHasFriendlyRipcord(IclusterIGC* pcluster, IsideIGC* pside, const ClusterListIGC& clustersRipcord) { const StationListIGC* stationList = pcluster->GetStations(); for (StationLinkIGC* stationLink = stationList->first(); stationLink; stationLink=stationLink->next()) { IstationIGC* pstation = stationLink->data(); if (pstation->GetStationType()->HasCapability(c_sabmRipcord) && (pstation->GetSide() == pside)) { return c_iClusterHasStationRipcord; } } float ripcordSpeed; { IshipIGC* pss = trekClient.GetShip()->GetSourceShip(); if (pss->GetBaseHullType()) ripcordSpeed = pss->GetHullType()->GetRipcordSpeed(); else ripcordSpeed = -1.0f; } for (ProbeLinkIGC* ppl = pcluster->GetProbes()->first(); (ppl != NULL); ppl = ppl->next()) { IprobeIGC* pprobe = ppl->data(); if ((pprobe->GetSide() == pside) && pprobe->GetCanRipcord(ripcordSpeed)) return c_iClusterHasStationRipcord; } return clustersRipcord.find(pcluster) != NULL ? c_iClusterHasShipRipcord : c_iClusterHasNoRipcord; } IstationIGC* FindFriendlyStation(IclusterIGC* pcluster, IsideIGC* pside) { const StationListIGC* stationList = pcluster->GetStations(); for (StationLinkIGC* stationLink = stationList->first(); stationLink; stationLink=stationLink->next()) { IstationIGC* pstation = stationLink->data(); if (pstation->GetStationType()->HasCapability(c_sabmRestart) && (pstation->GetSide() == pside)) { return pstation; } } return NULL; } bool HasBuildableAsteroid(IclusterIGC* pcluster, AsteroidAbilityBitMask aabm1, AsteroidAbilityBitMask aabm2) { AsteroidAbilityBitMask aabm = (aabm1 != 0) ? aabm1 : aabm2; if ((aabm & ~c_aabmBuildable) != 0) { aabm &= ~c_aabmBuildable; for (AsteroidLinkIGC* pal = pcluster->GetAsteroids()->first(); (pal != NULL); pal = pal->next()) { if (pal->data()->HasCapability(aabm)) return true; } } else if (aabm == c_aabmBuildable) { for (AsteroidLinkIGC* pal = pcluster->GetAsteroids()->first(); (pal != NULL); pal = pal->next()) { if (pal->data()->HasCapability(aabm)) return true; } } return false; } bool HasCommandTarget(IclusterIGC* pCluster, Command cmd) { ImodelIGC* ptarget = trekClient.GetShip()->GetCommandTarget(cmd); if (ptarget) { if (ptarget->GetCluster() == pCluster) return true; else if (ptarget->GetObjectType() == OT_ship) { IshipIGC* pship = (IshipIGC*)ptarget; PlayerInfo* ppi = (PlayerInfo*)(pship->GetPrivateData()); if (ppi->LastSeenSector() == pCluster->GetObjectID()) return true; } } return false; } void Render(Context* pcontext) { pcontext->SetShadeMode(ShadeModeFlat); Rect rectClip = m_bounds.GetRect(); rectClip.Expand(-1); pcontext->Clip(rectClip); CalculateScreenMinAndMax(); // draw the background pcontext->DrawImage(m_pimageBkgnd->GetSurface()); // draw all the connecting lines for warps { const WarpListIGC* warps = trekClient.m_pCoreIGC->GetWarps(); TVector vertices(warps->n(), 0); TVector indices(warps->n(), 0); for (WarpLinkIGC* warpLink = warps->first(); warpLink != NULL; warpLink = warpLink->next()) { IwarpIGC* pWarp = warpLink->data(); IwarpIGC* pwarpDestination = pWarp->GetDestination(); assert (pwarpDestination != NULL); if (pWarp->GetObjectID() > pwarpDestination->GetObjectID()) { Color colorWarp = 0.5f * Color::White(); WinPoint point1 = GetClusterPoint(pWarp->GetCluster()); WinPoint point2 = GetClusterPoint(pwarpDestination->GetCluster()); indices.PushEnd(vertices.GetCount()); vertices.PushEnd(VertexL(Vector(point1.x, point1.y, 0), colorWarp)); indices.PushEnd(vertices.GetCount()); vertices.PushEnd(VertexL(Vector(point2.x, point2.y, 0), colorWarp)); } } if (vertices.GetCount() != 0) { pcontext->SetLineWidth(1.0f); pcontext->DrawLines(vertices, indices); } } ImodelIGC* pmodelRipcord = trekClient.GetShip()->GetRipcordModel(); IclusterIGC* pclusterRipcord; IsideIGC* pside = trekClient.GetSide(); ClusterListIGC clustersRipcord; if (pmodelRipcord) { { IshipIGC* pshipSource = trekClient.GetShip()->GetSourceShip(); IhullTypeIGC* phtSource = pshipSource->GetBaseHullType(); HullAbilityBitMask habm = (phtSource && phtSource->HasCapability(c_habmCanLtRipcord)) ? (c_habmIsRipcordTarget | c_habmIsLtRipcordTarget) : c_habmIsRipcordTarget; for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if (pship != pshipSource) { IclusterIGC* pc = trekClient.GetRipcordCluster(pship, habm); if (pc) clustersRipcord.last(pc); } } } pclusterRipcord = pmodelRipcord->GetCluster(); if ((pclusterRipcord == NULL) && (pmodelRipcord->GetObjectType() == OT_ship)) { PlayerInfo* ppi = (PlayerInfo*)(((IshipIGC*)pmodelRipcord)->GetPrivateData()); if (ppi->StatusIsCurrent()) pclusterRipcord = trekClient.m_pCoreIGC->GetCluster(ppi->LastSeenSector()); } } // draw the data for each sector const ClusterListIGC* clusters = trekClient.m_pCoreIGC->GetClusters(); for (ClusterLinkIGC* cLink = clusters->first(); cLink != NULL; cLink = cLink->next()) { IclusterIGC* pCluster = cLink->data(); if (pCluster->GetModels()->n() != 0) { Point xy = Point::Cast(GetClusterPoint(pCluster)); // draw the sector outline pcontext->DrawImage3D(m_pimageSectorEmpty->GetSurface(), Color::White(), true, xy); // color it by the owner(s), if any SideID sideOwner; SideID sideSecondaryOwner; GetClusterOwners(pCluster, sideOwner, sideSecondaryOwner); // draw the conflict state AssetMask am = pCluster->GetClusterSite()->GetClusterAssetMask(); ClusterWarning warn = GetClusterWarning(am, trekClient.MyMission()->GetMissionParams().bInvulnerableStations); if (warn >= c_cwStationThreatened) { if (m_bFlashFrame) pcontext->DrawImage3D(m_pimageSectorWarning->GetSurface(), Color::White(), true, xy); } else if (warn >= c_cwCapitalInCluster) { if (m_bFlashFrame) pcontext->DrawImage3D(m_pimageCapitalWarning->GetSurface(), Color::White(), true, xy); } else if (warn >= c_cwBomberInCluster) { if (m_bFlashFrame) pcontext->DrawImage3D(m_pimageBomberWarning->GetSurface(), Color::White(), true, xy); } else if (warn >= c_cwMinerThreatened) { if (m_bFlashFrame) pcontext->DrawImage3D(m_pimageSectorMiner->GetSurface(), Color::White(), true, xy); } else if (warn >= c_cwCombatInCluster) { if (m_bFlashFrame) pcontext->DrawImage3D(m_pimageSectorCombat->GetSurface(), Color::White(), true, xy); } /* else if (warn >= c_cwEnemyMinerInCluster) { pcontext->DrawImage3D(m_pimageSectorMiner->GetSurface(), Color::White(), true, xy); } else if (warn > c_cwNoThreat) { pcontext->DrawImage3D(m_pimageSectorEnemy->GetSurface(), Color::White(), true, xy); } */ // highlight it if it is ours if (trekClient.GetChatCluster() == pCluster) pcontext->DrawImage3D(m_pimageSectorHighlight->GetSurface(), Color::White(), true, xy); else if (m_pClusterSel == pCluster) pcontext->DrawImage3D(m_pimageSectorSel->GetSurface(), Color::White(), true, xy); // highlight it if it contains our current or queued command target bool bHasCurrentCommand = HasCommandTarget(pCluster, c_cmdAccepted); bool bHasQueuedCommand = HasCommandTarget(pCluster, c_cmdQueued); if (bHasCurrentCommand) pcontext->DrawImage3D(m_pimageSectorTargetHighlight->GetSurface(), Color::White(), true, xy); else if (bHasQueuedCommand) pcontext->DrawImage3D(m_pimageSectorQueued->GetSurface(), Color::White(), true, xy); // overlay the ripcord info if we are ripcording if (pmodelRipcord) { if (pmodelRipcord->GetObjectType() == OT_ship) { if (pCluster == pclusterRipcord) { if (m_bFlashFrame) pcontext->DrawImage3D(m_pimageSectorPickTarget->GetSurface(), Color::White(), true, xy); else pcontext->DrawImage3D(m_pimageSectorPickable->GetSurface(), Color::White(), true, xy); } else { int cfr = ClusterHasFriendlyRipcord(pCluster, pside, clustersRipcord); if (cfr != c_iClusterHasNoRipcord) { pcontext->DrawImage3D(cfr == c_iClusterHasStationRipcord ? m_pimageSectorPickableStation->GetSurface() : m_pimageSectorPickable->GetSurface(), Color::White(), true, xy); } } } else { if (pCluster == pclusterRipcord) { if (m_bFlashFrame) pcontext->DrawImage3D(m_pimageSectorPickStationTarget->GetSurface(), Color::White(), true, xy); else pcontext->DrawImage3D(m_pimageSectorPickableStation->GetSurface(), Color::White(), true, xy); } else { int cfr = ClusterHasFriendlyRipcord(pCluster, pside, clustersRipcord); if (cfr != c_iClusterHasNoRipcord) { pcontext->DrawImage3D(cfr == c_iClusterHasStationRipcord ? m_pimageSectorPickableStation->GetSurface() : m_pimageSectorPickable->GetSurface(), Color::White(), true, xy); } } } } else if (GetWindow()->GetOverlayFlags() & ofTeleportPane) { IstationIGC* pstation = trekClient.GetShip()->GetStation(); if (pstation && (pstation->GetCluster() != pCluster) && (FindFriendlyStation(pCluster, pside) != NULL)) { pcontext->DrawImage3D(m_pimageSectorPickable->GetSurface(), Color::White(), true, xy); } } else if (HasBuildableAsteroid(pCluster, GetWindow()->GetCommandAsteroid(), GetWindow()->GetInvestAsteroid())) { pcontext->DrawImage3D(m_pimageSectorPickable->GetSurface(), Color::White(), true, xy); } if (sideOwner != NA) { pcontext->DrawImage3D( m_pimageOwnerHighlight->GetSurface(), trekClient.GetCore()->GetSide(sideOwner)->GetColor(), true, xy ); } if (sideSecondaryOwner != NA) { pcontext->DrawImage3D( m_pimageSecondaryOwnerHighlight->GetSurface(), trekClient.GetCore()->GetSide(sideSecondaryOwner)->GetColor(), true, xy ); } // draw the dots for the ships { int vnSidePopCount[c_cSidesMax]; int nSidesPresent = 0; // count the population and sides in the sector { for (SideLinkIGC* psl = trekClient.m_pCoreIGC->GetSides()->first(); (psl != NULL); psl = psl->next()) { SideID sideID = psl->data()->GetObjectID(); vnSidePopCount[sideID] = GetClusterPopulation(pCluster, psl->data()); if (vnSidePopCount[sideID] != 0) ++nSidesPresent; } } // draw the rows of dots if (nSidesPresent > 0) { const int nDotHeight = 2; const int nDotWidth = 2; const int nShipWidth = 1; const int nDotPitch = 1; const int nRowPitch = 1; const int nMaxPop = 7; int nY = xy.Y() - (nSidesPresent * (nDotHeight + nDotPitch) - nRowPitch)/2; for (SideLinkIGC* psl = trekClient.m_pCoreIGC->GetSides()->first(); (psl != NULL); psl = psl->next()) { int nPopulation = min(nMaxPop, vnSidePopCount[psl->data()->GetObjectID()]); int nX = xy.X() + 6; if (nPopulation > 0) { while (nPopulation > 0) { int nDotPopulation = min(nPopulation, nDotWidth/nShipWidth); pcontext->FillRect( WinRect( nX, nY, nX + nDotPopulation * nShipWidth, nY + nDotHeight ), psl->data()->GetColor() ); nX += nDotWidth + nDotPitch; nPopulation -= nDotPopulation; } nY += nDotHeight + nRowPitch; } } } } /* // draw the bars for the ships { int nX = xy.X() + 6; int nMaxPlayers = 32; const int nMaxBarHeight = 8; const int nBarWidth = (trekClient.m_pCoreIGC->GetSides()->n() > 2) ? 1 : 2; for (SideLinkIGC* psl = trekClient.m_pCoreIGC->GetSides()->first(); (psl != NULL); psl = psl->next()) { IsideIGC* pside = psl->data(); SideID sideID = pside->GetObjectID(); int nPopulation = GetClusterPopulation(pCluster, pside); if (nPopulation > 0) { // draw bars for ships int nBarHeight = min(min(nPopulation, nMaxBarHeight), max(1, (int)((nMaxBarHeight - 1) * log((float)nPopulation)/log((float)nMaxPlayers))+ 1)); pcontext->FillRect( WinRect( nX, xy.Y() - nMaxBarHeight/2, nX + nBarWidth, xy.Y() - nMaxBarHeight/2 + nBarHeight ), pside->GetColor() ); nX += nBarWidth + 1; } } } */ } } } private: WinPoint GetClusterPoint(IclusterIGC* pcluster) { WinPoint pt = WinPoint( (int)(m_rectMap.XMin() + ((pcluster->GetScreenX() - m_xMin)/(m_xMax - m_xMin))*(m_rectMap.XSize() - 2*c_nXBorder) + c_nXBorder), (int)(m_rectMap.YMin() + ((pcluster->GetScreenY() - m_yMin)/(m_yMax - m_yMin))*(m_rectMap.YSize() - 2*c_nYBorder) + c_nYBorder) ); return pt; } IclusterIGC* GetClusterAtPoint(const Point& point) { const ClusterListIGC* clusters = trekClient.m_pCoreIGC->GetClusters(); IclusterIGC* pClusterFound = NULL; int nMinDistance = 15; for (ClusterLinkIGC* cLink = clusters->first(); cLink != NULL; cLink = cLink->next()) { IclusterIGC* pCluster = cLink->data(); // if we know about this cluster... if (pCluster->GetModels()->n() != 0) { WinPoint xy = GetClusterPoint(pCluster); Point pointSector((float)xy.X(), (float)xy.Y()); Point offset = point - pointSector; int nDistance = sqrt(offset.LengthSquared()); if (nDistance < nMinDistance) { pClusterFound = pCluster; nMinDistance = nDistance; } } } return pClusterFound; } void AttemptTeleportTo(IclusterIGC* pcluster) { // try teleporting to that station IstationIGC* pstation = FindFriendlyStation(pcluster, trekClient.GetSide()); if (NULL == trekClient.GetShip()->GetStation()) { // race condition? } else if (trekClient.GetShip()->GetStation()->GetCluster() == pcluster) { trekClient.PostText(false, "You are already in this cluster."); trekClient.PlaySoundEffect(errorSound); } else if (pstation == NULL) { trekClient.PostText(false, "You do not have an appropriate station in this cluster."); trekClient.PlaySoundEffect(errorSound); } else if (trekClient.GetShip()->GetParentShip() != NULL) { trekClient.DisembarkAndTeleport(pstation); GetWindow()->TurnOffOverlayFlags(ofTeleportPane); } else { trekClient.SetMessageType(BaseClient::c_mtGuaranteed); BEGIN_PFM_CREATE(trekClient.m_fm, pfmDocked, C, DOCKED) END_PFM_CREATE pfmDocked->stationID = pstation->GetObjectID(); trekClient.StartLockDown( "Teleporting to " + ZString(pstation->GetName()) + "....", BaseClient::lockdownTeleporting); GetWindow()->TurnOffOverlayFlags(ofTeleportPane); } } void ChildChanged(Value* pvalue, Value* pvalueNew) { ZAssert(NULL == pvalueNew); ZAssert(GetChild(0) == pvalue); pvalue->Update(); if (trekClient.GetShip()->GetCluster() || trekClient.GetShip()->GetStation()) SetVisible((((int)Number::Cast(pvalue)->GetValue()) & m_nMaskModeActive) != 0); } const char* GetClusterCursor() { if (trekClient.GetShip()->fRipcordActive() || (GetWindow()->GetOverlayFlags() & ofTeleportPane)) return AWF_CURSOR_DEFAULT; else if (GetWindow()->GetConsoleImage()->GetConsoleData()->IsComposingCommand() || trekClient.GetShip()->GetCluster() != NULL) return "goto"; else return AWF_CURSOR_DEFAULT; } void MouseMove(IInputProvider* pprovider, const Point& point, bool bCaptured, bool bInside) { if (bCaptured) { ZAssert(m_bDragging && m_bCanDrag); float fScale = (m_xMax - m_xMin)/m_rectMap.XSize(); float fDeltaX = fScale * (m_pointLastDrag.X() - point.X()); float fDeltaY = fScale * (m_pointLastDrag.Y() - point.Y()); // make sure we don't drag the map off of the screen m_xDrag = max(min((m_xClusterMax - m_xClusterMin) - (m_xMax - m_xMin), m_xDrag + fDeltaX), 0); m_yDrag = max(min((m_yClusterMax - m_yClusterMin) - (m_yMax - m_yMin), m_yDrag + fDeltaY), 0); m_pointLastDrag = point; GetWindow()->SetCursor(AWF_CURSOR_DRAG); Changed(); } else { IclusterIGC* pClusterFound = GetClusterAtPoint(point); m_pSectorInfoPane->SelectMouseOverCluster(pClusterFound); if (pClusterFound) { m_bHovering = true; if (m_pClusterSel != pClusterFound) trekClient.PlaySoundEffect(mouseoverSound); SelectCluster(pClusterFound); GetWindow()->SetCursor(GetClusterCursor()); /* AssetMask am = pClusterFound->GetClusterSite()->GetClusterAssetMask(); ClusterWarning cw = GetClusterWarning(am, trekClient.MyMission()->GetMissionParams().bInvulnerableStations); trekClient.PostText(false, GetClusterWarningText(cw)); */ } else { m_bHovering = false; SelectCluster(trekClient.GetChatCluster()); if (m_bCanDrag) { GetWindow()->SetCursor(AWF_CURSOR_DRAG); } else { GetWindow()->SetCursor(AWF_CURSOR_DEFAULT); } } } Changed(); } virtual void MouseLeave(IInputProvider* pprovider) { m_bHovering = false; SelectCluster(trekClient.GetChatCluster()); m_pSectorInfoPane->SelectMouseOverCluster(NULL); if (!m_bDragging) GetWindow()->SetCursor(AWF_CURSOR_DEFAULT); Changed(); } MouseResult Button(IInputProvider* pprovider, const Point& point, int button, bool bCaptured, bool bInside, bool bDown) { IclusterIGC* pClusterFound = GetClusterAtPoint(point); if (bDown) { if (0 == button) { if (pClusterFound) { trekClient.PlaySoundEffect(mouseclickSound); SelectCluster(pClusterFound); // if we are ripcording, set the target ripcord cluster if (trekClient.GetShip()->fRipcordActive() && !trekClient.GetShip()->GetAutopilot()) { trekClient.RequestRipcord(trekClient.GetShip(), pClusterFound); } // if we are teleporting, try to teleport to a station in // the selected station. else if (GetWindow()->GetOverlayFlags() & ofTeleportPane) { AttemptTeleportTo(pClusterFound); } else { // if we are in a station, change the view cluster if (trekClient.GetShip()->GetCluster() == NULL) { ZAssert(trekClient.GetShip()->GetStation() != NULL); trekClient.RequestViewCluster(pClusterFound); GetWindow()->SetViewMode(TrekWindow::vmCommand); } else if (pClusterFound == trekClient.GetShip()->GetCluster()) { // do nothing - users find anything else confusing. } else if (true)//!TrekWindow::CommandCamera(GetWindow()->GetCameraMode())) { // give the player a command to goto that sector DataBuoyIGC db; db.position = Vector(0, 0, 0); db.clusterID = pClusterFound->GetObjectID(); db.type = c_buoyCluster; IbuoyIGC* b = (IbuoyIGC*)(trekClient.m_pCoreIGC->CreateObject(trekClient.m_now, OT_buoy, &db, sizeof(db))); assert (b); b->AddConsumer(); trekClient.SendChat(trekClient.GetShip(), CHAT_INDIVIDUAL, trekClient.GetShipID(), NA, NULL, c_cidGoto, OT_buoy, b->GetObjectID(), b); b->ReleaseConsumer(); b->Release(); } } } else if (m_bCanDrag) { // start a drag m_bDragging = true; m_pointLastDrag = point; GetWindow()->SetCursor(AWF_CURSOR_DRAG); Changed(); return MouseResultCapture(); } } else if (1 == button && pClusterFound) { // let the console handle this trekClient.PlaySoundEffect(mouseclickSound); TrekWindow* pWindow = GetWindow (); pWindow->GetConsoleImage()->GetConsoleData()->PickCluster(pClusterFound, button); pWindow->GetInput ()->SetFocus (false); } } else { if (m_bDragging && 0 == button) { m_bDragging = false; if (pClusterFound) { GetWindow()->SetCursor(GetClusterCursor()); } else if (!bInside) { GetWindow()->SetCursor(AWF_CURSOR_DEFAULT); } else { GetWindow()->SetCursor(AWF_CURSOR_DRAG); } Changed(); return MouseResultRelease(); } } Changed(); return MouseResult(); } void OnClusterChanged(IclusterIGC* pcluster) { if (!m_bHovering) { SelectCluster(pcluster); } } }; TRef SectorMapPaneFactory::Apply(ObjectStack& stack) { TRef pinfoPane; CastTo(pinfoPane, (Pane*)(IObject*)stack.Pop()); TRef pnumberMode; CastTo(pnumberMode, (IObject*)stack.Pop()); TRef pnumberMaskModeActive; CastTo(pnumberMaskModeActive, (IObject*)stack.Pop()); TRef psectormap = new SectorMapPane(pinfoPane, pnumberMode, (int)pnumberMaskModeActive->GetValue()); return (Value*)psectormap; }