diff --git a/Activities/GameActivity.cpp b/Activities/GameActivity.cpp
index 65a3a255a..6d2dfa0c3 100644
--- a/Activities/GameActivity.cpp
+++ b/Activities/GameActivity.cpp
@@ -921,14 +921,15 @@ int GameActivity::Start()
m_pBuyGUI[player]->Create(&m_PlayerController[player]);
// Load correct loadouts into buy menu if we're starting a non meta-game activity
- if (m_pBuyGUI[player]->GetMetaPlayer() == Players::NoPlayer)
- {
- m_pBuyGUI[player]->SetNativeTechModule(g_PresetMan.GetModuleID(GetTeamTech(GetTeamOfPlayer(player))));
+ if (m_pBuyGUI[player]->GetMetaPlayer() == Players::NoPlayer) {
+ int techModuleID = g_PresetMan.GetModuleID(GetTeamTech(GetTeamOfPlayer(player)));
+
+ m_pBuyGUI[player]->SetNativeTechModule(techModuleID);
m_pBuyGUI[player]->SetForeignCostMultiplier(1.0);
m_pBuyGUI[player]->LoadAllLoadoutsFromFile();
// Change Editor GUI native tech module so it could load and show correct deployment prices
- m_pEditorGUI[player]->SetNativeTechModule(g_PresetMan.GetModuleID(GetTeamTech(GetTeamOfPlayer(player))));
+ m_pEditorGUI[player]->SetNativeTechModule(techModuleID);
}
////////////////////////////////////
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91fb0ab0c..13569f526 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -81,6 +81,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- New `MovableObject` INI and Lua (R/W) property `SimUpdatesBetweenScriptedUpdates`, that lets `MovableObject`s run their Lua update function less frequently, for performance benefits.
+- Added faction themes.
+ Faction themes apply to the `BuyMenu` when the faction is set as the native module (i.e. playing as the faction) in both Conquest and Scenario battle.
+
+ The theme properties are defined in the `DataModule`'s `Index.ini` (before any `IncludeFile` lines) like so:
+ ```
+ FactionBuyMenuTheme = BuyMenuTheme
+ SkinFile = pathToSkinFile // GUI element visuals (NOT actual layout).
+ BackgroundColorIndex = paletteIndex // Color of the parent box that holds all the elements. Palette colors only, no support for images.
+ BannerFile = pathToBannerImage
+ LogoFile = pathToLogoImage
+ ```
+ All properties are optional, any combination works.
+ The skin and background color are also applied to the `ObjectPicker` (scene object placer) for visual consistency.
+
+- New `Settings.ini` property `DisableFactionBuyMenuThemes = 0/1` which will cause custom faction theme definitions in all modules to be ignored and the default theme to be used instead.
+
+- New `DataModule` INI and Lua (R/O) property `IsMerchant` which determines whether a module is an independent merchant. Defaults to false (0). ([Issue #401](https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/401))
+ A module defined as a merchant will stop being playable (in Conquest, etc.) but will have its buyable content available for purchase/placement when playing as any other faction (like how base content is).
+ Only has a noticeable effect when the "Allow purchases from other factions" (`Settings.ini` `ShowForeignItems`) gameplay setting is disabled.
+
+ Note that this property takes priority over the `IsFaction` property. A module that is set as both `IsFaction = 1` and `IsMerchant = 1` will be treated as `IsFaction = 0`.
+
Changed
@@ -117,6 +139,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- `Scene` layer data will now be saved as compressed PNG to reduce file sizes of MetaGame saves and is threaded to prevent the game from freezing when layer data is being saved.
+- Lua function `BuyMenuGUI:SetHeaderImage` renamed to `SetBannerImage`.
+
Fixed
@@ -1332,4 +1356,4 @@ Note: For a log of changes made prior to the commencement of the open source com
[0.1.0-pre1]: https://github.com/cortex-command-community/Cortex-Command-Community-Project-Data/releases/tag/v0.1.0-pre1
[0.1.0-pre2]: https://github.com/cortex-command-community/Cortex-Command-Community-Project-Data/releases/tag/v0.1.0-pre2
[0.1.0-pre3.0]: https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/releases/tag/v0.1.0-pre3.0
-[0.1.0-pre4.0]: https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/releases/tag/v0.1.0-pre4.0
+[0.1.0-pre4.0]: https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/releases/tag/v0.1.0-pre4.0
\ No newline at end of file
diff --git a/GUI/GUIListPanel.cpp b/GUI/GUIListPanel.cpp
index c1625d3e1..fa4678481 100644
--- a/GUI/GUIListPanel.cpp
+++ b/GUI/GUIListPanel.cpp
@@ -21,6 +21,7 @@ GUIListPanel::GUIListPanel(GUIManager *Manager) : GUIPanel(Manager) {
m_FontColor = 0;
m_FontSelectColor = 0;
m_SelectedColorIndex = 0;
+ m_UnselectedColorIndex = 0;
m_CapturedHorz = false;
m_CapturedVert = false;
m_ExternalCapture = false;
@@ -50,6 +51,7 @@ GUIListPanel::GUIListPanel() : GUIPanel() {
m_FontColor = 0;
m_FontSelectColor = 0;
m_SelectedColorIndex = 0;
+ m_UnselectedColorIndex = 0;
m_CapturedHorz = false;
m_CapturedVert = false;
m_ExternalCapture = false;
@@ -250,6 +252,9 @@ void GUIListPanel::BuildBitmap(bool UpdateBase, bool UpdateText) {
m_Skin->GetValue("Listbox", "SelectedColorIndex", &m_SelectedColorIndex);
m_SelectedColorIndex = m_Skin->ConvertColor(m_SelectedColorIndex, m_BaseBitmap->GetColorDepth());
+ m_Skin->GetValue("Listbox", "UnselectedColorIndex", &m_UnselectedColorIndex);
+ m_UnselectedColorIndex = m_Skin->ConvertColor(m_UnselectedColorIndex, m_BaseBitmap->GetColorDepth());
+
// Load the font
m_Skin->GetValue("Listbox", "Font", &Filename);
m_Font = m_Skin->GetFont(Filename);
@@ -346,9 +351,8 @@ void GUIListPanel::BuildDrawBitmap() {
m_Font->DrawAligned(m_DrawBitmap, textX, textY, I->m_Name, GUIFont::Left, GUIFont::Middle, mainTextWidth);
} else {
// Unselected
- // TODO: Don't hardcode unselected color index
- m_DrawBitmap->DrawLine(4, y + 1, m_Width - (m_VertScroll->_GetVisible() ? m_VertScroll->GetWidth() + 2 : 5), y + 1, 144);
- m_DrawBitmap->DrawLine(4, y + itemHeight, m_Width - (m_VertScroll->_GetVisible() ? m_VertScroll->GetWidth() + 2 : 5), y + itemHeight, 144);
+ m_DrawBitmap->DrawLine(4, y + 1, m_Width - (m_VertScroll->_GetVisible() ? m_VertScroll->GetWidth() + 2 : 5), y + 1, m_UnselectedColorIndex);
+ m_DrawBitmap->DrawLine(4, y + itemHeight, m_Width - (m_VertScroll->_GetVisible() ? m_VertScroll->GetWidth() + 2 : 5), y + itemHeight, m_UnselectedColorIndex);
m_Font->SetColor(m_FontColor);
m_Font->SetKerning(m_FontKerning);
m_Font->DrawAligned(m_DrawBitmap, x - 6 - (m_VertScroll->_GetVisible() ? m_VertScroll->GetWidth() : 0) + m_Width, textY, I->m_RightText, GUIFont::Right, GUIFont::Middle, m_Width, m_FontShadow);
@@ -356,7 +360,7 @@ void GUIListPanel::BuildDrawBitmap() {
}
// Draw another line to make sure the last item has two
- if (it == m_Items.end() - 1) { m_DrawBitmap->DrawLine(4, y + itemHeight + 1, m_Width - 5, y + itemHeight + 1, 144); }
+ if (it == m_Items.end() - 1) { m_DrawBitmap->DrawLine(4, y + itemHeight + 1, m_Width - 5, y + itemHeight + 1, m_UnselectedColorIndex); }
// Save the item height for later use in selection routines etc
I->m_Height = itemHeight;
diff --git a/GUI/GUIListPanel.h b/GUI/GUIListPanel.h
index 14fd6384f..96f308035 100644
--- a/GUI/GUIListPanel.h
+++ b/GUI/GUIListPanel.h
@@ -570,6 +570,7 @@ class GUIListPanel : public GUIPanel {
std::vector- m_Items;
std::vector
- m_SelectedList;
unsigned long m_SelectedColorIndex;
+ unsigned long m_UnselectedColorIndex;
//////////////////////////////////////////////////////////////////////////////////////////
// Method: BuildDrawBitmap
diff --git a/GUI/GUISkin.cpp b/GUI/GUISkin.cpp
index bc2d4454f..24dc3fdd6 100644
--- a/GUI/GUISkin.cpp
+++ b/GUI/GUISkin.cpp
@@ -37,10 +37,10 @@ bool GUISkin::Load(const std::string &directory, const std::string &fileName) {
// Destroy any previous instances
Destroy();
- m_Directory = directory;
+ m_Directory = !directory.empty() ? (directory + "/") : "";
GUIReader skinFile;
- if (skinFile.Create((m_Directory + "/" + fileName).c_str()) == -1) {
+ if (skinFile.Create(m_Directory + fileName) == -1) {
return false;
}
@@ -210,7 +210,7 @@ GUIBitmap * GUISkin::CreateBitmap(int Width, int Height) {
GUIBitmap * GUISkin::CreateBitmap(const std::string &Filename) {
// Add the filename onto the current directory
- std::string File = m_Directory + "/" + Filename;
+ std::string File = m_Directory + Filename;
// Check if the image is in our cache
std::vector::iterator it;
@@ -248,7 +248,7 @@ GUIFont * GUISkin::GetFont(const std::string &Name) {
// Not found, so we create the font
GUIFont *Font = new GUIFont(Name);
- if (!Font->Load(m_Screen, m_Directory + "/" + Name)) {
+ if (!Font->Load(m_Screen, m_Directory + Name)) {
delete Font;
return nullptr;
}
diff --git a/Lua/LuaBindingsGUI.cpp b/Lua/LuaBindingsGUI.cpp
index 35671dec6..fdbdc6626 100644
--- a/Lua/LuaBindingsGUI.cpp
+++ b/Lua/LuaBindingsGUI.cpp
@@ -62,7 +62,7 @@ namespace RTE {
.def("ForceRefresh", &BuyMenuGUI::ForceRefresh)
.def("SetOwnedItemsAmount", &BuyMenuGUI::SetOwnedItemsAmount)
.def("GetOwnedItemsAmount", &BuyMenuGUI::GetOwnedItemsAmount)
- .def("SetHeaderImage", &BuyMenuGUI::SetHeaderImage)
+ .def("SetBannerImage", &BuyMenuGUI::SetBannerImage)
.def("SetLogoImage", &BuyMenuGUI::SetLogoImage)
.def("ClearCartList", &BuyMenuGUI::ClearCartList)
.def("LoadDefaultLoadoutToCart", &BuyMenuGUI::LoadDefaultLoadoutToCart);
diff --git a/Lua/LuaBindingsSystem.cpp b/Lua/LuaBindingsSystem.cpp
index 5f4128ddc..61a7b070d 100644
--- a/Lua/LuaBindingsSystem.cpp
+++ b/Lua/LuaBindingsSystem.cpp
@@ -120,6 +120,7 @@ namespace RTE {
.property("Description", &DataModule::GetDescription)
.property("Version", &DataModule::GetVersionNumber)
.property("IsFaction", &DataModule::IsFaction)
+ .property("IsMerchant", &DataModule::IsMerchant)
.def_readwrite("Presets", &DataModule::m_EntityList, luabind::return_stl_iterator);
}
diff --git a/Managers/SettingsMan.cpp b/Managers/SettingsMan.cpp
index 4daad43c1..9426d8abc 100644
--- a/Managers/SettingsMan.cpp
+++ b/Managers/SettingsMan.cpp
@@ -43,6 +43,7 @@ namespace RTE {
m_RecommendedMOIDCount = 240;
m_SimplifiedCollisionDetection = false;
m_SceneBackgroundAutoScaleMode = 1;
+ m_DisableFactionBuyMenuThemes = false;
m_SkipIntro = false;
m_ShowToolTips = true;
@@ -174,6 +175,8 @@ namespace RTE {
reader >> m_SimplifiedCollisionDetection;
} else if (propName == "SceneBackgroundAutoScaleMode") {
SetSceneBackgroundAutoScaleMode(std::stoi(reader.ReadPropValue()));
+ } else if (propName == "DisableFactionBuyMenuThemes") {
+ reader >> m_DisableFactionBuyMenuThemes;
} else if (propName == "EnableParticleSettling") {
reader >> g_MovableMan.m_SettlingEnabled;
} else if (propName == "EnableMOSubtraction") {
@@ -349,6 +352,7 @@ namespace RTE {
writer.NewPropertyWithValue("RecommendedMOIDCount", m_RecommendedMOIDCount);
writer.NewPropertyWithValue("SimplifiedCollisionDetection", m_SimplifiedCollisionDetection);
writer.NewPropertyWithValue("SceneBackgroundAutoScaleMode", m_SceneBackgroundAutoScaleMode);
+ writer.NewPropertyWithValue("DisableFactionBuyMenuThemes", m_DisableFactionBuyMenuThemes);
writer.NewPropertyWithValue("EnableParticleSettling", g_MovableMan.m_SettlingEnabled);
writer.NewPropertyWithValue("EnableMOSubtraction", g_MovableMan.m_MOSubtractionEnabled);
writer.NewPropertyWithValue("DeltaTime", g_TimerMan.GetDeltaTimeSecs());
diff --git a/Managers/SettingsMan.h b/Managers/SettingsMan.h
index 075c6f3a7..702e85680 100644
--- a/Managers/SettingsMan.h
+++ b/Managers/SettingsMan.h
@@ -80,6 +80,18 @@ namespace RTE {
///
/// The new Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2.
void SetSceneBackgroundAutoScaleMode(int newMode) { m_SceneBackgroundAutoScaleMode = std::clamp(newMode, 0, 2); }
+
+ ///
+ /// Gets whether faction BuyMenu theme support is disabled.
+ ///
+ /// Whether faction BuyMenu theme support is disabled.
+ bool FactionBuyMenuThemesDisabled() const { return m_DisableFactionBuyMenuThemes; }
+
+ ///
+ /// Sets whether faction BuyMenu theme support is disabled.
+ ///
+ /// Whether faction BuyMenu theme support is disabled or not.
+ void SetFactionBuyMenuThemesDisabled(bool disable) { m_DisableFactionBuyMenuThemes = disable; }
#pragma endregion
#pragma region Gameplay Settings
@@ -470,6 +482,7 @@ namespace RTE {
int m_RecommendedMOIDCount; //!< Recommended max MOID's before removing actors from scenes.
bool m_SimplifiedCollisionDetection; //!< Whether simplified collision detection (reduced MOID layer sampling) is enabled.
int m_SceneBackgroundAutoScaleMode; //!< Scene background layer auto-scaling mode. 0 for off, 1 for fit screen dimensions and 2 for always upscaled to x2.
+ bool m_DisableFactionBuyMenuThemes; //!< Whether faction BuyMenu theme support is disabled.
bool m_SkipIntro; //!< Whether to play the intro of the game or skip directly to the main menu.
bool m_ShowToolTips; //!< Whether ToolTips are enabled or not.
diff --git a/Menus/BuyMenuGUI.cpp b/Menus/BuyMenuGUI.cpp
index f37617d3d..f2a581fa6 100644
--- a/Menus/BuyMenuGUI.cpp
+++ b/Menus/BuyMenuGUI.cpp
@@ -45,6 +45,9 @@ using namespace RTE;
BITMAP *RTE::BuyMenuGUI::s_pCursor = 0;
+const std::string BuyMenuGUI::c_DefaultBannerImagePath = "Base.rte/GUIs/BuyMenu/BuyMenuBanner.png";
+const std::string BuyMenuGUI::c_DefaultLogoImagePath = "Base.rte/GUIs/BuyMenu/BuyMenuLogo.png";
+
//////////////////////////////////////////////////////////////////////////////////////////
// Method: Clear
//////////////////////////////////////////////////////////////////////////////////////////
@@ -72,7 +75,8 @@ void BuyMenuGUI::Clear()
m_pParentBox = 0;
m_pPopupBox = 0;
m_pPopupText = 0;
- m_pLogo = 0;
+ m_Banner = nullptr;
+ m_Logo = nullptr;
for (int i = 0; i < CATEGORYCOUNT; ++i)
{
m_pCategoryTabs[i] = 0;
@@ -139,7 +143,7 @@ int BuyMenuGUI::Create(Controller *pController)
if (!m_pGUIScreen)
m_pGUIScreen = new AllegroScreen(g_FrameMan.GetNetworkBackBufferGUI8Current(pController->GetPlayer()));
if (!m_pGUIInput)
- m_pGUIInput = new AllegroInput(pController->GetPlayer());
+ m_pGUIInput = new AllegroInput(pController->GetPlayer());
if (!m_pGUIController)
m_pGUIController = new GUIControlManager();
if (!m_pGUIController->Create(m_pGUIScreen, m_pGUIInput, "Base.rte/GUIs/Skins", "DefaultSkin.ini")) {
@@ -158,31 +162,24 @@ int BuyMenuGUI::Create(Controller *pController)
if (g_FrameMan.IsInMultiplayerMode())
{
dynamic_cast(m_pGUIController->GetControl("base"))->SetSize(g_FrameMan.GetPlayerFrameBufferWidth(pController->GetPlayer()), g_FrameMan.GetPlayerFrameBufferHeight(pController->GetPlayer()));
- }
+ }
else
{
dynamic_cast(m_pGUIController->GetControl("base"))->SetSize(g_FrameMan.GetResX(), g_FrameMan.GetResY());
}
// Make sure we have convenient points to teh containing GUI colleciton boxes that we will manipulate the positions of
- if (!m_pParentBox)
- {
- m_pParentBox = dynamic_cast(m_pGUIController->GetControl("BuyGUIBox"));
-
- // Set the background settings of the parent collection box
- m_pParentBox->SetDrawBackground(true);
- m_pParentBox->SetDrawType(GUICollectionBox::Color);
-
- // Set the images for the logo and header decorations
- GUICollectionBox *pHeader = dynamic_cast(m_pGUIController->GetControl("CatalogHeader"));
- m_pLogo = dynamic_cast(m_pGUIController->GetControl("CatalogLogo"));
- ContentFile headerFile("Base.rte/GUIs/Skins/BuyMenu/BuyMenuHeader.png");
- ContentFile logoFile("Base.rte/GUIs/Skins/BuyMenu/BuyMenuLogo.png");
- pHeader->SetDrawImage(new AllegroBitmap(headerFile.GetAsBitmap()));
- m_pLogo->SetDrawImage(new AllegroBitmap(logoFile.GetAsBitmap()));
- pHeader->SetDrawType(GUICollectionBox::Image);
- m_pLogo->SetDrawType(GUICollectionBox::Image);
- }
+ if (!m_pParentBox) {
+ m_pParentBox = dynamic_cast(m_pGUIController->GetControl("BuyGUIBox"));
+ m_pParentBox->SetDrawBackground(true);
+ m_pParentBox->SetDrawType(GUICollectionBox::Color);
+
+ m_Banner = dynamic_cast(m_pGUIController->GetControl("CatalogHeader"));
+ SetBannerImage(c_DefaultBannerImagePath);
+
+ m_Logo = dynamic_cast(m_pGUIController->GetControl("CatalogLogo"));
+ SetLogoImage(c_DefaultLogoImagePath);
+ }
m_pParentBox->SetPositionAbs(-m_pParentBox->GetWidth(), 0);
m_pParentBox->SetEnabled(false);
m_pParentBox->SetVisible(false);
@@ -316,30 +313,20 @@ void BuyMenuGUI::Destroy()
Clear();
}
-//////////////////////////////////////////////////////////////////////////////////////////
-// Method: SetHeaderImage
-//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Changes the header image to the one specified in path
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-void BuyMenuGUI::SetHeaderImage(string path)
-{
- GUICollectionBox *pHeader = dynamic_cast(m_pGUIController->GetControl("CatalogHeader"));
- ContentFile headerFile(path.c_str());
- pHeader->SetDrawImage(new AllegroBitmap(headerFile.GetAsBitmap()));
- pHeader->SetDrawType(GUICollectionBox::Image);
+void BuyMenuGUI::SetBannerImage(const std::string &imagePath) {
+ ContentFile bannerFile((imagePath.empty() ? c_DefaultBannerImagePath : imagePath).c_str());
+ m_Banner->SetDrawImage(new AllegroBitmap(bannerFile.GetAsBitmap()));
+ m_Banner->SetDrawType(GUICollectionBox::Image);
}
-//////////////////////////////////////////////////////////////////////////////////////////
-// Method: SetLogoImage
-//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Changes the logo image to the one specified in path
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-void BuyMenuGUI::SetLogoImage(string path)
-{
- m_pLogo = dynamic_cast(m_pGUIController->GetControl("CatalogLogo"));
- ContentFile logoFile(path.c_str());
- m_pLogo->SetDrawImage(new AllegroBitmap(logoFile.GetAsBitmap()));
- m_pLogo->SetDrawType(GUICollectionBox::Image);
+void BuyMenuGUI::SetLogoImage(const std::string &imagePath) {
+ ContentFile logoFile((imagePath.empty() ? c_DefaultLogoImagePath : imagePath).c_str());
+ m_Logo->SetDrawImage(new AllegroBitmap(logoFile.GetAsBitmap()));
+ m_Logo->SetDrawType(GUICollectionBox::Image);
}
//////////////////////////////////////////////////////////////////////////////////////////
@@ -609,24 +596,29 @@ void BuyMenuGUI::SetMetaPlayer(int metaPlayer)
}
}
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////
-// Method: SetNativeTechModule
-//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Sets which DataModule ID should be treated as the native tech of the
-// user of this menu.
+void BuyMenuGUI::SetNativeTechModule(int whichModule) {
+ if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) {
+ m_NativeTechModule = whichModule;
+ SetModuleExpanded(m_NativeTechModule);
+ DeployLoadout(0);
-void BuyMenuGUI::SetNativeTechModule(int whichModule)
-{
- if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount())
- {
- // Set the multipliers and refresh everything that needs refreshing to reflect the change
- m_NativeTechModule = whichModule;
- SetModuleExpanded(m_NativeTechModule);
- DeployLoadout(0);
- }
-}
+ if (!g_SettingsMan.FactionBuyMenuThemesDisabled() && m_NativeTechModule > 0) {
+ if (const DataModule *techModule = g_PresetMan.GetDataModule(whichModule); techModule->IsFaction()) {
+ const DataModule::BuyMenuTheme &techBuyMenuTheme = techModule->GetFactionBuyMenuTheme();
+ if (!techBuyMenuTheme.SkinFilePath.empty()) {
+ // Not specifying the skin file directory allows us to load image files from the whole working directory in the skin file instead of just the specified directory.
+ m_pGUIController->ChangeSkin("", techBuyMenuTheme.SkinFilePath);
+ }
+ if (techBuyMenuTheme.BackgroundColorIndex >= 0) { m_pParentBox->SetDrawColor(std::clamp(techBuyMenuTheme.BackgroundColorIndex, 0, 255)); }
+ SetBannerImage(techBuyMenuTheme.BannerImagePath);
+ SetLogoImage(techBuyMenuTheme.LogoImagePath);
+ }
+ }
+ }
+}
//////////////////////////////////////////////////////////////////////////////////////////
// Method: SetModuleExpanded
@@ -738,7 +730,7 @@ float BuyMenuGUI::GetTotalOrderCost()
}
}
}
- else
+ else
{
for (vector::iterator itr = m_pCartList->GetItemList()->begin(); itr != m_pCartList->GetItemList()->end(); ++itr)
totalCost += dynamic_cast((*itr)->m_pEntity)->GetGoldValue(m_NativeTechModule, m_ForeignCostMult);
@@ -1385,7 +1377,7 @@ void BuyMenuGUI::Update()
// User mashed button on a regular shop item, add it to cargo, or select craft
else if (pItem && pItem->m_pEntity)
{
- // Select the craft
+ // Select the craft
if (m_MenuCategory == CRAFT)
{
if (m_pSelectedCraft = dynamic_cast(pItem->m_pEntity))
@@ -1706,7 +1698,7 @@ void BuyMenuGUI::Update()
m_CategoryItemIndex[m_MenuCategory] = m_ListItemIndex = m_pShopList->GetSelectedIndex();
m_pShopList->ScrollToSelected();
- // Select the craft
+ // Select the craft
if (m_MenuCategory == CRAFT)
{
if (m_pSelectedCraft = dynamic_cast(pItem->m_pEntity))
@@ -1739,7 +1731,7 @@ void BuyMenuGUI::Update()
if (IsAlwaysAllowedItem(pItem->m_Name))
m_pCartList->AddItem(pItem->m_Name, pItem->m_RightText, pItemBitmap, pItem->m_pEntity);
}
- else
+ else
{
m_pCartList->AddItem(pItem->m_Name, pItem->m_RightText, pItemBitmap, pItem->m_pEntity);
}
@@ -1749,7 +1741,7 @@ void BuyMenuGUI::Update()
{
m_pCartList->AddItem(pItem->m_Name, pItem->m_RightText, pItemBitmap, pItem->m_pEntity);
}
-
+
// If I just selected an AHuman, enable equipment selection mode
if (m_MenuCategory == BODIES && pItem->m_pEntity->GetClassName() == "AHuman")
{
@@ -1802,7 +1794,7 @@ void BuyMenuGUI::Update()
/*
// Somehting was just selected, so update the selection index to the new selected index
if(anEvent.GetMsg() == GUIListBox::Select)
- {
+ {
if (m_ListItemIndex != m_pCartList->GetSelectedIndex())
g_GUISound.SelectionChangeSound()->Play(m_pController->GetPlayer());
m_ListItemIndex = m_pCartList->GetSelectedIndex();
@@ -1860,7 +1852,7 @@ void BuyMenuGUI::Update()
}
// Mouse moved over the panel, show the popup with item description
else if(anEvent.GetMsg() == GUIListBox::MouseMove)
- {
+ {
// Mouse is moving within the list, so make it focus on the list
m_pCartList->SetFocus();
m_MenuFocus = ORDER;
@@ -1899,7 +1891,7 @@ void BuyMenuGUI::Draw(BITMAP *drawBitmap) const
AllegroScreen drawScreen(drawBitmap);
m_pGUIController->Draw(&drawScreen);
- // Draw the cursor on top of everything
+ // Draw the cursor on top of everything
if (IsEnabled() && m_pController->IsMouseControlled())
// m_pGUIController->DrawMouse();
draw_sprite(drawBitmap, s_pCursor, m_CursorPos.GetFloorIntX(), m_CursorPos.GetFloorIntY());
@@ -1971,7 +1963,7 @@ void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs)
// Hide/show the logo and special sets category buttons, and add all current presets to the list, and we're done.
if (m_MenuCategory == SETS)
{
- m_pLogo->SetVisible(false);
+ m_Logo->SetVisible(false);
m_pSaveButton->SetVisible(true);
m_pClearButton->SetVisible(true);
// Add and done!
@@ -1981,7 +1973,7 @@ void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs)
// Hide the sets buttons otherwise
else
{
- m_pLogo->SetVisible(true);
+ m_Logo->SetVisible(true);
m_pSaveButton->SetVisible(false);
m_pClearButton->SetVisible(false);
}
@@ -2060,15 +2052,15 @@ void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs)
pItemBitmap = new AllegroBitmap((*tItr)->GetGraphicalIcon());
// Passing in ownership of the bitmap, but not of the pSpriteObj
if (m_OwnedItems.size() > 0 || m_OnlyShowOwnedItems)
- {
+ {
if (GetOwnedItemsAmount((*tItr)->GetModuleAndPresetName()) > 0)
{
string amount = std::to_string(GetOwnedItemsAmount((*tItr)->GetModuleAndPresetName())) + " pcs";
m_pShopList->AddItem((*tItr)->GetPresetName(), amount , pItemBitmap, *tItr);
- }
- else
- {
+ }
+ else
+ {
if (!m_OnlyShowOwnedItems)
m_pShopList->AddItem((*tItr)->GetPresetName(), (*tItr)->GetGoldValueString(m_NativeTechModule, m_ForeignCostMult), pItemBitmap, *tItr);
else
@@ -2254,7 +2246,7 @@ void BuyMenuGUI::AddObjectsToItemList(vector > &moduleList, strin
// Go through all the data modules, gathering the objects that match the criteria in each one
for (int moduleID = 0; moduleID < g_PresetMan.GetTotalModuleCount(); ++moduleID)
{
- if (moduleID == 0 || moduleID == m_NativeTechModule)
+ if (moduleID == 0 || moduleID == m_NativeTechModule || g_PresetMan.GetDataModule(moduleID)->IsMerchant())
{
if (group.empty() || group == "All")
g_PresetMan.GetAllOfType(moduleList[moduleID], type, moduleID);
diff --git a/Menus/BuyMenuGUI.h b/Menus/BuyMenuGUI.h
index 3d79f5373..757625ec8 100644
--- a/Menus/BuyMenuGUI.h
+++ b/Menus/BuyMenuGUI.h
@@ -203,14 +203,12 @@ class BuyMenuGUI {
int GetMetaPlayer() const { return m_MetaPlayer; }
-//////////////////////////////////////////////////////////////////////////////////////////
-// Method: SetNativeTechModule
-//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Sets which DataModule ID should be treated as the native tech of the
-// user of this menu.
-// Arguments: The module ID to set as the native one. 0 means everything is native.
-// Return value: None.
+ ///
+ /// Sets which DataModule ID should be treated as the native tech of the user of this menu.
+ /// This will also apply the DataModule's faction BuyMenu theme, if applicable.
+ ///
+ /// The module ID to set as the native one. 0 means everything is native.
void SetNativeTechModule(int whichModule);
@@ -396,7 +394,7 @@ class BuyMenuGUI {
//////////////////////////////////////////////////////////////////////////////////////////
// Method: AddAllowedItem
//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Adds an item to the list of allowed items.
+// Description: Adds an item to the list of allowed items.
// If the list is not empty then everything not in the list is removed from the buy menu
// Items will be removed from the buy menu when it's called, category changed or after a ForceRefresh().
// Arguments: Full preset name to add.
@@ -407,7 +405,7 @@ class BuyMenuGUI {
//////////////////////////////////////////////////////////////////////////////////////////
// Method: RemoveAllowedItem
//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Removes an item from the list of allowed items.
+// Description: Removes an item from the list of allowed items.
// Arguments: Full preset name to remove.
// Return value: None.
@@ -443,7 +441,7 @@ class BuyMenuGUI {
//////////////////////////////////////////////////////////////////////////////////////////
// Method: RemoveAlwaysAllowedItem
//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Removes an item from the list of always allowed items.
+// Description: Removes an item from the list of always allowed items.
// Arguments: Full preset name to remove.
// Return value: None.
@@ -472,7 +470,7 @@ class BuyMenuGUI {
//////////////////////////////////////////////////////////////////////////////////////////
// Method: AddProhibitedItem
//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Adds an item prohibited to buy from the buy menu.
+// Description: Adds an item prohibited to buy from the buy menu.
// The item will be removed from the buy menu when it's called, category changed or after a ForceRefresh().
// Arguments: Full preset name to add.
// Return value: None.
@@ -522,7 +520,7 @@ class BuyMenuGUI {
// Arguments: None.
// Return value: None.
- void ClearCartList();
+ void ClearCartList();
//////////////////////////////////////////////////////////////////////////////////////////
// Method: LoadDefaultLoadoutToCart
@@ -565,7 +563,7 @@ class BuyMenuGUI {
// Method: GetOwnedItemsAmount
//////////////////////////////////////////////////////////////////////////////////////////
// Description: Returns the amount of specified items owned in this buy menu
-// Arguments: Full preset name of item.
+// Arguments: Full preset name of item.
// Return value: Amount of owned items.
int GetOwnedItemsAmount(std::string presetName) { if (m_OwnedItems.find(presetName) != m_OwnedItems.end()) return m_OwnedItems[presetName]; else return 0; };
@@ -574,28 +572,24 @@ class BuyMenuGUI {
// Method: CommitPurchase
//////////////////////////////////////////////////////////////////////////////////////////
// Description: Deducts 1 piece of owned item and return true if purchase can be made or false if the item is out of stock.
-// Arguments: Full preset name of item.
+// Arguments: Full preset name of item.
// Return value: Whether the purchase can be conducted or the item is out of stock.
bool CommitPurchase(string presetName);
-//////////////////////////////////////////////////////////////////////////////////////////
-// Method: SetHeaderImage
-//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Changes the header image to the one specified in path
-// Arguments: Path to image to set as header.
-// Return value: None.
-
- void SetHeaderImage(string path);
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Method: SetLogoImage
-//////////////////////////////////////////////////////////////////////////////////////////
-// Description: Changes the logo image to the one specified in path
-// Arguments: Path to image to set as logo.
-// Return value: None.
+#pragma region Faction Theme Handling
+ ///
+ /// Changes the banner image to the one specified. If none is specified, resets it to the default banner image.
+ ///
+ /// Path to image to set as banner.
+ void SetBannerImage(const std::string &imagePath);
- void SetLogoImage(string path);
+ ///
+ /// Changes the logo image to the one specified. If none is specified, resets it to the default logo image.
+ ///
+ /// Path to image to set as logo.
+ void SetLogoImage(const std::string &imagePath);
+#pragma endregion
//////////////////////////////////////////////////////////////////////////////////////////
// Protected member variable and method declarations
@@ -789,8 +783,10 @@ class BuyMenuGUI {
GUICollectionBox *m_pPopupBox;
// Label displaying the item popup description
GUILabel *m_pPopupText;
+ // Top banner
+ GUICollectionBox *m_Banner;
// Logo label that disappears when the sets category is selected
- GUICollectionBox *m_pLogo;
+ GUICollectionBox *m_Logo;
// All the radio buttons for the different shop categories
GUITab *m_pCategoryTabs[CATEGORYCOUNT];
// The Listbox which lists all the shop's items in the currently selected category
@@ -859,6 +855,9 @@ class BuyMenuGUI {
private:
+ static const std::string c_DefaultBannerImagePath; //!< Path to the default banner image.
+ static const std::string c_DefaultLogoImagePath; //!< Path to the default logo image.
+
///
/// Refresh tab disabled states, so tabs get properly enabled/disabled based on whether or not equipment selection mode is enabled.
///
@@ -883,4 +882,4 @@ class BuyMenuGUI {
} // namespace RTE
-#endif // File
\ No newline at end of file
+#endif // File
diff --git a/Menus/ObjectPickerGUI.cpp b/Menus/ObjectPickerGUI.cpp
index 1bd31847b..7483619e5 100644
--- a/Menus/ObjectPickerGUI.cpp
+++ b/Menus/ObjectPickerGUI.cpp
@@ -147,7 +147,21 @@ namespace RTE {
void ObjectPickerGUI::SetNativeTechModule(int whichModule) {
if (whichModule >= 0 && whichModule < g_PresetMan.GetTotalModuleCount()) {
m_NativeTechModuleID = whichModule;
- if (m_NativeTechModuleID > 0) { SetObjectsListModuleGroupExpanded(m_NativeTechModuleID); }
+ if (m_NativeTechModuleID > 0) {
+ SetObjectsListModuleGroupExpanded(m_NativeTechModuleID);
+
+ if (!g_SettingsMan.FactionBuyMenuThemesDisabled()) {
+ if (const DataModule *techModule = g_PresetMan.GetDataModule(whichModule); techModule->IsFaction()) {
+ const DataModule::BuyMenuTheme &techBuyMenuTheme = techModule->GetFactionBuyMenuTheme();
+
+ if (!techBuyMenuTheme.SkinFilePath.empty()) {
+ // Not specifying the skin file directory allows us to load image files from the whole working directory in the skin file instead of just the specified directory.
+ m_GUIControlManager->ChangeSkin("", techBuyMenuTheme.SkinFilePath);
+ }
+ if (techBuyMenuTheme.BackgroundColorIndex >= 0) { m_ParentBox->SetDrawColor(std::clamp(techBuyMenuTheme.BackgroundColorIndex, 0, 255)); }
+ }
+ }
+ }
}
}
@@ -359,7 +373,7 @@ namespace RTE {
}
} else {
for (int moduleID = 0; moduleID < moduleList.size(); ++moduleID) {
- if (moduleID == 0 || moduleID == m_NativeTechModuleID) { g_PresetMan.GetAllOfGroup(moduleList.at(moduleID), groupListItem->m_Name, m_ShowType, moduleID); }
+ if (moduleID == 0 || moduleID == m_NativeTechModuleID || g_PresetMan.GetDataModule(moduleID)->IsMerchant()) { g_PresetMan.GetAllOfGroup(moduleList.at(moduleID), groupListItem->m_Name, m_ShowType, moduleID); }
}
}
} else {
diff --git a/Menus/ObjectPickerGUI.h b/Menus/ObjectPickerGUI.h
index 3d98b3dfd..5560df9a0 100644
--- a/Menus/ObjectPickerGUI.h
+++ b/Menus/ObjectPickerGUI.h
@@ -92,6 +92,7 @@ namespace RTE {
///
/// Sets which DataModule ID should be treated as the native tech of the user of this menu.
+ /// This will also apply the DataModule's faction BuyMenu theme skin and background color for visual consistency, if applicable.
///
/// The module ID to set as the native one. 0 means everything is native.
void SetNativeTechModule(int whichModule);
diff --git a/Menus/SettingsMiscGUI.cpp b/Menus/SettingsMiscGUI.cpp
index d68523be1..fcfd67a8e 100644
--- a/Menus/SettingsMiscGUI.cpp
+++ b/Menus/SettingsMiscGUI.cpp
@@ -34,6 +34,9 @@ namespace RTE {
m_UseMonospaceConsoleFontCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxUseMonospaceConsoleFont"));
m_UseMonospaceConsoleFontCheckbox->SetCheck(g_ConsoleMan.GetConsoleUseMonospaceFont());
+ m_DisableFactionBuyMenuThemesCheckbox = dynamic_cast(m_GUIControlManager->GetControl("CheckboxDisableFactionBuyMenuThemes"));
+ m_DisableFactionBuyMenuThemesCheckbox->SetCheck(g_SettingsMan.FactionBuyMenuThemesDisabled());
+
m_SceneBackgroundAutoScaleLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelSceneBackgroundAutoScaleSetting"));
UpdateSceneBackgroundAutoScaleLabel();
@@ -80,6 +83,8 @@ namespace RTE {
g_SettingsMan.MeasureModuleLoadTime(m_MeasureLoadTimeCheckbox->GetCheck());
} else if (guiEvent.GetControl() == m_UseMonospaceConsoleFontCheckbox) {
g_ConsoleMan.SetConsoleUseMonospaceFont(m_UseMonospaceConsoleFontCheckbox->GetCheck());
+ } else if (guiEvent.GetControl() == m_DisableFactionBuyMenuThemesCheckbox) {
+ g_SettingsMan.SetFactionBuyMenuThemesDisabled(m_DisableFactionBuyMenuThemesCheckbox->GetCheck());
} else if (guiEvent.GetControl() == m_SceneBackgroundAutoScaleSlider) {
g_SettingsMan.SetSceneBackgroundAutoScaleMode(m_SceneBackgroundAutoScaleSlider->GetValue());
UpdateSceneBackgroundAutoScaleLabel();
diff --git a/Menus/SettingsMiscGUI.h b/Menus/SettingsMiscGUI.h
index 3c4d354eb..2288bd362 100644
--- a/Menus/SettingsMiscGUI.h
+++ b/Menus/SettingsMiscGUI.h
@@ -53,6 +53,7 @@ namespace RTE {
GUICheckbox *m_ShowAdvancedPerfStatsCheckbox;
GUICheckbox *m_MeasureLoadTimeCheckbox;
GUICheckbox *m_UseMonospaceConsoleFontCheckbox;
+ GUICheckbox *m_DisableFactionBuyMenuThemesCheckbox;
GUILabel *m_SceneBackgroundAutoScaleLabel;
GUISlider *m_SceneBackgroundAutoScaleSlider;
diff --git a/Menus/TitleScreen.cpp b/Menus/TitleScreen.cpp
index da9fbe8b0..2ba29038a 100644
--- a/Menus/TitleScreen.cpp
+++ b/Menus/TitleScreen.cpp
@@ -132,7 +132,7 @@ namespace RTE {
newStar.Intensity = RandomNum(111, 185);
} else {
newStar.Size = Star::StarSize::StarHuge;
- newStar.Bitmap = starHugeBitmaps.at(RandomNum(0, starLargeBitmapCount - 1));
+ newStar.Bitmap = starHugeBitmaps.at(RandomNum(0, starHugeBitmapCount - 1));
newStar.Intensity = RandomNum(166, 185);
}
newStar.Position = Vector(RandomNum(0.0F, static_cast(g_FrameMan.GetResX())), RandomNum(-100.0F, static_cast(m_Nebula.GetBitmap()->h)));
diff --git a/System/DataModule.cpp b/System/DataModule.cpp
index d21daedb1..5a46a7445 100644
--- a/System/DataModule.cpp
+++ b/System/DataModule.cpp
@@ -28,6 +28,7 @@ namespace RTE {
m_CrabToHumanSpawnRatio = 0;
m_ScriptPath.clear();
m_IsFaction = false;
+ m_IsMerchant = false;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -116,6 +117,10 @@ namespace RTE {
}
} else if (propName == "IsFaction") {
reader >> m_IsFaction;
+ if (m_IsMerchant) { m_IsFaction = false; }
+ } else if (propName == "IsMerchant") {
+ reader >> m_IsMerchant;
+ if (m_IsMerchant) { m_IsFaction = false; }
} else if (propName == "SupportedGameVersion") {
reader >> m_SupportedGameVersion;
} else if (propName == "Version") {
@@ -139,6 +144,23 @@ namespace RTE {
} else if (propName == "IconFile") {
reader >> m_IconFile;
m_Icon = m_IconFile.GetAsBitmap();
+ } else if (propName == "FactionBuyMenuTheme") {
+ if (reader.ReadPropValue() == "BuyMenuTheme") {
+ while (reader.NextProperty()) {
+ std::string themePropName = reader.ReadPropName();
+ if (themePropName == "SkinFile") {
+ m_BuyMenuTheme.SkinFilePath = reader.ReadPropValue();
+ } else if (themePropName == "BannerFile") {
+ m_BuyMenuTheme.BannerImagePath = reader.ReadPropValue();
+ } else if (themePropName == "LogoFile") {
+ m_BuyMenuTheme.LogoImagePath = reader.ReadPropValue();
+ } else if (themePropName == "BackgroundColorIndex") {
+ m_BuyMenuTheme.BackgroundColorIndex = std::clamp(std::stoi(reader.ReadPropValue()), 0, 255);
+ } else {
+ break;
+ }
+ }
+ }
} else if (propName == "AddMaterial") {
return g_SceneMan.ReadProperty(propName, reader);
} else {
diff --git a/System/DataModule.h b/System/DataModule.h
index 3b6d8585b..69cd9d3b6 100644
--- a/System/DataModule.h
+++ b/System/DataModule.h
@@ -22,6 +22,17 @@ namespace RTE {
SerializableClassNameGetter;
SerializableOverrideMethods;
+ ///
+ /// Struct that holds data about the custom BuyMenu/ObjectPicker theme of this DataModule.
+ /// Themes are used when a DataModule is considered a faction and is selected to be played as in an Activity.
+ ///
+ struct BuyMenuTheme {
+ std::string SkinFilePath = ""; //!< Path to the custom BuyMenu skin file.
+ std::string BannerImagePath = ""; //!< Path to the custom BuyMenu banner image.
+ std::string LogoImagePath = ""; //< Path to the custom BuyMenu logo.
+ int BackgroundColorIndex = -1; //!< The custom BuyMenu background color.
+ };
+
#pragma region Creation
///
/// Constructor method used to instantiate a DataModule object in system memory. Create() should be called before using the object.
@@ -110,6 +121,12 @@ namespace RTE {
/// Whether this DataModule is considered a faction or not.
bool IsFaction() const { return m_IsFaction; }
+ ///
+ /// Gets whether this DataModule is considered a merchant.
+ ///
+ /// Whether this DataModule is considered a merchant or not.
+ bool IsMerchant() const { return m_IsMerchant; }
+
///
/// Gets the version number of this DataModule.
///
@@ -127,6 +144,12 @@ namespace RTE {
///
/// Crab-to-human spawn ration value.
float GetCrabToHumanSpawnRatio() const { return m_CrabToHumanSpawnRatio; }
+
+ ///
+ /// Gets the faction BuyMenu theme data of this DataModule.
+ ///
+ /// The faction BuyMenu theme information of this DataModule
+ const BuyMenuTheme & GetFactionBuyMenuTheme() const { return m_BuyMenuTheme; }
#pragma endregion
#pragma region Entity Mapping
@@ -265,6 +288,7 @@ namespace RTE {
std::string m_Description; //!< Brief description of what this module is and contains.
std::string m_ScriptPath; //!< Path to script to execute when this module is loaded.
bool m_IsFaction; //!< Whether this data module is considered a faction.
+ bool m_IsMerchant; //!< Whether this data module is considered a merchant.
std::string m_SupportedGameVersion; //!< Game version this DataModule supports. Needs to match exactly for this DataModule to be allowed. Base DataModules don't need this.
int m_Version; //!< Version number, starting with 1.
int m_ModuleID; //!< ID number assigned to this upon loading, for internal use only, don't reflect in ini's.
@@ -272,6 +296,8 @@ namespace RTE {
ContentFile m_IconFile; //!< File to the icon/symbol bitmap.
BITMAP *m_Icon; //!< Bitmap with the icon loaded from above file.
+ BuyMenuTheme m_BuyMenuTheme; //!< Faction BuyMenu theme data.
+
float m_CrabToHumanSpawnRatio; //!< Crab-to-human Spawn ratio to replace value from Constants.lua.
std::list m_EntityList; //!< A list of loaded entities solely for the purpose of enumeration presets from Lua.