#include "ht.h" #include "wistrings.h" namespace wi { // GCCBUG: We must not have this inline because when Gcc inlines the call to // the base classes' constructor (Timer::Timer) it gets the reference wrong. TimeoutTimer::TimeoutTimer() { m_ptimo = NULL; } // // CommandQueue implementation // CommandQueue::CommandQueue() { m_amsg = NULL; m_cmsg = 0; } CommandQueue::~CommandQueue() { Assert(m_amsg == NULL); } bool CommandQueue::Init(int cmsgMax) { m_cmsg = 0; m_cmsgMax = cmsgMax; Assert(m_amsg == NULL); m_amsg = (Message *)gmmgr.AllocPtr(sizeof(Message) * m_cmsgMax); return m_amsg != NULL; } void CommandQueue::Exit() { if (m_amsg != NULL) { gmmgr.FreePtr(m_amsg); m_amsg = NULL; } } #define knVerCommandQueueState 1 bool CommandQueue::LoadState(Stream *pstm) { // Version check byte nVer = pstm->ReadByte(); if (nVer != knVerCommandQueueState) return false; // Read msg count int cmsgs = pstm->ReadWord(); // Read in messages while (cmsgs-- != 0) { Message msg; if (pstm->Read(&msg, sizeof(msg)) == 0) return false; Enqueue(&msg); } return pstm->IsSuccess(); } bool CommandQueue::SaveState(Stream *pstm) { // Save version pstm->WriteByte(knVerCommandQueueState); // Save count of messages pstm->WriteWord(m_cmsg); // Save messages for (int nmsg = 0; nmsg < m_cmsg; nmsg++) pstm->Write(&m_amsg[nmsg], sizeof(m_amsg[0])); return pstm->IsSuccess(); } void CommandQueue::Enqueue(MessageId mid, StateMachineId smidReceiver) { Message msg; memset(&msg, 0, sizeof(msg)); msg.mid = mid; msg.smidReceiver = smidReceiver; SetEntry(&msg); } void CommandQueue::Enqueue(Message *pmsg) { pmsg->tDelivery = 0; SetEntry(pmsg); } void CommandQueue::SetEntry(Message *pmsg) { if (m_cmsg < m_cmsgMax) { gmmgr.WritePtr(m_amsg, m_cmsg * sizeof(Message), pmsg, sizeof(Message)); m_cmsg++; } } bool IsTileFree(TCoord tx, TCoord ty, byte bf, Gob **ppgob) { // Gob to return if there is one if (ppgob != NULL) *ppgob = NULL; // TCoord needs to be on the map Size sizMap; gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&sizMap); if (tx < 0 || tx >= sizMap.cx || ty < 0 || ty >= sizMap.cy) return false; // Needs to be free of gobs if (bf & (kbfMobileUnit | kbfStructure)) { for (Gid gid = ggobm.GetFirstGid(tx, ty); gid != kgidNull; gid = ggobm.GetNextGid(gid)) { Gob *pgob = ggobm.GetGob(gid); if (pgob != NULL) { if (ppgob != NULL) *ppgob = pgob; return false; } } } // No gobs can be transitioning into this tile if (bf & kbfMobileUnit) { Gob *pgob = MobileUnitGob::AnyTransitionsIntoTile(tx, ty, NULL); if (pgob != NULL) { if (ppgob != NULL) *ppgob = pgob; return false; } } // Tile can't be occupied if (gsim.GetLevel()->GetTerrainMap()->IsBlocked(tx, ty, bf)) return false; // Looks free! return true; } void BringInBounds(WCoord *pwx, WCoord *pwy) { Size sizMap; gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&sizMap); WCoord cwxMap = WcFromTc(sizMap.cx); WCoord cwyMap = WcFromTc(sizMap.cy); if (*pwx < 0) *pwx = 0; else if (*pwx >= cwxMap) *pwx = cwxMap - 1; if (*pwy < 0) *pwy = 0; else if (*pwy >= cwyMap) *pwy = cwyMap - 1; } // // Rect implementation // bool Rect::Intersect(Rect *prcSrc1, Rect *prcSrc2) { left = _max(prcSrc1->left, prcSrc2->left); right = _min(prcSrc1->right, prcSrc2->right); // Check for empty rect if (left < right) { top = _max(prcSrc1->top, prcSrc2->top); bottom = _min(prcSrc1->bottom, prcSrc2->bottom); // Check for empty rect if (top < bottom) return true; } SetEmpty(); return false; } void Rect::Set(Point pt1, Point pt2) { if (pt1.x < pt2.x) { left = pt1.x; right = pt2.x; } else { left = pt2.x; right = pt1.x; } if (pt1.y < pt2.y) { top = pt1.y; bottom = pt2.y; } else { top = pt2.y; bottom = pt1.y; } } bool Rect::Subtract(Rect *prcSrc1, Rect *prcSrc2) { // prcSrc1 - prcSrc2 int xLeft = prcSrc1->left; int yTop = prcSrc1->top; int xRight = prcSrc1->right; int yBottom = prcSrc1->bottom; // Vert edges if (prcSrc2->top <= prcSrc1->top && prcSrc2->bottom >= prcSrc1->bottom) { // left edge if (prcSrc2->left <= prcSrc1->left && prcSrc2->right > prcSrc1->left) xLeft = prcSrc2->right; // right edge if (prcSrc2->left < prcSrc1->right && prcSrc2->right >= prcSrc1->right) xRight = prcSrc2->left; } // Horz edges if (prcSrc2->left <= prcSrc1->left && prcSrc2->right >= prcSrc1->right) { // top edge if (prcSrc2->top <= prcSrc1->top && prcSrc2->bottom > prcSrc1->top) yTop = prcSrc2->bottom; // bottom edge if (prcSrc2->top < prcSrc1->bottom && prcSrc2->bottom >= prcSrc1->bottom) yBottom = prcSrc2->top; } Set(xLeft, yTop, xRight, yBottom); return !IsEmpty(); } void Rect::Add(Rect *prcSrc1, Rect *prcSrc2) { if (prcSrc1->IsEmpty()) { *this = *prcSrc2; return; } if (prcSrc2->IsEmpty()) { *this = *prcSrc1; return; } // prcSrc1 + prcSrc2 (not Union) int xLeft = prcSrc1->left; int yTop = prcSrc1->top; int xRight = prcSrc1->right; int yBottom = prcSrc1->bottom; // Vert edges if (prcSrc2->top <= prcSrc1->top && prcSrc2->bottom >= prcSrc1->bottom) { // left edge if (prcSrc2->left < prcSrc1->left && prcSrc2->right >= prcSrc1->left) xLeft = prcSrc2->left; // right edge if (prcSrc2->left <= prcSrc1->right && prcSrc2->right > prcSrc1->right) xRight = prcSrc2->right; } // Horz edges if (prcSrc2->left <= prcSrc1->left && prcSrc2->right >= prcSrc1->right) { // top edge if (prcSrc2->top < prcSrc1->top && prcSrc2->bottom >= prcSrc1->top) yTop = prcSrc2->top; // bottom edge if (prcSrc2->top <= prcSrc1->bottom && prcSrc2->bottom > prcSrc1->bottom) yBottom = prcSrc2->bottom; } Set(xLeft, yTop, xRight, yBottom); } void Rect::FromWorldRect(WRect *pwrc) { left = PcFromWc(pwrc->left); right = PcFromWc(pwrc->right); top = PcFromWc(pwrc->top); bottom = PcFromWc(pwrc->bottom); } void Rect::FromTileRect(TRect *ptrc) { left = PcFromTc(ptrc->left); right = PcFromTc(ptrc->right); top = PcFromTc(ptrc->top); bottom = PcFromTc(ptrc->bottom); } void Rect::Union(Rect *prc) { if (prc->left >= prc->right || prc->top >= prc->bottom) return; if (prc->left < left) left = prc->left; if (prc->top < top) top = prc->top; if (prc->right > right) right = prc->right; if (prc->bottom > bottom) bottom = prc->bottom; } bool Rect::Equal(Rect *prc) { if (left == prc->left && top == prc->top && right == prc->right && bottom == prc->bottom) return true; return false; } void Rect::GetCenter(Point *ppt) { ppt->x = left + (right - left) / 2; ppt->y = top + (bottom - top) / 2; } int Rect::GetDistance(int x, int y) { // Use isqrt. If too expensive, could change this to // GetSquaredDistance and change callers. if (y >= bottom) { if (x >= right) { return isqrt(x - right, y - bottom); } if (x >= left) { return y - bottom; } return isqrt(left - x, y - bottom); } else if (y >= top) { if (x >= right) { return x - right; } if (x >= left) { return 0; } return left - x; } else { if (x >= right) { return isqrt(x - right, top - y); } if (x >= left) { return top - y; } return isqrt(left - x, top - y); } } // // WRect implementation // void WRect::Set(WPoint wpt1, WPoint wpt2) { if (wpt1.wx < wpt2.wx) { left = wpt1.wx; right = wpt2.wx; } else { left = wpt2.wx; right = wpt1.wx; } if (wpt1.wy < wpt2.wy) { top = wpt1.wy; bottom = wpt2.wy; } else { top = wpt2.wy; bottom = wpt1.wy; } } #if 0 // UNUSED void WRect::FromPixelRect(Rect *prc) { left = WcFromPc(prc->left); right = WcFromPc(prc->right); top = WcFromPc(prc->top); bottom = WcFromPc(prc->bottom); } #endif void WRect::FromTileRect(TRect *ptrc) { left = WcFromTc(ptrc->left); right = WcFromTc(ptrc->right); top = WcFromTc(ptrc->top); bottom = WcFromTc(ptrc->bottom); } // fgets char *_fgets(char *psz, int cch, File *pfil) { // Read into the buffer dword cbRead = gpakr.fread(psz, 1, cch, pfil); if (cbRead == 0) return NULL; psz[cbRead] = 0; // Look for a newline, and end there int cchOld = (int)strlen(psz); int nchNew; for (nchNew = 0; nchNew < cchOld; nchNew++) { if (psz[nchNew] == '\n') { nchNew++; psz[nchNew] = 0; break; } } // Set the file pointer backwards int cchNew = nchNew; gpakr.fseek(pfil, cchNew - cchOld, SEEK_CUR); return psz; } // String uppercasing function because PalmOS doesn't have one void HtStrupr(char *psz) { while (true) { char ch = *psz; if (ch == 0) break; if (ch >= 'a' && ch <= 'z') *psz = ch + 'A' - 'a'; psz++; } } // Bring up an Hostile Takeover-style message box with a title and a formatted message bool HtMessageBox(word wf, const char *pszTitle, const char *pszFormat, ...) { va_list va; va_start(va, pszFormat); char szBody[512]; vsprintf(szBody, pszFormat, va); va_end(va); return HtMessageBox(kidfMessageBox, wf, pszTitle, szBody); } bool HtMessageBox(word idf, word wf, const char *pszTitle, const char *pszBody) { DialogForm *pfrm = CreateHtMessageBox(idf, wf, pszTitle, pszBody); if (!(wf & kfMbKeepTimersEnabled)) { gtimm.Enable(false); } // Bring up form bool f = pfrm->DoModal(); delete pfrm; if (!(wf & kfMbKeepTimersEnabled)) { gtimm.Enable(true); } return f; } DialogForm *CreateHtMessageBox(word idf, word wf, const char *pszTitle, const char *pszBody) { DialogForm *pfrm = (DialogForm *)gpmfrmm->LoadForm(gpiniForms, idf, new DialogForm()); Assert(pfrm != NULL, "Unable to load HtMessageBox form"); if (wf & kfMbWhiteBorder) pfrm->SetBorderColorIndex(kiclrWhite); pfrm->SetTitleColor(GetColor(kiclrRed)); pfrm->SetBackgroundColorIndex(kiclrShadow2x); LabelControl *plbl = (LabelControl *)pfrm->GetControlPtr(kidcTitle); plbl->SetText(pszTitle); // The label self-adjusts its height. Use that to format the messagebox as well plbl = (LabelControl *)pfrm->GetControlPtr(kidcMessage); Rect rcOld; plbl->GetRect(&rcOld); plbl->SetText(pszBody); Rect rcNew; plbl->GetRect(&rcNew); // Move the buttons ButtonControl *pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcOk); Rect rcButton; pbtn->GetRect(&rcButton); rcButton.Offset(0, rcNew.Height() - rcOld.Height()); pbtn->SetRect(&rcButton); pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcCancel); if (pbtn != NULL) { pbtn->GetRect(&rcButton); rcButton.Offset(0, rcNew.Height() - rcOld.Height()); pbtn->SetRect(&rcButton); } // Resize the form Rect rcForm; pfrm->GetRect(&rcForm); rcForm.bottom += rcNew.Height() - rcOld.Height(); // Now center the form again DibBitmap *pbm = pfrm->GetFormMgr()->GetDib(); Size siz; pbm->GetSize(&siz); int yNew = (siz.cy - rcForm.Height()) / 2; rcForm.Offset(0, yNew - rcForm.top); pfrm->SetRect(&rcForm); // Clear if asked if (wf & kfMbClearDib) { pfrm->SetClearDibFlag(); } return pfrm; } #ifdef STATUS_LINE void Status(const char *psz) { // Display::DrawText draws directly to the screen, no buffering if (gpdisp != NULL) { gpdisp->DrawText(psz, 0, -1, kfDtClearLine); Trace((char *)psz); } } #endif // Returns a pseudo-random number 0 through 32767. // This random number generator must be kept in sync across all // machines in a multiplayer game. Use it in response to game // events that are seen by all players. static long glSeed = 1L; #ifdef DEBUG int _GetRandom(char *pszFile, int nLine) #else int _GetRandom() #endif { #ifdef DEBUG // MpTrace("- GetRandom() [%s, %d]", pszFile, nLine); #endif return ((glSeed = glSeed * 214013L + 2531011L) >> 16) & 0x7fff; } void SetRandomSeed(unsigned long nSeed) { glSeed = (long)nSeed; } unsigned long GetRandomSeed() { return glSeed; } // This random number generator does not need to be in sync across // all machines in a multiplayer game. Use it when you want a random // number that is only relevent to the local player (e.g., picking // a random voice response for a unit). static long glAsyncSeed = 1L; int GetAsyncRandom() { return ((glAsyncSeed = glAsyncSeed * 214013L + 2531011L) >> 16) & 0x7fff; } Direction TurnToward(Direction dirTo, Direction dirFrom) { int d = dirTo - dirFrom; if (d == 0) return dirTo; if (d < -4) d = 1; else if (d > 4) d = -1; if (d < 0) dirFrom--; else dirFrom++; return ((unsigned int)dirFrom) % 8; } int isqrt(int val1, int val2) { return (int)isqrt((dword)val1 * (dword)val1 + (dword)val2 * (dword)val2); } unsigned int isqrt(unsigned long val) { unsigned long temp, g=0; if (val >= 0x40000000) { g = 0x8000; val -= 0x40000000; } #define INNER_MBGSQRT(s) \ temp = (g << (s)) + (1L << ((s) * 2L - 2L)); \ if (val >= temp) { \ g += 1L << ((s)-1L); \ val -= temp; \ } INNER_MBGSQRT (15) INNER_MBGSQRT (14) INNER_MBGSQRT (13) INNER_MBGSQRT (12) INNER_MBGSQRT (11) INNER_MBGSQRT (10) INNER_MBGSQRT ( 9) INNER_MBGSQRT ( 8) INNER_MBGSQRT ( 7) INNER_MBGSQRT ( 6) INNER_MBGSQRT ( 5) INNER_MBGSQRT ( 4) INNER_MBGSQRT ( 3) INNER_MBGSQRT ( 2) #undef INNER_MBGSQRT temp = g+g+1; if (val >= temp) g++; return (unsigned int)g; } // LineIterator implementation // OPT: expensive! Lots of multiplies, divides and that fixed-point square root void WLineIterator::Init(WCoord wx1, WCoord wy1, WCoord wx2, WCoord wy2, int nIncr) { Assert(nIncr != 0); m_wx = wx1; m_wy = wy1; // Calc the number of nSteps needed to step the line: // cSteps = sqrt(dx^2 + dy^2) / nStep WCoord wdx = wx2 - wx1; WCoord wdy = wy2 - wy1; int nLen = isqrt(((long)wdx * (long)wdx) + ((long)wdy * (long)wdy)); // Handle case where destination is less than one step away from the start. if (nLen < nIncr) { m_cStepsRemaining = 0; return; } int cSteps = nLen / nIncr; Assert(cSteps != 0); m_wdx = wdx / cSteps; m_wdy = wdy / cSteps; m_cStepsRemaining = cSteps; #if 0 // doing without for now // Some fraction of a step is lost when we convert cSteps to an integer. // Add this fractional step to m_fx, m_fy to start things off so the // final step will leave us at the desired destination. fix fxFrac = fracfx(cfxSteps); m_fx = addfx(m_fx, (fix)mulfx(m_fdx, fxFrac)); m_fy = addfx(m_fy, (fix)mulfx(m_fdy, fxFrac)); #endif } // // Palette and color helpers // const short SCALEFACTOR = 128; const word SCALEMAX = 256 * (word)SCALEFACTOR; // nHueOffset is in the range from -100 to +100 // nLumOffset is in the range from -100 to +100 // nSatMultiplier is in the range from -100 to +100 and is scaled non-linearly // to cover the desired (finely tuned) range. void SetHslAdjustedPalette(Palette *ppal, short nHueOffset, short nSatMultiplier, short nLumOffset) { // Incorporate hardware-correcting values short nHueT, nSatT, nLumT; gpdisp->GetHslAdjustments(&nHueT, &nSatT, &nLumT); nHueOffset += nHueT; if (nHueT < -100) nHueT = -100; else if (nHueT > 100) nHueT = 100; nSatMultiplier += nSatT; if (nSatMultiplier < -100) nSatMultiplier = -100; else if (nSatMultiplier > 100) nSatMultiplier = 100; nLumOffset += nLumT; if (nLumOffset < -100) nLumOffset = -100; else if (nLumOffset > 100) nLumOffset = 100; short nHueAdd = (nHueOffset * (3 * SCALEFACTOR)) / 100; short nLumAdd = (short)((nLumOffset * 10000L) / 100); // maps +/-100 to 128-384 long nT = 128 + (((nSatMultiplier + 100L) * 256) / 200); // non-linearly transforms to the range 0-1024 (actually 64-576) short nSatMult = (short)((nT * nT) / 256); int cEntries = BigWord(ppal->cEntries); Palette *ppalMod = (Palette *)new byte[sizeof(word) + (cEntries * sizeof(ppal->argb))]; ppalMod->cEntries = ppal->cEntries; word nH, nS, nL; for (int i = 0; i < cEntries; i++) { RgbToHsl(ppal->argb[i][0], ppal->argb[i][1], ppal->argb[i][2], &nH, &nS, &nL); int nT = nH + nHueAdd; if (nT >= 6 * SCALEFACTOR) nT -= 6 * SCALEFACTOR; else if (nT < 0) nT += 6 * SCALEFACTOR; long lS = (nS * (long)nSatMult) / 256L; if (lS > SCALEMAX) lS = (long)SCALEMAX; long lL = nL + (long)nLumAdd; if (lL > SCALEMAX) lL = (long)SCALEMAX; else if (lL < 0) lL = 0; HslToRgb((word)nT, (word)lS, (word)lL, &ppalMod->argb[i][0], &ppalMod->argb[i][1], &ppalMod->argb[i][2]); } gpdisp->SetPalette(ppalMod); delete[] ppalMod; } // Takes byte-sized RGB values in the range from 0-255 and returns // word-sized HSL values in the range from 0-32768 (H, S, L). // H is special and ranges from 0 to SCALEFACTOR (128) * 6 void RgbToHsl(byte bR, byte bG, byte bB, word *pnH, word *pnS, word *pnL) { word nR = bR * SCALEFACTOR; word nG = bG * SCALEFACTOR; word nB = bB * SCALEFACTOR; word nMax = _max(nR, nG); nMax = _max(nMax, nB); word nMin = _min(nR, nG); nMin = _min(nMin, nB); *pnH = 0; *pnS = 0; *pnL = (word)((nMin + nMax) / 2); if (*pnL == 0) return; word nDelta = nMax - nMin; *pnS = nDelta; if (nDelta == 0) return; word n; if (*pnL < SCALEMAX / 2) n = nMax + nMin; else n = (word)(((long)SCALEMAX * 2) - nMax - nMin); *pnS = (word)((nDelta * (long)SCALEMAX) / n); word nDeltaDiv = nDelta / SCALEFACTOR; word r2 = (nMax - nR) / nDeltaDiv; word g2 = (nMax - nG) / nDeltaDiv; word b2 = (nMax - nB) / nDeltaDiv; if (nR == nMax) { if (nG == nMin) { if (b2 == SCALEFACTOR) *pnH = 0; else *pnH = (5 * SCALEFACTOR) + b2; } else { *pnH = (1 * SCALEFACTOR) - g2; } } else if (nG == nMax) { *pnH = (nB == nMin ? (1 * SCALEFACTOR) + r2 : (3 * SCALEFACTOR) - b2); } else { *pnH = (nR == nMin ? (3 * SCALEFACTOR) + g2 : (5 * SCALEFACTOR) - r2); } } void HslToRgb(word nH, word nS, word nL, byte *pbR, byte *pbG, byte *pbB) { word v; if (nL <= SCALEMAX / 2) { Assert((nL * (SCALEMAX + (long)nS)) / SCALEMAX < 65536); v = (word)((nL * (SCALEMAX + (long)nS)) / SCALEMAX); } else { Assert((nL + nS - ((nL * (long)nS) / SCALEMAX)) < 65536); v = (word)(nL + nS - ((nL * (long)nS) / SCALEMAX)); } if (v == 0) { *pbR = *pbG = *pbB = 0; return; } word m = nL + nL - v; word sv = (word)(((v - m) * (long)SCALEMAX) / v); word sextant = nH / SCALEFACTOR; word fract = nH & (SCALEFACTOR - 1); word vsf = (word)((((v * (long)sv) / SCALEMAX) * fract) / SCALEFACTOR); word mid1 = m + vsf; word mid2 = v - vsf; // UNDONE: remove if (v == SCALEMAX) v = SCALEMAX - 1; if (m == SCALEMAX) m = SCALEMAX - 1; if (mid1 == SCALEMAX) mid1 = SCALEMAX - 1; if (mid2 == SCALEMAX) mid2 = SCALEMAX - 1; Assert(v / SCALEFACTOR < 256); Assert(m / SCALEFACTOR < 256); Assert(mid1 / SCALEFACTOR < 256); Assert(mid2 / SCALEFACTOR < 256); switch (sextant) { case 0: *pbR = v / SCALEFACTOR; *pbG = mid1 / SCALEFACTOR; *pbB = m / SCALEFACTOR; break; case 1: *pbR = mid2 / SCALEFACTOR; *pbG = v / SCALEFACTOR; *pbB = m / SCALEFACTOR; break; case 2: *pbR = m / SCALEFACTOR; *pbG = v / SCALEFACTOR; *pbB = mid1 / SCALEFACTOR; break; case 3: *pbR = m / SCALEFACTOR; *pbG = mid2 / SCALEFACTOR; *pbB = v / SCALEFACTOR; break; case 4: *pbR = mid1 / SCALEFACTOR; *pbG = m / SCALEFACTOR; *pbB = v / SCALEFACTOR; break; case 5: *pbR = v / SCALEFACTOR; *pbG = m / SCALEFACTOR; *pbB = mid2 / SCALEFACTOR; break; } } int CompareDates(Date *pdate1, Date *pdate2) { // Check years if (pdate1->nYear < pdate2->nYear) return -1; if (pdate1->nYear > pdate2->nYear) return 1; // Years are equal. Check months. if (pdate1->nMonth < pdate2->nMonth) return -1; if (pdate1->nMonth > pdate2->nMonth) return 1; // Months are equal. Check days if (pdate1->nDay < pdate2->nDay) return -1; if (pdate1->nDay > pdate2->nDay) return 1; // All equal return 0; } // CheckBetaTimeout // false means beta has timed out // true means beta has not timed out #ifdef BETA_TIMEOUT #define knMonthBetaStart 10 #define knDayBetaStart 1 #define knYearBetaStart 2009 #define knMonthBetaEnd 12 #define knDayBetaEnd 1 #define knYearBetaEnd 2009 bool CheckBetaTimeout() { // Get current date Date date; HostGetCurrentDate(&date); // Get prefs. If we have prefs, get the last date run from there Date dateLast; dateLast.nYear = gprefsInit.nYearLastRun; dateLast.nMonth = gprefsInit.nMonthLastRun; dateLast.nDay = gprefsInit.nDayLastRun; // See if the date has been set backwards to trick us :) if (CompareDates(&date, &dateLast) < 0) { TimeOut: HtMessageBox(kfMbWhiteBorder | kfMbClearDib, "Test Release", "Thank you for playing Hostile Takeover. Unfortunately this test release has expired! Please contact us at:\n\nhttp://www.spiffcode.com"); return false; } // See if the current date is ahead of the demo start date Date dateStart; dateStart.nYear = knYearBetaStart; dateStart.nMonth = knMonthBetaStart; dateStart.nDay = knDayBetaStart; if (CompareDates(&date, &dateStart) < 0) goto TimeOut; // See if the current date is before the demo end date Date dateEnd; dateEnd.nYear = knYearBetaEnd; dateEnd.nMonth = knMonthBetaEnd; dateEnd.nDay = knDayBetaEnd; if (CompareDates(&date, &dateEnd) > 0) goto TimeOut; // All looks ok. Save the prefs which'll save the current date ggame.SavePreferences(); return true; } #endif // Hardwired sfx categories // Infantry destroyed Sfx gasfxInfantryDestroyed[] = { ksfxInfantryDestroyed0, ksfxInfantryDestroyed1, ksfxInfantryDestroyed2, ksfxInfantryDestroyed3, ksfxInfantryDestroyed4, }; // Vehicle destroyed Sfx gasfxVehicleDestroyed[] = { ksfxVehicleDestroyed, }; // Male01 Sfx gasfxMale01Select[] = { ksfxMale01Select0, ksfxMale01Select1, ksfxMale01Select2, ksfxMale01Select3, }; Sfx gasfxMale01Move[] = { ksfxMale01Move0, ksfxMale01Move1, ksfxMale01Move2, ksfxMale01Move3, }; Sfx gasfxMale01Attack[] = { ksfxMale01Attack0, ksfxMale01Attack1, ksfxMale01Attack2, ksfxMale01Attack3, }; // Male03 Sfx gasfxMale03Select[] = { ksfxMale03Select0, ksfxMale03Select1, ksfxMale03Select2, ksfxMale03Select3, }; Sfx gasfxMale03Move[] = { ksfxMale03Move0, ksfxMale03Move1, ksfxMale03Move2, ksfxMale03Move3, }; Sfx gasfxMale03Attack[] = { ksfxMale03Attack0, ksfxMale03Attack1, ksfxMale03Attack2, ksfxMale03Attack3, }; // Male06 Sfx gasfxMale06Select[] = { ksfxMale06Select0, ksfxMale06Select1, ksfxMale06Select2, ksfxMale06Select3, }; Sfx gasfxMale06Move[] = { ksfxMale06Move0, ksfxMale06Move1, ksfxMale06Move2, ksfxMale06Move3, }; Sfx gasfxMale06Attack[] = { ksfxMale06Attack0, ksfxMale06Attack1, ksfxMale06Attack2, ksfxMale06Attack3, }; // Major01 Sfx gasfxMajor01Select[] = { ksfxMajor01Select0, ksfxMajor01Select1, ksfxMajor01Select2, ksfxMajor01Select3, }; Sfx gasfxMajor01Move[] = { ksfxMajor01Move0, ksfxMajor01Move1, ksfxMajor01Move2, ksfxMajor01Move3, }; Sfx gasfxMajor01Attack[] = { ksfxMajor01Attack0, ksfxMajor01Attack1, ksfxMajor01Attack2, ksfxMajor01Attack3, }; // Major02 Sfx gasfxMajor02Select[] = { ksfxMajor02Select0, ksfxMajor02Select1, ksfxMajor02Select2, ksfxMajor02Select3, }; Sfx gasfxMajor02Move[] = { ksfxMajor02Move0, ksfxMajor02Move1, ksfxMajor02Move2, ksfxMajor02Move3, }; Sfx gasfxMajor02Attack[] = { ksfxMajor02Attack0, ksfxMajor02Attack1, ksfxMajor02Attack2, ksfxMajor02Attack3, }; // Andy Sfx gasfxAndySelect[] = { ksfxAndySelect, }; Sfx gasfxAndyMove[] = { ksfxAndyMove, }; Sfx gasfxAndyAttack[] = { ksfxAndyAttack, }; Sfx gasfxAndyDestroyed[] = { ksfxAndyDestroyed, }; // Fox Sfx gasfxFoxDestroyed[] = { ksfxFoxDestroyed, }; struct SfxCategoryEntry // sfxce { Sfx *asfx; int csfx; }; SfxCategoryEntry gasfxce[] = { { gasfxInfantryDestroyed, ARRAYSIZE(gasfxInfantryDestroyed) }, { gasfxVehicleDestroyed, ARRAYSIZE(gasfxVehicleDestroyed) }, { gasfxMale01Select, ARRAYSIZE(gasfxMale01Select) }, { gasfxMale01Move, ARRAYSIZE(gasfxMale01Move) }, { gasfxMale01Attack, ARRAYSIZE(gasfxMale01Attack) }, { gasfxMale03Select, ARRAYSIZE(gasfxMale03Select) }, { gasfxMale03Move, ARRAYSIZE(gasfxMale03Move) }, { gasfxMale03Attack, ARRAYSIZE(gasfxMale03Attack) }, { gasfxMale06Select, ARRAYSIZE(gasfxMale06Select) }, { gasfxMale06Move, ARRAYSIZE(gasfxMale06Move) }, { gasfxMale06Attack, ARRAYSIZE(gasfxMale06Attack) }, { gasfxMajor01Select, ARRAYSIZE(gasfxMajor01Select) }, { gasfxMajor01Move, ARRAYSIZE(gasfxMajor01Move) }, { gasfxMajor01Attack, ARRAYSIZE(gasfxMajor01Attack) }, { gasfxMajor02Select, ARRAYSIZE(gasfxMajor02Select) }, { gasfxMajor02Move, ARRAYSIZE(gasfxMajor02Move) }, { gasfxMajor02Attack, ARRAYSIZE(gasfxMajor02Attack) }, { gasfxAndySelect, ARRAYSIZE(gasfxAndySelect) }, { gasfxAndyMove, ARRAYSIZE(gasfxAndyMove) }, { gasfxAndyAttack, ARRAYSIZE(gasfxAndyAttack) }, { gasfxAndyDestroyed, ARRAYSIZE(gasfxAndyDestroyed) }, { gasfxFoxDestroyed, ARRAYSIZE(gasfxFoxDestroyed) }, }; Sfx SfxFromCategory(SfxCategory sfxc) { if (sfxc < 0 || sfxc >= ARRAYSIZE(gasfxce)) return (Sfx)-1; SfxCategoryEntry *psfxce = &gasfxce[sfxc]; // NOTE: We use GetAsyncRandom() to avoid disturbing the in-sync random // number generator when we're performing this operation which is meant // only for the local player. return psfxce->asfx[GetAsyncRandom() % psfxce->csfx]; } // Stream helpers void Stream::ReadString(char *psz, int cb) { // Fill the passed buffer *psz = 0; while (cb-- != 0) { char ch = (char)ReadByte(); *psz++ = ch; if (ch == 0) return; } // More string in the stream than size of psz. Cap off psz, read till 0 psz[cb - 1] = 0; while (ReadByte() != 0) ; } void Stream::WriteString(char *psz) { Write(psz, (int)(strlen(psz) + 1)); } void Stream::WriteBytesRLE(byte *pb, int cb) { byte *pbMax = &pb[cb]; for (byte *pbChunk = pb; pbChunk < pbMax; ) { // Literal runs are separated by repeats of at least 3 or more. byte *pbRepeat = FindRLERepeat(pbChunk, pbMax, 3); // If there is a literal to write, do it int cbLiteral = 0; if (pbRepeat == NULL) { cbLiteral = (int)(pbMax - pbChunk); } else if (pbRepeat > pbChunk) { cbLiteral = (int)(pbRepeat - pbChunk); } if (cbLiteral != 0) { WriteRLEChunk(pbChunk, cbLiteral, false); pbChunk += cbLiteral; continue; } // There should be a repeat to write Assert(pbRepeat != NULL && pbRepeat == pbChunk); byte *pbT; for (pbT = pbRepeat + 1; pbT < pbMax; pbT++) { if (*pbT != *pbRepeat) break; } int cbRepeat = (int)(pbT - pbRepeat); Assert(cbRepeat >= 3); WriteRLEChunk(pbChunk, cbRepeat, true); pbChunk += cbRepeat; } } byte *Stream::FindRLERepeat(byte *pbStart, byte *pbMax, int cbMin) { for (; pbStart < pbMax; pbStart++) { byte bFind = *pbStart; int cRepeat = 1; for (byte *pbT = pbStart + 1; pbT < pbMax; pbT++) { if (*pbT != bFind) break; cRepeat++; if (cRepeat >= cbMin) return pbStart; } } return 0; } void Stream::WriteRLEChunk(byte *pb, int cb, bool fRepeat) { if (!fRepeat) { // Literal while (cb != 0) { int cbWrite = cb < 128 ? cb : 128; WriteByte(128 | (cbWrite - 1)); Write(pb, cbWrite); pb += cbWrite; cb -= cbWrite; if (!IsSuccess()) break; } } else { // Repeat while (cb != 0) { int cbWrite = cb < 128 ? cb : 128; WriteByte(0 | (cbWrite - 1)); WriteByte(*pb); cb -= cbWrite; if (!IsSuccess()) break; } } } void Stream::ReadBytesRLE(byte *pb, int cb) { while (cb != 0) { byte bT = ReadByte(); int cbRun = (bT & ~128) + 1; if (bT & 128) { Read(pb, cbRun); } else { byte bRepeat = ReadByte(); memset(pb, bRepeat, cbRun); } pb += cbRun; cb -= cbRun; if (!IsSuccess()) break; } } // // Return the appropriate color for the current device // Color gaclr4bpp[] = { 0x000f, // black 0x0000, // white 0x0007, // red 0x0000, // green 0x0003, // yellow 0x0008, // side 1 color 0x000d, // side 2 color 0x0004, // side 3 color 0x000f, // side 4 color 0x0008, // button fill 0x000a, // button border 0x000f, // menu background 0x000f, // form background 0x000d, // mini map border area 0x000b, // Galaxite 0x0007, // button fill highlight 0x0008, // medium gray 0x0004, // blue side 0 0x0006, // blue side 1 0x0008, // blue side 2 0x000a, // blue side 3 0x000c, // blue side 4 0x000b, // red side 0 0x000c, // red side 1 0x000d, // red side 2 0x000e, // red side 3 0x000f, // red side 4 0x0001, // yellow side 0 0x0003, // yellow side 1 0x0004, // yellow side 2 0x0005, // yellow side 3 0x0005, // yellow side 4 0x0000, // cyan side 0 0x000c, // cyan side 1 0x0002, // cyan side 2 0x000e, // cyan side 3 0x0004, // cyan side 4 0x000e, // list background 0x0003, // list border 0x0000, // Jana Font 0x0000, // Andy Font 0x0000, // Olstrom Font 0x0000, // Fox Font 0x000f, // neutral side 0 0x000f, // neutral side 1 0x000f, // neutral side 2 0x000f, // neutral side 3 0x000f, // neutral side 4 }; Color gaclr8bpp[] = { 0x0000, // black 0x0001, // white 0x0002, // red 0x0003, // green 0x0004, // yellow 0x0010, // side 1 color 0x0015, // side 2 color 0x001a, // side 3 color 0x000b, // side 4 color 0x0005, // button fill 0x0011, // button border 0x0000, // menu background 0x0000, // form background 0x000f, // mini map border area 0x001f, // Galaxite minimap color 0x000c, // button fill highlight 35, // medium gray 0x0010, // blue side 0 0x0011, // blue side 1 0x0012, // blue side 2 0x0013, // blue side 3 0x0014, // blue side 4 0x0015, // red side 0 0x0016, // red side 1 0x0017, // red side 2 0x0018, // red side 3 0x0019, // red side 4 0x001a, // yellow side 0 0x001b, // yellow side 1 0x001c, // yellow side 2 0x001d, // yellow side 3 0x001e, // yellow side 4 0x000b, // cyan side 0 0x000c, // cyan side 1 0x000d, // cyan side 2 0x000e, // cyan side 3 0x000f, // cyan side 4 0x000a, // list background 0x000c, // list border 0x0006, // Jana font 0x000b, // Andy font 0x001a, // Olstrom font 0x0015, // Fox font 32, // neutral side 0 33, // neutral side 1 36, // neutral side 2 40, // neutral side 3 45, // neutral side 4 52, // Galaxite fullness indicator/neon pink }; int gaiclrSide[kcSides] = { kiclrSideNeutral, kiclrSide1, kiclrSide2, kiclrSide3, kiclrSide4 }; void DrawBorder(DibBitmap *pbm, Rect *prc, int nThickness, Color clr, UpdateMap *pupd) { if (pupd == NULL) { pbm->Fill(prc->left, prc->top, prc->Width(), nThickness, clr); pbm->Fill(prc->left, prc->top, nThickness, prc->Height(), clr); pbm->Fill(prc->right - nThickness, prc->top, nThickness, prc->Height(), clr); pbm->Fill(prc->left, prc->bottom - nThickness, prc->Width(), nThickness, clr); } else { Rect rcT; rcT.Set(prc->left, prc->top, prc->right, prc->top + nThickness); FillHelper(pbm, pupd, &rcT, clr); rcT.Set(prc->left, prc->top, prc->left + nThickness, prc->bottom); FillHelper(pbm, pupd, &rcT, clr); rcT.Set(prc->right - nThickness, prc->top, prc->right, prc->bottom); FillHelper(pbm, pupd, &rcT, clr); rcT.Set(prc->left, prc->bottom - nThickness, prc->right, prc->bottom); FillHelper(pbm, pupd, &rcT, clr); } } #if 0 void DrawBitmapBorder(DibBitmap *pbm, UpdateMap *pupd, Rect *prc, AnimationData *panid, int ifrm, Side side) { // Get piece bounds Rect rcTL; panid->GetBounds(0, ifrm, &rcTL); Rect rcT; panid->GetBounds(1, ifrm, &rcT); Rect rcTR; panid->GetBounds(2, ifrm, &rcTR); Rect rcR; panid->GetBounds(3, ifrm, &rcR); Rect rcBR; panid->GetBounds(4, ifrm, &rcBR); Rect rcB; panid->GetBounds(5, ifrm, &rcB); Rect rcBL; panid->GetBounds(6, ifrm, &rcBL); Rect rcL; panid->GetBounds(7, ifrm, &rcL); // Draw repeating top edge Rect rcBounds; int xL = prc->left + rcTL.Width(); int xR = prc->right - rcTR.Width(); int cxPiece = rcT.Width(); for (; xL < xR; xL += cxPiece) { rcBounds = rcT; rcBounds.Offset(xL, prc->top); if (pupd->IsRectInvalid(&rcBounds)) panid->DrawFrame(1, ifrm, pbm, xL, prc->top, side); } // Draw repeating bottom edge xL = prc->left + rcBL.Width(); xR = prc->right - rcBR.Width(); cxPiece = rcB.Width(); for (; xL < xR; xL += cxPiece) { rcBounds = rcB; rcBounds.Offset(xL, prc->bottom); if (pupd->IsRectInvalid(&rcBounds)) panid->DrawFrame(5, ifrm, pbm, xL, prc->bottom, side); } // Draw repeating left edge int yT = prc->top + rcTL.Height(); int yB = prc->bottom - rcBL.Height(); int cyPiece = rcL.Height(); for (; yT < yB; yT += cyPiece) { rcBounds = rcL; rcBounds.Offset(prc->left, yT); if (pupd->IsRectInvalid(&rcBounds)) panid->DrawFrame(7, ifrm, pbm, prc->left, yT, side); } // Draw repeating right edge yT = prc->top + rcTR.Height(); yB = prc->bottom - rcBR.Height(); cyPiece = rcR.Height(); for (; yT < yB; yT += cyPiece) { rcBounds = rcR; rcBounds.Offset(prc->right, yT); if (pupd->IsRectInvalid(&rcBounds)) panid->DrawFrame(3, ifrm, pbm, prc->right, yT, side); } // Draw the corners rcBounds = rcTL; rcBounds.Offset(prc->left, prc->top); if (pupd->IsRectInvalid(&rcBounds)) panid->DrawFrame(0, ifrm, pbm, prc->left, prc->top, side); rcBounds = rcTR; rcBounds.Offset(prc->right, prc->top); if (pupd->IsRectInvalid(&rcBounds)) panid->DrawFrame(2, ifrm, pbm, prc->right, prc->top, side); rcBounds = rcBR; rcBounds.Offset(prc->right, prc->bottom); if (pupd->IsRectInvalid(&rcBounds)) panid->DrawFrame(4, ifrm, pbm, prc->right, prc->bottom, side); rcBounds = rcBL; rcBounds.Offset(prc->left, prc->bottom); if (pupd->IsRectInvalid(&rcBounds)) panid->DrawFrame(6, ifrm, pbm, prc->left, prc->bottom, side); } #endif // // Basic string dictionary class. Limited to short keys and values. // Dictionary::Dictionary() { m_cde = 0; m_pdeHead = NULL; } Dictionary::~Dictionary() { Clear(); } // Clone the passed-in dictionary over self bool Dictionary::Init(Dictionary *pdict) { Clear(); DictionaryEntry *pdeSrc = pdict->m_pdeHead; DictionaryEntry **ppdeDst = &m_pdeHead; while (pdeSrc != NULL) { DictionaryEntry *pdeDst = new DictionaryEntry; Assert(pdeDst != NULL, "out of memory!"); if (pdeDst == NULL) { Clear(); return false; } pdeDst->pdeNext = NULL; strcpy(pdeDst->szName, pdeSrc->szName); strcpy(pdeDst->szValue, pdeSrc->szValue); *ppdeDst = pdeDst; ppdeDst = &pdeDst->pdeNext; pdeSrc = pdeSrc->pdeNext; m_cde++; } return true; } void Dictionary::Clear() { DictionaryEntry *pde = m_pdeHead; while (pde != NULL) { DictionaryEntry *pdeNext = pde->pdeNext; delete pde; pde = pdeNext; } m_cde = 0; m_pdeHead = NULL; } const char *Dictionary::Get(const char *pszName) { DictionaryEntry **ppde = Find(pszName); if (*ppde == NULL) return NULL; // Relink at head of the list (if not already there) DictionaryEntry *pde = *ppde; if (m_pdeHead != pde) { *ppde = pde->pdeNext; pde->pdeNext = m_pdeHead; m_pdeHead = pde; } return pde->szValue; } bool Dictionary::Set(const char *pszName, const char *pszValue) { // Is it already in the dictionary? DictionaryEntry **ppde = Find(pszName); // Create new entry if needed DictionaryEntry *pde; if (*ppde == NULL) { pde = new DictionaryEntry; Assert(pde != NULL, "out of memory!"); if (pde == NULL) return false; m_cde++; } else { pde = *ppde; *ppde = pde->pdeNext; } // Set its name/value and link it to the head of the list pde->pdeNext = m_pdeHead; m_pdeHead = pde; strcpy(pde->szName, pszName); strcpy(pde->szValue, pszValue); return true; } bool Dictionary::Remove(const char *pszName) { DictionaryEntry **ppde = Find(pszName); if (*ppde == NULL) return false; m_cde--; DictionaryEntry *pde = *ppde; *ppde = pde->pdeNext; delete pde; return true; } #define knVerDictionaryState 0 bool Dictionary::LoadState(Stream *pstm) { Clear(); // Do version handling byte nVer = pstm->ReadByte(); if (nVer != knVerDictionaryState) return false; DictionaryEntry **ppde = &m_pdeHead; m_cde = pstm->ReadByte(); for (int i = 0; i < m_cde; i++) { DictionaryEntry *pde = new DictionaryEntry; Assert(pde != NULL, "out of memory!"); if (pde == NULL) return false; pstm->ReadString(pde->szName, sizeof(pde->szName)); pstm->ReadString(pde->szValue, sizeof(pde->szValue)); pde->pdeNext = NULL; *ppde = pde; ppde = &pde->pdeNext; } return pstm->IsSuccess(); } bool Dictionary::SaveState(Stream *pstm) { pstm->WriteByte(knVerDictionaryState); pstm->WriteByte(m_cde); DictionaryEntry *pde = m_pdeHead; while (pde != NULL) { pstm->WriteString(pde->szName); pstm->WriteString(pde->szValue); pde = pde->pdeNext; } return pstm->IsSuccess(); } DictionaryEntry **Dictionary::Find(const char *pszName) { DictionaryEntry **ppde = &m_pdeHead; while (*ppde != NULL) { // OPT: upcase entries as they're added to the dictionary if (stricmp((*ppde)->szName, pszName) == 0) break; ppde = &(*ppde)->pdeNext; } return ppde; } // Takes a string with embedded variable references in the form of "{pvar}" // and expands them to the appropriate values. void ExpandVars(char *pszSrc, char *pszBuff, int cbBuff) { while (*pszSrc != 0 && cbBuff > 1) { char ch = *pszSrc++; // Variable? if (ch == '{') { // yes char szT[kcbPvarNameMax]; char *pszT = szT; *pszT = 0; int cbT = sizeof(szT); char *pszBeforeVar = pszSrc; while (*pszSrc != 0 && *pszSrc != '}' && cbT-- > 0) *pszT++ = *pszSrc++; if (*pszSrc != 0) { *pszT = 0; ggame.GetVar(szT, pszBuff, cbBuff); int cb = (int)strlen(pszBuff); cbBuff -= cb; pszBuff += cb; pszSrc++; } else { // Variable not terminated properly pszSrc = pszBeforeVar; *pszBuff++ = ch; cbBuff--; } } else { // No, just copy it *pszBuff++ = ch; cbBuff--; } } *pszBuff = 0; } void GetRankTitle(char *psz, int cb) { // Note: rank "Challenger" is hardcoded in the level files as -1 int nRank = 0; char szT[20]; if (ggame.GetVar("rank", szT, sizeof(szT))) nRank = atoi(szT); if (!gpstrtbl->GetString(kidsRank0 + nRank, psz, cb)) *psz = 0; } // Helpers - no need to call runtime for these // These are here because Metrowerks doesn't have ARM runtime support for them, and we // don't want to create thunks int strnicmp(const char *psz1, const char *psz2, int cch) { while (cch-- != 0) { byte b1 = *psz1++; if (b1 >= 'A' && b1 <= 'Z') b1 += 'a' - 'A'; byte b2 = *psz2++; if (b2 >= 'A' && b2 <= 'Z') b2 += 'a' - 'A'; if (b1 != b2) return b1 - b2; if (b1 == 0) return 0; } return 0; } static void xtoa ( unsigned long val, char *buf, unsigned radix, int is_neg ) { char *p; /* pointer to traverse string */ char *firstdig; /* pointer to first digit */ char temp; /* temp char */ unsigned digval; /* value of digit */ p = buf; if (is_neg) { /* negative, so output '-' and negate */ *p++ = '-'; val = (unsigned long)(-(long)val); } firstdig = p; /* save pointer to first digit */ do { digval = (unsigned) (val % radix); val /= radix; /* get next digit */ /* convert to ascii and store */ if (digval > 9) *p++ = (char) (digval - 10 + 'a'); /* a letter */ else *p++ = (char) (digval + '0'); /* a digit */ } while (val > 0); /* We now have the digit of the number in the buffer, but in reverse order. Thus we reverse them now. */ *p-- = '\0'; /* terminate string; p points to last digit */ do { temp = *p; *p = *firstdig; *firstdig = temp; /* swap *p and *firstdig */ --p; ++firstdig; /* advance to next two digits */ } while (firstdig < p); /* repeat until halfway */ } /* Actual functions just call conversion helper with neg flag set correctly, and return pointer to buffer. */ char *itoa ( int val, char *buf, int radix ) { if (radix == 10 && val < 0) xtoa((unsigned long)val, buf, radix, 1); else xtoa((unsigned long)(unsigned int)val, buf, radix, 0); return buf; } } // namespace wi