1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-23 10:09:43 +01:00

Fix tile of disappearing platforms during final stretch

During the final stretch, after Viridian turns off the Dimensional
Stability Generator, the map goes all psychedelic and changes colors
every 40 frames. Entities change their colors too, including conveyors,
moving platforms, and disappearing platforms.

But play around with the disappearing platforms for a bit and you'll
notice they seem a bit glitchy. If you run on them at the right time,
the tile they use while disappearing seems to abruptly change whenever
the color of the room changes. If there's a color change while they're
reappearing (when you die and respawn in the same room as them), they'll
have the wrong tile and look like a conveyor. And even if you've never
interacted with them at all, dying and respawning in the same room as
them will change their tile to something wrong and also look like a
conveyor.

So, what's the problem? Well, first off, the tile of every untouched
disappearing platform changing into a conveyor after you die and respawn
in the same room is caused by a block of code in gamelogic() that gets
run on each entity whenever you die. This block of code is the exact
same block of code that gets ran on a disappearing platform if it's in
the middle of disappearing.

As a quick primer, every entity in the game has a state, which is just a
number. You can view each entity's state in
entityclass::updateentities().

State 0 of disappearing platforms is doing nothing, and they start with
an onentity of 1, which means they turn to state 1 when they get
touched. State 1 moves to state 2. State 2 does some decrementing, then
moves to state 3 and sets the onentity to 4. State 3 also does nothing.
After being touched, state 4 makes the platform reappear and move to
state 5, but state 5 does the actual reappearing; state 5 then sets the
state back to 0 and onentity back to 1.

So, back to the copy-pasted block of code. The block of code was
originally intended to fast-forward disappearing platforms if they were
in the middle of disappearing, so the player respawn code would properly
respawn the disappearing platform, instead of leaving it disappeared.
What it does is keep updating the entity, while the state of the entity
is 2, until it is no longer in state 2, then sets it to state 4.

Crucially, the original block of code only ran if the disappearing
platform was in state 2. But the other block of code, which was
copy-pasted with slight modifications, runs on ALL disappearing
platforms in final stretch, regardless of if they are in state 2 or not.

Thus, all untouched platforms will be set to state 4, and state 4 will
do the animation of the platform reappearing, which is invalid given
that the platform never disappeared in the first place. So that's why
dying and respawning in the same room as some disappearing platforms
during final stretch will change their tiles to be conveyors.

It seems to me that doing anything with death is wrong, here. The root
cause is that map.changefinalcol() "resets" the tile of every
disappearing platform, which is a function that gets called on every
color change. The color change has nothing to do with dying, so why
fiddle with the death code?

Thus, I've deleted that entire block of code.

What I've done to fix the issue is to make it so the tile of
disappearing platforms aren't manually controlled. You see, unlike other
entities in the game, the tile of disappearing platforms gets manually
modified whenever it disappears or reappears. Other entities use the
tile as a base and store their tile offset in the separate walkingframe
attribute, which will be added to the tile attribute to produce the
drawframe, which is the final thing that gets rendered - but for
disappearing platforms, their tile gets directly incremented or
decremented whenever they disappear or reappear, so when
map.changefinalcol() gets ran to update the tile of every platform and
conveyor, it basically discards the tile offset that was manually added
in.

Instead, what I've done is make it so disappearing platforms now use
walkingframe, and thus their final drawframe will be their tile plus
their walkingframe. Whenever map.changefinalcol() gets called, it is now
free to modify the tile of disappearing platforms accordingly - after
all, the tile offset is now stored in walkingframe, so no weird
glitchiness can happen there.
This commit is contained in:
Misa 2021-01-09 14:50:06 -08:00
parent a405635cb2
commit f9e76d9dc0
2 changed files with 7 additions and 19 deletions

View file

@ -2506,7 +2506,7 @@ bool entityclass::updateentities( int i )
else if (entities[i].state == 2) else if (entities[i].state == 2)
{ {
entities[i].life--; entities[i].life--;
if (entities[i].life % 3 == 0) entities[i].tile++; if (entities[i].life % 3 == 0) entities[i].walkingframe++;
if (entities[i].life <= 0) if (entities[i].life <= 0)
{ {
disableblockat(entities[i].xp, entities[i].yp); disableblockat(entities[i].xp, entities[i].yp);
@ -2524,19 +2524,19 @@ bool entityclass::updateentities( int i )
createblock(0, entities[i].xp, entities[i].yp, 32, 8); createblock(0, entities[i].xp, entities[i].yp, 32, 8);
entities[i].state = 4; entities[i].state = 4;
entities[i].invis = false; entities[i].invis = false;
entities[i].tile--; entities[i].walkingframe--;
entities[i].state++; entities[i].state++;
entities[i].onentity = 1; entities[i].onentity = 1;
} }
else if (entities[i].state == 5) else if (entities[i].state == 5)
{ {
entities[i].life+=3; entities[i].life+=3;
if (entities[i].life % 3 == 0) entities[i].tile--; if (entities[i].life % 3 == 0) entities[i].walkingframe--;
if (entities[i].life >= 12) if (entities[i].life >= 12)
{ {
entities[i].life = 12; entities[i].life = 12;
entities[i].state = 0; entities[i].state = 0;
entities[i].tile++; entities[i].walkingframe++;
} }
} }
break; break;
@ -3593,6 +3593,9 @@ void entityclass::animateentities( int _i )
break; break;
} }
break; break;
case 2: //Disappearing platforms
entities[_i].drawframe = entities[_i].tile + entities[_i].walkingframe;
break;
case 11: case 11:
entities[_i].drawframe = entities[_i].tile; entities[_i].drawframe = entities[_i].tile;
if(entities[_i].animate==2) if(entities[_i].animate==2)

View file

@ -418,21 +418,6 @@ void gamelogic(void)
} }
if (!entitygone) obj.entities[i].state = 4; if (!entitygone) obj.entities[i].state = 4;
} }
else if (map.finalstretch && obj.entities[i].type == 2)
{
//for the final level. probably something 99% of players won't see.
bool entitygone = false;
while (obj.entities[i].state == 2)
{
entitygone = obj.updateentities(i);
if (entitygone)
{
i--;
break;
}
}
if (!entitygone) obj.entities[i].state = 4;
}
else if (obj.entities[i].type == 23 && game.swnmode && game.deathseq<15) else if (obj.entities[i].type == 23 && game.swnmode && game.deathseq<15)
{ {
//if playing SWN, get the enemies offscreen. //if playing SWN, get the enemies offscreen.