Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ class GlobalData : public SubsystemInterface
Bool m_saveCameraInReplay;
Bool m_useCameraInReplay;

// TheSuperHackers @feature L3-M 21/08/2025 toggle the money per minute display; 'no' shows only the original current money
Bool m_showMoneyPerMinute;

// TheSuperHackers @feature Mauller 21/06/2025 allow the system time and game time font size to be set, a size of zero disables them
Int m_systemTimeFontSize;
Int m_gameTimeFontSize;
Expand Down
12 changes: 10 additions & 2 deletions GeneralsMD/Code/GameEngine/Include/Common/Money.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ class Money : public Snapshot

public:

inline Money() : m_money(0), m_playerIndex(0)
inline Money() : m_playerIndex(0)
{
init();
}

void init()
{
m_money = 0;
setStartingCash(0);
}

inline UnsignedInt countMoney() const
Expand All @@ -82,6 +83,10 @@ class Money : public Snapshot
UnsignedInt withdraw(UnsignedInt amountToWithdraw, Bool playSound = TRUE);
void deposit(UnsignedInt amountToDeposit, Bool playSound = TRUE);

void setStartingCash(UnsignedInt amount);
void updateIncomeBucket();
UnsignedInt getCashPerMinute() const;

void setPlayerIndex(Int ndx) { m_playerIndex = ndx; }

static void parseMoneyAmount( INI *ini, void *instance, void *store, const void* userData );
Expand All @@ -105,6 +110,9 @@ class Money : public Snapshot

UnsignedInt m_money; ///< amount of money
Int m_playerIndex; ///< what is my player index?
UnsignedInt m_incomeBuckets[60];
UnsignedInt m_currentBucket;
UnsignedInt m_lastBucketFrame;
};

#endif // _MONEY_H_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class OptionPreferences : public UserPreferences
Bool getFPSLimitEnabled(void);
Bool getNoDynamicLODEnabled(void);
Bool getBuildingOcclusionEnabled(void);
Bool getShowMoneyPerMinute(void);
Int getParticleCap(void);

Int getCampaignDifficulty(void);
Expand Down
3 changes: 3 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,8 @@ GlobalData::GlobalData()
m_saveCameraInReplay = FALSE;
m_useCameraInReplay = FALSE;

m_showMoneyPerMinute = FALSE;

m_systemTimeFontSize = 8;
m_gameTimeFontSize = 8;

Expand Down Expand Up @@ -1203,6 +1205,7 @@ void GlobalData::parseGameDataDefinition( INI* ini )

TheWritableGlobalData->m_systemTimeFontSize = optionPref.getSystemTimeFontSize();
TheWritableGlobalData->m_gameTimeFontSize = optionPref.getGameTimeFontSize();
TheWritableGlobalData->m_showMoneyPerMinute = optionPref.getShowMoneyPerMinute();

Int val=optionPref.getGammaValue();
//generate a value between 0.6 and 2.0.
Expand Down
44 changes: 44 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/RTS/Money.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,16 @@

#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Money.h"
#include <numeric>
#include <algorithm>

#include "Common/AudioSettings.h"
#include "Common/GameAudio.h"
#include "Common/MiscAudio.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/Xfer.h"
#include "GameLogic/GameLogic.h"

// ------------------------------------------------------------------------------------------------
UnsignedInt Money::withdraw(UnsignedInt amountToWithdraw, Bool playSound)
Expand Down Expand Up @@ -86,6 +89,7 @@ void Money::deposit(UnsignedInt amountToDeposit, Bool playSound)
if (playSound)
{
triggerAudioEvent(TheAudio->getMiscAudio()->m_moneyDepositSound);
m_incomeBuckets[m_currentBucket] += amountToDeposit;
}

m_money += amountToDeposit;
Expand All @@ -100,6 +104,45 @@ void Money::deposit(UnsignedInt amountToDeposit, Bool playSound)
}
}

// ------------------------------------------------------------------------------------------------
void Money::setStartingCash(UnsignedInt amount)
{
m_money = amount;
m_currentBucket = 0;
m_lastBucketFrame = 0;
std::fill(m_incomeBuckets, m_incomeBuckets + ARRAY_SIZE(m_incomeBuckets), 0u);
}

// ------------------------------------------------------------------------------------------------
void Money::updateIncomeBucket()
{
UnsignedInt frame = TheGameLogic->getFrame();
UnsignedInt lastSec = m_lastBucketFrame / LOGICFRAMES_PER_SECOND;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this value not be derived from m_currentBucket ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, m_lastBucketFrame records the exact frame of the last update

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But do we care for this frame number? We only care for bucket position, no? I would expect that this logic can be simplified.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnsignedInt lastSec = curSec - ((curSec + ARRAY_SIZE(m_incomeBuckets) - m_currentBucket) % ARRAY_SIZE(m_incomeBuckets));

UnsignedInt curSec = frame / LOGICFRAMES_PER_SECOND;
UnsignedInt diff = (curSec > lastSec) ? curSec - lastSec : 0;
if (diff > 0)
{
if (diff > ARRAY_SIZE(m_incomeBuckets))
diff = ARRAY_SIZE(m_incomeBuckets);

UnsignedInt next = (m_currentBucket + 1) % ARRAY_SIZE(m_incomeBuckets);
UnsignedInt first = std::min(diff, ARRAY_SIZE(m_incomeBuckets) - next);
std::fill(m_incomeBuckets + next, m_incomeBuckets + next + first, 0u);

if (diff > first)
std::fill(m_incomeBuckets, m_incomeBuckets + (diff - first), 0u);

m_currentBucket = (m_currentBucket + diff) % ARRAY_SIZE(m_incomeBuckets);
}
m_lastBucketFrame = frame;
}

// ------------------------------------------------------------------------------------------------
UnsignedInt Money::getCashPerMinute() const
{
return std::accumulate(m_incomeBuckets, m_incomeBuckets + ARRAY_SIZE(m_incomeBuckets), 0u);
}

void Money::triggerAudioEvent(const AudioEventRTS& audioEvent)
{
Real volume = TheAudio->getAudioSettings()->m_preferredMoneyTransactionVolume;
Expand Down Expand Up @@ -157,4 +200,5 @@ void Money::parseMoneyAmount( INI *ini, void *instance, void *store, const void*
// Someday, maybe, have mulitple fields like Gold:10000 Wood:1000 Tiberian:10
Money * money = (Money *)store;
INI::parseUnsignedInt( ini, instance, &money->m_money, userData );
money->setStartingCash(money->m_money);
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ AsciiString PlayerTemplate::getStartingUnit( Int i ) const
Money *theMoney = (Money *)store;
theMoney->init();
theMoney->deposit( money );
theMoney->setStartingCash(money);

} // end parseStartMoney

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,19 @@ Int OptionPreferences::getGameTimeFontSize(void)
return fontSize;
}

Bool OptionPreferences::getShowMoneyPerMinute(void)
{
OptionPreferences::const_iterator it = find("ShowMoneyPerMinute");
if (it == end())
return TheGlobalData->m_showMoneyPerMinute;

if (stricmp(it->second.str(), "yes") == 0)
{
return TRUE;
}
return FALSE;
}

static OptionPreferences *pref = NULL;

static void setDefaults( void )
Expand Down Expand Up @@ -1354,6 +1367,15 @@ static void saveOptions( void )
TheInGameUI->refreshGameTimeResources();
}

//-------------------------------------------------------------------------------------------------
// Set Money Per Minute
{
Bool showIncome = pref->getShowMoneyPerMinute();
AsciiString prefString;
prefString = showIncome ? "yes" : "no";
(*pref)["ShowMoneyPerMinute"] = prefString;
}

//-------------------------------------------------------------------------------------------------
// Resolution
//
Expand Down
72 changes: 65 additions & 7 deletions GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,40 @@
static const Real placementOpacity = 0.45f;
static const RGBColor illegalBuildColor = { 1.0, 0.0, 0.0 };

// ------------------------------------------------------------------------------------------------
static UnicodeString formatMoneyValue(UnsignedInt amount)
{
UnicodeString result;
if (amount >= 100000)
{
result.format(L"%dk", amount / 1000);
}
else
{
result.format(L"%d", amount);
}
return result;
}

static UnicodeString formatIncomeValue(UnsignedInt cashPerMin)
{
UnicodeString result;
if (cashPerMin >= 10000)
{
result.format(L"%dk", cashPerMin / 1000);
}
else if (cashPerMin >= 1000)
{
UnsignedInt k = cashPerMin / 100;
result.format(L"%d.%dk", k / 10, k % 10);
}
else
{
result.format(L"%d", cashPerMin);
}
return result;
}

//-------------------------------------------------------------------------------------------------
/// The InGameUI singleton instance.
InGameUI *TheInGameUI = NULL;
Expand Down Expand Up @@ -1837,6 +1871,7 @@ void InGameUI::update( void )
// update the player money window if the money amount has changed
// this seems like as good a place as any to do the power hide/show
static Int lastMoney = -1;
static Int lastIncome = -1;
static NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" );
static NameKeyType powerWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:PowerWindow" );

Expand All @@ -1852,16 +1887,39 @@ void InGameUI::update( void )
Player* moneyPlayer = TheControlBar->getCurrentlyViewedPlayer();
if( moneyPlayer)
{
Int currentMoney = moneyPlayer->getMoney()->countMoney();
if( lastMoney != currentMoney )
Money *money = moneyPlayer->getMoney();
Bool showIncome = TheGlobalData->m_showMoneyPerMinute;
if (!showIncome)
{
UnicodeString buffer;
Int currentMoney = money->countMoney();
if( lastMoney != currentMoney )
{
UnicodeString buffer;

buffer.format( TheGameText->fetch( "GUI:ControlBarMoneyDisplay" ), currentMoney );
GadgetStaticTextSetText( moneyWin, buffer );
lastMoney = currentMoney;
buffer.format(TheGameText->fetch( "GUI:ControlBarMoneyDisplay" ), currentMoney );
GadgetStaticTextSetText( moneyWin, buffer );
lastMoney = currentMoney;

} // end if
} // end if
}
else
{
// TheSuperHackers @feature L3-M 21/08/2025 player money per minute
money->updateIncomeBucket();
UnsignedInt currentMoney = money->countMoney();
UnsignedInt cashPerMin = money->getCashPerMinute();
if (lastMoney != (Int)currentMoney || lastIncome != (Int)cashPerMin)
{
UnicodeString buffer;
UnicodeString moneyStr = formatMoneyValue(currentMoney);
UnicodeString incomeStr = formatIncomeValue(cashPerMin);

buffer.format(TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:ControlBarMoneyDisplayIncome", L"%ls (%ls)", moneyStr.str(), incomeStr.str()));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the Money symbol?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The limitation here would be the ControlBarPro money texture. It has much less space compared to the original game's ControlBar, hence I removed it

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show image for it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.
1920x1080. Money symbol and ControlBarPro:
1920x1080ControlBarPro2-2

1920x1080. No money symbol and ControlBarPro:
1920x1080ControlBarPro2

1920x1080. Money symbol with Mauller font scaling and ControlBarPro:
1920x1080ControlBarPro1_1

1920x1080. Money symbol and original ControlBar:
1920x1080ControlBar

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You tested Control Bar Pro correctly in 16:9.

The Original Control Bar always needs to be tested in 4:3. That is because its elements are stretched wide in 16:9 and beyond.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oof it seems to be broken on ControlBar too in 4:3, we can drop the brackets or not use the money symbol.

Image

Image

GadgetStaticTextSetText(moneyWin, buffer);
lastMoney = currentMoney;
lastIncome = cashPerMin;
}
}
moneyWin->winHide(FALSE);
powerWin->winHide(FALSE);
}
Expand Down
Loading