1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-11-14 06:59:42 +01:00

Compare commits

...

10 commits

Author SHA1 Message Date
leo60228
b5ef10cae5 Consistently use Screen::GetWindowSize 2021-01-18 13:10:22 -05:00
leo60228
46d8599f62 Use SDL_GetRendererOutputSize instead of SDL_GetWindowSize
SDL_GetRendererOutputSize will always return the actual size, even in
some obscure HiDPI/macOS cases.
2021-01-18 13:10:06 -05:00
leo60228
d9fff2a8c3 Set SDL window as DPI-aware
This works on macOS, Wayland, and a few more esoteric platforms. X11
doesn't have a concept of DPI-awareness. Note that with this flag
SDL_GetWindowSize() isn't guaranteed to return the actual window size.
2021-01-18 13:09:47 -05:00
Misa
a32fc4a307 Improve support for retextured checkpoints and terminals in the editor
Retextured checkpoints have always been in the game, but clicking on
them in the editor would lead to them losing their retextured-ness. So,
checkpoints should be left alone if their p1 isn't either 0 or 1. Also,
they don't show up properly in the editor, so that's fixed, too.

Retextured and flipped terminals were added in 2.3, and show up properly
in-game, but don't properly show up in the editor, either. So now they
show up in the editor. Additionally, clicking on them will flip the
terminal as well, but only if its p1 is 0 or 1, just like checkpoints
now do.
2021-01-18 13:07:32 -05:00
Misa
afba320083 Remove duplicate graphics.Makebfont() in main()
This call to Makebfont() always existed, but ever since 2.3's per-level
custom assets were added, graphics.reloadresources() also calls
graphics.Makebfont(), meaning this call is unnecessary. Calling it twice
results in graphics.bfont and graphics.flipbfont having twice the number
of elements, until custom assets get mounted (or unmounted, technically).
2021-01-18 13:06:59 -05:00
Misa
adbab6355b Free data upon failure in LoadImage()
Otherwise, if SDL_CreateRGBSurfaceFrom() returned NULL, then this memory
would be leaked.
2021-01-18 13:06:43 -05:00
Misa
626aac59fb Fix No Death Mode results being reset before being shown
This does the same thing as the last commit, but for No Death Mode
instead of Time Trials. Whenever you die in No Death Mode, or complete
it, all the relevant variables get copied to variables prefixed with
'ndmresult' that never get reset by script.hardreset(), and these
variables are what titlerender() use, instead of the "live" ones.
2021-01-18 13:06:15 -05:00
Misa
4d7baa9e9e Fix Time Trial results being reset before being shown
This makes it so when a Time Trial gets completed, all the relevant
variables get copied onto variables prefixed with 'timetrialresult',
which never get reset by script.hardreset(). Then titlerender() will use
those variables accordingly.
2021-01-18 13:06:15 -05:00
Misa
ee0ba8a723 Clean up all exit paths to the menu to use common code
There are multiple different exit paths to the main menu. In 2.2, they
all had a bunch of copy-pasted code. In 2.3 currently, most of them use
game.quittomenu(), but there are some stragglers that still use
hand-copied code.

This is a bit of a problem, because all exit paths should consistently
have FILESYSTEM_unmountassets(), as part of the 2.3 feature of per-level
custom assets. Furthermore, most (but not all) of the paths call
script.hardreset() too, and some of the stragglers don't. So there could
be something persisting through to the title screen (like a really long
flash/shake timer) that could only persist if exiting to the title
screen through those paths.

But, actually, it seems like there's a good reason for some of those to
not call script.hardreset() - namely, dying or completing No Death Mode
and completing a Time Trial presents some information onscreen that
would get reset by script.hardreset(), so I'll fix that in a later
commit.

So what I've done for this commit is found every exit path that didn't
already use game.quittomenu(), and made them use game.quittomenu(). As
well, some of them had special handling that existed on top of them
already having a corresponding entry in game.quittomenu() (but the path
would take the special handling because it never did game.quittomenu()),
so I removed that special handling as well (e.g. exiting from a custom
level used returntomenu(Menu::levellist) when quittomenu() already had
that same returntomenu()).

