From eb52657c23a193457139b5d64316954f63b6d0ea Mon Sep 17 00:00:00 2001 From: AllyTally Date: Fri, 12 Jun 2020 19:04:35 -0300 Subject: [PATCH] Add a player trail to the editor (ghosts) A few months ago, I added ghosts to the VVVVVV: Community Edition editor. I was told recently I should think about upstreaming it, and with Terry saying go ahead I finally ported them into VVVVVV. There's one slight difference however--you can choose whether you have them or not in the editor's settings menu. They're off by default, and this is saved to the save file. Anyway, when you're playtesting, the game saves the players position, color, room coordinates and sprite every 3 frames. The max is 100, where if it tries to add more, the oldest one gets removed. When you exit playtesting, the saved positions appear one at a time, and you can use the Z key to speed it up. [Here's a video of them in action.](https://o.lol-sa.me/4H21zCv.mp4) --- desktop_version/src/Game.cpp | 14 +++++++++- desktop_version/src/Game.h | 3 +++ desktop_version/src/Graphics.h | 2 ++ desktop_version/src/Render.cpp | 26 +++++++++++++++++- desktop_version/src/Script.cpp | 1 + desktop_version/src/editor.cpp | 48 ++++++++++++++++++++++++++++++++-- desktop_version/src/editor.h | 11 ++++++++ desktop_version/src/main.cpp | 8 +++++- 8 files changed, 108 insertions(+), 5 deletions(-) diff --git a/desktop_version/src/Game.cpp b/desktop_version/src/Game.cpp index be4ca5f1..6627b630 100644 --- a/desktop_version/src/Game.cpp +++ b/desktop_version/src/Game.cpp @@ -364,6 +364,8 @@ void Game::init(void) skipfakeload = false; + ghostsenabled = false; + cliplaytest = false; playx = 0; playy = 0; @@ -4461,6 +4463,11 @@ void Game::loadstats() } } + if (pKey == "ghostsenabled") + { + ghostsenabled = atoi(pText); + } + if (pKey == "skipfakeload") { skipfakeload = atoi(pText); @@ -4691,6 +4698,10 @@ void Game::savestats() msg->LinkEndChild( doc.NewText( help.String(usingmmmmmm).c_str())); dataNode->LinkEndChild( msg ); + msg = doc.NewElement("ghostsenabled"); + msg->LinkEndChild(doc.NewText(help.String((int) ghostsenabled).c_str())); + dataNode->LinkEndChild(msg); + msg = doc.NewElement("skipfakeload"); msg->LinkEndChild(doc.NewText(help.String((int) skipfakeload).c_str())); dataNode->LinkEndChild(msg); @@ -6778,11 +6789,12 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option("change description"); option("edit scripts"); option("change music"); + option("editor ghosts"); option("load level"); option("save level"); option("quit to main menu"); - menuxoff = -50; + menuxoff = -46; menuyoff = -20; break; case Menu::ed_desc: diff --git a/desktop_version/src/Game.h b/desktop_version/src/Game.h index db3043f2..b50f0f58 100644 --- a/desktop_version/src/Game.h +++ b/desktop_version/src/Game.h @@ -371,6 +371,7 @@ public: std::vector controllerButton_esc; bool skipfakeload; + bool ghostsenabled; bool cliplaytest; int playx; @@ -390,6 +391,8 @@ public: void returntoeditor(); bool shouldreturntoeditor; #endif + + int gametimer = 0; }; extern Game game; diff --git a/desktop_version/src/Graphics.h b/desktop_version/src/Graphics.h index 97d13770..3f1bdf91 100644 --- a/desktop_version/src/Graphics.h +++ b/desktop_version/src/Graphics.h @@ -286,6 +286,8 @@ public: bool showmousecursor; std::map font_positions; + + SDL_Surface* ghostbuffer; }; extern Graphics graphics; diff --git a/desktop_version/src/Render.cpp b/desktop_version/src/Render.cpp index 612fdc37..d6f7199b 100644 --- a/desktop_version/src/Render.cpp +++ b/desktop_version/src/Render.cpp @@ -1,4 +1,4 @@ -#include "Render.h" + #include "Render.h" #include "Graphics.h" #include "UtilityClass.h" @@ -1381,6 +1381,30 @@ void gamerender() { graphics.drawtowerspikes(); } + + // Editor ghosts! + if (game.ghostsenabled) + { + if (map.custommode && !map.custommodeforreal) + { + if (game.gametimer % 3 == 0) + { + int i = obj.getplayer(); + GhostInfo ghost; + ghost.rx = game.roomx-100; + ghost.ry = game.roomy-100; + ghost.x = obj.entities[i].xp; + ghost.y = obj.entities[i].yp; + ghost.col = obj.entities[i].colour; + ghost.frame = obj.entities[i].drawframe; + ed.ghosts.push_back(ghost); + } + if (ed.ghosts.size() > 100) + { + ed.ghosts.erase(ed.ghosts.begin()); + } + } + } } if(map.extrarow==0 || (map.custommode && map.roomname!="")) diff --git a/desktop_version/src/Script.cpp b/desktop_version/src/Script.cpp index e5b90bee..1e283017 100644 --- a/desktop_version/src/Script.cpp +++ b/desktop_version/src/Script.cpp @@ -3151,6 +3151,7 @@ void scriptclass::startgamemode( int t ) game.customstart(); game.jumpheld = true; + ed.ghosts.clear(); map.custommode = true; map.customx = 100; diff --git a/desktop_version/src/editor.cpp b/desktop_version/src/editor.cpp index 93aa8c18..c97405bb 100644 --- a/desktop_version/src/editor.cpp +++ b/desktop_version/src/editor.cpp @@ -383,6 +383,8 @@ void editorclass::reset() script.customscripts.clear(); returneditoralpha = 0; + + ghosts.clear(); } void editorclass::gethooks() @@ -2238,6 +2240,13 @@ void editormenurender(int tr, int tg, int tb) { case Menu::ed_settings: graphics.bigprint( -1, 75, "Map Settings", tr, tg, tb, true); + if (game.currentmenuoption == 3) + { + if (!game.ghostsenabled) + graphics.Print(2, 230, "Editor ghost trail is OFF", tr/2, tg/2, tb/2); + else + graphics.Print(2, 230, "Editor ghost trail is ON", tr, tg, tb); + } break; case Menu::ed_desc: if(ed.titlemod) @@ -2801,6 +2810,36 @@ void editorrender() } } + //Draw ghosts (spooky!) + if (game.ghostsenabled) { + SDL_FillRect(graphics.ghostbuffer, NULL, SDL_MapRGBA(graphics.ghostbuffer->format, 0, 0, 0, 0)); + for (int i = 0; i < (int)ed.ghosts.size(); i++) { + if (i <= ed.currentghosts) { // We don't want all of them to show up at once :) + if (ed.ghosts[i].rx != ed.levx || ed.ghosts[i].ry != ed.levy) + continue; + point tpoint; + tpoint.x = ed.ghosts[i].x; + tpoint.y = ed.ghosts[i].y; + graphics.setcol(ed.ghosts[i].col); + Uint32 alpha = graphics.ct.colour & graphics.backBuffer->format->Amask; + Uint32 therest = graphics.ct.colour & 0x00FFFFFF; + alpha = (3 * (alpha >> 24) / 4) << 24; + graphics.ct.colour = therest | alpha; + SDL_Rect drawRect = graphics.sprites_rect; + drawRect.x += tpoint.x; + drawRect.y += tpoint.y; + BlitSurfaceColoured(graphics.sprites[ed.ghosts[i].frame],NULL, graphics.ghostbuffer, &drawRect, graphics.ct); + } + } + if (ed.currentghosts + 1 < (int)ed.ghosts.size()) { + ed.currentghosts++; + if (ed.zmod) ed.currentghosts++; + } else { + ed.currentghosts = (int)ed.ghosts.size() - 1; + } + SDL_BlitSurface(graphics.ghostbuffer, NULL, graphics.backBuffer, &graphics.bg_rect); + } + //Draw Cursor switch(ed.drawmode) { @@ -3573,6 +3612,10 @@ void editormenuactionpress() if(ed.levmusic>0) music.play(ed.levmusic); break; case 3: + music.playef(11); + game.ghostsenabled = !game.ghostsenabled; + break; + case 4: //Load level ed.settingsmod=false; graphics.backgrounddrawn=false; @@ -3586,7 +3629,7 @@ void editormenuactionpress() game.mapheld=true; graphics.backgrounddrawn=false; break; - case 4: + case 5: //Save level ed.settingsmod=false; graphics.backgrounddrawn=false; @@ -3600,7 +3643,7 @@ void editormenuactionpress() game.mapheld=true; graphics.backgrounddrawn=false; break; - case 5: + case 6: music.playef(11); game.createmenu(Menu::ed_quit); map.nexttowercolour(); @@ -4442,6 +4485,7 @@ void editorinput() } else { + ed.currentghosts = 0; if(startpoint==0) { //Checkpoint spawn diff --git a/desktop_version/src/editor.h b/desktop_version/src/editor.h index 2723ce4c..8801ddfd 100644 --- a/desktop_version/src/editor.h +++ b/desktop_version/src/editor.h @@ -75,6 +75,14 @@ private: }; +struct GhostInfo { + int rx; // game.roomx-100 + int ry; // game.roomy-100 + int x; // .xp + int y; // .yp + int col; // .colour + int frame; // .drawframe +}; class editorclass{ //Special class to handle ALL editor variables locally @@ -228,6 +236,9 @@ class editorclass{ int dmtileeditor; int returneditoralpha; + + std::vector ghosts; + int currentghosts = 0; }; void addedentity(int xp, int yp, int tp, int p1=0, int p2=0, int p3=0, int p4=0, int p5=320, int p6=240); diff --git a/desktop_version/src/main.cpp b/desktop_version/src/main.cpp index 82ae8298..12b5fe29 100644 --- a/desktop_version/src/main.cpp +++ b/desktop_version/src/main.cpp @@ -175,6 +175,11 @@ int main(int argc, char *argv[]) SDL_SetSurfaceBlendMode(graphics.footerbuffer, SDL_BLENDMODE_BLEND); SDL_SetSurfaceAlphaMod(graphics.footerbuffer, 127); FillRect(graphics.footerbuffer, SDL_MapRGB(fmt, 0, 0, 0)); + + graphics.ghostbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 240, fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask); + SDL_SetSurfaceBlendMode(graphics.ghostbuffer, SDL_BLENDMODE_BLEND); + SDL_SetSurfaceAlphaMod(graphics.ghostbuffer, 127); + graphics.Makebfont(); graphics.foregroundBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE ,320 ,240 ,fmt->BitsPerPixel,fmt->Rmask,fmt->Gmask,fmt->Bmask,fmt->Amask ); @@ -299,6 +304,7 @@ int main(int argc, char *argv[]) volatile Uint32 time, timePrev = 0; game.infocus = true; key.isActive = true; + game.gametimer = 0; while(!key.quitProgram) { @@ -384,7 +390,7 @@ int main(int argc, char *argv[]) { Mix_Resume(-1); Mix_ResumeMusic(); - + game.gametimer++; switch(game.gamestate) { case PRELOADER: