loadout.cpp 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160
  1. #include "pch.h"
  2. #include "trekctrls.h"
  3. #include "loadout.h"
  4. #include "limits.h"
  5. #include "trekmdl.h"
  6. //////////////////////////////////////////////////////////////////////////////
  7. //
  8. // PartListItem
  9. //
  10. //////////////////////////////////////////////////////////////////////////////
  11. #define shipIDLoadout SHRT_MAX //review: NA doesn't work here
  12. class PartListItem : public ListItem
  13. {
  14. private:
  15. long m_lData;
  16. TRef<IbucketIGC> m_pBucket;
  17. TRef<IbuyableIGC> m_pBuyable;
  18. Money m_moneyCost;
  19. const char* m_szModel;
  20. TRef<Image> m_pImagePart;
  21. TRef<Image> m_pImageBkgnd;
  22. TRef<Image> m_pImageSelect;
  23. TRef<Image> m_pImageBrackets;
  24. public:
  25. IbuyableIGC* GetBuyable()
  26. { return m_pBuyable;}
  27. PartListItem(IbucketIGC* pBucket)
  28. {
  29. m_lData = (long)pBucket;
  30. m_pBucket = pBucket;
  31. m_pBuyable = pBucket;
  32. m_moneyCost = m_pBuyable->GetPrice();
  33. Construct();
  34. }
  35. void Construct()
  36. {
  37. assert(m_pBuyable);
  38. m_szModel = m_pBuyable->GetModelName();
  39. m_pImageBrackets = GetModeler()->LoadImage("conshadebracketsbmp", true);
  40. m_pImageBkgnd = GetModeler()->LoadImage(AWF_LOADOUT_ITEM, true);
  41. m_pImageSelect = GetModeler()->LoadImage("investhighlightbmp", true);
  42. m_pImagePart = GetModeler()->LoadImage(
  43. (ZString("i") + ZString(m_szModel) + "bmp").ToLower(),
  44. false
  45. );
  46. }
  47. static int CountDronesOfType(IdroneTypeIGC* pDroneType)
  48. {
  49. DroneTypeID dtid = pDroneType->GetObjectID();
  50. SideID sid = trekClient.GetSideID();
  51. int nCount = 0;
  52. for (PlayerLink* ppl = trekClient.GetPlayerList()->first();
  53. (ppl != NULL);
  54. ppl = ppl->next())
  55. {
  56. //We only know about drones in our mission so only check the side
  57. const PlayerInfo& pi = ppl->data();
  58. if ((pi.GetDroneTypeID() == dtid) && (pi.SideID() == sid))
  59. {
  60. nCount++;
  61. }
  62. }
  63. return nCount;
  64. }
  65. static int CountStationsOfType(IstationTypeIGC* pStationtypeTest)
  66. {
  67. IsideIGC* pSide = trekClient.GetSide();
  68. const StationListIGC* stationList = pSide->GetStations();
  69. StationClassID classID = pStationtypeTest->GetClassID();
  70. int nCount = 0;
  71. for (StationLinkIGC* stationLink = (stationList ? stationList->first() : NULL);
  72. stationLink; stationLink=stationLink->next())
  73. {
  74. IstationTypeIGC* pstationtype = stationLink->data()->GetBaseStationType();
  75. // if this station is in the same class of station
  76. if (pstationtype->GetClassID() == classID)
  77. nCount++;
  78. }
  79. return nCount;
  80. }
  81. static bool BuildSpotFoundForStationType(IstationTypeIGC* pStationtype)
  82. {
  83. AsteroidAbilityBitMask aabm = pStationtype->GetBuildAABM();
  84. for (AsteroidLinkIGC* asteriodLink = trekClient.GetCore()->GetAsteroids()->first();
  85. asteriodLink != NULL; asteriodLink = asteriodLink->next())
  86. {
  87. if (asteriodLink->data()->HasCapability(aabm))
  88. return true;
  89. }
  90. return false;
  91. }
  92. static IshipIGC* FindBuilderForStationType(IstationTypeIGC* pStationtype)
  93. {
  94. if (trekClient.GetSide())
  95. {
  96. for (ShipLinkIGC* shipLink = trekClient.GetSide()->GetShips()->first();
  97. shipLink != NULL; shipLink = shipLink->next())
  98. {
  99. if (shipLink->data()->GetBaseData() == pStationtype)
  100. return shipLink->data();
  101. }
  102. }
  103. return NULL;
  104. }
  105. ZString GetBuildingStatusText()
  106. {
  107. IstationTypeIGC* pStationtype;
  108. CastTo(pStationtype, m_pBucket->GetBuyable());
  109. IshipIGC* pshipBuilder = FindBuilderForStationType(pStationtype);
  110. if (m_pBucket->GetPercentBought() >= 100)
  111. {
  112. return "Preparing builder ...";
  113. }
  114. if (pshipBuilder)
  115. {
  116. if (pshipBuilder->GetCommandTarget(c_cmdAccepted) &&
  117. (pshipBuilder->GetCommandID(c_cmdAccepted) == c_cidBuild))
  118. {
  119. return "Constructor en route ...";
  120. }
  121. else
  122. {
  123. return "Constructor waiting ...";
  124. }
  125. }
  126. else if (BuildSpotFoundForStationType(pStationtype))
  127. {
  128. return "";
  129. }
  130. else
  131. {
  132. return ZString("no ")
  133. + IasteroidIGC::GetTypeName(pStationtype->GetBuildAABM())
  134. + " asteroid available";
  135. }
  136. }
  137. void DrawText(Surface* pSurface, const WinRect& rect)
  138. {
  139. Color colorShadow(0, 0, 33.0f / 256.0f);
  140. char szPrice[20];
  141. sprintf(szPrice, "$ %d", m_moneyCost);
  142. pSurface->DrawStringWithShadow(
  143. TrekResources::SmallFont(),
  144. Color::White(),
  145. colorShadow,
  146. rect.Min() + WinPoint(147,1),
  147. szPrice
  148. );
  149. ZString strName = m_pBuyable->GetName();
  150. if (m_pBucket)
  151. {
  152. ObjectType otBucket = m_pBucket->GetBucketType();
  153. if (otBucket != OT_development)
  154. {
  155. int nTotalNumber;
  156. switch (otBucket)
  157. {
  158. case OT_stationType:
  159. {
  160. pSurface->DrawStringWithShadow(
  161. TrekResources::SmallFont(),
  162. Color::White(),
  163. colorShadow,
  164. rect.Min() + WinPoint(11,42),
  165. GetBuildingStatusText()
  166. );
  167. IstationTypeIGC* pStationtype;
  168. CastTo(pStationtype, m_pBucket->GetBuyable());
  169. nTotalNumber = CountStationsOfType(pStationtype);
  170. strName = "Build " + strName + " (" + ZString(nTotalNumber) + ")";
  171. }
  172. break;
  173. case OT_droneType:
  174. {
  175. IdroneTypeIGC* pDronetype;
  176. int nMaxMiners = trekClient.MyMission()->GetMissionDef().misparms.nMaxMinersPerTeam;
  177. CastTo(pDronetype, m_pBucket->GetBuyable());
  178. nTotalNumber = CountDronesOfType(pDronetype);
  179. if (nTotalNumber >= nMaxMiners){
  180. pSurface->DrawStringWithShadow(
  181. TrekResources::SmallFont(),
  182. Color::White(),
  183. colorShadow,
  184. rect.Min() + WinPoint(11,42),
  185. "At maximum " + strName + "s"
  186. );
  187. }
  188. strName = "Build " + strName + " (" + ZString(nTotalNumber)
  189. + "/" + ZString(nMaxMiners) + ")";
  190. }
  191. break;
  192. case OT_partType:
  193. case OT_hullType:
  194. {
  195. nTotalNumber = m_pBucket->GetSide()->GetStockpile(m_pBucket->GetBuyable());
  196. strName = "Build " + strName + " (" + ZString(nTotalNumber) + ")";
  197. }
  198. }
  199. }
  200. }
  201. pSurface->DrawStringWithShadow(
  202. TrekResources::SmallFont(),
  203. Color::White(),
  204. colorShadow,
  205. rect.Min() + WinPoint(11,1),
  206. strName
  207. );
  208. }
  209. void FillClippedRect(Context* pcontext, const WinRect& rect, const WinRect& rectClip, const Color& color)
  210. {
  211. WinRect rectClipped = rect;
  212. rectClipped.Intersect(rectClip);
  213. if (!rectClipped.IsEmpty()) {
  214. pcontext->FillRect(Rect::Cast(rect), color);
  215. }
  216. }
  217. void DrawShade(Surface* pSurface, const WinRect& rect)
  218. {
  219. int nWidth = (int)(rect.XSize() - 23);
  220. int nPaidShadeWidth = (int)(nWidth * m_pBucket->GetPercentBought()/100);
  221. int nCompletedShadeWidth = (int)(nWidth * m_pBucket->GetPercentComplete()/100);
  222. if (nPaidShadeWidth > nWidth)
  223. nPaidShadeWidth = nWidth;
  224. if (nCompletedShadeWidth > nWidth)
  225. nCompletedShadeWidth = nWidth;
  226. pSurface->BitBlt(rect.Min(), m_pImageBrackets->GetSurface());
  227. Point offset = Point::Cast(pSurface->GetOffset() + rect.Min() + WinPoint(8, 2));
  228. const WinPoint& size = pSurface->GetSize();
  229. WinRect rectClip = pSurface->GetClipRect();
  230. rectClip.Offset(pSurface->GetOffset());
  231. rectClip =
  232. WinRect(
  233. rectClip.XMin(),
  234. size.Y() - rectClip.YMax(),
  235. rectClip.XMax(),
  236. size.Y() - rectClip.YMin()
  237. );
  238. TRef<Context> pcontext = pSurface->GetContext();
  239. if (pcontext) {
  240. pcontext->SetBlendMode(BlendModeSourceAlpha);
  241. float bright = 0.5f;
  242. FillClippedRect(
  243. pcontext,
  244. WinRect(
  245. offset.X(),
  246. size.Y() - (offset.Y() + rect.YSize()-13),
  247. offset.X() + nCompletedShadeWidth,
  248. size.Y() - offset.Y()
  249. ),
  250. rectClip,
  251. Color(0.0, bright, 0.0, bright)
  252. );
  253. Point offsetPaid = offset + Point(nCompletedShadeWidth, 0);
  254. FillClippedRect(
  255. pcontext,
  256. WinRect(
  257. offsetPaid.X(),
  258. size.Y() - (offsetPaid.Y() + rect.YSize()-13),
  259. offsetPaid.X() + nPaidShadeWidth - nCompletedShadeWidth,
  260. size.Y() - offsetPaid.Y()
  261. ),
  262. rectClip,
  263. Color(bright, bright, 0.0, bright)
  264. );
  265. pSurface->ReleaseContext(pcontext);
  266. }
  267. }
  268. short GetItemHeight()
  269. {
  270. return 1;
  271. }
  272. long GetItemData()
  273. {
  274. return m_lData;
  275. };
  276. bool Update()
  277. {
  278. return true;
  279. }
  280. void SetSortOrder(long lSortOrder)
  281. {
  282. }
  283. bool SetFilter(long lFilter)
  284. {
  285. return true;
  286. }
  287. void DrawItem(Surface* pSurface, const WinRect& rect, bool fSelected, int iFirstSlot)
  288. { if (m_pImagePart) {
  289. pSurface->BitBlt(rect.Min() + WinPoint(10, 2), m_pImagePart->GetSurface());
  290. } else {
  291. pSurface->BitBlt(rect.Min(), m_pImageBkgnd->GetSurface());
  292. }
  293. if (fSelected)
  294. {
  295. pSurface->BitBlt(rect.Min() + WinPoint(10, 2), m_pImageSelect->GetSurface());
  296. /*Point offset = Point::Cast(pSurface->GetOffset() + rect.Min() + WinPoint(8, 2));
  297. const WinPoint& size = pSurface->GetSize();
  298. TRef<Context> pcontext = pSurface->GetContext();
  299. pcontext->SetBlendMode(BlendModeSourceAlpha);
  300. pcontext->SetColor(Color(1.0, 0.0, 0.0, 0.5));
  301. pcontext->FillRect(
  302. Rect(
  303. offset.X(),
  304. size.Y() - (offset.Y() + rect.YSize()-13),
  305. offset.X() + rect.XSize() - 23,
  306. size.Y() - offset.Y()
  307. )
  308. );
  309. pSurface->ReleaseContext(pcontext);
  310. */
  311. }
  312. if (m_pBucket)
  313. DrawShade(pSurface, rect);
  314. DrawText(pSurface, rect);
  315. }
  316. };
  317. //////////////////////////////////////////////////////////////////////////////
  318. //
  319. // PurchasesPane
  320. //
  321. //////////////////////////////////////////////////////////////////////////////
  322. class PurchasesPaneImpl : public PurchasesPane,
  323. public EventTargetContainer<PurchasesPaneImpl>,
  324. public TrekClientEventSink
  325. {
  326. private:
  327. //TRef<TabPane> m_pTabPaneTeamPurchases;
  328. TRef<ListPaneOld> m_pListPane;
  329. int m_nTabSel;
  330. TRef<PartInfoPane> m_pPartInfoPane;
  331. int m_nLastHoverIndex;
  332. TRef<IstationIGC> m_pStation;
  333. TRef<ButtonPane> m_pButtonInvest;
  334. TRef<ButtonPane> m_pButtonTab0;
  335. TRef<ButtonPane> m_pButtonTab1;
  336. TRef<ButtonPane> m_pButtonTab2;
  337. TRef<ButtonPane> m_pButtonTab3;
  338. TRef<ButtonPane> m_pButtonTab4;
  339. TRef<ButtonPane> m_pButtonTab5;
  340. TRef<ButtonPane> m_pButtonClose;
  341. Window* m_pPaneWindow;
  342. TRef<Pane> m_pBlankPane;
  343. TRef<Pane> m_pBlankPane0;
  344. TRef<Pane> m_pBlankPane1;
  345. TRef<Pane> m_pBlankPane2;
  346. TRef<Pane> m_pBlankPane3;
  347. TRef<Pane> m_pBlankPane4;
  348. TRef<Pane> m_pBlankPane5;
  349. public:
  350. PurchasesPaneImpl(PartInfoPane* pPartInfoPane) :
  351. m_nTabSel(-1),
  352. m_nLastHoverIndex(-1),
  353. m_pPartInfoPane(pPartInfoPane),
  354. m_pStation(NULL),
  355. m_pPaneWindow(NULL)
  356. {
  357. TRef<ImagePane> pImagePane = new ImagePane(GetModeler()->LoadImage(
  358. AWF_LOADOUT_FLIGHT_PURCHASE_PANEL, false));
  359. InsertAtBottom(pImagePane);
  360. // main loadout column pane
  361. TRef<ColumnPane> pColumnPane = new ColumnPane();
  362. TRef<RowPane> pRowPane = new RowPane();
  363. const int nTabWidth = 180;
  364. m_pBlankPane5 = new Pane();
  365. InternalSetSize(m_pBlankPane5, WinPoint(nTabWidth, 3));
  366. pColumnPane->InsertAtBottom(m_pBlankPane5);
  367. m_pButtonTab0 =
  368. CreateTrekButton(
  369. CreateButtonFacePane(
  370. GetModeler()->LoadSurface("btndronesbmp", true),
  371. ButtonNormal
  372. ),
  373. false, mouseclickSound
  374. );
  375. pColumnPane->InsertAtBottom(m_pButtonTab0);
  376. //m_pBlankPane0 = new Pane();
  377. //InternalSetSize(m_pBlankPane0, WinPoint(nTabWidth, 1));
  378. //pColumnPane->InsertAtBottom(m_pBlankPane0);
  379. m_pButtonTab1 =
  380. CreateTrekButton(
  381. CreateButtonFacePane(
  382. GetModeler()->LoadSurface("btnstarbasebmp", true),
  383. ButtonNormal
  384. ),
  385. false, mouseclickSound
  386. );
  387. pColumnPane->InsertAtBottom(m_pButtonTab1);
  388. m_pBlankPane1 = new Pane();
  389. InternalSetSize(m_pBlankPane1, WinPoint(nTabWidth, 1));
  390. pColumnPane->InsertAtBottom(m_pBlankPane1);
  391. m_pButtonTab2 =
  392. CreateTrekButton(
  393. CreateButtonFacePane(
  394. GetModeler()->LoadSurface("btnordnancebmp", true),
  395. ButtonNormal
  396. ),
  397. false, mouseclickSound
  398. );
  399. pColumnPane->InsertAtBottom(m_pButtonTab2);
  400. m_pBlankPane2 = new Pane();
  401. InternalSetSize(m_pBlankPane2, WinPoint(nTabWidth, 1));
  402. pColumnPane->InsertAtBottom(m_pBlankPane2);
  403. m_pButtonTab3 =
  404. CreateTrekButton(
  405. CreateButtonFacePane(
  406. GetModeler()->LoadSurface("btnelectronicsbmp", true),
  407. ButtonNormal
  408. ),
  409. false, mouseclickSound
  410. );
  411. pColumnPane->InsertAtBottom(m_pButtonTab3);
  412. m_pBlankPane3 = new Pane();
  413. InternalSetSize(m_pBlankPane3, WinPoint(nTabWidth, 1));
  414. pColumnPane->InsertAtBottom(m_pBlankPane3);
  415. m_pButtonTab4 =
  416. CreateTrekButton(
  417. CreateButtonFacePane(
  418. GetModeler()->LoadSurface("btnresearchbmp", true),
  419. ButtonNormal
  420. ),
  421. false, mouseclickSound
  422. );
  423. pColumnPane->InsertAtBottom(m_pButtonTab4);
  424. m_pBlankPane4 = new Pane();
  425. InternalSetSize(m_pBlankPane4, WinPoint(nTabWidth, 1));
  426. pColumnPane->InsertAtBottom(m_pBlankPane4);
  427. m_pButtonTab5 =
  428. CreateTrekButton(
  429. CreateButtonFacePane(
  430. GetModeler()->LoadSurface("btnshipyardbmp", true),
  431. ButtonNormal
  432. ),
  433. false, mouseclickSound
  434. );
  435. pColumnPane->InsertAtBottom(m_pButtonTab5);
  436. pRowPane->InsertAtBottom(pColumnPane);
  437. m_pBlankPane = new Pane();
  438. InternalSetSize(m_pBlankPane, WinPoint(30, 10));
  439. pRowPane->InsertAtBottom(m_pBlankPane);
  440. // close button
  441. m_pButtonClose =
  442. CreateTrekButton(
  443. CreateButtonFacePane(
  444. GetModeler()->LoadSurface("btnclosepanebmp", true),
  445. ButtonNormal
  446. ),
  447. false
  448. );
  449. // parts list
  450. m_pListPane = ListPaneOld::Create(WinPoint(203, 432), 72, true, m_pButtonClose),
  451. pRowPane->InsertAtBottom(m_pListPane);
  452. // invest button
  453. m_pButtonInvest =
  454. CreateTrekButton(
  455. CreateButtonFacePane(
  456. GetModeler()->LoadSurface(AWF_LOADOUT_INVEST_BUTTON, true),
  457. ButtonNormal
  458. ),
  459. false
  460. );
  461. pImagePane->InsertAtBottom(m_pButtonInvest);
  462. m_pButtonInvest->SetOffset(WinPoint(63,315));
  463. pImagePane->InsertAtBottom(pRowPane);
  464. pRowPane->SetOffset(WinPoint(17,9));
  465. //initialize tab selection to Starbase Tab
  466. m_pButtonTab0->SetChecked(true);
  467. OnTabSelect0();
  468. AddEventTarget(OnTabSelect0, m_pButtonTab0->GetEventSource());
  469. AddEventTarget(OnTabSelect1, m_pButtonTab1->GetEventSource());
  470. AddEventTarget(OnTabSelect2, m_pButtonTab2->GetEventSource());
  471. AddEventTarget(OnTabSelect3, m_pButtonTab3->GetEventSource());
  472. AddEventTarget(OnTabSelect4, m_pButtonTab4->GetEventSource());
  473. AddEventTarget(OnTabSelect5, m_pButtonTab5->GetEventSource());
  474. //AddEventTarget(OnTabSelect, m_pTabPaneTeamPurchases->GetEventSource());
  475. AddEventTarget(OnListSelect, m_pListPane->GetEventSource());
  476. AddEventTarget(OnListHover, m_pListPane->GetMouseOverEvent());
  477. AddEventTarget(OnInvest, m_pButtonInvest->GetEventSource());
  478. AddEventTarget(OnButtonBack, m_pButtonClose->GetEventSource());
  479. AddEventTarget(OnMouseEnterInvestButton, m_pButtonInvest->GetMouseEnterEventSource());
  480. AddEventTarget(OnMouseLeaveInvestButton, m_pButtonInvest->GetMouseLeaveEventSource());
  481. //m_pTabPaneTeamPurchases->ShowSelPane();
  482. }
  483. ~PurchasesPaneImpl()
  484. {
  485. GetWindow()->SetInvestAsteroid(0);
  486. }
  487. bool OnInvest()
  488. {
  489. if (trekClient.MyMissionInProgress())
  490. {
  491. TRef<ListItem> pItem = m_pListPane->GetSelItem();
  492. if (pItem)
  493. {
  494. IbucketIGC* pBucket = (IbucketIGC*)m_pListPane->GetSelItem()->GetItemData();
  495. if (CanInvest(pBucket))
  496. {
  497. Money bucketCost = pBucket->GetPrice() - pBucket->GetMoney();
  498. Money investmentAmount = min(bucketCost, trekClient.GetMoney());
  499. trekClient.AddMoneyToBucket(pBucket, investmentAmount);
  500. }
  501. }
  502. }
  503. return true;
  504. }
  505. bool OnButtonBack()
  506. {
  507. GetWindow()->TurnOffOverlayFlags(ofInvestment);
  508. return true;
  509. }
  510. void OnBucketChange(BucketChange bc, IbucketIGC* b)
  511. {
  512. if (bc == c_bcTerminated)
  513. m_pListPane->RemoveItemByData((long)b);
  514. else
  515. m_pListPane->UpdateItemByData((long)b);
  516. UpdateSpendButtons();
  517. }
  518. void OnModelTerminated(ImodelIGC* pmodel)
  519. {
  520. // make sure we correct the number of existing stations on the investment list
  521. if ((pmodel->GetObjectType() == OT_station) && (pmodel->GetSide() != NULL)
  522. && (pmodel->GetSide()->GetObjectID() == trekClient.GetSideID()))
  523. {
  524. m_pListPane->UpdateAll();
  525. UpdateSpendButtons();
  526. }
  527. }
  528. void OnStationCaptured(StationID stationID, SideID sideID)
  529. {
  530. // this may change our station count
  531. m_pListPane->UpdateAll();
  532. UpdateSpendButtons();
  533. }
  534. void OnDiscoveredStation(IstationIGC* pstation)
  535. {
  536. // this may change our station count
  537. m_pListPane->UpdateAll();
  538. UpdateSpendButtons();
  539. }
  540. void OnDiscoveredAsteroid(IasteroidIGC* pasteroid)
  541. {
  542. // this may change our station status text
  543. m_pListPane->UpdateAll();
  544. UpdateSpendButtons();
  545. }
  546. void UpdateTechList()
  547. {
  548. // Update the selected tech list
  549. // HACK: This is currently O(n^2), but it seems to work well enough.
  550. int nInsertionIndex = 0;
  551. BuyableGroupID group = (BuyableGroupID)m_nTabSel;
  552. IsideIGC* pSide = trekClient.GetSide();
  553. if (pSide)
  554. {
  555. const BucketListIGC* pList = pSide->GetBuckets();
  556. for (BucketLinkIGC* pBucketNode = pList->first();
  557. pBucketNode != NULL; pBucketNode = pBucketNode->next())
  558. {
  559. IbucketIGC* pBucket = pBucketNode->data();
  560. if (pBucket->GetGroupID() == group)
  561. {
  562. if (pSide->CanBuy(pBucket))
  563. {
  564. IbucketIGC* pbucketPredecessor = pBucket->GetPredecessor();
  565. if ((pbucketPredecessor == NULL) || pbucketPredecessor->GetCompleteF())
  566. {
  567. int nExistingIndex = m_pListPane->GetItemIdxByData((long)pBucket);
  568. if (nExistingIndex == NA)
  569. m_pListPane->InsertItem(nInsertionIndex++, new PartListItem(pBucket));
  570. else
  571. nInsertionIndex = nExistingIndex + 1;
  572. }
  573. }
  574. else
  575. {
  576. m_pListPane->RemoveItemByData((long)pBucket);
  577. }
  578. }
  579. }
  580. }
  581. m_pButtonTab0->SetEnabled(BuyableTechExistsInGroup(0));
  582. m_pButtonTab1->SetEnabled(BuyableTechExistsInGroup(1));
  583. m_pButtonTab2->SetEnabled(BuyableTechExistsInGroup(2));
  584. m_pButtonTab3->SetEnabled(BuyableTechExistsInGroup(3));
  585. m_pButtonTab4->SetEnabled(BuyableTechExistsInGroup(4));
  586. m_pButtonTab5->SetEnabled(BuyableTechExistsInGroup(5));
  587. UpdateSpendButtons();
  588. }
  589. bool BuyableTechExistsInGroup(BuyableGroupID group)
  590. {
  591. IsideIGC* pSide = trekClient.GetSide();
  592. if (pSide)
  593. {
  594. const BucketListIGC* pList = pSide->GetBuckets();
  595. for (BucketLinkIGC* pBucketNode = pList->first();
  596. pBucketNode != NULL; pBucketNode = pBucketNode->next())
  597. {
  598. IbucketIGC* pBucket = pBucketNode->data();
  599. if (pBucket->GetGroupID() == group)
  600. {
  601. if (pSide->CanBuy(pBucket))
  602. {
  603. IbucketIGC* pbucketPredecessor = pBucket->GetPredecessor();
  604. if ((pbucketPredecessor == NULL) || pbucketPredecessor->GetCompleteF())
  605. {
  606. return true;
  607. }
  608. }
  609. }
  610. }
  611. }
  612. return false;
  613. }
  614. void OnTechTreeChanged(SideID sid)
  615. {
  616. if (trekClient.GetSideID() == sid)
  617. {
  618. UpdateTechList();
  619. }
  620. }
  621. bool CanInvest(IbucketIGC* pBucket)
  622. {
  623. // if the game has not started yet, don't let them invest
  624. if (!trekClient.MyMissionInProgress())
  625. return false;
  626. if (!trekClient.GetSide())
  627. return false;
  628. // if this is a drone, make sure that we have less than
  629. // 4 of that type of drone.
  630. if (pBucket->GetBucketType() == OT_droneType)
  631. {
  632. IdroneTypeIGC* pDronetype;
  633. CastTo(pDronetype, pBucket->GetBuyable());
  634. if (PartListItem::CountDronesOfType(pDronetype)
  635. >= trekClient.MyMission()->GetMissionDef().misparms.nMaxMinersPerTeam)
  636. {
  637. return false;
  638. }
  639. }
  640. // if this is a station, make sure we have the
  641. // appropriate rock and don't have a builder.
  642. if (pBucket->GetBucketType() == OT_stationType)
  643. {
  644. IstationTypeIGC* pStationtype;
  645. CastTo(pStationtype, pBucket->GetBuyable());
  646. if (!PartListItem::BuildSpotFoundForStationType(pStationtype)
  647. || PartListItem::FindBuilderForStationType(pStationtype) != NULL)
  648. return false;
  649. }
  650. // they need to have money to invest
  651. if (trekClient.GetMoney() <= 0)
  652. return false;
  653. // there needs to be something in which to invest
  654. if (pBucket->GetPercentBought() >= 100)
  655. return false;
  656. return true;
  657. }
  658. void UpdateSpendButtons()
  659. {
  660. TRef<ListItem> pItem = m_pListPane->GetSelItem();
  661. if (pItem)
  662. {
  663. IbucketIGC* pBucket = (IbucketIGC*)(pItem->GetItemData());
  664. // if the mission has started, the player has money to invest, and the investment
  665. // is not at 100%...
  666. m_pButtonInvest->SetEnabled(CanInvest(pBucket));
  667. }
  668. else
  669. {
  670. // no item to buy or invest in
  671. m_pButtonInvest->SetEnabled(false);
  672. }
  673. }
  674. void OnMoneyChange(PlayerInfo* pPlayerInfo)
  675. {
  676. UpdateSpendButtons();
  677. }
  678. bool OnListHover(int nSel)
  679. {
  680. if (nSel == -1)
  681. {
  682. m_pPartInfoPane->DisplayNothing();
  683. }
  684. else
  685. {
  686. IbucketIGC* pBucket = (IbucketIGC*)m_pListPane->GetItemByIdx(nSel)->GetItemData();
  687. if (m_nLastHoverIndex != nSel)
  688. trekClient.PlaySoundEffect(mouseoverSound);
  689. m_pPartInfoPane->DisplayTeamPurchase(pBucket, false);
  690. }
  691. m_nLastHoverIndex = nSel;
  692. return true;
  693. }
  694. bool OnMouseEnterInvestButton()
  695. {
  696. OnListHover(m_pListPane->GetSelItemIdx());
  697. return true;
  698. }
  699. bool OnMouseLeaveInvestButton()
  700. {
  701. OnListHover(-1);
  702. return true;
  703. }
  704. bool OnListSelect(int nSel)
  705. {
  706. UpdateSpendButtons();
  707. return true;
  708. }
  709. void OnAddPlayer(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo)
  710. {
  711. // update the drone counts
  712. m_pListPane->UpdateAll();
  713. UpdateSpendButtons();
  714. }
  715. void OnDelPlayer(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo, QuitSideReason reason, const char* szMessageParam)
  716. {
  717. // update the drone counts
  718. m_pListPane->UpdateAll();
  719. UpdateSpendButtons();
  720. }
  721. bool OnTabSelect0()
  722. {
  723. m_pListPane->RemoveAll();
  724. m_nTabSel = 0;
  725. m_pButtonTab0->SetChecked(true);
  726. m_pButtonTab1->SetChecked(false);
  727. m_pButtonTab2->SetChecked(false);
  728. m_pButtonTab3->SetChecked(false);
  729. m_pButtonTab4->SetChecked(false);
  730. m_pButtonTab5->SetChecked(false);
  731. UpdateTechList();
  732. m_pListPane->SetSelItemByIdx(0);
  733. return true;
  734. }
  735. bool OnTabSelect1()
  736. {
  737. m_pListPane->RemoveAll();
  738. m_nTabSel = 1;
  739. m_pButtonTab0->SetChecked(false);
  740. m_pButtonTab1->SetChecked(true);
  741. m_pButtonTab2->SetChecked(false);
  742. m_pButtonTab3->SetChecked(false);
  743. m_pButtonTab4->SetChecked(false);
  744. m_pButtonTab5->SetChecked(false);
  745. UpdateTechList();
  746. m_pListPane->SetSelItemByIdx(0);
  747. return true;
  748. }
  749. bool OnTabSelect2()
  750. {
  751. m_pListPane->RemoveAll();
  752. m_nTabSel = 2;
  753. m_pButtonTab0->SetChecked(false);
  754. m_pButtonTab1->SetChecked(false);
  755. m_pButtonTab2->SetChecked(true);
  756. m_pButtonTab3->SetChecked(false);
  757. m_pButtonTab4->SetChecked(false);
  758. m_pButtonTab5->SetChecked(false);
  759. UpdateTechList();
  760. m_pListPane->SetSelItemByIdx(0);
  761. return true;
  762. }
  763. bool OnTabSelect3()
  764. {
  765. m_pListPane->RemoveAll();
  766. m_nTabSel = 3;
  767. m_pButtonTab0->SetChecked(false);
  768. m_pButtonTab1->SetChecked(false);
  769. m_pButtonTab2->SetChecked(false);
  770. m_pButtonTab3->SetChecked(true);
  771. m_pButtonTab4->SetChecked(false);
  772. m_pButtonTab5->SetChecked(false);
  773. UpdateTechList();
  774. m_pListPane->SetSelItemByIdx(0);
  775. return true;
  776. }
  777. bool OnTabSelect4()
  778. {
  779. m_pListPane->RemoveAll();
  780. m_nTabSel = 4;
  781. m_pButtonTab0->SetChecked(false);
  782. m_pButtonTab1->SetChecked(false);
  783. m_pButtonTab2->SetChecked(false);
  784. m_pButtonTab3->SetChecked(false);
  785. m_pButtonTab4->SetChecked(true);
  786. m_pButtonTab5->SetChecked(false);
  787. UpdateTechList();
  788. m_pListPane->SetSelItemByIdx(0);
  789. return true;
  790. }
  791. bool OnTabSelect5()
  792. {
  793. m_pListPane->RemoveAll();
  794. m_nTabSel = 5;
  795. m_pButtonTab0->SetChecked(false);
  796. m_pButtonTab1->SetChecked(false);
  797. m_pButtonTab2->SetChecked(false);
  798. m_pButtonTab3->SetChecked(false);
  799. m_pButtonTab4->SetChecked(false);
  800. m_pButtonTab5->SetChecked(true);
  801. UpdateTechList();
  802. m_pListPane->SetSelItemByIdx(0);
  803. return true;
  804. }
  805. void UpdateLayout()
  806. {
  807. DefaultUpdateLayout();
  808. }
  809. void SetPaneWindow(Window* pWindow)
  810. {
  811. m_pPaneWindow = pWindow;
  812. }
  813. };
  814. TRef<PurchasesPane> PurchasesPane::Create(PartInfoPane* pPartInfoPane)
  815. {
  816. return new PurchasesPaneImpl(pPartInfoPane);
  817. }
  818. TRef<IObject> PurchasesPaneFactory::Apply(ObjectStack& stack)
  819. {
  820. TRef<PartInfoPane> pinfoPane; CastTo(pinfoPane, (Pane*)(IObject*)stack.Pop());
  821. return (Pane*)PurchasesPane::Create(pinfoPane);
  822. }
  823. //////////////////////////////////////////////////////////////////////////////
  824. //
  825. // PartInfoPane
  826. //
  827. //////////////////////////////////////////////////////////////////////////////
  828. static WinPoint s_rgPtLabels[] =
  829. {
  830. WinPoint(9,25), WinPoint(9,42), WinPoint(9,59),
  831. WinPoint(9,76), WinPoint(9,92),
  832. WinPoint(120,25), WinPoint(120,42), WinPoint(120,59),
  833. WinPoint(120,76), WinPoint(120,92),
  834. };
  835. static WinPoint s_rgPtStats[] =
  836. {
  837. WinPoint(67,25), WinPoint(67,42), WinPoint(67,59),
  838. WinPoint(67,76), WinPoint(67,92),
  839. WinPoint(178,25), WinPoint(178,42), WinPoint(178,59),
  840. WinPoint(178,76), WinPoint(178,92),
  841. };
  842. class PartInfoPaneImpl : public PartInfoPane
  843. {
  844. private:
  845. TRef<IbuyableIGC> m_pBuyable;
  846. TRef<IhullTypeIGC> m_pHullType;
  847. TRef<IpartTypeIGC> m_pPartType;
  848. Money m_moneyCost;
  849. const char* m_szModel;
  850. TRef<Image> m_pImagePart;
  851. TRef<Image> m_pImageBkgnd;
  852. TRef<ConnectorPane> m_pConnectorPaneInventory;
  853. TRef<ConnectorPane> m_pConnectorPanePurchases;
  854. public:
  855. PartInfoPaneImpl(ConnectorPane* pConnectorPanePurchases, ConnectorPane* pConnectorPaneInventory) :
  856. m_pConnectorPaneInventory(pConnectorPaneInventory),
  857. m_pConnectorPanePurchases(pConnectorPanePurchases)
  858. {
  859. m_pImageBkgnd = GetModeler()->LoadImage(AWF_LOADOUT_INFO_PANEL, false);
  860. DisplayNothing();
  861. }
  862. void DisplayTeamPurchase(IbucketIGC* pBucket, bool bPartInInventory)
  863. {
  864. if (m_pConnectorPaneInventory)
  865. m_pConnectorPaneInventory->SetVisible(bPartInInventory);
  866. if (m_pConnectorPanePurchases)
  867. m_pConnectorPanePurchases->SetVisible(!bPartInInventory);
  868. m_pPartType = NULL;
  869. m_pHullType = NULL;
  870. m_pBuyable = pBucket;
  871. InternalSetSize(m_pImageBkgnd->GetSurface()->GetSize());
  872. NeedPaint();
  873. GetWindow()->SetInvestAsteroid(0);
  874. IbuyableIGC* pbuyable = pBucket->GetBuyable();
  875. if (pbuyable->GetObjectType() == OT_stationType)
  876. {
  877. IstationTypeIGC* pst = (IstationTypeIGC*)pbuyable;
  878. AsteroidAbilityBitMask aabm = pst->GetBuildAABM();
  879. GetWindow()->SetInvestAsteroid(aabm);
  880. }
  881. }
  882. void DisplayNothing()
  883. {
  884. GetWindow()->SetInvestAsteroid(0);
  885. if (m_pConnectorPaneInventory)
  886. m_pConnectorPaneInventory->SetVisible(false);
  887. if (m_pConnectorPanePurchases)
  888. m_pConnectorPanePurchases->SetVisible(false);
  889. m_pImagePart = NULL;
  890. m_pPartType = NULL;
  891. m_pHullType = NULL;
  892. m_pBuyable = NULL;
  893. m_szModel = NULL;
  894. NeedPaint();
  895. }
  896. private:
  897. void UpdateLayout()
  898. {
  899. InternalSetSize(m_pImageBkgnd->GetSurface()->GetSize());
  900. Pane::UpdateLayout();
  901. }
  902. void DrawName(Surface* pSurface, IbuyableIGC* pBuyable) const
  903. {
  904. pSurface->DrawString(
  905. TrekResources::SmallBoldFont(),
  906. Color(64.0f / 255.0f, 70.0f / 255.0f, 1.0f),
  907. WinPoint(15,10),
  908. pBuyable->GetName()
  909. );
  910. }
  911. void DrawDescription(Surface* pSurface, IbuyableIGC* pBuyable) const
  912. {
  913. const int nBorderWidth = 13;
  914. const int nLineHeight = 12;
  915. const int nFirstLineY = 4*nLineHeight;
  916. const int nWidth = (int)m_pImageBkgnd->GetBounds().GetRect().XSize() - nBorderWidth*2;
  917. int nCurrentYPos = nFirstLineY + nBorderWidth;
  918. TRef<IEngineFont> pfont = TrekResources::SmallFont();
  919. Color color = MakeColorFromCOLORREF(RGB(180,180,180));
  920. pSurface->DrawString(pfont, color, WinPoint(nBorderWidth, 15 + nLineHeight), ZString("Cost: $") + ZString(pBuyable->GetPrice()));
  921. pSurface->DrawString(pfont, color, WinPoint(nBorderWidth, 15 + 2*nLineHeight), ZString("Time: ") + ZString((long)pBuyable->GetTimeToBuild()) + ZString(" Sec."));
  922. ZString strDescription(pBuyable->GetDescription() + ZString(" "));
  923. while (strDescription.GetLength() != 0)
  924. {
  925. // calculate the number of characters we can display on this line
  926. int nLineLength = TrekResources::SmallFont()->GetMaxTextLength(strDescription, nWidth, true);
  927. // try doing word breaking
  928. int nWordBreakLength = nLineLength;
  929. while (nWordBreakLength > 0 && strDescription[nWordBreakLength] != ' '
  930. && strDescription[nWordBreakLength-1] != ' ')
  931. nWordBreakLength--;
  932. if (nWordBreakLength != 0)
  933. nLineLength = nWordBreakLength;
  934. if (nLineLength <= 0)
  935. {
  936. // skip the unknown character
  937. nLineLength = 1;
  938. }
  939. else
  940. {
  941. pSurface->DrawString(pfont, color, WinPoint(nBorderWidth,nCurrentYPos),
  942. strDescription.Left(nLineLength));
  943. nCurrentYPos += nLineHeight;
  944. }
  945. strDescription = strDescription.RightOf(nLineLength);
  946. }
  947. }
  948. void Paint(Surface* pSurface)
  949. {
  950. pSurface->BitBlt(WinPoint(0, 0),
  951. m_pImageBkgnd->GetSurface(),
  952. WinRect(0, 0, XSize(), YSize()));
  953. if (m_pBuyable)
  954. {
  955. DrawName(pSurface, m_pBuyable);
  956. DrawDescription(pSurface, m_pBuyable);
  957. }
  958. }
  959. };
  960. TRef<PartInfoPane> PartInfoPane::Create(ConnectorPane* pConnectorPanePurchases, ConnectorPane* pConnectorPaneInventory)
  961. {
  962. return new PartInfoPaneImpl(pConnectorPanePurchases, pConnectorPaneInventory);
  963. }
  964. TRef<IObject> PartInfoPaneFactory::Apply(ObjectStack& stack)
  965. {
  966. return (Pane*)PartInfoPane::Create(NULL, NULL);
  967. }