The menu that exiting from the level editor returns to is now handled in
game.quittomenu() as well, where the map.custommode branch now also
checks for map.custommodeforreal. Unfortunately, it seems like entering
the level editor doesn't properly initialize map.custommode, so entering
the level editor now initializes map.custommode, too.

I've also taken the music.play(6) out of game.quittomenu(), because not
all exit paths immediately play Presenting VVVVVV, so all exit paths
that DO immediately play Presenting VVVVVV now have music.play(6)
special-cased for them, which is fine enough for me.

Here is the list of all exit paths to the menu:
- Exiting through the pause menu (without glitchrunner mode)
- Exiting through the pause menu (with glitchrunner mode)
- Completing a custom level
- Completing a Time Trial
- Dying in No Death Mode
- Completing No Death Mode
- Completing an Intermission replay
- Exiting from the level editor
- Completing the main game
2021-01-18 13:06:15 -05:00
Misa
07cc5f68ac Remove commented-out code from gamestates 3101 and 3500
Comments in general don't get verified by the compiler, but
commented-out code is even worse. Especially since this looks to be
outdated code.

As always, if we need some of this code, then we can just look back in
the Git history.
2021-01-18 13:06:15 -05:00
10 changed files with 105 additions and 100 deletions

View file

@ -171,10 +171,13 @@ void Game::init(void)
nodeathmode = false;
nocutscenes = false;
ndmresultcrewrescued = 0;
ndmresulttrinkets = 0;
customcol=0;
SDL_memset(crewstats, false, sizeof(crewstats));
SDL_memset(ndmresultcrewstats, false, sizeof(ndmresultcrewstats));
SDL_memset(tele_crewstats, false, sizeof(tele_crewstats));
SDL_memset(quick_crewstats, false, sizeof(quick_crewstats));
SDL_memset(besttimes, -1, sizeof(besttimes));
@ -227,6 +230,10 @@ void Game::init(void)
timetrialpar = 0;
timetrialresulttime = 0;
timetrialresultframes = 0;
timetrialresultshinytarget = 0;
timetrialresulttrinkets = 0;
timetrialresultpar = 0;
timetrialresultdeaths = 0;
totalflips = 0;
hardestroom = "Welcome Aboard";
@ -1288,6 +1295,7 @@ void Game::updatestate()
break;
case 81:
quittomenu();
music.play(6); //should be after quittomenu()
state = 0;
break;
@ -1295,8 +1303,14 @@ void Game::updatestate()
//Time Trial Complete!
obj.removetrigger(82);
hascontrol = false;
timetrialresulttime = seconds + (minutes * 60) + (hours * 60 * 60);
timetrialresultframes = frames;
timetrialresulttrinkets = trinkets();
timetrialresultshinytarget = timetrialshinytarget;
timetrialresultpar = timetrialpar;
timetrialresultdeaths = deathcounts;
timetrialrank = 0;
if (timetrialresulttime <= timetrialpar) timetrialrank++;
if (trinkets() >= timetrialshinytarget) timetrialrank++;
@ -1309,7 +1323,7 @@ void Game::updatestate()
besttimes[timetriallevel] = timetrialresulttime;
bestframes[timetriallevel] = timetrialresultframes;
}
if (trinkets() > besttrinkets[timetriallevel] || besttrinkets[timetriallevel]==-1)
if (timetrialresulttrinkets > besttrinkets[timetriallevel] || besttrinkets[timetriallevel]==-1)
{
besttrinkets[timetriallevel] = trinkets();
}
@ -1341,11 +1355,7 @@ void Game::updatestate()
if(graphics.fademode == 1) state++;
break;
case 84:
graphics.flipmode = false;
gamestate = TITLEMODE;
graphics.fademode = 4;
graphics.backgrounddrawn = true;
graphics.titlebg.tdrawback = true;
quittomenu();
createmenu(Menu::timetrialcomplete);
state = 0;
break;
@ -1871,7 +1881,6 @@ void Game::updatestate()
if(!muted && ed.levmusic>0) music.fadeMusicVolumeIn(3000);
}
graphics.showcutscenebars = false;
returntomenu(Menu::levellist);
break;
#endif
case 1014:
@ -1896,6 +1905,7 @@ void Game::updatestate()
}
#endif
quittomenu();
music.play(6); //should be after quittomenu()
state = 0;
break;
@ -2852,7 +2862,6 @@ void Game::updatestate()
{
graphics.fademode = 2;
companion = 0;
returnmenu();
state=3100;
}
else
@ -2883,7 +2892,6 @@ void Game::updatestate()
state++;
graphics.fademode = 2;
music.fadeout();
returnmenu();
state=3100;
}
else
@ -2909,44 +2917,15 @@ void Game::updatestate()
if(graphics.fademode == 1) state++;
break;
case 3101:
graphics.flipmode = false;
gamestate = TITLEMODE;
graphics.fademode = 4;
graphics.backgrounddrawn = true;
graphics.titlebg.tdrawback = true;
music.play(6);
quittomenu();
music.play(6); //should be after quittomenu();
state = 0;
break;
//startscript = true; newscript="returntohub";
//state = 0;
/*case 3025:
if (recording == 1) {
//if recording the input, output it to debug here
trace(recordstring);
help.toclipboard(recordstring);
}
test = true; teststring = recordstring;
graphics.createtextbox(" Congratulations! ", 50, 80, 164, 164, 255);
graphics.addline("");
graphics.addline("Your play of this level has");
graphics.addline("been copied to the clipboard.");
graphics.addline("");
graphics.addline("Please consider pasting and");
graphics.addline("sending it to me! Even if you");
graphics.addline("made a lot of mistakes - knowing");
graphics.addline("exactly where people are having");
graphics.addline("trouble is extremely useful!");
graphics.textboxcenter();
state = 0;
break;*/
case 3500:
music.fadeout();
state++;
statedelay = 120;
//state = 3511; //testing
break;
case 3501:
//Game complete!
@ -3243,11 +3222,8 @@ void Game::updatestate()
if(graphics.fademode == 1) state++;
break;
case 3522:
graphics.flipmode = false;
gamestate = TITLEMODE;
graphics.fademode = 4;
graphics.backgrounddrawn = true;
graphics.titlebg.tdrawback = true;
copyndmresults();
quittomenu();
createmenu(Menu::nodeathmodecomplete);
state = 0;
break;
@ -6997,9 +6973,7 @@ void Game::quittomenu()
{
gamestate = TITLEMODE;
graphics.fademode = 4;
FILESYSTEM_unmountassets(); // should be before music.play(6)
music.play(6);
graphics.backgrounddrawn = false;
FILESYSTEM_unmountassets();
graphics.titlebg.tdrawback = true;
graphics.flipmode = false;
//Don't be stuck on the summary screen,
@ -7020,7 +6994,15 @@ void Game::quittomenu()
}
else if (map.custommode)
{
returntomenu(Menu::levellist);
if (map.custommodeforreal)
{
returntomenu(Menu::levellist);
}
else
{
//Returning from editor
returntomenu(Menu::playerworlds);
}
}
else if (save_exists() || anything_unlocked())
{
@ -7138,3 +7120,11 @@ void Game::mapmenuchange(const int newgamestate)
}
graphics.oldmenuoffset = graphics.menuoffset;
}
void Game::copyndmresults()
{
ndmresultcrewrescued = crewrescued();
ndmresulttrinkets = trinkets();
ndmresulthardestroom = hardestroom;
SDL_memcpy(ndmresultcrewstats, crewstats, sizeof(ndmresultcrewstats));
}

