diff --git a/Phobos.vcxproj b/Phobos.vcxproj
index 2663fc3bc2..b30862576c 100644
--- a/Phobos.vcxproj
+++ b/Phobos.vcxproj
@@ -35,6 +35,7 @@
+
@@ -81,7 +82,6 @@
-
@@ -117,6 +117,7 @@
+
@@ -140,7 +141,6 @@
-
diff --git a/src/Misc/ExtendedToolTips.h b/src/Misc/ExtendedToolTips.h
deleted file mode 100644
index 460975ed7f..0000000000
--- a/src/Misc/ExtendedToolTips.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-class ExtToolTip
-{
-public:
- static inline void ClearBuffer() {
- _uesExtBuffer = false;
- _ExtBuffer[0] = NULL;
- }
-
- static inline void UseExtBuffer() {
- _uesExtBuffer = true;
- _ExtBuffer[TOOLTIP_BUFFER_LENGTH - 1] = NULL;
- }
-
- static inline void SetBuffer(REGISTERS* R) {
- if (_uesExtBuffer)
- R->EDI(_ExtBuffer);
- }
-
- static inline void Append(const wchar_t* str) {
- wcscat_s(_ExtBuffer, str);
- }
-
- static inline void Append_NewLineLater() {
- _addNewLine = true;
- }
-
- static inline void Append_SpaceLater() {
- _addSpace = true;
- }
-
- static void Apply_SeparatorAsNewLine() {
- if (_addNewLine || _addSpace) {
- wcscat_s(_ExtBuffer, L"\n");
- }
- Clear_Separator();
- }
-
- static void Apply_Separator() {
- if (_addNewLine) {
- wcscat_s(_ExtBuffer, L"\n");
- }
- else if (_addSpace) {
- wcscat_s(_ExtBuffer, L" ");
- }
- Clear_Separator();
- }
-
- static inline void Clear_Separator() {
- _addNewLine = false;
- _addSpace = false;
- }
-
- static void CreateHelpText(AbstractType itemType, int itemIndex);
-private:
- static const int inline TOOLTIP_BUFFER_LENGTH = 1024;
- static bool inline _uesExtBuffer = false;
- static wchar_t inline _ExtBuffer[TOOLTIP_BUFFER_LENGTH] = L"";
-
- static bool inline _addSpace = false;
- static bool inline _addNewLine = false;
-
-public:
- static const inline wchar_t* pseudoBuff = L"ToolTip";
-
- static bool inline isCameo = false;
- static bool inline slaveDraw = false;
-};
diff --git a/src/Misc/PhobosToolTip.cpp b/src/Misc/PhobosToolTip.cpp
new file mode 100644
index 0000000000..2761b4d75d
--- /dev/null
+++ b/src/Misc/PhobosToolTip.cpp
@@ -0,0 +1,321 @@
+#include
+
+#include "PhobosToolTip.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+PhobosToolTip PhobosToolTip::Instance;
+
+inline bool PhobosToolTip::IsEnabled() const
+{
+ return Phobos::UI::ExtendedToolTips;
+}
+
+inline const wchar_t* PhobosToolTip::GetUIDescription(TechnoTypeExt::ExtData* pData) const
+{
+ return Phobos::Config::ToolTipDescriptions && !pData->UIDescription.Get().empty()
+ ? pData->UIDescription.Get().Text
+ : nullptr;
+}
+
+inline const wchar_t* PhobosToolTip::GetUIDescription(SWTypeExt::ExtData* pData) const
+{
+ return Phobos::Config::ToolTipDescriptions && !pData->UIDescription.Get().empty()
+ ? pData->UIDescription.Get().Text
+ : nullptr;
+}
+
+inline int PhobosToolTip::GetBuildTime(TechnoTypeClass* pType) const
+{
+ static char pTrick[0x6C8]; // Just big enough to hold all types
+ switch (pType->WhatAmI())
+ {
+ case AbstractType::BuildingType:
+ *reinterpret_cast(pTrick) = 0x7E3EBC; // BuildingClass::`vtable`
+ reinterpret_cast(pTrick)->Type = (BuildingTypeClass*)pType;
+ break;
+ case AbstractType::AircraftType:
+ *reinterpret_cast(pTrick) = 0x7E22A4; // AircraftClass::`vtable`
+ reinterpret_cast(pTrick)->Type = (AircraftTypeClass*)pType;
+ break;
+ case AbstractType::InfantryType:
+ *reinterpret_cast(pTrick) = 0x7EB058; // InfantryClass::`vtable`
+ reinterpret_cast(pTrick)->Type = (InfantryTypeClass*)pType;
+ break;
+ case AbstractType::UnitType:
+ *reinterpret_cast(pTrick) = 0x7F5C70; // UnitClass::`vtable`
+ reinterpret_cast(pTrick)->Type = (UnitTypeClass*)pType;
+ break;
+ }
+
+ // TechnoTypeClass only has 4 final classes :
+ // BuildingTypeClass, AircraftTypeClass, InfantryTypeClass and UnitTypeClass
+ // It has to be these four classes, otherwise pType will just be nullptr
+ reinterpret_cast(pTrick)->Owner = HouseClass::Player;
+ int nTimeToBuild = reinterpret_cast(pTrick)->TimeToBuild();
+ // 54 frames at least
+ return nTimeToBuild < 54 ? 54 : nTimeToBuild;
+}
+
+inline int PhobosToolTip::GetPower(TechnoTypeClass* pType) const
+{
+ if (auto const pBldType = abstract_cast(pType))
+ return pBldType->PowerBonus - pBldType->PowerDrain;
+
+ return 0;
+}
+
+inline const wchar_t* PhobosToolTip::GetBuffer() const
+{
+ return this->TextBuffer.c_str();
+}
+
+bool PhobosToolTip::HelpText(BuildType& cameo)
+{
+ if (!this->IsEnabled())
+ return false;
+
+ if (cameo.ItemType == AbstractType::Special)
+ this->HelpText(SuperWeaponTypeClass::Array->GetItem(cameo.ItemIndex));
+ else
+ this->HelpText(ObjectTypeClass::GetTechnoType(cameo.ItemType, cameo.ItemIndex));
+
+ return true;
+}
+
+void PhobosToolTip::HelpText(TechnoTypeClass* pType)
+{
+ if (!pType)
+ return;
+
+ auto const pData = TechnoTypeExt::ExtMap.Find(pType);
+
+ int nBuildTime = this->GetBuildTime(pType);
+ int nSec = nBuildTime / 15 % 60;
+ int nMin = nBuildTime / 15 / 60 /* % 60*/;
+ // int nHour = pType->RechargeTime / 15 / 60 / 60;
+
+ std::wostringstream oss;
+ oss << pType->UIName << L"\n"
+ << Phobos::UI::CostLabel << pType->GetActualCost(HouseClass::Player) << L" "
+ << Phobos::UI::TimeLabel
+ // << std::setw(2) << std::setfill(L'0') << nHour << L":"
+ << std::setw(2) << std::setfill(L'0') << nMin << L":"
+ << std::setw(2) << std::setfill(L'0') << nSec;
+
+ if (auto const nPower = this->GetPower(pType))
+ {
+ oss << L" " << Phobos::UI::PowerLabel;
+ if (nPower > 0)
+ oss << L"+";
+ oss << std::setw(1) << nPower;
+ }
+
+ if (auto pDesc = this->GetUIDescription(pData))
+ oss << L"\n" << pDesc;
+
+ this->TextBuffer = oss.str();
+}
+
+void PhobosToolTip::HelpText(SuperWeaponTypeClass* pType)
+{
+ auto const pData = SWTypeExt::ExtMap.Find(pType);
+
+ std::wostringstream oss;
+ oss << pType->UIName << L"\n";
+
+ if (int nCost = -pData->Money_Amount)
+ oss << Phobos::UI::CostLabel << nCost << L" ";
+
+ if (pType->RechargeTime)
+ {
+ int nSec = pType->RechargeTime / 15 % 60;
+ int nMin = pType->RechargeTime / 15 / 60 /* % 60*/;
+ // int nHour = pType->RechargeTime / 15 / 60 / 60;
+
+ oss << Phobos::UI::TimeLabel
+ // << std::setw(2) << std::setfill(L'0') << nHour << L":"
+ << std::setw(2) << std::setfill(L'0') << nMin << L":"
+ << std::setw(2) << std::setfill(L'0') << nSec;
+ }
+
+ if (auto pDesc = this->GetUIDescription(pData))
+ oss << L"\n" << pDesc;
+
+ this->TextBuffer = oss.str();
+}
+
+// Hooks
+
+DEFINE_HOOK(0x6A9316, SidebarClass_StripClass_HelpText, 0x6)
+{
+ GET(StripClass*, pThis, EAX);
+
+ if (PhobosToolTip::Instance.HelpText(pThis->Cameos[0])) // pStrip->Cameos[nID] in fact
+ {
+ PhobosToolTip::Instance.IsCameo = true;
+ R->EAX(L"X");
+ return 0x6A93DE;
+ }
+
+ return 0;
+}
+
+// TODO: reimplement CCToolTip::Draw2 completely
+
+DEFINE_HOOK(0x478EE1, CCToolTip_Draw2_SetBuffer, 0x6)
+{
+ if (PhobosToolTip::Instance.IsCameo)
+ R->EDI(PhobosToolTip::Instance.GetBuffer());
+ return 0;
+}
+
+DEFINE_HOOK(0x478E10, CCToolTip_Draw1, 0x0)
+{
+ GET(CCToolTip*, pThis, ECX);
+ GET_STACK(bool, bFullRedraw, 0x4);
+
+ // !onSidebar or (onSidebar && ExtToolTip::IsCameo)
+ if (!bFullRedraw || PhobosToolTip::Instance.IsCameo)
+ {
+ PhobosToolTip::Instance.IsCameo = false;
+ PhobosToolTip::Instance.SlaveDraw = false;
+
+ pThis->ToolTipManager::Process(); //this function re-create CCToolTip
+ }
+
+ if (pThis->CurrentToolTip)
+ {
+ if (!bFullRedraw)
+ PhobosToolTip::Instance.SlaveDraw = PhobosToolTip::Instance.IsCameo;
+
+ pThis->FullRedraw = bFullRedraw;
+ pThis->Draw2(pThis->CurrentToolTipData);
+ }
+ return 0x478E25;
+}
+
+DEFINE_HOOK(0x478E4A, CCToolTip_Draw2_SetSurface, 0x6)
+{
+ if (PhobosToolTip::Instance.SlaveDraw)
+ {
+ R->ESI(DSurface::Composite());
+ return 0x478ED3;
+ }
+ return 0;
+}
+
+DEFINE_HOOK(0x478EF8, CCToolTip_Draw2_SetMaxWidth, 0x5)
+{
+ if (PhobosToolTip::Instance.IsCameo)
+ {
+ if (Phobos::UI::MaxToolTipWidth > 0)
+ R->EAX(Phobos::UI::MaxToolTipWidth);
+ else
+ R->EAX(DSurface::ViewBounds->Width);
+
+ }
+ return 0;
+}
+
+DEFINE_HOOK(0x478F52, CCToolTip_Draw2_SetX, 0x8)
+{
+ if (PhobosToolTip::Instance.SlaveDraw)
+ R->EAX(R->EAX() + DSurface::Sidebar->GetWidth());
+
+ return 0;
+}
+
+DEFINE_HOOK(0x478F77, CCToolTip_Draw2_SetY, 0x6)
+{
+ if (PhobosToolTip::Instance.IsCameo)
+ {
+ LEA_STACK(RectangleStruct*, Rect, STACK_OFFS(0x3C, 0x20));
+
+ int const maxHeight = DSurface::ViewBounds->Height - 32;
+
+ if (Rect->Height > maxHeight)
+ Rect->Y += maxHeight - Rect->Height;
+
+ if (Rect->Y < 0)
+ Rect->Y = 0;
+ }
+ return 0;
+}
+
+// TODO in the future
+//
+//DEFINE_HOOK(0x478E30, CCToolTip_Draw2, 0x7)
+//{
+// GET(CCToolTip*, pThis, ECX);
+// GET_STACK(ToolTipManagerData*, pManagerData, 0x4);
+//
+// DSurface* pSurface = nullptr;
+//
+// RectangleStruct bounds = pManagerData->Dimension;
+//
+// if (GameOptionsClass::Instance->SidebarSide == 1)
+// {
+// int nR = DSurface::ViewBounds->X + DSurface::ViewBounds->Width;
+// if (bounds.X + pManagerData->Dimension.Width <= nR)
+// pSurface = DSurface::Composite;
+// else
+// {
+// if (!pThis->FullRedraw || bounds.X < nR)
+// return 0x479048;
+// pSurface = DSurface::Sidebar;
+// bounds.X -= nR;
+// *reinterpret_cast(0xB0B518) = true;
+// }
+// }
+// else
+// {
+// int nR = DSurface::SidebarBounds->X + DSurface::SidebarBounds->Width;
+// if (bounds.X < nR)
+// {
+// if (!pThis->FullRedraw || bounds.X + pManagerData->Dimension.Width >= nR)
+// return 0x479048;
+// pSurface = DSurface::Sidebar;
+// *reinterpret_cast(0xB0B518) = true;
+// }
+// else
+// {
+// pSurface = DSurface::Composite;
+// bounds.X -= nR;
+// }
+// }
+//
+// if (pSurface)
+// {
+// BitFont::Instance->GetTextDimension(
+// PhobosToolTip::Instance.GetBuffer(), bounds.Width, bounds.Height,
+// Phobos::UI::MaxToolTipWidth > 0 ? Phobos::UI::MaxToolTipWidth : DSurface::WindowBounds->Width);
+//
+// if (pManagerData->Dimension.Width + bounds.X > pSurface->GetWidth())
+// bounds.X = pSurface->GetWidth() - pManagerData->Dimension.Width;
+//
+// bounds.Width = pManagerData->Dimension.Width;
+// bounds.Height += 4;
+//
+// BitFont::Instance->field_41 = 1;
+// BitFont::Instance->SetBounds(&bounds);
+// BitFont::Instance->Color = static_cast(Drawing::RGB2DWORD(191, 98, 10));
+//
+// BitText::Instance->DrawText(BitFont::Instance, pSurface, PhobosToolTip::Instance.GetBuffer(),
+// bounds.X + 4, bounds.Y + 2, bounds.Width, bounds.Height, 0, 0, 0);
+// }
+//
+// return 0x479048;
+//}
\ No newline at end of file
diff --git a/src/Misc/PhobosToolTip.h b/src/Misc/PhobosToolTip.h
new file mode 100644
index 0000000000..2199172b53
--- /dev/null
+++ b/src/Misc/PhobosToolTip.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+
+struct StripClass;
+
+class PhobosToolTip
+{
+public:
+ static PhobosToolTip Instance;
+
+private:
+ inline bool IsEnabled() const;
+ inline const wchar_t* GetUIDescription(TechnoTypeExt::ExtData* pData) const;
+ inline const wchar_t* GetUIDescription(SWTypeExt::ExtData* pData) const;
+ inline int GetBuildTime(TechnoTypeClass* pType) const;
+ inline int GetPower(TechnoTypeClass* pType) const;
+
+public:
+ inline const wchar_t* GetBuffer() const;
+
+ bool HelpText(BuildType& cameo);
+ void HelpText(TechnoTypeClass* pType);
+ void HelpText(SuperWeaponTypeClass* pType);
+
+// Properties
+private:
+ std::wstring TextBuffer {};
+
+public:
+ bool IsCameo { false };
+ bool SlaveDraw { false };
+};
\ No newline at end of file
diff --git a/src/Phobos.cpp b/src/Phobos.cpp
index e8fd0e57dc..57e721ade3 100644
--- a/src/Phobos.cpp
+++ b/src/Phobos.cpp
@@ -25,7 +25,7 @@ const char* Phobos::AppIconPath = nullptr;
#ifdef STR_GIT_COMMIT
const wchar_t* Phobos::VersionDescription = L"Phobos nightly build (" STR_GIT_COMMIT L" @ " STR_GIT_BRANCH L"). DO NOT SHIP IN MODS!";
#elif !defined(IS_RELEASE_VER)
-const wchar_t* Phobos::VersionDescription = L"Phobos development build #" str(BUILD_NUMBER) L". Please test the build before shipping.";
+const wchar_t* Phobos::VersionDescription = L"Phobos development build #" _STR(BUILD_NUMBER) L". Please test the build before shipping.";
#else
//const wchar_t* Phobos::VersionDescription = L"Phobos release build v" FILE_VERSION_STR L".";
#endif
@@ -61,8 +61,8 @@ void Phobos::CmdLineParse(char** ppArgs, int nNumArgs)
{
Phobos::AppIconPath = ppArgs[++i];
}
-#ifndef IS_RELEASE_VER
- if (_stricmp(pArg, "-b=" str(BUILD_NUMBER)) == 0)
+#ifndef IS_RELEASE_VER
+ if (_stricmp(pArg, "-b=" _STR(BUILD_NUMBER)) == 0)
{
HideWarning = true;
}
diff --git a/src/Phobos.version.h b/src/Phobos.version.h
index 976e140992..fbbe340caf 100644
--- a/src/Phobos.version.h
+++ b/src/Phobos.version.h
@@ -1,10 +1,10 @@
#ifndef VERSION_H
#define VERSION_H
-#define wstr(x) wstr_(x)
-#define wstr_(x) L ## #x
-#define str(x) str_(x)
-#define str_(x) #x
+#define _WSTR(x) _WSTR_(x)
+#define _WSTR_(x) L ## #x
+#define _STR(x) _STR_(x)
+#define _STR_(x) #x
#pragma region Release build version numbering
@@ -30,12 +30,12 @@
#ifdef IS_RELEASE_VER // Release build metadata
#define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8) | VERSION_PATCH)
#define FILE_DESCRIPTION "Phobos, Ares-compatible YR engine extension"
- #define FILE_VERSION_STR str(VERSION_MAJOR) "." str(VERSION_MINOR) "." str(VERSION_REVISION) "." str(VERSION_PATCH)
+ #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "." _STR(VERSION_REVISION) "." _STR(VERSION_PATCH)
#define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH
#define PRODUCT_VERSION "Release Build " FILE_VERSION_STR
#elif defined(GIT_COMMIT) // Nightly devbuild metadata
- #define STR_GIT_COMMIT str(GIT_COMMIT)
- #define STR_GIT_BRANCH str(GIT_BRANCH)
+ #define STR_GIT_COMMIT _STR(GIT_COMMIT)
+ #define STR_GIT_BRANCH _STR(GIT_BRANCH)
#define SAVEGAME_ID ((BUILD_NUMBER << 24) | (BUILD_NUMBER << 12) | (BUILD_NUMBER))
#define FILE_DESCRIPTION "Unstable nightly devbuild of Phobos engine extension"
@@ -45,9 +45,9 @@
#else // Regular devbuild metadata
#define SAVEGAME_ID ((BUILD_NUMBER << 24) | (BUILD_NUMBER << 12) | (BUILD_NUMBER))
#define FILE_DESCRIPTION "Development build of Phobos engine extension"
- #define FILE_VERSION_STR "Build #" str(BUILD_NUMBER)
+ #define FILE_VERSION_STR "Build #" _STR(BUILD_NUMBER)
#define FILE_VERSION 0,0,0,BUILD_NUMBER
- #define PRODUCT_VERSION "Development Build #" str(BUILD_NUMBER)
+ #define PRODUCT_VERSION "Development Build #" _STR(BUILD_NUMBER)
#endif
#endif // VERSION_H