mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2025-01-24 17:54:59 +01:00
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)
This commit is contained in:
parent
b2f842376b
commit
eb52657c23
8 changed files with 108 additions and 5 deletions
|
@ -364,6 +364,8 @@ void Game::init(void)
|
||||||
|
|
||||||
skipfakeload = false;
|
skipfakeload = false;
|
||||||
|
|
||||||
|
ghostsenabled = false;
|
||||||
|
|
||||||
cliplaytest = false;
|
cliplaytest = false;
|
||||||
playx = 0;
|
playx = 0;
|
||||||
playy = 0;
|
playy = 0;
|
||||||
|
@ -4461,6 +4463,11 @@ void Game::loadstats()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pKey == "ghostsenabled")
|
||||||
|
{
|
||||||
|
ghostsenabled = atoi(pText);
|
||||||
|
}
|
||||||
|
|
||||||
if (pKey == "skipfakeload")
|
if (pKey == "skipfakeload")
|
||||||
{
|
{
|
||||||
skipfakeload = atoi(pText);
|
skipfakeload = atoi(pText);
|
||||||
|
@ -4691,6 +4698,10 @@ void Game::savestats()
|
||||||
msg->LinkEndChild( doc.NewText( help.String(usingmmmmmm).c_str()));
|
msg->LinkEndChild( doc.NewText( help.String(usingmmmmmm).c_str()));
|
||||||
dataNode->LinkEndChild( msg );
|
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 = doc.NewElement("skipfakeload");
|
||||||
msg->LinkEndChild(doc.NewText(help.String((int) skipfakeload).c_str()));
|
msg->LinkEndChild(doc.NewText(help.String((int) skipfakeload).c_str()));
|
||||||
dataNode->LinkEndChild(msg);
|
dataNode->LinkEndChild(msg);
|
||||||
|
@ -6778,11 +6789,12 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ )
|
||||||
option("change description");
|
option("change description");
|
||||||
option("edit scripts");
|
option("edit scripts");
|
||||||
option("change music");
|
option("change music");
|
||||||
|
option("editor ghosts");
|
||||||
option("load level");
|
option("load level");
|
||||||
option("save level");
|
option("save level");
|
||||||
option("quit to main menu");
|
option("quit to main menu");
|
||||||
|
|
||||||
menuxoff = -50;
|
menuxoff = -46;
|
||||||
menuyoff = -20;
|
menuyoff = -20;
|
||||||
break;
|
break;
|
||||||
case Menu::ed_desc:
|
case Menu::ed_desc:
|
||||||
|
|
|
@ -371,6 +371,7 @@ public:
|
||||||
std::vector<SDL_GameControllerButton> controllerButton_esc;
|
std::vector<SDL_GameControllerButton> controllerButton_esc;
|
||||||
|
|
||||||
bool skipfakeload;
|
bool skipfakeload;
|
||||||
|
bool ghostsenabled;
|
||||||
|
|
||||||
bool cliplaytest;
|
bool cliplaytest;
|
||||||
int playx;
|
int playx;
|
||||||
|
@ -390,6 +391,8 @@ public:
|
||||||
void returntoeditor();
|
void returntoeditor();
|
||||||
bool shouldreturntoeditor;
|
bool shouldreturntoeditor;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int gametimer = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Game game;
|
extern Game game;
|
||||||
|
|
|
@ -286,6 +286,8 @@ public:
|
||||||
bool showmousecursor;
|
bool showmousecursor;
|
||||||
|
|
||||||
std::map<int, int> font_positions;
|
std::map<int, int> font_positions;
|
||||||
|
|
||||||
|
SDL_Surface* ghostbuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Graphics graphics;
|
extern Graphics graphics;
|
||||||
|
|
|
@ -1381,6 +1381,30 @@ void gamerender()
|
||||||
{
|
{
|
||||||
graphics.drawtowerspikes();
|
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!=""))
|
if(map.extrarow==0 || (map.custommode && map.roomname!=""))
|
||||||
|
|
|
@ -3151,6 +3151,7 @@ void scriptclass::startgamemode( int t )
|
||||||
game.customstart();
|
game.customstart();
|
||||||
game.jumpheld = true;
|
game.jumpheld = true;
|
||||||
|
|
||||||
|
ed.ghosts.clear();
|
||||||
|
|
||||||
map.custommode = true;
|
map.custommode = true;
|
||||||
map.customx = 100;
|
map.customx = 100;
|
||||||
|
|
|
@ -383,6 +383,8 @@ void editorclass::reset()
|
||||||
script.customscripts.clear();
|
script.customscripts.clear();
|
||||||
|
|
||||||
returneditoralpha = 0;
|
returneditoralpha = 0;
|
||||||
|
|
||||||
|
ghosts.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorclass::gethooks()
|
void editorclass::gethooks()
|
||||||
|
@ -2238,6 +2240,13 @@ void editormenurender(int tr, int tg, int tb)
|
||||||
{
|
{
|
||||||
case Menu::ed_settings:
|
case Menu::ed_settings:
|
||||||
graphics.bigprint( -1, 75, "Map Settings", tr, tg, tb, true);
|
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;
|
break;
|
||||||
case Menu::ed_desc:
|
case Menu::ed_desc:
|
||||||
if(ed.titlemod)
|
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
|
//Draw Cursor
|
||||||
switch(ed.drawmode)
|
switch(ed.drawmode)
|
||||||
{
|
{
|
||||||
|
@ -3573,6 +3612,10 @@ void editormenuactionpress()
|
||||||
if(ed.levmusic>0) music.play(ed.levmusic);
|
if(ed.levmusic>0) music.play(ed.levmusic);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
music.playef(11);
|
||||||
|
game.ghostsenabled = !game.ghostsenabled;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
//Load level
|
//Load level
|
||||||
ed.settingsmod=false;
|
ed.settingsmod=false;
|
||||||
graphics.backgrounddrawn=false;
|
graphics.backgrounddrawn=false;
|
||||||
|
@ -3586,7 +3629,7 @@ void editormenuactionpress()
|
||||||
game.mapheld=true;
|
game.mapheld=true;
|
||||||
graphics.backgrounddrawn=false;
|
graphics.backgrounddrawn=false;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 5:
|
||||||
//Save level
|
//Save level
|
||||||
ed.settingsmod=false;
|
ed.settingsmod=false;
|
||||||
graphics.backgrounddrawn=false;
|
graphics.backgrounddrawn=false;
|
||||||
|
@ -3600,7 +3643,7 @@ void editormenuactionpress()
|
||||||
game.mapheld=true;
|
game.mapheld=true;
|
||||||
graphics.backgrounddrawn=false;
|
graphics.backgrounddrawn=false;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 6:
|
||||||
music.playef(11);
|
music.playef(11);
|
||||||
game.createmenu(Menu::ed_quit);
|
game.createmenu(Menu::ed_quit);
|
||||||
map.nexttowercolour();
|
map.nexttowercolour();
|
||||||
|
@ -4442,6 +4485,7 @@ void editorinput()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
ed.currentghosts = 0;
|
||||||
if(startpoint==0)
|
if(startpoint==0)
|
||||||
{
|
{
|
||||||
//Checkpoint spawn
|
//Checkpoint spawn
|
||||||
|
|
|
@ -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{
|
class editorclass{
|
||||||
//Special class to handle ALL editor variables locally
|
//Special class to handle ALL editor variables locally
|
||||||
|
@ -228,6 +236,9 @@ class editorclass{
|
||||||
int dmtileeditor;
|
int dmtileeditor;
|
||||||
|
|
||||||
int returneditoralpha;
|
int returneditoralpha;
|
||||||
|
|
||||||
|
std::vector<GhostInfo> 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);
|
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);
|
||||||
|
|
|
@ -175,6 +175,11 @@ int main(int argc, char *argv[])
|
||||||
SDL_SetSurfaceBlendMode(graphics.footerbuffer, SDL_BLENDMODE_BLEND);
|
SDL_SetSurfaceBlendMode(graphics.footerbuffer, SDL_BLENDMODE_BLEND);
|
||||||
SDL_SetSurfaceAlphaMod(graphics.footerbuffer, 127);
|
SDL_SetSurfaceAlphaMod(graphics.footerbuffer, 127);
|
||||||
FillRect(graphics.footerbuffer, SDL_MapRGB(fmt, 0, 0, 0));
|
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.Makebfont();
|
||||||
|
|
||||||
graphics.foregroundBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE ,320 ,240 ,fmt->BitsPerPixel,fmt->Rmask,fmt->Gmask,fmt->Bmask,fmt->Amask );
|
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;
|
volatile Uint32 time, timePrev = 0;
|
||||||
game.infocus = true;
|
game.infocus = true;
|
||||||
key.isActive = true;
|
key.isActive = true;
|
||||||
|
game.gametimer = 0;
|
||||||
|
|
||||||
while(!key.quitProgram)
|
while(!key.quitProgram)
|
||||||
{
|
{
|
||||||
|
@ -384,7 +390,7 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
Mix_Resume(-1);
|
Mix_Resume(-1);
|
||||||
Mix_ResumeMusic();
|
Mix_ResumeMusic();
|
||||||
|
game.gametimer++;
|
||||||
switch(game.gamestate)
|
switch(game.gamestate)
|
||||||
{
|
{
|
||||||
case PRELOADER:
|
case PRELOADER:
|
||||||
|
|
Loading…
Add table
Reference in a new issue