View file

@ -290,11 +290,17 @@ public:
bool nodeathmode;
int gameoverdelay;
bool nocutscenes;
int ndmresultcrewrescued;
int ndmresulttrinkets;
std::string ndmresulthardestroom;
void copyndmresults();
//Time Trials
bool intimetrial, timetrialparlost;
int timetrialcountdown, timetrialshinytarget, timetriallevel;
int timetrialpar, timetrialresulttime, timetrialresultframes, timetrialrank;
int timetrialresultshinytarget, timetrialresulttrinkets, timetrialresultpar;
int timetrialresultdeaths;
int creditposition;
int oldcreditposition;
@ -304,6 +310,7 @@ public:
static const int numcrew = 6;
bool crewstats[numcrew];
bool ndmresultcrewstats[numcrew];
bool alarmon;
int alarmdelay;

View file

@ -76,6 +76,7 @@ static SDL_Surface* LoadImage(const char *filename, bool noBlend = true, bool no
}
else
{
SDL_free(data);
fprintf(stderr,"Image not found: %s\n", filename);
SDL_assert(0 && "Image not found! See stderr.");
return NULL;

View file

@ -2117,6 +2117,7 @@ void mapinput()
else
{
game.quittomenu();
music.play(6); //should be after game.quittomenu()
game.fadetomenu = false;
}
}

