void CCompositeImageSelector::WriteToStream (IWriteStream *pStream) const // WriteToStream // // Writes to a stream // // DWORD No of entries // For each // DWORD dwID // DWORD iVariant // DWORD Shipwreck UNID { int i; DWORD dwSave; dwSave = m_Sel.GetCount(); pStream->Write((char *)&dwSave, sizeof(DWORD)); // Save each entry for (i = 0; i < m_Sel.GetCount(); i++) { pStream->Write((char *)&m_Sel[i].dwID, sizeof(DWORD)); pStream->Write((char *)&m_Sel[i].iVariant, sizeof(DWORD)); CShipClass *pWreckClass = (CShipClass *)m_Sel[i].dwExtra; dwSave = (pWreckClass ? pWreckClass->GetUNID() : 0); pStream->Write((char *)&dwSave, sizeof(DWORD)); } }
CObjectImageArray &CCompositeImageSelector::GetShipwreckImage (DWORD dwID) const // GetShipwreckImage // // Returns the shipwreck image for the given selection { CShipClass *pWreckClass = GetShipwreckClass(dwID); if (pWreckClass == NULL) return EMPTY_IMAGE; return pWreckClass->GetWreckImage(); }
void ArrangeByCell (CSymbolTable &Table, int cxDesiredWidth, CPaintMap &Map) { int i; // Compute the size of each image cell int cxCell = 32; int cyCell = 32; // Compute the number of rows & columns int iInitCols = (cxDesiredWidth / cxCell) + ((cxDesiredWidth % cxCell) ? 1 : 0); int iInitRows = AlignUp(20 * Table.GetCount(), iInitCols) / iInitCols; // Create an array that keeps track of which cells we've used up CCellMap CellMap(iInitCols, iInitRows); // Figure out where to place all the ships for (i = 0; i < Table.GetCount(); i++) { CShipClass *pClass = (CShipClass *)Table.GetValue(i); // Figure out how many cells we need int cxSize = RectWidth(pClass->GetImage().GetImageRect()); int iCellsNeeded = AlignUp(cxSize, cxCell) / cxCell; int cxAreaSize = iCellsNeeded * cxCell; // Position the ship int x, y; if (CellMap.GetSpace(iCellsNeeded, &x, &y)) Map.Place(i, x * cxCell + (cxAreaSize - cxSize) / 2, y * cyCell + (cxAreaSize - cxSize) / 2, cxSize, cxSize); } }
ALERROR CAdventureDesc::GetStartingShipClasses (TSortMap<CString, CShipClass *> *retClasses, CString *retsError) // GetStartingShipClasses // // Returns a sorted list of ship classes for this adventure { int i; bool bShowDebugShips = g_pUniverse->InDebugMode(); // Make a list retClasses->DeleteAll(); for (i = 0; i < g_pUniverse->GetShipClassCount(); i++) { CShipClass *pClass = g_pUniverse->GetShipClass(i); if (pClass->IsShownAtNewGame() && IsValidStartingClass(pClass) && (!pClass->IsDebugOnly() || bShowDebugShips)) { CString sKey = strPatternSubst(CONSTLIT("%d %s !%x"), (pClass->IsDebugOnly() ? 2 : 1), pClass->GetName(), pClass->GetUNID()); retClasses->Insert(sKey, pClass); } } return NOERROR; }
void CPlayerGameStats::OnObjDestroyedByPlayer (const SDestroyCtx &Ctx, CSpaceObject *pPlayer) // OnDestroyedByPlayer // // Object destroyed by player { bool bIsEnemy = Ctx.pObj->IsEnemy(pPlayer); // Is this a ship? CShip *pShip; if (Ctx.pObj->GetCategory() == CSpaceObject::catShip && (pShip = Ctx.pObj->AsShip())) { CShipClass *pClass = pShip->GetClass(); SShipClassStats *pStats = GetShipStats(pClass->GetUNID()); if (bIsEnemy) { pStats->iEnemyDestroyed++; m_iScore += pClass->GetScore(); } else pStats->iFriendDestroyed++; } // Is this a station? else if (Ctx.pObj->GetCategory() == CSpaceObject::catStation) { if (Ctx.pObj->HasAttribute(CONSTLIT("populated"))) { SStationTypeStats *pStats = GetStationStats(Ctx.pObj->GetType()->GetUNID()); pStats->iDestroyed++; } } }
int CPlayerGameStats::GetBestEnemyShipsDestroyed (DWORD *retdwUNID) const // GetBestEnemyShipDestroyed // // Returns the number of enemy ships destroyed of the most powerful ship class { CMapIterator i; int iBestScore = 0; DWORD dwBestUNID = 0; SShipClassStats *pBest = NULL; m_ShipStats.Reset(i); while (m_ShipStats.HasMore(i)) { SShipClassStats *pStats; DWORD dwUNID = m_ShipStats.GetNext(i, &pStats); CShipClass *pClass = g_pUniverse->FindShipClass(dwUNID); if (pClass) { int iScore = pClass->GetScore(); if (iScore > iBestScore) { dwBestUNID = dwUNID; iBestScore = iScore; pBest = pStats; } } } if (pBest == NULL) return 0; if (retdwUNID) *retdwUNID = dwBestUNID; return pBest->iEnemyDestroyed; }
void CGSelectorArea::SetRegionsFromArmor (CSpaceObject *pSource) // SetRegionsFromArmor // // Generates regions showing armor and shields for the given ship. { int i; ASSERT(pSource); if (pSource == NULL) return; CShip *pShip = pSource->AsShip(); if (pShip == NULL) return; CShipClass *pClass = pShip->GetClass(); // Compute some metrics. // // We place the shield generator in the center and the armor segments in a // circle around it. const RECT &rcRect = GetRect(); int cxArea = RectWidth(rcRect); int cyArea = RectHeight(rcRect); const int iRadius = WIDE_COLUMN_SPACING; // Now add all the armor segments for (i = 0; i < pShip->GetArmorSectionCount(); i++) { SEntry *pEntry = m_Regions.Insert(); CInstalledArmor *pArmor = pShip->GetArmorSection(i); pEntry->iType = typeInstalledItem; pEntry->pItemCtx = new CItemCtx(pShip, pArmor); // Position the armor segment in a circle (add 90 degrees because the // ship image points up). const CShipArmorSegmentDesc &Section = pClass->GetHullSection(i); int iCenterAngle = 90 + Section.GetCenterAngle(); int xCenter; int yCenter; IntPolarToVector(iCenterAngle, iRadius, &xCenter, &yCenter); pEntry->rcRect.left = xCenter - (ITEM_ENTRY_WIDTH / 2); pEntry->rcRect.top = -yCenter - (ITEM_ENTRY_HEIGHT / 2); pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; } // Add the shield generator last SEntry *pEntry = m_Regions.Insert(); CInstalledDevice *pShields = pShip->GetNamedDevice(devShields); if (pShields) { pEntry->iType = typeInstalledItem; pEntry->pItemCtx = new CItemCtx(pShip, pShields); } else { pEntry->iType = typeEmptySlot; pEntry->iSlotType = devShields; } pEntry->rcRect.left = -ITEM_ENTRY_WIDTH / 2; pEntry->rcRect.top = -ITEM_ENTRY_HEIGHT / 2; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; }
void OutputByShipClass (SItemTableCtx &Ctx, const SItemTypeList &ItemList, bool bShowUsage) { int i, j; // Make a map of ship classes for each item TSortMap<DWORD, TArray<CShipClass *>> ItemToShipClass; for (i = 0; i < g_pUniverse->GetShipClassCount(); i++) { CShipClass *pClass = g_pUniverse->GetShipClass(i); // Skip non-generic ones if (!pClass->HasLiteralAttribute(CONSTLIT("genericClass"))) continue; // Add the list of types used by the ship TSortMap<DWORD, bool> TypesUsed; pClass->AddTypesUsed(&TypesUsed); // For each item type, add it to the map for (j = 0; j < TypesUsed.GetCount(); j++) { CDesignType *pType = g_pUniverse->FindDesignType(TypesUsed.GetKey(j)); if (pType && pType->GetType() == designItemType) { TArray<CShipClass *> *pList = ItemToShipClass.SetAt(pType->GetUNID()); pList->Insert(pClass); } } } // If we want to show usage, then we print each item along with the // ship classes using each item. if (bShowUsage) { for (i = 0; i < ItemList.GetCount(); i++) { CItemType *pType = ItemList[i]; printf("%s\n", (LPSTR)pType->GetNounPhrase()); TArray<CShipClass *> *pList = ItemToShipClass.SetAt(pType->GetUNID()); for (j = 0; j < pList->GetCount(); j++) printf("\t%s\n", (LPSTR)pList->GetAt(j)->GetName()); if (pList->GetCount() == 0) printf("\t(none)\n"); printf("\n"); } } // Otherwise we categorize by ship class else { // Now make a list of all ship classes that have our items SByShipClassTypeList ByShipClassTable; for (i = 0; i < ItemList.GetCount(); i++) { const CString &sKey = ItemList.GetKey(i); CItemType *pType = ItemList[i]; // Loop over all ship classes TArray<CShipClass *> *pList = ItemToShipClass.SetAt(pType->GetUNID()); for (j = 0; j < pList->GetCount(); j++) { CString sClassName = pList->GetAt(j)->GetName(); bool bNew; SShipClassEntry *pEntry = ByShipClassTable.SetAt(sClassName, &bNew); if (bNew) pEntry->sShipClassName = sClassName; pEntry->ItemTable.Insert(sKey, pType); } // If no ship class if (pList->GetCount() == 0) { bool bNew; SShipClassEntry *pEntry = ByShipClassTable.SetAt(CONSTLIT("(none)"), &bNew); if (bNew) pEntry->sShipClassName = CONSTLIT("(none)"); pEntry->ItemTable.Insert(sKey, pType); } } // Now loop over all attributes for (i = 0; i < ByShipClassTable.GetCount(); i++) { const SShipClassEntry &Entry = ByShipClassTable[i]; printf("%s\n\n", Entry.sShipClassName.GetASCIIZPointer()); OutputHeader(Ctx); OutputTable(Ctx, Entry.ItemTable); printf("\n"); } } }
void RunEncounterSim (CUniverse &Universe, CXMLElement *pCmdLine) { int iRuns = pCmdLine->GetAttributeIntegerBounded(COUNT_ATTRIB, 1, -1, DEFAULT_RUN_COUNT); int iTimeOut = DEFAULT_TIME_OUT; // Get the station criteria CString sCriteria = strPatternSubst(CONSTLIT("%s t"), pCmdLine->GetAttribute(DEFENDERS_ATTRIB)); CDesignTypeCriteria DefenderCriteria; if (CDesignTypeCriteria::ParseCriteria(sCriteria, &DefenderCriteria) != NOERROR) { printf("ERROR: Unable to parse defenders criteria.\n"); return; } bool bAll = pCmdLine->GetAttributeBool(CONSTLIT("all")); // Get the criteria from the command line. Always append 's' because we // want ship classes. sCriteria = strPatternSubst(CONSTLIT("%s s"), pCmdLine->GetAttribute(ATTACKERS_ATTRIB)); CDesignTypeCriteria AttackerCriteria; if (CDesignTypeCriteria::ParseCriteria(sCriteria, &AttackerCriteria) != NOERROR) { printf("ERROR: Unable to parse attackers criteria.\n"); return; } // Create a viewer, if desired CSimViewer Viewer; if (pCmdLine->GetAttributeBool(VIEWER_ATTRIB)) Viewer.Create(); // Print headers printf("Encounter\tLevel"); int iAttacker; for (iAttacker = 0; iAttacker < Universe.GetShipClassCount(); iAttacker++) { CShipClass *pAttackerClass = Universe.GetShipClass(iAttacker); if (!pAttackerClass->MatchesCriteria(AttackerCriteria)) continue; printf("\t%s", pAttackerClass->GetName().GetASCIIZPointer()); } printf("\n"); // Loop over all defenders int iDefender; for (iDefender = 0; iDefender < Universe.GetStationTypeCount(); iDefender++) { CStationType *pDefenderType = Universe.GetStationType(iDefender); if (!bAll && pDefenderType->GetLevel() == 0) continue; if (!pDefenderType->MatchesCriteria(DefenderCriteria)) continue; // Compute an enemy of the station CSovereign *pAttackerSovereign = GetAttackerSovereign(Universe, pDefenderType); if (pAttackerSovereign == NULL) { printf("ERROR: Unable to find enemy sovereign of encounter: %s [%x].\n", pDefenderType->GetNounPhrase().GetASCIIZPointer(), pDefenderType->GetUNID()); return; } printf("%s [%x]\t%d", pDefenderType->GetNounPhrase().GetASCIIZPointer(), pDefenderType->GetUNID(), pDefenderType->GetLevel()); // Loop over all attackers for (iAttacker = 0; iAttacker < Universe.GetShipClassCount(); iAttacker++) { CShipClass *pAttackerClass = Universe.GetShipClass(iAttacker); if (!pAttackerClass->MatchesCriteria(AttackerCriteria)) continue; // Do several runs so we can get some statistical data int iAttackerSurvived = 0; int iRun; for (iRun = 0; iRun < iRuns; iRun++) { EResults iResult = RunEncounter(Universe, Viewer, pDefenderType, pAttackerClass, pAttackerSovereign); if (iResult == resultError) return; // Did the attacker survive? if (iResult != resultAttackerDestroyed) iAttackerSurvived++; } // Output results for this attacker int iSurvivability = 100 * iAttackerSurvived / iRuns; printf("\t%d%%", iSurvivability); } printf("\n"); } Viewer.Destroy(); }
void GenerateShipTable (CUniverse &Universe, CXMLElement *pCmdLine, CIDTable &EntityTable) { int i, j; // Some options bool bAllClasses = (pCmdLine->GetAttributeBool(CONSTLIT("allClasses")) || pCmdLine->GetAttributeBool(CONSTLIT("all"))); // Get the criteria from the command line. Always append 's' because we // want ship classes. CString sCriteria = strPatternSubst(CONSTLIT("%s s"), pCmdLine->GetAttribute(CONSTLIT("criteria"))); CDesignTypeCriteria Criteria; if (CDesignTypeCriteria::ParseCriteria(sCriteria, &Criteria) != NOERROR) { printf("ERROR: Unable to parse criteria.\n"); return; } // Generate a list of columns to display TArray<CString> Cols; Cols.Insert(FIELD_LEVEL); Cols.Insert(FIELD_NAME); for (i = 0; i < pCmdLine->GetAttributeCount(); i++) { CString sAttrib = pCmdLine->GetAttributeName(i); if (strEquals(sAttrib, FIELD_BALANCE)) { Cols.Insert(CONSTLIT("balanceType")); Cols.Insert(CONSTLIT("combatStrength")); Cols.Insert(CONSTLIT("damage")); Cols.Insert(CONSTLIT("defenseStrength")); } else if (!IsMainCommandParam(sAttrib) && !strEquals(sAttrib, CONSTLIT("shiptable"))) { CString sValue = pCmdLine->GetAttribute(i); if (!strEquals(sValue, CONSTLIT("true"))) Cols.Insert(strPatternSubst(CONSTLIT("%s:%s"), sAttrib, sValue)); else Cols.Insert(sAttrib); } } // Output the header for (j = 0; j < Cols.GetCount(); j++) { if (j != 0) printf("\t"); printf(Cols[j].GetASCIIZPointer()); } printf("\n"); // Generate a table CSymbolTable Table(FALSE, TRUE); // Loop over all items that match and add them to // a sorted table. for (i = 0; i < Universe.GetShipClassCount(); i++) { CShipClass *pClass = Universe.GetShipClass(i); // Only include generic classes unless otherwise specified if (!bAllClasses && !pClass->HasLiteralAttribute(CONSTLIT("genericClass"))) continue; if (!pClass->MatchesCriteria(Criteria)) continue; // Figure out the sort order char szBuffer[1024]; wsprintf(szBuffer, "%04d%s%d", pClass->GetLevel(), pClass->GetNounPhrase(0).GetASCIIZPointer(), pClass->GetUNID()); Table.AddEntry(CString(szBuffer), (CObject *)pClass); } // Output table for (i = 0; i < Table.GetCount(); i++) { CShipClass *pClass = (CShipClass *)Table.GetValue(i); // Output each row for (j = 0; j < Cols.GetCount(); j++) { if (j != 0) printf("\t"); const CString &sField = Cols[j]; CString sValue; if (strEquals(sField, FIELD_ENTITY)) { CString *pValue; if (EntityTable.Lookup(pClass->GetUNID(), (CObject **)&pValue) == NOERROR) sValue = *pValue; else sValue = CONSTLIT("?"); } else sValue = pClass->GetDataField(sField); if (strEquals(sField, FIELD_MANEUVER) || strEquals(sField, FIELD_THRUST_TO_WEIGHT)) printf("%.1f", strToInt(sValue, 0, NULL) / 1000.0); else if (strEquals(sField, FIELD_SCORE_CALC)) printf("%d", pClass->CalcScore()); else printf(sValue.GetASCIIZPointer()); } printf("\n"); } printf("\n"); }
void CListSaveFilesTask::CreateFileEntry (CGameFile &GameFile, const CTimeDate &ModifiedTime, int yStart, IAnimatron **retpEntry, int *retcyHeight) // CreateFileEntry // // Creates a display entry for the save file { const CVisualPalette &VI = m_HI.GetVisuals(); const CG16bitFont &MediumFont = VI.GetFont(fontMedium); const CG16bitFont &SubTitleFont = VI.GetFont(fontSubTitle); int x = 0; int y = 0; int xText = x + ADVENTURE_ICON_WIDTH + ICON_SPACING_HORZ; int cxText = m_cxWidth - (ADVENTURE_ICON_WIDTH + 2 * ICON_SPACING_HORZ + SHIP_IMAGE_WIDTH); // Start with a sequencer CAniSequencer *pRoot = new CAniSequencer; pRoot->SetPropertyVector(PROP_POSITION, CVector(0, yStart)); // Add the character name and current star system CString sHeading = strPatternSubst(CONSTLIT("%s — %s"), GameFile.GetPlayerName(), GameFile.GetSystemName()); IAnimatron *pName = new CAniText; pName->SetPropertyVector(PROP_POSITION, CVector(xText, y)); pName->SetPropertyVector(PROP_SCALE, CVector(10000, 1000)); pName->SetPropertyColor(PROP_COLOR, VI.GetColor(colorTextDialogInput)); pName->SetPropertyFont(PROP_FONT, &SubTitleFont); pName->SetPropertyString(PROP_TEXT, sHeading); pRoot->AddTrack(pName, 0); y += SubTitleFont.GetHeight(); // Now add some additional information CShipClass *pClass = g_pUniverse->FindShipClass(GameFile.GetPlayerShip()); CString sShipClass = (pClass ? pClass->GetName() : NULL_STR); CString sGenome = strCapitalize(GetGenomeName(GameFile.GetPlayerGenome())); CString sState; if (GameFile.IsGameResurrect()) sState = strPatternSubst(CONSTLIT("Resurrect in the %s System"), GameFile.GetSystemName()); else sState = strPatternSubst(CONSTLIT("Continue in the %s System"), GameFile.GetSystemName()); CString sDesc; if (!sGenome.IsBlank() && !sShipClass.IsBlank()) sDesc = strPatternSubst(CONSTLIT("%s — %s — %s"), sGenome, sShipClass, sState); else sDesc = sState; IAnimatron *pDesc = new CAniText; pDesc->SetPropertyVector(PROP_POSITION, CVector(xText, y)); pDesc->SetPropertyVector(PROP_SCALE, CVector(cxText, 1000)); pDesc->SetPropertyColor(PROP_COLOR, VI.GetColor(colorTextDialogInput)); pDesc->SetPropertyFont(PROP_FONT, &MediumFont); pDesc->SetPropertyString(PROP_TEXT, sDesc); RECT rcLine; pDesc->GetSpacingRect(&rcLine); pRoot->AddTrack(pDesc, 0); y += RectHeight(rcLine); // Adventure info CExtension *pAdventure = NULL; bool bHasAdventureIcon = false; if (g_pUniverse->FindExtension(GameFile.GetAdventure(), 0, &pAdventure)) { // Adventure icon CG16bitImage *pIcon; pAdventure->CreateIcon(ADVENTURE_ICON_WIDTH, ADVENTURE_ICON_HEIGHT, &pIcon); if (pIcon) { int xOffset = (ADVENTURE_ICON_WIDTH - pIcon->GetWidth()) / 2; IAnimatron *pIconAni = new CAniRect; pIconAni->SetPropertyVector(PROP_POSITION, CVector(x + xOffset, 0)); pIconAni->SetPropertyVector(PROP_SCALE, CVector(pIcon->GetWidth(), pIcon->GetHeight())); pIconAni->SetFillMethod(new CAniImageFill(pIcon, true)); pRoot->AddTrack(pIconAni, 0); bHasAdventureIcon = true; } // Adventure name pName = new CAniText; pName->SetPropertyVector(PROP_POSITION, CVector(xText, y)); pName->SetPropertyVector(PROP_SCALE, CVector(10000, 1000)); pName->SetPropertyColor(PROP_COLOR, VI.GetColor(colorTextDialogLabel)); pName->SetPropertyFont(PROP_FONT, &MediumFont); pName->SetPropertyString(PROP_TEXT, pAdventure->GetName()); pRoot->AddTrack(pName, 0); y += MediumFont.GetHeight(); } // Create an image of the ship class if (pClass) { const CObjectImageArray &ObjImage = pClass->GetImage(); if (ObjImage.IsLoaded()) { RECT rcRect = ObjImage.GetImageRect(); CG16bitImage &Image = ObjImage.GetImage(NULL_STR); int cxImage = RectWidth(rcRect); int cyImage = RectHeight(rcRect); int cxNewWidth = Min(SHIP_IMAGE_WIDTH, cxImage); int cyNewHeight = cxNewWidth; CG16bitImage *pNewImage = new CG16bitImage; pNewImage->CreateFromImageTransformed(Image, rcRect.left, rcRect.top, cxImage, cyImage, (Metric)cxNewWidth / cxImage, (Metric)cyNewHeight / cyImage, 0.0); // Position int xImage = x + m_cxWidth - SHIP_IMAGE_WIDTH + (SHIP_IMAGE_WIDTH - cxNewWidth) / 2; int yImage = (SHIP_IMAGE_HEIGHT - cyNewHeight) / 2; // New image frame IAnimatron *pImageFrame = new CAniRect; pImageFrame->SetPropertyVector(PROP_POSITION, CVector(xImage, yImage)); pImageFrame->SetPropertyVector(PROP_SCALE, CVector(cxNewWidth, cyNewHeight)); pImageFrame->SetFillMethod(new CAniImageFill(pNewImage, true)); pRoot->AddTrack(pImageFrame, 0); } } // Extra information CString sEpitaph = GameFile.GetEpitaph(); int iScore = GameFile.GetScore(); CTimeDate LocalTime = ModifiedTime.ToLocalTime(); CString sModifiedTime = LocalTime.Format("%d %B %Y %I:%M %p"); CString sFilename = pathGetFilename(GameFile.GetFilespec()); CString sGameType; if (GameFile.IsRegistered()) sGameType = CONSTLIT("Registered"); else if (GameFile.IsDebug()) sGameType = CONSTLIT("Debug"); else sGameType = CONSTLIT("Unregistered"); CString sExtra; if (!sEpitaph.IsBlank()) sExtra = strPatternSubst(CONSTLIT("Score %d — %s\n%s — %s — %s"), iScore, sEpitaph, sGameType, sModifiedTime, sFilename); else if (iScore > 0) sExtra = strPatternSubst(CONSTLIT("Score %d\n%s — %s — %s"), iScore, sGameType, sModifiedTime, sFilename); else sExtra = strPatternSubst(CONSTLIT("%s — %s — %s"), sGameType, sModifiedTime, sFilename); pDesc = new CAniText; pDesc->SetPropertyVector(PROP_POSITION, CVector(xText, y)); pDesc->SetPropertyVector(PROP_SCALE, CVector(cxText, 1000)); pDesc->SetPropertyColor(PROP_COLOR, VI.GetColor(colorTextDialogLabel)); pDesc->SetPropertyFont(PROP_FONT, &MediumFont); pDesc->SetPropertyString(PROP_TEXT, sExtra); pDesc->GetSpacingRect(&rcLine); pRoot->AddTrack(pDesc, 0); y += RectHeight(rcLine); // Done *retpEntry = pRoot; if (retcyHeight) *retcyHeight = (bHasAdventureIcon ? Max(ADVENTURE_ICON_HEIGHT, y) : y); }
ALERROR CTranscendenceWnd::CreateRandomShip (CSystem *pSystem, CSovereign *pSovereign, CShip **retpShip) // CreateRandomShip // // Creates a random ship { ALERROR error; int i; // Figure out the class CShipClass *pShipClass; if (m_dwIntroShipClass == 0) { do pShipClass = m_Universe.GetShipClass(mathRandom(0, m_Universe.GetShipClassCount()-1)); while (pShipClass->GetScore() > 1000 || pShipClass->IsPlayerShip()); } else { int i; int iIndex = -1; for (i = 0; i < m_Universe.GetShipClassCount(); i++) if (m_Universe.GetShipClass(i)->GetUNID() == m_dwIntroShipClass) { iIndex = i; break; } if (iIndex == -1 || (iIndex + 1) == m_Universe.GetShipClassCount()) pShipClass = m_Universe.GetShipClass(0); else pShipClass = m_Universe.GetShipClass(iIndex + 1); m_dwIntroShipClass = 0; } // Normally we create a single ship, but sometimes we create lots int iCount; int iRoll = mathRandom(1, 100); // Adjust the roll for capital ships if (pShipClass->GetHullMass() >= 10000) iRoll -= 9; else if (pShipClass->GetHullMass() >= 1000) iRoll -= 6; if (iRoll == 100) iCount = mathRandom(30, 60); else if (iRoll >= 98) iCount = mathRandom(10, 20); else if (iRoll >= 95) iCount = mathRandom(5, 10); else if (iRoll >= 90) iCount = mathRandom(2, 5); else iCount = 1; // Create the ships for (i = 0; i < iCount; i++) { CShip *pShip; if ((error = pSystem->CreateShip(pShipClass->GetUNID(), NULL, pSovereign, PolarToVector(mathRandom(0, 359), mathRandom(250, 2500) * g_KlicksPerPixel), NullVector, mathRandom(0, 359), NULL, &pShip))) return error; // Override the controller CIntroShipController *pNewController = new CIntroShipController(this, pShip->GetController()); pShip->SetController(pNewController, false); pNewController->SetShip(pShip); pShip->SetData(CONSTLIT("IntroController"), CONSTLIT("True")); *retpShip = pShip; } return NOERROR; }
void GenerateShipImageChart (CUniverse &Universe, CXMLElement *pCmdLine) { int i; enum OrderTypes { orderSmallest = 1, orderLargest = 2, orderName = 3, }; // Options bool bTextBoxesOnly = pCmdLine->GetAttributeBool(CONSTLIT("textBoxesOnly")); // Figure out what order we want CString sOrder = pCmdLine->GetAttribute(CONSTLIT("sort")); int iOrder; if (strEquals(sOrder, CONSTLIT("smallest"))) iOrder = orderSmallest; else if (strEquals(sOrder, CONSTLIT("largest"))) iOrder = orderLargest; else iOrder = orderName; // Image size int cxDesiredWidth; if (pCmdLine->FindAttributeInteger(CONSTLIT("width"), &cxDesiredWidth)) cxDesiredWidth = Max(512, cxDesiredWidth); else cxDesiredWidth = 1280; // Spacing int cxSpacing = pCmdLine->GetAttributeInteger(CONSTLIT("xSpacing")); int cxExtraMargin = pCmdLine->GetAttributeInteger(CONSTLIT("xMargin")); // Rotation int iRotation = pCmdLine->GetAttributeInteger(CONSTLIT("rotation")); // Font for text CString sTypeface; int iSize; bool bBold; bool bItalic; if (!CG16bitFont::ParseFontDesc(pCmdLine->GetAttribute(CONSTLIT("font")), &sTypeface, &iSize, &bBold, &bItalic)) { sTypeface = CONSTLIT("Arial"); iSize = 10; bBold = false; bItalic = false; } CG16bitFont NameFont; NameFont.Create(sTypeface, -PointsToPixels(iSize), bBold, bItalic); WORD wNameColor = CG16bitImage::RGBValue(255, 255, 255); // Output file CString sFilespec = pCmdLine->GetAttribute(CONSTLIT("output")); if (!sFilespec.IsBlank()) sFilespec = pathAddExtensionIfNecessary(sFilespec, CONSTLIT(".bmp")); // Generate a table of ships CSymbolTable Table(FALSE, TRUE); for (i = 0; i < Universe.GetShipClassCount(); i++) { CShipClass *pClass = Universe.GetShipClass(i); // Skip player ship classes if (pClass->GetPlayerSettings()) continue; // Skip non-generic classes if (!pClass->HasAttribute(CONSTLIT("genericClass"))) continue; // Compute the sort key char szBuffer[1024]; switch (iOrder) { case orderLargest: wsprintf(szBuffer, "%04d%s%x", 2048 - RectWidth(pClass->GetImage().GetImageRect()), pClass->GetName().GetASCIIZPointer(), pClass); break; case orderSmallest: wsprintf(szBuffer, "%04d%s%x", RectWidth(pClass->GetImage().GetImageRect()), pClass->GetName().GetASCIIZPointer(), pClass); break; default: wsprintf(szBuffer, "%s%x", pClass->GetName().GetASCIIZPointer(), pClass); break; } // Add to list Table.AddEntry(CString(szBuffer), (CObject *)pClass); } // Allocate a map that tracks where to paint each ship CPaintMap Map(Table.GetCount()); // Arrange the ships SArrangeDesc Desc; Desc.cxDesiredWidth = Max(512, cxDesiredWidth - (2 * (cxSpacing + cxExtraMargin))); Desc.cxSpacing = cxSpacing; Desc.cxExtraMargin = cxExtraMargin; Desc.pHeader = &NameFont; ArrangeByRow(Table, Desc, Map); //ArrangeByCell(Table, cxDesiredWidth, Map); // Create a large image CG16bitImage Output; int cxWidth = Max(cxDesiredWidth, Map.GetWidth()); int cyHeight = Map.GetHeight(); Output.CreateBlank(cxWidth, cyHeight, false); printf("Creating %dx%d image.\n", cxWidth, cyHeight); // Paint the images for (i = 0; i < Table.GetCount(); i++) { CShipClass *pClass = (CShipClass *)Table.GetValue(i); int x = Map.GetX(i); int y = Map.GetY(i); if (x != -1) { if (!bTextBoxesOnly) pClass->GetImage().PaintImageUL(Output, x, y, 0, Angle2Direction(iRotation)); // Paint name int xText = Map.GetTextX(i); int yText = Map.GetTextY(i); if (xText != -1) { if (bTextBoxesOnly) Output.Fill(xText, yText, Map.GetTextWidth(i), Map.GetTextHeight(i), 0xffff); if (!bTextBoxesOnly) { Output.FillColumn(x + (Map.GetWidth(i) / 2), y + Map.GetHeight(i), yText - (y + Map.GetHeight(i)), wNameColor); NameFont.DrawText(Output, xText, yText, wNameColor, 255, pClass->GetNounPhrase(0)); } } } } // Write to file or clipboard OutputImage(Output, sFilespec); }
void ArrangeByRow (CSymbolTable &Table, SArrangeDesc &Desc, CPaintMap &Map) { int iNext = 0; int y = 0; int cyHeader = Desc.pHeader->GetHeight(); int cxInternalSpacing = 8; int cyInternalSpacing = 2 * Desc.pHeader->GetHeight(); int cyNameSpacing = Desc.pHeader->GetHeight() / 2; while (iNext < Table.GetCount()) { int i; int cxWidthLeft = Desc.cxDesiredWidth; int cyRowHeight = 0; int iStart = iNext; // First figure out how many ships will fit while (iNext < Table.GetCount()) { CShipClass *pClass = (CShipClass *)Table.GetValue(iNext); int cxSize = RectWidth(pClass->GetImage().GetImageRect()); if (cxSize > cxWidthLeft && iStart != iNext) break; int cxCell = Max(cxSize + cxInternalSpacing, Desc.cxSpacing); cxWidthLeft -= cxCell; if (cxSize > cyRowHeight) cyRowHeight = cxSize; iNext++; } // Compute the total width int cxRowWidth = Min(Desc.cxDesiredWidth - cxWidthLeft, Desc.cxDesiredWidth); int xOffset = (Desc.cxDesiredWidth - cxRowWidth) / 2; // See if any of the ships overlap the text from the previous ships // If so, we increase y a little bit int x = Desc.cxSpacing + Desc.cxExtraMargin; for (i = iStart; i < iNext; i++) { CShipClass *pClass = (CShipClass *)Table.GetValue(i); int cxSize = RectWidth(pClass->GetImage().GetImageRect()); int yOffset = (cyRowHeight - cxSize) / 2; int xPoint = x + xOffset + cxSize / 2; int yPoint = y + yOffset; for (int j = 0; j < iStart; j++) { int xText = Map.GetTextX(j); int yText = Map.GetTextY(j); int cxText = Map.GetTextWidth(j); int cyText = Map.GetTextHeight(j) + cyInternalSpacing; if (xPoint >= xText && xPoint < xText + cxText && yPoint < yText + cyText) y = yText + cyText + cyInternalSpacing; } int cxCell = Max(cxSize + cxInternalSpacing, Desc.cxSpacing); x += cxCell; } // Place the ships x = Desc.cxSpacing + Desc.cxExtraMargin; int yOverlapOffset = 0; for (i = iStart; i < iNext; i++) { CShipClass *pClass = (CShipClass *)Table.GetValue(i); int cxSize = RectWidth(pClass->GetImage().GetImageRect()); int cxCell = Max(cxSize + cxInternalSpacing, Desc.cxSpacing); // Center vertically int yOffset = (cyRowHeight - cxSize) / 2; // Place Map.Place(i, x + xOffset, y + yOffset, cxSize, cxSize); // Figure out the position of the text int cyName; int cxName = Desc.pHeader->MeasureText(pClass->GetNounPhrase(0), &cyName); if (cxName <= cxSize) { int yText = y + yOffset + cxSize + cyNameSpacing; Map.PlaceText(i, x + xOffset + (cxSize - cxName) / 2, yText, cxName, cyName); yOverlapOffset = yText + cyName + cyNameSpacing; } else { // See if the name fits above the previous name int yText; if (yOverlapOffset > 0 && i > iStart && (x + xOffset + cxSize - (Map.GetX(i - 1) + Map.GetWidth(i - 1) / 2)) > cxName + 8) { yText = y + yOffset + cxSize + cyNameSpacing; if (yText < Map.GetY(i - 1) + Map.GetHeight(i - 1)) yText = Map.GetY(i - 1) + Map.GetHeight(i - 1) + cyNameSpacing; if (yText > Map.GetTextY(i - 1) - (cyName + cyNameSpacing)) yText = yOverlapOffset; } // Otherwise, use the overlap, if it exits else if (yOverlapOffset == 0) yText = y + yOffset + cxSize + cyNameSpacing; else yText = yOverlapOffset; Map.PlaceText(i, x + xOffset + cxSize - cxName, yText, cxName, cyName); yOverlapOffset = yText + cyName + cyNameSpacing; } x += cxCell; } y += cyRowHeight; } // Add a margin to the right and bottom Map.AddMargin(Desc.cxSpacing + Desc.cxExtraMargin, cyInternalSpacing); }
void CPlayerGameStats::GenerateGameStats (CGameStats &Stats, CSpaceObject *pPlayerShip, bool bGameOver) const // GenerateGameStats // // Generates a stats for everything we track { int j; CShip *pShip = (pPlayerShip ? pPlayerShip->AsShip() : NULL); if (pShip == NULL) return; CPlayerShipController *pPlayer = (CPlayerShipController *)pShip->GetController(); if (pPlayer == NULL) return; CSovereign *pPlayerSovereign = g_pUniverse->FindSovereign(g_PlayerSovereignUNID); if (pPlayerSovereign == NULL) return; // Base stats Stats.Insert(CONSTLIT("Genome"), strCapitalize(GetGenomeName(pPlayer->GetPlayerGenome()))); Stats.Insert(CONSTLIT("Score"), strFormatInteger(CalcEndGameScore(), -1, FORMAT_THOUSAND_SEPARATOR | FORMAT_UNSIGNED)); Stats.Insert(CONSTLIT("Ship class"), pShip->GetNounPhrase(0)); CTimeSpan Time = GetPlayTime(); if (!Time.IsBlank()) Stats.Insert(CONSTLIT("Time played"), Time.Format(NULL_STR)); #ifdef REAL_TIME Time = GetGameTime(); if (!Time.IsBlank()) Stats.Insert(CONSTLIT("Time elapsed in game"), Time.Format(NULL_STR)); #endif // Some combat stats CString sDestroyed = GetStat(ENEMY_SHIPS_DESTROYED_STAT); if (!sDestroyed.IsBlank()) Stats.Insert(CONSTLIT("Enemy ships destroyed"), sDestroyed, CONSTLIT("combat")); sDestroyed = GetStat(FRIENDLY_SHIPS_DESTROYED_STAT); if (!sDestroyed.IsBlank()) Stats.Insert(CONSTLIT("Friendly ships destroyed"), sDestroyed, CONSTLIT("combat")); sDestroyed = GetStat(ENEMY_STATIONS_DESTROYED_STAT); if (!sDestroyed.IsBlank()) Stats.Insert(CONSTLIT("Enemy stations destroyed"), sDestroyed, CONSTLIT("combat")); sDestroyed = GetStat(FRIENDLY_STATIONS_DESTROYED_STAT); if (!sDestroyed.IsBlank()) Stats.Insert(CONSTLIT("Friendly stations destroyed"), sDestroyed, CONSTLIT("combat")); // Add stat for every station destroyed CStatCounterArray CounterArray; CMapIterator i; m_StationStats.Reset(i); while (m_StationStats.HasMore(i)) { SStationTypeStats *pStats; DWORD dwUNID = m_StationStats.GetNext(i, &pStats); CStationType *pType = g_pUniverse->FindStationType(dwUNID); if (pType == NULL) continue; CString sName = pType->GetNounPhrase(0); CString sSort = strPatternSubst(CONSTLIT("%03d%s"), 100 - pType->GetLevel(), sName); if (pType->GetSovereign()->IsEnemy(pPlayerSovereign)) CounterArray.Insert(sName, pStats->iDestroyed, CONSTLIT("Enemy stations destroyed"), sSort); else CounterArray.Insert(sName, pStats->iDestroyed, CONSTLIT("Friendly stations destroyed"), sSort); } CounterArray.GenerateGameStats(Stats); // Add stat for every ship class destroyed CounterArray.DeleteAll(); m_ShipStats.Reset(i); while (m_ShipStats.HasMore(i)) { SShipClassStats *pStats; DWORD dwUNID = m_ShipStats.GetNext(i, &pStats); CShipClass *pClass = g_pUniverse->FindShipClass(dwUNID); if (pClass == NULL) continue; CString sName = pClass->GetNounPhrase(0); CString sSort = strPatternSubst(CONSTLIT("%09d%s"), 100000000 - pClass->GetScore(), sName); if (pStats->iEnemyDestroyed > 0) CounterArray.Insert(sName, pStats->iEnemyDestroyed, CONSTLIT("Enemy ships destroyed"), sSort); if (pStats->iFriendDestroyed > 0) CounterArray.Insert(sName, pStats->iFriendDestroyed, CONSTLIT("Friendly ships destroyed"), sSort); } CounterArray.GenerateGameStats(Stats); // Add stat for every weapon fired m_ItemStats.Reset(i); while (m_ItemStats.HasMore(i)) { SItemTypeStats *pStats; DWORD dwUNID = m_ItemStats.GetNext(i, &pStats); CItemType *pItemType = g_pUniverse->FindItemType(dwUNID); if (pItemType == NULL) continue; CString sName = pItemType->GetNounPhrase(nounShort); CString sSort = strPatternSubst(CONSTLIT("%03d%s"), 100 - pItemType->GetLevel(), sName); // Installed items if (pStats->dwFirstInstalled != INVALID_TIME) Stats.Insert(sName, NULL_STR, CONSTLIT("Items installed"), sSort); if (pStats->iCountFired > 0) Stats.Insert(sName, strFormatInteger(pStats->iCountFired, -1, FORMAT_THOUSAND_SEPARATOR | FORMAT_UNSIGNED), CONSTLIT("Weapons fired"), sSort); } // Stats for player equipment (but only if the game is done) if (bGameOver) { TSortMap<CString, CItem> InstalledItems; // First we generate a sorted list of installed items // (We do this in case there are multiple of the same device/armor so that // we can coalesce them together into a single line). CItemListManipulator ItemList(pShip->GetItemList()); ItemList.ResetCursor(); while (ItemList.MoveCursorForward()) { const CItem &Item(ItemList.GetItemAtCursor()); if (Item.IsInstalled()) { CString sEnhancement = Item.GetEnhancedDesc(pShip); CString sItemName = Item.GetNounPhrase(nounActual | nounCountOnly | nounShort); CString sLine = (sEnhancement.IsBlank() ? sItemName : strPatternSubst(CONSTLIT("%s [%s]"), sItemName, sEnhancement)); bool bInserted; CItem *pEntry = InstalledItems.SetAt(sLine, &bInserted); if (bInserted) { *pEntry = Item; pEntry->SetCount(1); } else pEntry->SetCount(pEntry->GetCount() + 1); } } // Now add all the installed items to the stats for (j = 0; j < InstalledItems.GetCount(); j++) { // Redo the line now that we know the proper count CString sEnhancement = InstalledItems[j].GetEnhancedDesc(pShip); CString sItemName = InstalledItems[j].GetNounPhrase(nounActual | nounCountOnly); CString sLine = (sEnhancement.IsBlank() ? sItemName : strPatternSubst(CONSTLIT("%s [%s]"), sItemName, sEnhancement)); // Compute the sort order int iOrder; switch (InstalledItems[j].GetType()->GetCategory()) { case itemcatWeapon: iOrder = 0; break; case itemcatLauncher: iOrder = 1; break; case itemcatShields: iOrder = 2; break; case itemcatArmor: iOrder = 3; break; case itemcatReactor: iOrder = 4; break; case itemcatDrive: iOrder = 5; break; default: iOrder = 6; break; } CString sSort = strPatternSubst(CONSTLIT("%d%03d%s"), iOrder, 100 - InstalledItems[j].GetType()->GetLevel(), sLine); Stats.Insert(sLine, NULL_STR, CONSTLIT("Final equipment"), sSort); } // Add the remaining items ItemList.ResetCursor(); while (ItemList.MoveCursorForward()) { const CItem &Item(ItemList.GetItemAtCursor()); if (!Item.IsInstalled()) { CString sEnhancement = Item.GetEnhancedDesc(pShip); CString sItemName = Item.GetNounPhrase(nounActual | nounCountOnly); CString sLine = (sEnhancement.IsBlank() ? sItemName : strPatternSubst(CONSTLIT("%s [%s]"), sItemName, sEnhancement)); CString sSort = strPatternSubst(CONSTLIT("%03d%s"), 100 - Item.GetType()->GetLevel(), sLine); Stats.Insert(sLine, NULL_STR, CONSTLIT("Final items"), sSort); } } } }
void CGSelectorArea::SetRegionsFromMiscDevices (CSpaceObject *pSource) // SetRegionsFromMiscDevices // // Generates regions showing misc devices (including reactor, drive, // and cargo hold). { int i; ASSERT(pSource); if (pSource == NULL) return; CShip *pShip = pSource->AsShip(); if (pShip == NULL) return; CShipClass *pClass = pShip->GetClass(); // Keep track of layouts that have already been used. TArray<bool> SlotStatus; SlotStatus.InsertEmpty(MISC_DEVICES_LAYOUT_COUNT); for (i = 0; i < MISC_DEVICES_LAYOUT_COUNT; i++) SlotStatus[i] = true; // Reserve the slots for named device types SlotStatus[REACTOR_SLOT_INDEX] = false; SlotStatus[DRIVE_SLOT_INDEX] = false; SlotStatus[CARGO_SLOT_INDEX] = false; // Count the number of miscellaneous devices with 0 // slots (because we may need to bump them). int iSlottedDevices = 0; int iNonSlotDevices = 0; for (i = 0; i < pShip->GetDeviceCount(); i++) { CInstalledDevice *pDevice = pShip->GetDevice(i); if (pDevice->IsEmpty() || pDevice->GetCategory() != itemcatMiscDevice) continue; if (pDevice->GetClass()->GetSlotsRequired() > 0) iSlottedDevices++; else iNonSlotDevices++; } // We try to fit all other devices (and placeholders) before we add a // non-slotted device. int iNonSlotDeviceSlotsAvail = Max(0, MISC_DEVICES_LAYOUT_COUNT - 4 - iSlottedDevices); // Create a region for each device. int iIndex = -1; bool bHasReactor = false; bool bHasDrive = false; bool bHasCargo = false; int iNextUnamedSlot = FIRST_UNNAMED_SLOT_INDEX; for (i = 0; i < pShip->GetDeviceCount(); i++) { CInstalledDevice *pDevice = pShip->GetDevice(i); if (pDevice->IsEmpty()) continue; // Figure out the layout descriptor iIndex = -1; const SLayoutDesc *pLayout = NULL; switch (pDevice->GetCategory()) { case itemcatCargoHold: pLayout = &g_MiscDevicesLayout[CARGO_SLOT_INDEX]; bHasCargo = true; break; case itemcatDrive: pLayout = &g_MiscDevicesLayout[DRIVE_SLOT_INDEX]; bHasDrive = true; break; case itemcatMiscDevice: { // If this is a 0-slot device and we have no more room for // 0-slot devices, then we skip it. if (pDevice->GetClass()->GetSlotsRequired() == 0 && iNonSlotDeviceSlotsAvail <= 0) continue; // If the device already has a position index, then use that (assuming // it's free). iIndex = pDevice->GetSlotPosIndex(); if (iIndex < 0 || iIndex >= SlotStatus.GetCount() || !SlotStatus[iIndex]) iIndex = -1; // If we don't have an assigned slot, figure it out. if (iIndex == -1) { // Look for a new position if (!FindLayoutForPos(pDevice->GetPosOffset(pShip), SlotStatus, &iIndex)) continue; // Remember so we stay in this location. pDevice->SetSlotPosIndex(iIndex); } // Remember the layout and mark it as used. pLayout = &g_MiscDevicesLayout[iIndex]; SlotStatus[iIndex] = false; break; } case itemcatReactor: pLayout = &g_MiscDevicesLayout[REACTOR_SLOT_INDEX]; bHasReactor = true; break; } // Create the region (but only if we have a layout position // for it). if (pLayout) { SEntry *pEntry = m_Regions.Insert(); pEntry->iType = typeInstalledItem; pEntry->pItemCtx = new CItemCtx(pShip, pDevice); pEntry->iSlotPosIndex = iIndex; pEntry->rcRect.left = pLayout->xLeft; pEntry->rcRect.top = pLayout->yTop; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; } } // Add empty slots, if necessary if (!bHasReactor) { const SLayoutDesc *pLayout = &g_MiscDevicesLayout[REACTOR_SLOT_INDEX]; SEntry *pEntry = m_Regions.Insert(); pEntry->iType = typeEmptySlot; pEntry->iSlotType = devReactor; pEntry->rcRect.left = pLayout->xLeft; pEntry->rcRect.top = pLayout->yTop; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; } if (!bHasDrive) { const SLayoutDesc *pLayout = &g_MiscDevicesLayout[DRIVE_SLOT_INDEX]; SEntry *pEntry = m_Regions.Insert(); pEntry->iType = typeEmptySlot; pEntry->iSlotType = devDrive; pEntry->rcRect.left = pLayout->xLeft; pEntry->rcRect.top = pLayout->yTop; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; } if (!bHasCargo) { const SLayoutDesc *pLayout = &g_MiscDevicesLayout[CARGO_SLOT_INDEX]; SEntry *pEntry = m_Regions.Insert(); pEntry->iType = typeEmptySlot; pEntry->iSlotType = devCargo; pEntry->rcRect.left = pLayout->xLeft; pEntry->rcRect.top = pLayout->yTop; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; } // Figure out how many empty weapon slots we should create. We add one // empty slot for each weapon slot, but we subtract one if we don't have // a launcher and we always have at least 1 empty slot, in case the player // finds a slot-less weapon. #ifdef SINGLE_FREE_SLOT int iEmptySlots = 1; #else int iNonWeaponSlotsInUse; int iTotalSlotsInUse = pShip->CalcDeviceSlotsInUse(NULL, &iNonWeaponSlotsInUse); int iEmptySlots = Max(1, Min((pClass->GetMaxDevices() - iTotalSlotsInUse), (pClass->GetMaxNonWeapons() - iNonWeaponSlotsInUse)) - (bHasReactor ? 0 : 1) - (bHasDrive ? 0 : 1) - (bHasCargo ? 0 : 1)); #endif for (i = 0; i < iEmptySlots; i++) { if (FindLayoutForPos(CVector(), SlotStatus, &iIndex)) { const SLayoutDesc *pLayout = &g_MiscDevicesLayout[iIndex]; SEntry *pEntry = m_Regions.Insert(); pEntry->iType = typeEmptySlot; pEntry->iSlotType = devNone; pEntry->iSlotPosIndex = iIndex; pEntry->rcRect.left = pLayout->xLeft; pEntry->rcRect.top = pLayout->yTop; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; SlotStatus[iIndex] = false; } } }
void CGSelectorArea::SetRegionsFromWeapons (CSpaceObject *pSource) // SetRegionsFromWeapons // // Creates regions based on installed weapons. { int i; ASSERT(pSource); if (pSource == NULL) return; CShip *pShip = pSource->AsShip(); if (pShip == NULL) return; CShipClass *pClass = pShip->GetClass(); // Keep track of layouts that have already been used. TArray<bool> SlotStatus; SlotStatus.InsertEmpty(MISC_DEVICES_LAYOUT_COUNT); for (i = 0; i < MISC_DEVICES_LAYOUT_COUNT; i++) SlotStatus[i] = true; // If we don't have a launcher, we place the launcher slot first because we // want it to take precedence (position-wise). int iIndex; bool bHasLauncher = (pShip->GetNamedDevice(devMissileWeapon) != NULL); if (!bHasLauncher) { // See if we can figure out the proper position for the launcher based // on the class slots CVector vLauncherPos; SDeviceDesc DeviceDesc; if (pClass->FindDeviceSlotDesc(devMissileWeapon, &DeviceDesc)) vLauncherPos = pClass->GetPosOffset(DeviceDesc.iPosAngle, DeviceDesc.iPosRadius, DeviceDesc.iPosZ, DeviceDesc.b3DPosition); // Find a layout if (FindLayoutForPos(vLauncherPos, SlotStatus, &iIndex)) { const SLayoutDesc *pLayout = &g_MiscDevicesLayout[iIndex]; SEntry *pEntry = m_Regions.Insert(); pEntry->iType = typeEmptySlot; pEntry->iSlotType = devMissileWeapon; pEntry->iSlotPosIndex = iIndex; pEntry->rcRect.left = pLayout->xLeft; pEntry->rcRect.top = pLayout->yTop; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; SlotStatus[iIndex] = false; } } // Create a region for each weapon. for (i = 0; i < pShip->GetDeviceCount(); i++) { CInstalledDevice *pDevice = pShip->GetDevice(i); if (pDevice->IsEmpty() || (pDevice->GetCategory() != itemcatWeapon && pDevice->GetCategory() != itemcatLauncher)) continue; if (pDevice->GetCategory() == itemcatLauncher) bHasLauncher = true; // If the device already has a position index, then use that (assuming // it's free). iIndex = pDevice->GetSlotPosIndex(); if (iIndex < 0 || iIndex >= SlotStatus.GetCount() || !SlotStatus[iIndex]) iIndex = -1; // If we don't have an assigned slot, figure it out. if (iIndex == -1) { if (!FindLayoutForPos(pDevice->GetPosOffset(pShip), SlotStatus, &iIndex)) continue; // Remember so we stay in this location. pDevice->SetSlotPosIndex(iIndex); } // Create the region const SLayoutDesc *pLayout = &g_MiscDevicesLayout[iIndex]; SEntry *pEntry = m_Regions.Insert(); pEntry->iType = typeInstalledItem; pEntry->pItemCtx = new CItemCtx(pShip, pDevice); pEntry->iSlotPosIndex = iIndex; pEntry->rcRect.left = pLayout->xLeft; pEntry->rcRect.top = pLayout->yTop; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; // Mark the layout as used SlotStatus[iIndex] = false; } // Figure out how many empty weapon slots we should create. We add one // empty slot for each weapon slot, but we subtract one if we don't have // a launcher and we always have at least 1 empty slot, in case the player // finds a slot-less weapon. #ifdef SINGLE_FREE_SLOT int iEmptySlots = 1; #else int iWeaponSlotsInUse; int iTotalSlotsInUse = pShip->CalcDeviceSlotsInUse(&iWeaponSlotsInUse); int iEmptySlots = Max(1, Min((pClass->GetMaxDevices() - iTotalSlotsInUse), (pClass->GetMaxWeapons() - iWeaponSlotsInUse)) - (bHasLauncher ? 0 : 1)); #endif // Try to position the empty slots CVector vWeaponPos; SDeviceDesc DeviceDesc; if (pClass->FindDeviceSlotDesc(devPrimaryWeapon, &DeviceDesc)) vWeaponPos = pClass->GetPosOffset(DeviceDesc.iPosAngle, DeviceDesc.iPosRadius, DeviceDesc.iPosZ, DeviceDesc.b3DPosition); for (i = 0; i < iEmptySlots; i++) { // Find a position if (FindLayoutForPos(vWeaponPos, SlotStatus, &iIndex)) { const SLayoutDesc *pLayout = &g_MiscDevicesLayout[iIndex]; SEntry *pEntry = m_Regions.Insert(); pEntry->iType = typeEmptySlot; pEntry->iSlotType = devPrimaryWeapon; pEntry->iSlotPosIndex = iIndex; pEntry->rcRect.left = pLayout->xLeft; pEntry->rcRect.top = pLayout->yTop; pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH; pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT; SlotStatus[iIndex] = false; } } }
void GenerateImageChart (CUniverse &Universe, CXMLElement *pCmdLine) { int i; enum OrderTypes { orderSmallest = 1, orderLargest = 2, orderName = 3, orderLevel = 4, orderSovereign = 5, orderManufacturer = 6, }; // Item criteria bool bHasItemCriteria; CString sCriteria; CItemCriteria ItemCriteria; if (bHasItemCriteria = pCmdLine->FindAttribute(CONSTLIT("itemCriteria"), &sCriteria)) CItem::ParseCriteria(sCriteria, &ItemCriteria); else CItem::InitCriteriaAll(&ItemCriteria); // Get the criteria from the command line. CDesignTypeCriteria Criteria; if (pCmdLine->FindAttribute(CONSTLIT("criteria"), &sCriteria)) { if (CDesignTypeCriteria::ParseCriteria(sCriteria, &Criteria) != NOERROR) { printf("ERROR: Unable to parse criteria.\n"); return; } } else if (bHasItemCriteria) { if (CDesignTypeCriteria::ParseCriteria(CONSTLIT("i"), &Criteria) != NOERROR) { printf("ERROR: Unable to parse criteria.\n"); return; } } else { printf("ERROR: Expected criteria.\n"); return; } bool bAll = pCmdLine->GetAttributeBool(CONSTLIT("all")); // Options bool bTextBoxesOnly = pCmdLine->GetAttributeBool(CONSTLIT("textBoxesOnly")); bool bFieldUNID = pCmdLine->GetAttributeBool(CONSTLIT("unid")); // Figure out what order we want CString sOrder = pCmdLine->GetAttribute(CONSTLIT("sort")); int iOrder; if (strEquals(sOrder, CONSTLIT("smallest"))) iOrder = orderSmallest; else if (strEquals(sOrder, CONSTLIT("largest"))) iOrder = orderLargest; else if (strEquals(sOrder, CONSTLIT("level"))) iOrder = orderLevel; else if (strEquals(sOrder, CONSTLIT("sovereign"))) iOrder = orderSovereign; else if (strEquals(sOrder, CONSTLIT("manufacturer"))) iOrder = orderManufacturer; else iOrder = orderName; bool b3DGrid = pCmdLine->GetAttributeBool(CONSTLIT("3DGrid")); bool bDockingPorts = pCmdLine->GetAttributeBool(CONSTLIT("portPos")); bool bDevicePos = pCmdLine->GetAttributeBool(CONSTLIT("devicePos")); // Image size int cxDesiredWidth; if (pCmdLine->FindAttributeInteger(CONSTLIT("width"), &cxDesiredWidth)) cxDesiredWidth = Max(512, cxDesiredWidth); else cxDesiredWidth = 1280; // Spacing int cxSpacing = pCmdLine->GetAttributeInteger(CONSTLIT("xSpacing")); int cxExtraMargin = pCmdLine->GetAttributeInteger(CONSTLIT("xMargin")); int cxImageMargin = 2 * pCmdLine->GetAttributeInteger(CONSTLIT("xImageMargin")); // Font for text CString sTypeface; int iSize; bool bBold; bool bItalic; if (!CG16bitFont::ParseFontDesc(pCmdLine->GetAttribute(CONSTLIT("font")), &sTypeface, &iSize, &bBold, &bItalic)) { sTypeface = CONSTLIT("Arial"); iSize = 10; bBold = false; bItalic = false; } CG16bitFont NameFont; NameFont.Create(sTypeface, -PointsToPixels(iSize), bBold, bItalic); CG32bitPixel rgbNameColor = CG32bitPixel(255, 255, 255); // Rotation int iRotation = pCmdLine->GetAttributeInteger(CONSTLIT("rotation")); // Output file CString sFilespec = pCmdLine->GetAttribute(CONSTLIT("output")); if (!sFilespec.IsBlank()) sFilespec = pathAddExtensionIfNecessary(sFilespec, CONSTLIT(".bmp")); // Generate a sorted table of types TSortMap<CString, SEntryDesc> Table; for (i = 0; i < Universe.GetDesignTypeCount(); i++) { CDesignType *pType = Universe.GetDesignType(i); SEntryDesc NewEntry; // Make sure we match the criteria if (!pType->MatchesCriteria(Criteria)) continue; // Figure stuff stuff out based on the specific design type switch (pType->GetType()) { case designItemType: { CItemType *pItemType = CItemType::AsType(pType); CItem Item(pItemType, 1); // Skip if not in item criteria if (!Item.MatchesCriteria(ItemCriteria)) continue; // Skip virtual classes if (pItemType->IsVirtual()) continue; // Initialize the entry NewEntry.pType = pType; NewEntry.sName = pItemType->GetNounPhrase(0); NewEntry.pImage = &pItemType->GetImage(); NewEntry.iSize = RectWidth(NewEntry.pImage->GetImageRect()); break; } case designShipClass: { CShipClass *pClass = CShipClass::AsType(pType); // Skip non-generic classess if (!bAll && !pClass->HasLiteralAttribute(CONSTLIT("genericClass"))) continue; // Initialize the entry NewEntry.pType = pType; NewEntry.sName = pClass->GetNounPhrase(0); NewEntry.iSize = RectWidth(pClass->GetImage().GetImageRect()); NewEntry.pImage = &pClass->GetImage(); NewEntry.iRotation = pClass->Angle2Direction(iRotation); NewEntry.sSovereignName = (pClass->GetDefaultSovereign() ? pClass->GetDefaultSovereign()->GetTypeNounPhrase() : NULL_STR); break; } case designStationType: { CStationType *pStationType = CStationType::AsType(pType); // Skip generic classes if (!bAll && !pStationType->HasLiteralAttribute(CONSTLIT("generic"))) continue; NewEntry.pType = pType; NewEntry.sName = pStationType->GetNounPhrase(0); NewEntry.iSize = pStationType->GetSize(); NewEntry.sSovereignName = (pStationType->GetSovereign() ? pStationType->GetSovereign()->GetTypeNounPhrase() : NULL_STR); InitStationTypeImage(NewEntry, pStationType); break; } default: // Don't know how to handle this type continue; break; } // Adjust name if (bFieldUNID) NewEntry.sName = strPatternSubst(CONSTLIT("%s (%x)"), NewEntry.sName, NewEntry.pType->GetUNID()); // Compute the sort key char szBuffer[1024]; switch (iOrder) { case orderLargest: wsprintf(szBuffer, "%09d%s%x", 1000000 - NewEntry.iSize, NewEntry.sName.GetASCIIZPointer(), pType->GetUNID()); break; case orderLevel: wsprintf(szBuffer, "%09d%s%x", pType->GetLevel(), NewEntry.sName.GetASCIIZPointer(), pType->GetUNID()); break; case orderSmallest: wsprintf(szBuffer, "%09d%s%x", NewEntry.iSize, NewEntry.sName.GetASCIIZPointer(), pType->GetUNID()); break; case orderSovereign: wsprintf(szBuffer, "%s|%s|%x", NewEntry.sSovereignName.GetASCIIZPointer(), NewEntry.sName.GetASCIIZPointer(), pType->GetUNID()); NewEntry.sCategorize = NewEntry.sSovereignName; break; case orderManufacturer: { CString sManufacturer = NewEntry.pType->GetPropertyString(CONSTLIT("manufacturer")); wsprintf(szBuffer, "%s|%s|%x", sManufacturer.GetASCIIZPointer(), NewEntry.sName.GetASCIIZPointer(), pType->GetUNID()); NewEntry.sCategorize = sManufacturer; break; } default: wsprintf(szBuffer, "%s%x", NewEntry.sName.GetASCIIZPointer(), pType->GetUNID()); break; } // Add to list Table.Insert(CString(szBuffer), NewEntry); } // Allocate an arranger that tracks where to paint each world. CImageArranger Arranger; // Settings for the overall arrangement CImageArranger::SArrangeDesc Desc; Desc.cxDesiredWidth = Max(512, cxDesiredWidth - (2 * (cxSpacing + cxExtraMargin))); Desc.cxSpacing = cxSpacing; Desc.cxExtraMargin = cxExtraMargin; Desc.pHeader = &NameFont; // Generate a table of cells for the arranger TArray<CCompositeImageSelector> Selectors; Selectors.InsertEmpty(Table.GetCount()); CString sLastCategory; TArray<CImageArranger::SCellDesc> Cells; for (i = 0; i < Table.GetCount(); i++) { SEntryDesc &Entry = Table[i]; CImageArranger::SCellDesc *pNewCell = Cells.Insert(); pNewCell->cxWidth = (Entry.pImage ? RectWidth(Entry.pImage->GetImageRect()) : 0) + cxImageMargin; pNewCell->cyHeight = (Entry.pImage ? RectHeight(Entry.pImage->GetImageRect()) : 0) + cxImageMargin; pNewCell->sText = Entry.sName; if (!strEquals(sLastCategory, Entry.sCategorize)) { sLastCategory = Entry.sCategorize; pNewCell->bStartNewRow = true; } } // Arrange Arranger.ArrangeByRow(Desc, Cells); // Create a large image CG32bitImage Output; int cxWidth = Max(cxDesiredWidth, Arranger.GetWidth()); int cyHeight = Arranger.GetHeight(); Output.Create(cxWidth, cyHeight); printf("Creating %dx%d image.\n", cxWidth, cyHeight); // Paint the images for (i = 0; i < Table.GetCount(); i++) { SEntryDesc &Entry = Table[i]; int x = Arranger.GetX(i); int y = Arranger.GetY(i); // Paint if (x != -1) { int xCenter = x + (Arranger.GetWidth(i) / 2); int yCenter = y + (Arranger.GetHeight(i) / 2); int xOffset; int yOffset; Entry.pImage->GetImageOffset(0, Entry.iRotation, &xOffset, &yOffset); int cxImage = RectWidth(Entry.pImage->GetImageRect()); int cyImage = RectHeight(Entry.pImage->GetImageRect()); // Paint image if (!bTextBoxesOnly && Entry.pImage) { Entry.pImage->PaintImageUL(Output, x + (Arranger.GetWidth(i) - cxImage) / 2, y + (Arranger.GetHeight(i) - cyImage) / 2, 0, Entry.iRotation); } // Paint type specific stuff switch (Entry.pType->GetType()) { case designStationType: { CStationType *pStationType = CStationType::AsType(Entry.pType); int xStationCenter = xCenter - xOffset; int yStationCenter = yCenter - yOffset; if (bDockingPorts) pStationType->PaintDockPortPositions(Output, xStationCenter, yStationCenter); if (bDevicePos) pStationType->PaintDevicePositions(Output, xStationCenter, yStationCenter); // If we have docking or device positions, mark the center of the station if (bDockingPorts || bDevicePos) { const int LINE_HALF_LENGTH = 24; const CG32bitPixel RGB_CENTER_CROSS(255, 255, 0); Output.DrawLine(xStationCenter - LINE_HALF_LENGTH, yStationCenter, xStationCenter + LINE_HALF_LENGTH, yStationCenter, 1, RGB_CENTER_CROSS); Output.DrawLine(xStationCenter, yStationCenter - LINE_HALF_LENGTH, xStationCenter, yStationCenter + LINE_HALF_LENGTH, 1, RGB_CENTER_CROSS); } break; } } // Paint the 3D grid, if necessary if (b3DGrid) { int iScale = Entry.pImage->GetImageViewportSize(); Metric rMaxRadius = g_KlicksPerPixel * cxImage * 0.5; const Metric rGridSize = LIGHT_SECOND; Metric rRadius; for (rRadius = rGridSize; rRadius <= rMaxRadius; rRadius += rGridSize) { int iRadius = (int)((rRadius / g_KlicksPerPixel) + 0.5); const int iGridAngle = 8; int iPrevAngle = 0; int iAngle; for (iAngle = iGridAngle; iAngle <= 360; iAngle += iGridAngle) { int xFrom, yFrom; C3DConversion::CalcCoord(iScale, iPrevAngle, iRadius, 0, &xFrom, &yFrom); int xTo, yTo; C3DConversion::CalcCoord(iScale, iAngle, iRadius, 0, &xTo, &yTo); Output.DrawLine(xFrom + xCenter, yFrom + yCenter, xTo + xCenter, yTo + yCenter, 1, CG32bitPixel(255, 255, 0)); iPrevAngle = iAngle; } } } // Paint name int xText = Arranger.GetTextX(i); int yText = Arranger.GetTextY(i); if (xText != -1) { if (bTextBoxesOnly) Output.Fill(xText, yText, Arranger.GetTextWidth(i), Arranger.GetTextHeight(i), 0xffff); if (!bTextBoxesOnly) { Output.FillColumn(xCenter, y + Arranger.GetHeight(i), yText - (y + Arranger.GetHeight(i)), rgbNameColor); NameFont.DrawText(Output, xText, yText, rgbNameColor, Entry.sName); } } } } // Write to file or clipboard OutputImage(Output, sFilespec); }