123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2016 RWS Inc, All Rights Reserved
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of version 2 of the GNU General Public License as published by
- // the Free Software Foundation
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License along
- // with this program; if not, write to the Free Software Foundation, Inc.,
- // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- //
- // MenuTrans.cpp
- // Project: Nostril (aka Postal)
- //
- // This module sets up the system and RSPiX.
- //
- // History:
- // 11/19/96 MJR Started.
- //
- // 08/04/97 JMI Although the Remap() function was calling
- // rspLockVideoBuffer(), it was effectively ignoring it. That
- // is, it passed 'dummy' vars which it did not use to access
- // the screen. These values MUST be used or there is very
- // little point in locking the video buffer in the first place.
- // This probably occurs all over the place. I'm pretty sure
- // I've done it this way in spots.
- //
- // 08/21/97 JMI Changed occurrences of rspUpdateDisplay() to
- // UpdateDisplay().
- //
- // 08/22/97 JMI Changed to use rspLockBuffer() (the BLiT library version
- // of locking the composite buffer).
- //
- // 08/22/97 JMI Changed calls to UpdateDisplay() back to rspUpdateDisplay()
- // since we no longer need UpdateDisplay() now that we are
- // using rspLock/Unlock* functions properly.
- // Also, put locks around accesses (Blits, Rects, etc.) to the
- // composite buffer.
- //
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Things to change:
- //
- // I don't currently care much for the fade to red -- it looks pretty damn
- // plain.
- //
- // This currently requires a huge amount of memory since it needs to save the
- // screen data so that the effect can be run backwards. The problem with this
- // is that it means we're always wasting alot more memory while running the
- // game than we would otherwise need to, because we have to be ready to pop up
- // the menu (and hence to use this effect) at any time.
- //
- // One possible way to use less memory is to save the screen data to the
- // hard drive and load it back when we need it. I'm not sure how slow this
- // will be, and I'm also not sure what's involved in actually doing this since
- // the buffer is really just a stub, which may not support saving properly.
- //
- // Not sure what to do about EndMenuTrans(). Original idea was to be able to
- // call it at any time, and you could either tell it finish the effect as
- // quickly as possible or to abort, which might leave the screen in an ugly
- // state. The idea was that if the user hit a key or something that meant
- // "skip this stupid effect", that we could quickly do so. I'm not sure how
- // actual usage will turn out, so for now, I'm ignoring the flag being passed
- // to this function.
- //
- ////////////////////////////////////////////////////////////////////////////////
- #include "RSPiX.h"
- #ifdef PATHS_IN_INCLUDES
- #else
- #endif
- #include "game.h"
- #include "MenuTrans.h"
- #include "update.h"
- ////////////////////////////////////////////////////////////////////////////////
- // Macros/types/etc.
- ////////////////////////////////////////////////////////////////////////////////
- // Define number of shades along with mask that will create that many shades
- //#define NUM_SHADES 32
- //#define SHADE_MASK 0xf8
- #define NUM_SHADES 16
- #define SHADE_MASK 0xf0
- // Define range of palette indices to be affected (inclusive).
- // THIS RANGE MUST BE AT LEAST TWICE AS LARGE AS NUM_SHADES!!!
- #define EFFECT_BEG 10
- #define EFFECT_END 245
- #define EFFECT_LEN ((EFFECT_END - EFFECT_BEG) + 1)
- #define SHADE_BEG ((EFFECT_END + 1) - NUM_SHADES)
- #define SHADE_END (EFFECT_END)
- #define SHADE_LEN ((SHADE_END - SHADE_BEG) + 1)
- #define NONSHADE_BEG EFFECT_BEG
- #define NONSHADE_END (SHADE_BEG - 1)
- #define NONSHADE_LEN ((NONSHADE_END - NONSHADE_BEG) + 1)
- // Simple struct for working with palettes
- typedef struct
- {
- unsigned char r;
- unsigned char g;
- unsigned char b;
- unsigned char x;
- } rgb;
- ////////////////////////////////////////////////////////////////////////////////
- // Variables/data
- ////////////////////////////////////////////////////////////////////////////////
- static rgb* m_pOrig;
- static rgb* m_pWork;
- static rgb* m_pSaveStep4;
- static unsigned char* m_pUnmapStep4;
- static unsigned char* m_pUnmapStep5;
- static RImage* m_pim;
- static short m_sStep;
- static bool m_bFinishASAP;
- static long m_lTotalTime;
- static long m_lBaseTime;
- static double m_dReduce = 1.0;
- ////////////////////////////////////////////////////////////////////////////////
- // Function prototypes
- ////////////////////////////////////////////////////////////////////////////////
- static void Remap(
- unsigned char* aucMap);
- ////////////////////////////////////////////////////////////////////////////////
- // Call this to start the menu transition effect
- ////////////////////////////////////////////////////////////////////////////////
- extern void StartMenuTrans(
- long lTotalTime) // In: Effect time (in ms) must be >= 0
- {
- // Default to step 0 (nothing) in case something goes wrong
- m_sStep = 0;
- // Save total time
- if (lTotalTime < 0)
- {
- TRACE("StartMenuTransIn(): Moronic time specified: %ld -- changed to 0!\n", lTotalTime);
- lTotalTime = 0;
- }
- m_lTotalTime = lTotalTime;
- // Clear finish flag
- m_bFinishASAP = false;
- // Allocate lots of stuff
- m_pOrig = new rgb[256];
- m_pWork = new rgb[256];
- m_pSaveStep4 = new rgb[256];
- m_pUnmapStep4 = new unsigned char[256];
- m_pUnmapStep5 = new unsigned char[256];
- m_pim = new RImage;
- if (m_pOrig && m_pWork && m_pSaveStep4 && m_pUnmapStep4 && m_pUnmapStep5 && m_pim)
- {
- // Setup image to match screen buffer
- if (m_pim->CreateImage(g_pimScreenBuf->m_sWidth, g_pimScreenBuf->m_sHeight, RImage::BMP8) == 0)
- {
- // Everything's cool, so set for first step
- m_sStep = 1;
- }
- else
- TRACE("StartMenuTrans(): Error returned by RImage::CreateImage()!\n");
- }
- else
- TRACE("StartMenuTrans(): Couldn't allocate memory!\n");
- }
- ////////////////////////////////////////////////////////////////////////////////
- //
- // The starting time of the effect is considered to be the first time this
- // function is called (NOT when InitMenuTransIn() is called!)
- //
- // The return value is true when the effect has completed, false otherwise.
- ////////////////////////////////////////////////////////////////////////////////
- extern bool DoPreMenuTrans(void)
- {
- //---------------------------------------------------------------------------
- // Step 1: Start
- //
- // Get the current palette and the current time and go on to the next step.
- // This could have been done in the init function, but I didn't want the
- // timing to start then, but rather on the first call to this function.
- //---------------------------------------------------------------------------
- if (m_sStep == 1)
- {
- // Get the "original" palette
- rspGetPaletteEntries(0, 256, &(m_pOrig[0].r), &(m_pOrig[0].g), &(m_pOrig[0].b), sizeof(rgb));
- // Lock the buffer before reading from it.
- rspLockBuffer();
- // Copy the screen into our image
- rspBlitA(g_pimScreenBuf, m_pim, 0, 0, m_pim->m_sWidth, m_pim->m_sHeight);
- // Unlock now that we're done with the composite buffer.
- rspUnlockBuffer();
- // Calculate goal for each color's red component. We use the otherwise
- // unused member of the struct to store the goal.
- for (short i = EFFECT_BEG; i <= EFFECT_END; i++)
- // m_pOrig[i].x = (unsigned char)((double)(255 - m_pOrig[i].r) * m_dReduce) & SHADE_MASK;
- m_pOrig[i].x = (unsigned char)((double)(m_pOrig[i].r) * m_dReduce) & SHADE_MASK;
- // Get base time for next step
- m_lBaseTime = rspGetMilliseconds();
-
- // Go to next step
- m_sStep = 2;
- }
- //---------------------------------------------------------------------------
- // Step 2: Fade colors
- //
- // Over the specified period of time, we transform the original palette
- // entries into MAX_SHADES shades of red.
- //---------------------------------------------------------------------------
- else if (m_sStep == 2)
- {
- // Calculate how far into the effect we are based on the elapsed time
- // since we started. If the total time specified by the user was 0, or
- // if we're being asked to finish the effect ASAP, we go right to 100%.
- double dPercent;
- if ((m_lTotalTime == 0) || m_bFinishASAP)
- {
- dPercent = 1.0;
- }
- else
- {
- dPercent = (double)(rspGetMilliseconds() - m_lBaseTime) / (double)m_lTotalTime;
- if (dPercent > 1.0)
- dPercent = 1.0;
- }
- // Update all colors to where they should be based on given percentage
- for (short i = EFFECT_BEG; i <= EFFECT_END; i++)
- {
- double dRedDiff = m_pOrig[i].r - m_pOrig[i].x;
- m_pWork[i].r = m_pOrig[i].x + (unsigned char)(dRedDiff * (1.0 - dPercent));
- m_pWork[i].g = (unsigned char)((double)m_pOrig[i].g * (1.0 - dPercent));
- m_pWork[i].b = (unsigned char)((double)m_pOrig[i].b * (1.0 - dPercent));
- }
- // Set new palette
- rspSetPaletteEntries(EFFECT_BEG, EFFECT_LEN, &(m_pWork[EFFECT_BEG].r), &(m_pWork[EFFECT_BEG].g), &(m_pWork[EFFECT_BEG].b), sizeof(rgb));
- rspUpdatePalette();
- // If we're done with the fade, go to next step
- if (dPercent == 1.0)
- m_sStep = 3;
- }
- //---------------------------------------------------------------------------
- // Step 3: Consolidate
- //
- // At this point, the palette entries are all set to any of several shades of
- // red. Since the range of entries we're dealing with is at least twice the
- // maximum number of shades of red, there will definitely be some duplicate
- // entries. We remap the pixels to get rid of the duplicates, at the same
- // time creating some unused entries. We set flags showing which entries are
- // used and which are free in preperation for the next step.
- //---------------------------------------------------------------------------
- else if (m_sStep == 3)
- {
- // Start mapping table out as an "identity map" (pixels map to themselves)
- unsigned char aucMap[256];
- for (short m = 0; m < 256; m++)
- aucMap[m] = m;
- // Scan through the palette mapping each entry onto the first entry with the
- // same color. Only checks red since blue and green are always 0.
- for (short i = EFFECT_BEG; i <= EFFECT_END; i++)
- {
- // Loop ends on first match - worst case is that entry matches itself
- short j;
- for (j = EFFECT_BEG; m_pWork[i].r != m_pWork[j].r; j++) ;
- aucMap[i] = j;
- // Set flag to 0 if this entry will be free after remapping, 1 otherwise.
- // The next step relies on this!
- m_pWork[i].x = (j < i) ? 0 : 1;
- }
- // Remap screen pixels
- Remap(aucMap);
- rspUpdateDisplay();
- // Go to next step
- m_sStep = 4;
- }
- //---------------------------------------------------------------------------
- // Step 4: Clear Official Area
- //
- // The image is now only using a small number of entries, and there are at
- // least that many unused entries. Unfortunately, the used and unused
- // entries are scattered around in no particular order, so some of the used
- // entries may be where we eventually want the "official" set of shades to
- // reside. We remap the pixels to move any such entries into unused entries
- // outside of that "official" area.
- //---------------------------------------------------------------------------
- else if (m_sStep == 4)
- {
- // Start mapping table out as an "identity map" (pixels map to themselves)
- unsigned char aucMap[256];
- for (short m = 0; m < 256; m++)
- {
- aucMap[m] = m;
- m_pUnmapStep4[m] = m;
- }
- // Go through area reserved for "official" shades looking for used entries
- // and, if found, remap them to unused entries outside of that range.
- for (short s = SHADE_BEG; s <= SHADE_END; s++)
- {
- if (m_pWork[s].x)
- {
- short i;
- for (i = NONSHADE_BEG; i <= NONSHADE_END; i++)
- {
- if (m_pWork[i].x == 0)
- {
- aucMap[s] = i;
- m_pUnmapStep4[i] = s;
- m_pWork[i].r = m_pWork[s].r;
- m_pWork[i].x = 1;
- m_pWork[s].x = 0; // not required but makes table easier to "read" in debugger
- break;
- }
- }
- // Make sure we found an unused entry to map onto
- ASSERT(i <= NONSHADE_END);
- }
- }
- // Set new palette (some entries may have been changed by the above remapping)
- rspSetPaletteEntries(NONSHADE_BEG, NONSHADE_LEN, &(m_pWork[NONSHADE_BEG].r), &(m_pWork[NONSHADE_BEG].g), &(m_pWork[NONSHADE_BEG].b), sizeof(rgb));
- rspUpdatePalette();
- // Remap screen pixels
- Remap(aucMap);
- rspUpdateDisplay();
- // Save current palette so we can run the effect backwards
- for (short p = 0; p < 256; p++)
- m_pSaveStep4[p] = m_pWork[p];
- // Go to next step
- m_sStep = 5;
- }
- //---------------------------------------------------------------------------
- // Step 5: Pack Into Official Area
- //
- // We now put the full set of shades at the specified section of the palette
- // and remap the image so that only those "official" entries are used.
- //---------------------------------------------------------------------------
- else if (m_sStep == 5)
- {
- // Put full set of shades at proper position in palette
- for (short s = 0; s < SHADE_LEN; s++)
- m_pWork[SHADE_BEG + s].r = s * (~SHADE_MASK + 1);
-
- // Set new palette (do this before remapping so the shades will be there before they're needed)
- rspSetPaletteEntries(SHADE_BEG, SHADE_LEN, &(m_pWork[SHADE_BEG].r), &(m_pWork[SHADE_BEG].g), &(m_pWork[SHADE_BEG].b), sizeof(rgb));
- rspUpdatePalette();
- // Start mapping table out as an "identity map" (pixels map to themselves)
- unsigned char aucMap[256];
- for (short m = 0; m < 256; m++)
- {
- aucMap[m] = m;
- m_pUnmapStep5[m] = m;
- }
- // Scan through the palette mapping each used entry onto the first
- // "official" shade entry with the same color.
- for (short i = NONSHADE_BEG; i <= NONSHADE_END; i++)
- {
- // Only do this for used entries (the mapping would work without
- // this check, since it wouldn't hurt to map unused entries, but
- // the unmapping table would be screwed up -- it's really bizarre
- // to think about, but eventually it makes sense)
- if (m_pWork[i].x)
- {
- // Loop ends on first match (and there always will be a match)
- short j;
- for (j = SHADE_BEG; m_pWork[i].r != m_pWork[j].r; j++) ;
- aucMap[i] = j;
- m_pUnmapStep5[j] = i;
- }
- }
- // Remap screen pixels
- Remap(aucMap);
- rspUpdateDisplay();
- // Go to next step
- m_sStep = 6;
- }
- // Condition return value based on whether or not we're done
- if (m_sStep == 6)
- return true;
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////
- //
- // The starting time of the effect is considered to be the first time this
- // function is called (NOT when InitMenuTransIn() is called!)
- //
- // The return value is true when the effect has completed, false otherwise.
- ////////////////////////////////////////////////////////////////////////////////
- extern bool DoPostMenuTrans(void)
- {
- //---------------------------------------------------------------------------
- // Step 6: Undo Step 5
- //---------------------------------------------------------------------------
- if (m_sStep == 6)
- {
- // Restore palette to where it was prior to step 5
- for (short p = 0; p < 256; p++)
- m_pWork[p] = m_pSaveStep4[p];
- // Set only the non-shade portion of the palette until we remap the pixels
- rspSetPaletteEntries(NONSHADE_BEG, NONSHADE_LEN, &(m_pWork[NONSHADE_BEG].r), &(m_pWork[NONSHADE_BEG].g), &(m_pWork[NONSHADE_BEG].b), sizeof(rgb));
- rspUpdatePalette();
- // Remap screen pixels
- Remap(m_pUnmapStep5);
- rspUpdateDisplay();
- // Go to next step
- m_sStep = 7;
- }
- //---------------------------------------------------------------------------
- // Step 7: Undo Step 4
- //---------------------------------------------------------------------------
- else if (m_sStep == 7)
- {
- // Set the remainder of the palette now that the pixels are no longer using that part
- rspSetPaletteEntries(SHADE_BEG, SHADE_LEN, &(m_pWork[SHADE_BEG].r), &(m_pWork[SHADE_BEG].g), &(m_pWork[SHADE_BEG].b), sizeof(rgb));
- rspUpdatePalette();
- // Remap screen pixels
- Remap(m_pUnmapStep4);
- rspUpdateDisplay();
- // Go to next step
- m_sStep = 8;
- }
- //---------------------------------------------------------------------------
- // Step 8:
- //---------------------------------------------------------------------------
- else if (m_sStep == 8)
- {
- // Use the original palette to figure out what the colors should be like
- // as we start the reverse fade effect. If everything went right, we should
- // come up with the same values that already exist for those entries that
- // are actually being used by the pixels, and for those entries that aren't
- // being used, we're getting them ready for when we go back to the original
- // image.
- for (short i = EFFECT_BEG; i <= EFFECT_END; i++)
- {
- m_pWork[i].r = m_pOrig[i].x;
- m_pWork[i].g = (unsigned char)0;
- m_pWork[i].b = (unsigned char)0;
- }
- rspSetPaletteEntries(EFFECT_BEG, EFFECT_LEN, &(m_pWork[EFFECT_BEG].r), &(m_pWork[EFFECT_BEG].g), &(m_pWork[EFFECT_BEG].b), sizeof(rgb));
- rspUpdatePalette();
- // Lock the buffer before writing to it.
- rspLockBuffer();
- // Restore the original image
- rspBlitA(m_pim, g_pimScreenBuf, 0, 0, m_pim->m_sWidth, m_pim->m_sHeight);
- // Unlock now that we're done with the composite buffer.
- rspUnlockBuffer();
- rspUpdateDisplay();
- // Get base time for next step
- m_lBaseTime = rspGetMilliseconds();
-
- // Go to next step
- m_sStep = 9;
- }
- //---------------------------------------------------------------------------
- // Step 9: Do a reverse-fade of the colors
- //---------------------------------------------------------------------------
- else if (m_sStep == 9)
- {
- // Calculate how far into the effect we are based on the elapsed time
- // since we started. If the total time specified by the user was 0, or
- // if we're being asked to finish the effect ASAP, we go right to 100%.
- double dPercent;
- if ((m_lTotalTime == 0) || m_bFinishASAP)
- {
- dPercent = 1.0;
- }
- else
- {
- dPercent = (double)(rspGetMilliseconds() - m_lBaseTime) / (double)m_lTotalTime;
- if (dPercent > 1.0)
- dPercent = 1.0;
- }
- // Update all colors to where they should be based on given percentage
- for (short i = EFFECT_BEG; i <= EFFECT_END; i++)
- {
- double dRedDiff = m_pOrig[i].r - m_pOrig[i].x;
- m_pWork[i].r = m_pOrig[i].x + (unsigned char)(dRedDiff * dPercent);
- m_pWork[i].g = (unsigned char)((double)m_pOrig[i].g * dPercent);
- m_pWork[i].b = (unsigned char)((double)m_pOrig[i].b * dPercent);
- }
- // Set new palette
- rspSetPaletteEntries(EFFECT_BEG, EFFECT_LEN, &(m_pWork[EFFECT_BEG].r), &(m_pWork[EFFECT_BEG].g), &(m_pWork[EFFECT_BEG].b), sizeof(rgb));
- rspUpdatePalette();
- // If we're done with the fade, go to next step
- if (dPercent == 1.0)
- m_sStep = 10;
- }
- // Condition return value based on whether or not we're done
- if (m_sStep == 10)
- return true;
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////
- extern void EndMenuTrans(
- bool bFinish) // In: true to finish effect, false to abort it
- {
- // If step isn't over 0 then there's nothing to do
- if (m_sStep > 0)
- {
- // Check if caller wants to finish effect
- if (bFinish)
- {
- // Finish the effect
- m_bFinishASAP = bFinish;
- // while (!DoMenuTrans())
- // ;
- }
- // Free lots of stuff
- delete []m_pOrig;
- delete []m_pWork;
- delete []m_pSaveStep4;
- delete []m_pUnmapStep4;
- delete []m_pUnmapStep5;
- delete m_pim;
- // Reset step to "nothing"
- m_sStep = 0;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Remap the pixels using the specified map
- ////////////////////////////////////////////////////////////////////////////////
- static void Remap(
- unsigned char* aucMap)
- {
- // Jon brought up a potential problem with calling rspLockBuffer(), which
- // is BLiT's version of this. In debug mode, it apparently doesn't do
- // anything because it assumes you will be calling a BLiT function, which
- // would actually do the locking. Since we don't call a BLiT function
- // here, the end result would be no locking. This is only a problem in
- // debug mode. We'll have to check into a better solution, but for now
- // I'm just calling the "real" buffer lock.
- U8* pu8VideoBuf;
- long lPitch;
- // Note that we only need to do this in the case that the buffer is not already
- // locked. Since we keep it locked while the game is running now, we don't need
- // it (note also regarding the lock comment above that currently rspLockBuffer()
- // does the lock even in DEBUG mode)
- // IF you comment this back in, remember to comment in the unlock as well!
- #if 0
- if (rspLockVideoBuffer((void**)&pu8VideoBuf, &lPitch) ) == 0)
- {
- #else
- if (rspLockBuffer() == 0)
- {
- pu8VideoBuf = g_pimScreenBuf->m_pData;
- lPitch = g_pimScreenBuf->m_lPitch;
- #endif
- short sHeight = g_pimScreenBuf->m_sHeight;
- short sWidth = g_pimScreenBuf->m_sWidth;
- short sWidth2;
- long lNextRow = lPitch - (long)sWidth;
- unsigned char* pBuf = pu8VideoBuf;
- if ((sHeight > 0) && (sWidth > 0))
- {
- do {
- sWidth2 = sWidth;
- do {
- *pBuf = *(aucMap + (long)*pBuf); // may be faster than aucMap[*pBuf]
- pBuf++;
- } while (--sWidth2);
- pBuf += lNextRow;
- } while (--sHeight);
- }
- #if 0
- rspUnlockVideoBuffer();
- #else
- rspUnlockBuffer();
- #endif
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // EOF
- ////////////////////////////////////////////////////////////////////////////////
|