mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-23 06:57:23 +00:00
737 lines
17 KiB
C++
737 lines
17 KiB
C++
#include "game/ht.h"
|
|
#include "game/chatter.h"
|
|
#include "mpshared/netmessage.h"
|
|
|
|
namespace wi {
|
|
|
|
extern bool gfSuspendUpdates;
|
|
extern bool gfSingleStep;
|
|
|
|
//
|
|
// InputUIForm implementation
|
|
//
|
|
|
|
InputUIForm::InputUIForm(Chatter *chatter)
|
|
{
|
|
m_fTimerAdded = false;
|
|
m_pchatter = chatter;
|
|
}
|
|
|
|
InputUIForm::~InputUIForm()
|
|
{
|
|
if (m_fTimerAdded)
|
|
gtimm.RemoveTimer(this);
|
|
}
|
|
|
|
bool InputUIForm::Init(FormMgr *pfrmm, IniReader *pini, word idf)
|
|
{
|
|
if (!Form::Init(pfrmm, pini, idf))
|
|
return false;
|
|
|
|
// Set the form rect
|
|
|
|
DibBitmap *pbm = m_pfrmm->GetDib();
|
|
Size sizDib;
|
|
pbm->GetSize(&sizDib);
|
|
int cyCommandBar = sizDib.cy;
|
|
ModeInfo mode;
|
|
gpdisp->GetMode(&mode);
|
|
int cyInputForm = cyCommandBar + mode.cyGraffiti;
|
|
Rect rcT;
|
|
rcT.Set(0, 0, sizDib.cx, cyInputForm);
|
|
SetRect(&rcT);
|
|
|
|
// Get pointers to and dimensions of all the controls involved in the layout
|
|
|
|
Control *pctlMenuButton = GetControlPtr(kidcMenuButton);
|
|
Assert(pctlMenuButton != NULL);
|
|
Rect rcMenuButton;
|
|
pctlMenuButton->GetRect(&rcMenuButton);
|
|
|
|
Control *pctlCredits = GetControlPtr(kidcCreditsLabel);
|
|
Assert(pctlCredits != NULL);
|
|
Rect rcCredits;
|
|
pctlCredits->GetRect(&rcCredits);
|
|
|
|
Control *pctlPower = GetControlPtr(kidcPower);
|
|
Assert(pctlPower != NULL);
|
|
Rect rcPower;
|
|
pctlPower->GetRect(&rcPower);
|
|
|
|
// Calc space between controls
|
|
|
|
int xLeftMiniMap = m_rc.right - MiniMapControl::CalcWidth();
|
|
int cxSpace = (xLeftMiniMap - (rcMenuButton.Width() + rcCredits.Width() + rcPower.Width())) / 3;
|
|
if (cxSpace < 0)
|
|
cxSpace = 0;
|
|
|
|
// Position controls (ycoord of 1 to not overlap the separating line, except for menu button)
|
|
|
|
pctlMenuButton->SetPosition(0, 0);
|
|
pctlCredits->SetPosition(rcMenuButton.right + cxSpace, 1);
|
|
pctlPower->SetPosition(rcMenuButton.right + cxSpace + rcCredits.Width() + cxSpace, 1);
|
|
|
|
// Handle silkscreen area
|
|
|
|
static int s_aidcLayout[] = {
|
|
kidcGraffitiScroll, kidcAppsSilkButton, kidcMenuSilkButton,
|
|
kidcCalcSilkButton, kidcFindSilkButton
|
|
};
|
|
static int s_aircGraffiti[] = {
|
|
kircSilkGraffiti, kircSilkApps, kircSilkMenu,
|
|
kircSilkCalc, kircSilkFind
|
|
};
|
|
|
|
if (mode.cyGraffiti == 0) {
|
|
// No silkscreen graffiti area, so hide the controls
|
|
|
|
for (int n = 0; n < ARRAYSIZE(s_aidcLayout); n++)
|
|
GetControlPtr(s_aidcLayout[n])->Show(false);
|
|
} else {
|
|
// Note the returned graffiti rects are in native coordinates
|
|
// from the top of the screen, whereas the input form starts on-screen,
|
|
// so the rects need to be adjusted
|
|
|
|
int yOffset = mode.cy - cyCommandBar;
|
|
for (int n = 0; n < ARRAYSIZE(s_aidcLayout); n++) {
|
|
Rect rcT;
|
|
HostGetSilkRect(s_aircGraffiti[n], &rcT);
|
|
rcT.Offset(0, -yOffset);
|
|
GetControlPtr(s_aidcLayout[n])->SetRect(&rcT);
|
|
}
|
|
}
|
|
|
|
gtimm.AddTimer(this, kctMapScrollRate);
|
|
m_fTimerAdded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void InputUIForm::Update()
|
|
{
|
|
// Update the player credits display
|
|
|
|
CreditsControl *pctlCredits = (CreditsControl *)GetControlPtr(kidcCreditsLabel);
|
|
pctlCredits->Update(gpplrLocal->GetCredits(), gpplrLocal->GetNeedCreditsCount());
|
|
|
|
// Update the power indicator
|
|
|
|
PowerControl *pctlPower = (PowerControl *)GetControlPtr(kidcPower);
|
|
pctlPower->Update(gpplrLocal->GetPowerDemand(), gpplrLocal->GetPowerSupply());
|
|
}
|
|
|
|
void InputUIForm::OnTimer(long tCurrent)
|
|
{
|
|
if (gpmfrmm->GetFocus() == this) {
|
|
dword dwKeys = HostGetCurrentKeyState(keyBitDpadLeft | keyBitDpadRight | keyBitPageUp | keyBitPageDown | keyBitHard1 | keyBitHard2 | keyBitHard3 | keyBitHard4 | keyBitRockerUp | keyBitRockerDown | keyBitRockerLeft | keyBitRockerRight);
|
|
if (dwKeys != 0) {
|
|
WCoord wxView, wyView;
|
|
gsim.GetViewPos(&wxView, &wyView);
|
|
WCoord wcStep = kwcTile;
|
|
|
|
if (dwKeys & (keyBitPageUp | keyBitRockerUp))
|
|
wyView -= wcStep;
|
|
if (dwKeys & (keyBitPageDown | keyBitRockerDown))
|
|
wyView += wcStep;
|
|
if (dwKeys & (keyBitDpadLeft | keyBitRockerLeft | keyBitHard1 | keyBitHard2))
|
|
wxView -= wcStep;
|
|
if (dwKeys & (keyBitDpadRight | keyBitRockerRight | keyBitHard3 | keyBitHard4))
|
|
wxView += wcStep;
|
|
|
|
gsim.SetViewPos(wxView, wyView);
|
|
}
|
|
}
|
|
|
|
|
|
ggame.GetSimUIForm()->GetPenHandler()->CheckScroll();
|
|
|
|
}
|
|
|
|
void InputUIForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd)
|
|
{
|
|
// Fill background
|
|
|
|
Rect rcT;
|
|
gpmm->GetRect(&rcT);
|
|
int xLeftMiniMap = m_rc.right - rcT.Width();
|
|
rcT.Set(0, 0, xLeftMiniMap, m_rc.bottom);
|
|
FillHelper(pbm, pupd, &rcT, GetColor(kiclrBlack));
|
|
|
|
// Draw separating line
|
|
|
|
rcT.bottom = rcT.top + 1;
|
|
|
|
int iclr = gfMultiplayer ? gaiclrSide[gpplrLocal->GetSide()] : kiclr0CyanSide;
|
|
FillHelper(pbm, pupd, &rcT, GetColor(iclr));
|
|
}
|
|
|
|
bool InputUIForm::EventProc(Event *pevt)
|
|
{
|
|
if (pevt->eType == keyDownEvent) {
|
|
switch (pevt->chr) {
|
|
case vchrMenu:
|
|
OnControlSelected(kidcMenuButton);
|
|
break;
|
|
|
|
#ifdef DEBUG
|
|
case ' ':
|
|
if (gfSuspendUpdates)
|
|
gfSingleStep = true;
|
|
break;
|
|
|
|
case 'z':
|
|
{
|
|
static int s_cMark;
|
|
Trace("");
|
|
Trace("+++ MARK %d +++", s_cMark++);
|
|
Trace("");
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
{
|
|
// Restart mission
|
|
|
|
Event evt;
|
|
memset(&evt, 0, sizeof(evt));
|
|
evt.eType = gameOverEvent;
|
|
evt.dw = knGoRetryLevel;
|
|
gevm.PostEvent(&evt);
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
} else {
|
|
return Form::EventProc(pevt);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void InputUIForm::OnControlSelected(word idc)
|
|
{
|
|
switch (idc) {
|
|
case kidcMenuButton:
|
|
InGameMenu();
|
|
break;
|
|
|
|
#if 0
|
|
case kidcMapButton:
|
|
// To be replaced with control at some point
|
|
ggame.GetSimUIForm()->ToggleMiniMap();
|
|
break;
|
|
#endif
|
|
|
|
case kidcAppsSilkButton:
|
|
{
|
|
Event evt;
|
|
evt.eType = appStopEvent;
|
|
gevm.PostEvent(&evt);
|
|
}
|
|
break;
|
|
|
|
case kidcMenuSilkButton:
|
|
OnControlSelected(kidcMenuButton);
|
|
break;
|
|
|
|
#if 0
|
|
case kidcCalcSilkButton:
|
|
OnControlSelected(kidcMapButton);
|
|
break;
|
|
#endif
|
|
|
|
#if defined(DEV_BUILD)
|
|
case kidcFindSilkButton:
|
|
case kidcPower:
|
|
TestOptions();
|
|
break;
|
|
#endif
|
|
|
|
case kidcCreditsLabel:
|
|
if (!gfMultiplayer)
|
|
gsim.Pause(true);
|
|
gpplrLocal->ShowObjectives(ksoObjectives);
|
|
if (!gfMultiplayer)
|
|
gsim.Pause(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool InputUIForm::OnControlHeld(word idc)
|
|
{
|
|
return true;
|
|
|
|
#if 0
|
|
// Check for preset buttons as a group
|
|
|
|
int iPreset = idc - kidcPreset1Button;
|
|
if (iPreset < 0 || iPreset > 7)
|
|
return false;
|
|
|
|
if (m_aagidPreset[iPreset] != NULL) {
|
|
delete m_aagidPreset[iPreset];
|
|
m_aagidPreset[iPreset] = NULL;
|
|
}
|
|
|
|
m_awptViewPreset[iPreset].wx = kwxInvalid;
|
|
|
|
// Collect all selected Gobs
|
|
|
|
int cgob = 0;
|
|
Gid *pgid = (Gid *)gpbScratch;
|
|
for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) {
|
|
if (pgobT->GetFlags() & kfGobSelected) {
|
|
*pgid++ = pgobT->GetId();
|
|
cgob++;
|
|
}
|
|
}
|
|
|
|
if (cgob == 0) {
|
|
gsim.GetViewPos(&m_awptViewPreset[iPreset].wx, &m_awptViewPreset[iPreset].wy);
|
|
|
|
// UNDONE: Play 'preset set' sound
|
|
Control *pctl = GetControlPtr(kidcPreset1Button + iPreset);
|
|
pctl->SetFlags(pctl->GetFlags() | kfCtlSet);
|
|
} else {
|
|
Gid *agid = new Gid[cgob];
|
|
if (agid != NULL) {
|
|
memcpy(agid, gpbScratch, cgob * sizeof(Gid));
|
|
m_aagidPreset[iPreset] = agid;
|
|
m_acgidPreset[iPreset] = cgob;
|
|
|
|
// UNDONE: Play 'preset set' sound
|
|
Control *pctl = GetControlPtr(kidcPreset1Button + iPreset);
|
|
pctl->SetFlags(pctl->GetFlags() | kfCtlSet);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void InputUIForm::InGameMenu()
|
|
{
|
|
// Invoke modal InGameMenu form
|
|
|
|
ShellForm *pfrm = (ShellForm *)gpmfrmm->LoadForm(gpiniForms, kidfInGameMenu, new ShellForm());
|
|
|
|
#if defined(IPHONE) || defined(__IPHONEOS__) || defined(__ANDROID__)
|
|
pfrm->GetControlPtr(kidcExitGame)->Show(false);
|
|
#endif
|
|
|
|
if (gfMultiplayer) {
|
|
pfrm->GetControlPtr(kidcSaveGame)->Show(false);
|
|
pfrm->GetControlPtr(kidcLoadGame)->Show(false);
|
|
pfrm->GetControlPtr(kidcRestartMission)->Show(false);
|
|
pfrm->GetControlPtr(kidcHelp)->Show(false);
|
|
|
|
// Hack
|
|
|
|
ButtonControl *pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcAbortMission);
|
|
pbtn->SetText((gpplrLocal->GetFlags() & kfPlrObserver) ? (char *)"STOP OBSERVING" : (char *)"RESIGN");
|
|
|
|
word aidcFormat[] = { kidcObjectives, kidcSaveGame, kidcLoadGame, kidcRestartMission, kidcAbortMission, kidcOptions, kidcChat, kidcExitGame, kidcHelp };
|
|
FormatButtons(pfrm, aidcFormat, ARRAYSIZE(aidcFormat), kidcObjectives, kidcOptions);
|
|
|
|
// Hiding this here ensures kidcChat is spaced below the rest of the
|
|
// buttons.
|
|
pfrm->GetControlPtr(kidcOptions)->Show(false);
|
|
} else {
|
|
pfrm->GetControlPtr(kidcChat)->Show(false);
|
|
}
|
|
|
|
if (!gfMultiplayer)
|
|
gsim.Pause(true);
|
|
int idc;
|
|
pfrm->DoModal(&idc);
|
|
delete pfrm;
|
|
|
|
// While the dialog was up the player might have exited the app
|
|
|
|
if (!gevm.IsAppStopping()) {
|
|
// UNDONE: display mode change
|
|
|
|
switch (idc) {
|
|
case kidcSaveGame:
|
|
ggame.SaveGame();
|
|
break;
|
|
|
|
case kidcLoadGame:
|
|
{
|
|
Stream *pstm = PickLoadGameStream();
|
|
if (pstm == NULL)
|
|
break;
|
|
|
|
Event evt;
|
|
memset(&evt, 0, sizeof(evt));
|
|
evt.eType = gameOverEvent;
|
|
evt.dw = knGoLoadSavedGame;
|
|
gpstmSavedGame = pstm;
|
|
gevm.PostEvent(&evt);
|
|
}
|
|
break;
|
|
|
|
case kidcAbortMission:
|
|
case kidcRestartMission:
|
|
{
|
|
if (gfMultiplayer) {
|
|
if (!ggame.AskResignGame())
|
|
break;
|
|
}
|
|
|
|
if ((gpplrLocal->GetFlags() & kfPlrObserver) == 0) {
|
|
// HACK: only ksoWinSummary has the fields necessary to
|
|
// support the fAborting flag
|
|
gpplrLocal->ShowObjectives(
|
|
gfMultiplayer ? ksoLoseSummary : ksoWinSummary,
|
|
false, gfMultiplayer ? false : true);
|
|
}
|
|
|
|
if (!gfMultiplayer
|
|
|| (gpplrLocal->GetFlags() & kfPlrObserver) != 0
|
|
|| !ggame.AskObserveGame()) {
|
|
Event evt;
|
|
memset(&evt, 0, sizeof(evt));
|
|
evt.eType = gameOverEvent;
|
|
evt.dw = (idc == kidcAbortMission)
|
|
? knGoAbortLevel : knGoRetryLevel;
|
|
gevm.PostEvent(&evt);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kidcOptions:
|
|
DoModalGameOptionsForm(gsim.GetLevel()->GetPalette(), true);
|
|
break;
|
|
|
|
case kidcHelp:
|
|
Help();
|
|
break;
|
|
|
|
case kidcExitGame:
|
|
{
|
|
if (gfMultiplayer) {
|
|
if (!ggame.AskResignGame())
|
|
break;
|
|
}
|
|
|
|
Event evt;
|
|
memset(&evt, 0, sizeof(evt));
|
|
evt.eType = gameOverEvent;
|
|
evt.dw = knGoAppStop;
|
|
gevm.PostEvent(&evt);
|
|
|
|
ggame.SaveReinitializeGame();
|
|
}
|
|
break;
|
|
|
|
case kidcObjectives:
|
|
gpplrLocal->ShowObjectives(ksoObjectives);
|
|
break;
|
|
|
|
case kidcChat:
|
|
if (m_pchatter != NULL) {
|
|
m_pchatter->ShowChat();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!gfMultiplayer)
|
|
gsim.Pause(false);
|
|
}
|
|
|
|
void InputUIForm::TestOptions()
|
|
{
|
|
// Invoke modal test options form
|
|
|
|
Form *pfrm = gpmfrmm->LoadForm(gpiniForms, kidfTestOptions, new TestOptionsForm());
|
|
if (!gfMultiplayer)
|
|
gsim.Pause(true);
|
|
pfrm->DoModal();
|
|
if (!gfMultiplayer)
|
|
gsim.Pause(false);
|
|
delete pfrm;
|
|
}
|
|
|
|
//
|
|
// Graffiti scroll control
|
|
//
|
|
|
|
GraffitiScrollControl::GraffitiScrollControl()
|
|
{
|
|
m_nScale = 0;
|
|
m_xDragStart = 0;
|
|
m_yDragStart = 0;
|
|
m_wxViewStart = 0;
|
|
m_wyViewStart = 0;
|
|
m_fFrame = false;
|
|
}
|
|
|
|
bool GraffitiScrollControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
|
|
{
|
|
// Base initialization
|
|
|
|
if (!Control::Init(pfrm, pini, pfind))
|
|
return false;
|
|
|
|
// idc (x y cx cy) nScale
|
|
|
|
char sz[32];
|
|
int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %d %s", &m_nScale, sz);
|
|
if (cArgs == 1) {
|
|
m_fFrame = false;
|
|
return true;
|
|
}
|
|
if (cArgs == 2 && strcmp(sz, "frame") == 0) {
|
|
m_fFrame = true;
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void GraffitiScrollControl::OnPenEvent(Event *pevt)
|
|
{
|
|
switch (pevt->eType) {
|
|
case penDownEvent:
|
|
m_xDragStart = pevt->x;
|
|
m_yDragStart = pevt->y;
|
|
gsim.GetViewPos(&m_wxViewStart, &m_wyViewStart);
|
|
break;
|
|
|
|
case penMoveEvent:
|
|
#if 0
|
|
// BUGBUG: overflowed WcFromPc on Mission 3 ((pevt->y - m_yDragStart) * m_nScale == -1548)
|
|
gsim.SetViewPos(m_wxViewStart - WcFromPc((pevt->x - m_xDragStart) * m_nScale),
|
|
m_wyViewStart - WcFromPc((pevt->y - m_yDragStart) * m_nScale));
|
|
#else
|
|
{
|
|
int pcMac = PcFromWc(kwcMax - 1);
|
|
int dxT = (pevt->x - m_xDragStart) * m_nScale;
|
|
if (dxT < -pcMac)
|
|
dxT = -pcMac;
|
|
if (dxT > pcMac)
|
|
dxT = pcMac;
|
|
int dyT = (pevt->y - m_yDragStart) * m_nScale;
|
|
if (dyT < -pcMac)
|
|
dyT = -pcMac;
|
|
if (dyT > pcMac)
|
|
dyT = pcMac;
|
|
gsim.SetViewPos(m_wxViewStart + WcFromPc(dxT), m_wyViewStart + WcFromPc(dyT));
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GraffitiScrollControl::OnPaint(DibBitmap *pbm)
|
|
{
|
|
if (!m_fFrame)
|
|
return;
|
|
|
|
Rect rcForm;
|
|
m_pfrm->GetRect(&rcForm);
|
|
Rect rcT = m_rc;
|
|
rcT.Offset(rcForm.left, rcForm.top);
|
|
|
|
int iclr = GetColor(kiclrWhite);
|
|
pbm->Fill(rcT.left + 1, rcT.top, rcT.Width() - 2, 1, iclr);
|
|
pbm->Fill(rcT.left, rcT.top + 1, 1, rcT.Height() - 2, iclr);
|
|
pbm->Fill(rcT.right - 1, rcT.top + 1, 1, rcT.Height() - 2, iclr);
|
|
pbm->Fill(rcT.left + 1, rcT.bottom - 1, rcT.Width() - 2, 1, iclr);
|
|
}
|
|
|
|
bool GraffitiScrollControl::IsPainting()
|
|
{
|
|
return m_fFrame;
|
|
}
|
|
|
|
//
|
|
// Credits control
|
|
//
|
|
|
|
bool CreditsControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
|
|
{
|
|
// Base initialization
|
|
|
|
if (!Control::Init(pfrm, pini, pfind))
|
|
return false;
|
|
|
|
m_nCredits = 0;
|
|
Font *pfnt = gapfnt[kifntHud];
|
|
m_rc.right = m_rc.left + pfnt->GetTextExtent("ABC"); // the credits background image
|
|
m_rc.bottom = m_rc.top + pfnt->GetHeight();
|
|
m_fDrawCreditSymbol = true;
|
|
return true;
|
|
}
|
|
|
|
void CreditsControl::OnPaint(DibBitmap *pbm)
|
|
{
|
|
// Draw background
|
|
|
|
int cx = m_rc.Width();
|
|
Font *pfnt = gapfnt[kifntHud];
|
|
if (m_fDrawCreditSymbol)
|
|
pfnt->DrawText(pbm, "ABC", m_rc.left, m_rc.top, cx, -1);
|
|
else
|
|
pfnt->DrawText(pbm, "JBC", m_rc.left, m_rc.top, cx, -1);
|
|
|
|
// Draw credits
|
|
|
|
char szT[20];
|
|
sprintf(szT, "%ld", m_nCredits > 99999 ? 99999 : m_nCredits);
|
|
pfnt->DrawText(pbm, szT, m_rc.left + pfnt->GetTextExtent("AB") - pfnt->GetTextExtent(szT), m_rc.top, cx, -1);
|
|
}
|
|
|
|
void CreditsControl::Update(long nCredits, word cCreditNeeders)
|
|
{
|
|
if ((nCredits != m_nCredits) || (cCreditNeeders != m_cCreditNeeders)) {
|
|
m_nCredits = nCredits;
|
|
m_cCreditNeeders = cCreditNeeders;
|
|
|
|
if (cCreditNeeders == 0) {
|
|
m_fDrawCreditSymbol = true;
|
|
}
|
|
Invalidate();
|
|
}
|
|
|
|
if (cCreditNeeders > 0) {
|
|
long cUpdates = gsim.GetUpdateCount();
|
|
long cupdFlash = cUpdates % kcupdSymbolFlashRate;
|
|
|
|
// Low Credits will flash OFF for odd intervals
|
|
|
|
if (cupdFlash == 0) {
|
|
if (gwfPerfOptions & kfPerfSymbolFlashing) {
|
|
m_fDrawCreditSymbol = !((short)(cUpdates / kcupdSymbolFlashRate) & 1);
|
|
Invalidate();
|
|
} else {
|
|
if (!m_fDrawCreditSymbol)
|
|
Invalidate();
|
|
m_fDrawCreditSymbol = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Power control
|
|
//
|
|
|
|
bool PowerControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
|
|
{
|
|
// Base initialization
|
|
|
|
if (!Control::Init(pfrm, pini, pfind))
|
|
return false;
|
|
|
|
m_fShowPowerSymbol = true;
|
|
m_nDemand = m_nSupply = 0;
|
|
Font *pfnt = gapfnt[kifntHud];
|
|
m_rc.right = m_rc.left + pfnt->GetTextExtent("DEF"); // the power background image
|
|
m_rc.bottom = m_rc.top + pfnt->GetHeight();
|
|
|
|
// Figure out the maximum amount of power required by an individual structure type
|
|
// so we can draw the 'yellow zone' at OnPaint time.
|
|
|
|
m_nDemandMax = 0;
|
|
UnitMask um = 1;
|
|
for (int i = 0; i < 32; i++, um <<= 1) {
|
|
if (um & kumStructures) {
|
|
StructConsts *pstruc = (StructConsts *)gapuntc[i];
|
|
if (pstruc->nPowerDemand > m_nDemandMax)
|
|
m_nDemandMax = pstruc->nPowerDemand;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// meter is crafted to divide evenly by 10
|
|
|
|
const int kcPowerMeterPips = 10;
|
|
|
|
void PowerControl::OnPaint(DibBitmap *pbm)
|
|
{
|
|
// Draw background
|
|
|
|
int cx = m_rc.Width();
|
|
Font *pfnt = gapfnt[kifntHud];
|
|
pfnt->DrawText(pbm, "DEF", m_rc.left, m_rc.top, cx, -1);
|
|
|
|
// Draw power symbol
|
|
|
|
if (m_fShowPowerSymbol)
|
|
pfnt->DrawText(pbm, "G", m_rc.left, m_rc.top, cx, -1);
|
|
|
|
// Draw power meter
|
|
|
|
int cxLeftSide = pfnt->GetTextExtent("D");
|
|
int x = m_rc.left + cxLeftSide;
|
|
int cxMeter = pfnt->GetTextExtent("E");
|
|
int cxPip = pfnt->GetTextExtent("I");
|
|
|
|
Color clr;
|
|
if (m_nSupply < m_nDemand) {
|
|
clr = GetColor(kiclrRed);
|
|
} else {
|
|
// Dip into the yellow if building a structure might cause demand
|
|
// to become less than supply.
|
|
|
|
if (m_nSupply - m_nDemand <= m_nDemandMax)
|
|
clr = GetColor(kiclrYellow);
|
|
else
|
|
clr = GetColor(kiclrGreen);
|
|
}
|
|
|
|
// UNDONE: handle > 400 units of power
|
|
int nSupplyPerPip = ((StructConsts *)gapuntc[kutReactor])->nPowerSupply;
|
|
int cSupplyPips = m_nSupply / nSupplyPerPip;
|
|
if (cSupplyPips > kcPowerMeterPips)
|
|
cSupplyPips = kcPowerMeterPips;
|
|
|
|
byte bclr = clr & 0xff;
|
|
dword dwClr = MAKEDWORD(bclr);
|
|
|
|
for (int i = 0; i < cSupplyPips; i++, x += cxPip)
|
|
pfnt->DrawText(pbm, "I", x, m_rc.top, 1, &dwClr);
|
|
|
|
// Draw demand indicator
|
|
|
|
int cxDemand = pfnt->GetTextExtent("H");
|
|
int xDemand = (m_nDemand * cxMeter) / (nSupplyPerPip * kcPowerMeterPips);
|
|
pfnt->DrawText(pbm, "H", m_rc.left + cxLeftSide + xDemand - (cxDemand / 2), m_rc.top, cxDemand, -1);
|
|
}
|
|
|
|
void PowerControl::Update(int nDemand, int nSupply)
|
|
{
|
|
if (nDemand != m_nDemand || nSupply != m_nSupply) {
|
|
m_nDemand = nDemand;
|
|
m_nSupply = nSupply;
|
|
m_fPowerLow = (m_nDemand > m_nSupply);
|
|
if (!m_fPowerLow)
|
|
m_fShowPowerSymbol = true;
|
|
Invalidate();
|
|
}
|
|
|
|
if (m_fPowerLow) {
|
|
long cUpdates = gsim.GetUpdateCount();
|
|
long cupdFlash = cUpdates % kcupdSymbolFlashRate;
|
|
|
|
// Low Power will flash ON for odd intervals
|
|
|
|
if (cupdFlash == 0) {
|
|
if (gwfPerfOptions & kfPerfSymbolFlashing) {
|
|
m_fShowPowerSymbol = ((short)(cUpdates / kcupdSymbolFlashRate) & 1);
|
|
Invalidate();
|
|
} else {
|
|
if (!m_fShowPowerSymbol)
|
|
Invalidate();
|
|
m_fShowPowerSymbol = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace wi
|