View file

@ -113,13 +113,9 @@ void gamecompletelogic2()
game.savetele();
music.currentsong=tmp;
//Return to game
graphics.titlebg.colstate = 10;
game.gamestate = TITLEMODE;
graphics.fademode = 4;
FILESYSTEM_unmountassets(); // should be before music.playef(18)
music.playef(18);
game.returntomenu(Menu::play);
game.quittomenu();
game.createmenu(Menu::gamecompletecontinue);
graphics.titlebg.colstate = 10;
map.nexttowercolour();
}
}
@ -449,7 +445,11 @@ void gamelogic()
game.gethardestroom();
//start depressing sequence here...
if (game.gameoverdelay <= -10 && graphics.fademode==0) graphics.fademode = 2;
if (graphics.fademode == 1) script.resetgametomenu();
if (graphics.fademode == 1)
{
game.copyndmresults();
script.resetgametomenu();
}
}
else
{

View file

@ -686,22 +686,22 @@ static void menurender()
{
graphics.bigprint( -1, 25, "GAME OVER", tr, tg, tb, true, 3);
for (int i = 0; i < 6; i++)
for (size_t i = 0; i < SDL_arraysize(game.ndmresultcrewstats); i++)
{
graphics.drawcrewman(169-(3*42)+(i*42), 68, i, game.crewstats[i], true);
graphics.drawcrewman(169-(3*42)+(i*42), 68, i, game.ndmresultcrewstats[i], true);
}
std::string tempstring;
tempstring = "You rescued " + help.number(game.crewrescued()) + (game.crewrescued() == 1 ? " crewmate" : " crewmates");
tempstring = "You rescued " + help.number(game.ndmresultcrewrescued) + (game.ndmresultcrewrescued == 1 ? " crewmate" : " crewmates");
graphics.Print(0, 100, tempstring, tr, tg, tb, true);
tempstring = "and found " + help.number(game.trinkets()) + (game.trinkets() == 1 ? " trinket." : " trinkets.");
tempstring = "and found " + help.number(game.ndmresulttrinkets) + (game.ndmresulttrinkets == 1 ? " trinket." : " trinkets.");
graphics.Print(0, 110, tempstring, tr, tg, tb, true);
tempstring = "You managed to reach:";
graphics.Print(0, 145, tempstring, tr, tg, tb, true);
graphics.Print(0, 155, game.hardestroom, tr, tg, tb, true);
graphics.Print(0, 155, game.ndmresulthardestroom, tr, tg, tb, true);
switch (game.crewrescued())
switch (game.ndmresultcrewrescued)
{
case 1:
tempstring = "Keep trying! You'll get there!";
@ -731,14 +731,14 @@ static void menurender()
{
graphics.bigprint( -1, 8, "WOW", tr, tg, tb, true, 4);
for (int i = 0; i < 6; i++)
for (size_t i = 0; i < SDL_arraysize(game.ndmresultcrewstats); i++)
{
graphics.drawcrewman(169-(3*42)+(i*42), 68, i, game.crewstats[i], true);
graphics.drawcrewman(169-(3*42)+(i*42), 68, i, game.ndmresultcrewstats[i], true);
}
std::string tempstring = "You rescued all the crewmates!";
graphics.Print(0, 100, tempstring, tr, tg, tb, true);
tempstring = "And you found " + help.number(game.trinkets()) + " trinkets.";
tempstring = "And you found " + help.number(game.ndmresulttrinkets) + " trinkets.";
graphics.Print(0, 110, tempstring, tr, tg, tb, true);
graphics.Print(0, 160, "A new trophy has been awarded and", tr, tg, tb, true);
@ -752,30 +752,30 @@ static void menurender()
{
graphics.bigprint( -1, 20, "Results", tr, tg, tb, true, 3);
std::string tempstring = game.resulttimestring() + " / " + game.partimestring() + ".99";
std::string tempstring = game.resulttimestring() + " / " + game.timetstring(game.timetrialresultpar) + ".99";
graphics.drawspritesetcol(30, 80-15, 50, 22);
graphics.Print(65, 80-15, "TIME TAKEN:", 255, 255, 255);
graphics.Print(65, 90-15, tempstring, tr, tg, tb);
if (game.timetrialresulttime <= game.timetrialpar)
if (game.timetrialresulttime <= game.timetrialresultpar)
{
graphics.Print(220, 85-15, "+1 Rank!", 255, 255, 255);
}
tempstring = help.String(game.deathcounts);
tempstring = help.String(game.timetrialresultdeaths);
graphics.drawspritesetcol(30-4, 80+20-4, 12, 22);
graphics.Print(65, 80+20, "NUMBER OF DEATHS:", 255, 255, 255);
graphics.Print(65, 90+20, tempstring, tr, tg, tb);
if (game.deathcounts == 0)
if (game.timetrialresultdeaths == 0)
{
graphics.Print(220, 85+20, "+1 Rank!", 255, 255, 255);
}
tempstring = help.String(game.trinkets()) + " of " + help.String(game.timetrialshinytarget);
tempstring = help.String(game.timetrialresulttrinkets) + " of " + help.String(game.timetrialresultshinytarget);
graphics.drawspritesetcol(30, 80+55, 22, 22);
graphics.Print(65, 80+55, "SHINY TRINKETS:", 255, 255, 255);
graphics.Print(65, 90+55, tempstring, tr, tg, tb);
if (game.trinkets() >= game.timetrialshinytarget)
if (game.timetrialresulttrinkets >= game.timetrialresultshinytarget)
{
graphics.Print(220, 85+55, "+1 Rank!", 255, 255, 255);
}

View file

@ -60,7 +60,7 @@ void Screen::init(const ScreenSettings& settings)
SDL_CreateWindowAndRenderer(
640,
480,
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE,
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI,
&m_window,
&m_renderer
);
@ -170,7 +170,7 @@ void Screen::ResizeScreen(int x, int y)
if (stretchMode == 1)
{
int winX, winY;
SDL_GetWindowSize(m_window, &winX, &winY);
GetWindowSize(&winX, &winY);
int result = SDL_RenderSetLogicalSize(m_renderer, winX, winY);
if (result != 0)
{
@ -256,7 +256,7 @@ void Screen::ResizeToNearestMultiple()
void Screen::GetWindowSize(int* x, int* y)
{
SDL_GetWindowSize(m_window, x, y);
SDL_GetRendererOutputSize(m_renderer, x, y);
}
void Screen::UpdateScreen(SDL_Surface* buffer, SDL_Rect* rect )

View file

@ -2627,11 +2627,8 @@ void scriptclass::run()
void scriptclass::resetgametomenu()
{
game.gamestate = TITLEMODE;
graphics.flipmode = false;
obj.entities.clear();
graphics.fademode = 4;
graphics.titlebg.tdrawback = true;
game.quittomenu();
game.createmenu(Menu::gameover);
}
@ -3262,6 +3259,8 @@ void scriptclass::startgamemode( int t )
hardreset();
ed.reset();
music.fadeout();
map.custommode = true;
map.custommodeforreal = false;
game.gamestate = EDITORMODE;
game.jumpheld = true;
@ -3595,7 +3594,6 @@ void scriptclass::hardreset()
game.timetrialshinytarget = 0;
game.timetrialparlost = false;
game.timetrialpar = 0;
game.timetrialresulttime = 0;
game.totalflips = 0;
game.hardestroom = "Welcome Aboard";

View file

@ -2660,14 +2660,7 @@ void editorrender()
fillboxabs((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8),16,16,graphics.getRGB(164,164,255));
break;
case 10: //Checkpoints
if(edentity[i].p1==0) //From roof
{
graphics.drawsprite((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8),20,196,196,196);
}
else if(edentity[i].p1==1) //From floor
{
graphics.drawsprite((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8),21,196,196,196);
}
graphics.drawsprite((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8),20 + edentity[i].p1,196,196,196);
fillboxabs((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8),16,16,graphics.getRGB(164,164,255));
break;
case 11: //Gravity lines
@ -2767,13 +2760,27 @@ void editorrender()
graphics.Print((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8), edentity[i].scriptname, 196, 196, 255 - help.glow);
break;
case 18: //Terminals
graphics.drawsprite((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8)+8,17,96,96,96);
{
int usethistile = edentity[i].p1;
int usethisy = (edentity[i].y % 30) * 8;
// Not a boolean: just swapping 0 and 1, leaving the rest alone
if (usethistile == 0)
{
usethistile = 1; // Unflipped
}
else if (usethistile == 1)
{
usethistile = 0; // Flipped;
usethisy -= 8;
}
graphics.drawsprite((edentity[i].x*8)- (ed.levx*40*8), usethisy + 8, usethistile + 16, 96,96,96);
fillboxabs((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8),16,24,graphics.getRGB(164,164,164));
if(temp2==i)
{
graphics.bprint((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8)-8,edentity[i].scriptname,210,210,255);
}
break;
}
case 19: //Script Triggers
fillboxabs((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8),edentity[i].p1*8,edentity[i].p2*8,graphics.getRGB(255,164,255));
fillboxabs((edentity[i].x*8)- (ed.levx*40*8),(edentity[i].y*8)- (ed.levy*30*8),8,8,graphics.getRGB(255,255,255));
@ -3627,18 +3634,11 @@ void editorlogic()
if (graphics.fademode == 1)
{
//Return to game
map.nexttowercolour();
graphics.titlebg.colstate = 10;
game.gamestate = TITLEMODE;
script.hardreset();
graphics.fademode = 4;
music.haltdasmusik();
FILESYSTEM_unmountassets(); // should be before music.play(6)
music.play(6);
map.nexttowercolour();
game.quittomenu();
music.play(6); //should be before game.quittomenu()
ed.settingsmod=false;
graphics.backgrounddrawn=false;
game.returntomenu(Menu::playerworlds);
}
}
@ -5170,7 +5170,11 @@ void editorinput()
}
else if(edentity[tmp].t==10)
{
edentity[tmp].p1=(edentity[tmp].p1+1)%2;
// If it's not textured as a checkpoint, leave it alone
if (edentity[tmp].p1 == 0 || edentity[tmp].p1 == 1)
{
edentity[tmp].p1=(edentity[tmp].p1+1)%2;
}
ed.lclickdelay=1;
}
else if(edentity[tmp].t==11)
@ -5199,6 +5203,12 @@ void editorinput()
ed.lclickdelay=1;
ed.textent=tmp;
ed.getlin(TEXT_SCRIPT, "Enter script name:", &(edentity[ed.textent].scriptname));
if (edentity[tmp].t == 18
&& (edentity[tmp].p1 == 0 || edentity[tmp].p1 == 1))
{
// Flip the terminal, but if it's not textured as a terminal leave it alone
edentity[tmp].p1 = (edentity[tmp].p1 + 1) % 2;
}
}
}
}

View file

@ -265,8 +265,6 @@ int main(int argc, char *argv[])
SDL_SetSurfaceBlendMode(graphics.ghostbuffer, SDL_BLENDMODE_BLEND);
SDL_SetSurfaceAlphaMod(graphics.ghostbuffer, 127);
graphics.Makebfont();
graphics.foregroundBuffer = CREATE_SURFACE(320, 240);
SDL_SetSurfaceBlendMode(graphics.foregroundBuffer, SDL_BLENDMODE_BLEND);