1
0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-06-18 10:38:31 +02:00
VVVVVV/desktop_version/src/Ent.cpp

655 lines
9.4 KiB
C++
Raw Normal View History

2020-01-01 21:29:24 +01:00
#include "Ent.h"
#include "Game.h"
#include "Graphics.h"
2020-01-01 21:29:24 +01:00
entclass::entclass()
{
invis = false;
type = 0;
size = 0;
tile = 0;
rule = 0;
state = 0;
statedelay = 0;
life = 0;
colour = 0;
para = 0;
behave = 0;
animate = 0;
xp = 0;
yp = 0;
ax = 0;
ay = 0;
vx = 0;
vy = 0;
w = 16;
h = 16;
cx = 0;
cy = 0;
newxp = 0;
newyp = 0;
oldxp = 0;
oldyp = 0;
2020-01-01 21:29:24 +01:00
x1 = 0;
y1 = 0;
x2 = 320;
y2 = 240;
gravity = false;
onground = 0;
onroof = 0;
onentity = 0;
harmful = false;
onwall = 0;
onxwall = 0;
onywall = 0;
isplatform = false;
framedelay = 0;
drawframe = 0;
walkingframe = 0;
dir = 0;
actionframe = 0;
Fix, for in-GAMEMODE sprites, their colors updating too fast Okay, so the problem here is that Graphics::setcol() is called right before a sprite is drawn in a render function, but render functions are done in deltatime, meaning that the color of a sprite keeps being recalculated every time. This only affects sprites that use fRandom() (the other thing that can dynamically determine a color is help.glow, but that's only updated in the fixed-timestep loop), but is especially noticeable for sprites that flash wildly, like the teleporter, trinket, and elephant. To fix this, we need to make the color be recalculated only in the fixed-timestep loop. However, this means that we MUST store the color of the sprite SOMEWHERE for the delta-timesteps to render it, otherwise the color calculation will just be lost or something. So each entity now has a new attribute, `realcol`, which is the actual raw color used to render the sprite in render functions. This is not to be confused with their `colour` attribute, which is more akin to a color "ID" of sorts, but which isn't an actual color. At the end of gamelogic(), as well as when an entity is first created, the `colour` is given to Graphics::setcol() and then `realcol` gets set to the actual color. Then when it comes time to render the entity, `realcol` gets used instead. Gravitron squares are a somewhat tricky case where there's technically TWO colors for it - one is the actual sprite itself and the other is the indicator. However, usually the indicator and the square aren't both onscreen at the same time, so we can simply switch the realcol between the two as needed. However, we can't use this system for the sprite colors used on the title and map screen, so we'll have to do something else for those.
2020-05-01 02:34:37 +02:00
realcol = 0;
Restore previous oldxp/oldyp variables in favor of lerpoldxp/lerpoldyp I was investigating a desync in my Nova TAS, and it turns out that the gravity line collision functions check for the `oldxp` and `oldyp` of the player, i.e. their position on the previous frame, along with their position on the current frame. So, if the player either collided with the gravity line last frame or this frame, then the player collided with the gravity line this frame. Except, that's not actually true. It turns out that `oldxp` and `oldyp` don't necessarily always correspond to the `xp` and `yp` of the player on the previous frame. It turns out that your `oldyp` will be updated if you stand on a vertically moving platform, before the gravity line collision function gets ran. So, if you were colliding with a gravity line on the previous frame, but you got moved out of there by a vertically moving platform, then you just don't collide with the gravity line at all. However, this behavior changed in 2.3 after my over-30-FPS patch got merged (#220). That patch took advantage of the existing `oldxp` and `oldyp` entity attributes, and uses them to interpolate their positions during rendering to make everything look real smooth. Previously, `oldxp` and `oldyp` would both be updated in `entityclass::updateentitylogic()`. However, I moved it in that patch to update right before `gameinput()` in `main.cpp`. As a result, `oldyp` no longer gets updated whenever the player stands on a vertically moving platform. This ends up desyncing my TAS. As expected, updating `oldyp` in `entityclass::movingplatformfix()` (the function responsible for moving the player whenever they stand on a vertically moving platform) makes it so that my TAS syncs, but the visuals are glitchy when standing on a vertically moving platform. And as much as I'd like to get rid of gravity lines checking for whether you've collided with them on the previous frame, doing that desyncs my TAS, too. In the end, it seems like I should just leave `oldxp` and `oldyp` alone, and switch to using dedicated variables that are never used in the physics of the game. So I'm introducing `lerpoldxp` and `lerpoldyp`, and replacing all instances of using `oldxp` and `oldyp` that my over-30-FPS patch added, with `lerpoldxp` and `lerpoldyp` instead. After doing this, and applying #503 as well, my Nova TAS syncs after some minor but acceptable fixes with Viridian's walkingframe.
2020-10-10 05:58:58 +02:00
lerpoldxp = 0;
lerpoldyp = 0;
2020-01-01 21:29:24 +01:00
}
bool entclass::outside()
{
// Returns true if any point of the entity is outside the map.
// Adjusts velocity for a clean collision.
if (xp < x1)
{
xp = x1;
return true;
}
if (yp < y1)
{
yp = y1;
return true;
}
if (xp + w > x2)
{
xp = x2 - w;
return true;
}
if (yp + h > y2)
{
yp = y2 - h;
return true;
}
return false;
}
void entclass::setenemy( int t )
{
switch(t)
{
case 0:
//lies emitter
switch ((int) para)
{
case 0:
tile = 60;
animate = 2;
colour = 6;
behave = 10;
w = 32;
h = 32;
x1 = -200;
break;
case 1:
yp += 10;
lerpoldyp += 10;
tile = 63;
animate = 100; //LIES
colour = 6;
behave = 11;
para = 9; //destroyed when outside
x1 = -200;
x2 = 400;
w = 26;
h = 10;
cx = 1;
cy = 1;
break;
case 2:
tile = 62;
animate = 100;
colour = 6;
behave = -1;
w = 32;
h = 32;
break;
}
break;
case 1:
//FACTORY emitter
switch ((int) para)
{
case 0:
tile = 72;
animate = 3;
size = 9;
colour = 6;
behave = 12;
w = 64;
h = 40;
cx = 0;
cy = 24;
break;
case 1:
xp += 4;
lerpoldxp += 4;
yp -= 4;
lerpoldyp -= 4;
tile = 76;
animate = 100; // Clouds
colour = 6;
behave = 13;
para = -6; //destroyed when outside
x2 = 400;
w = 32;
h = 12;
cx = 0;
cy = 6;
break;
case 2:
tile = 77;
animate = 100;
colour = 6;
behave = -1;
w = 32;
h = 16;
break;
}
break;
default:
break;
}
}
void entclass::setenemyroom( int rx, int ry )
{
//Simple function to initilise simple enemies
rx -= 100;
ry -= 100;
switch(rn(rx, ry))
{
//Space Station 1
case rn(12, 3): //Security Drone
tile = 36;
colour = 8;
animate = 1;
break;
case rn(13, 3): //Wavelengths
tile = 32;
colour = 7;
animate = 1;
w = 32;
break;
case rn(15, 3): //Traffic
tile = 28;
colour = 6;
animate = 1;
w = 22;
h = 32;
break;
case rn(12, 5): //The Yes Men
tile = 40;
colour = 9;
animate = 1;
w = 20;
h = 20;
break;
case rn(13, 6): //Hunchbacked Guards
tile = 44;
colour = 8;
animate = 1;
w = 16;
h = 20;
break;
case rn(13, 4): //Communication Station
harmful = false;
if (xp == 256)
{
//transmittor
tile = 104;
colour = 4;
animate = 7;
w = 16;
h = 16;
xp -= 24;
lerpoldxp -= 24;
yp -= 16;
lerpoldyp -= 16;
}
else
{
//radar dish
tile =124;
colour = 4;
animate = 6;
w = 32;
h = 32;
cx = 4;
size = 9;
xp -= 4;
lerpoldxp -= 4;
yp -= 32;
lerpoldyp -= 32;
}
break;
//The Lab
case rn(4, 0):
tile = 78;
colour = 7;
animate = 1;
w = 16;
h = 16;
break;
case rn(2, 0):
tile = 88;
colour = 11;
animate = 1;
w = 16;
h = 16;
break;
//Space Station 2
case rn(14, 11):
colour = 17;
break; //Lies
case rn(16, 11):
colour = 8;
break; //Lies
case rn(13, 10):
colour = 11;
break; //Factory
case rn(13, 9):
colour = 9;
break; //Factory
case rn(13, 8):
colour = 8;
break; //Factory
case rn(11, 13): //Truth
tile = 64;
colour = 7;
animate = 100;
w = 44;
h = 10;
size = 10;
break;
case rn(17, 7): //Brass sent us under the top
tile =82;
colour = 8;
animate = 5;
w = 28;
h = 32;
cx = 4;
break;
case rn(10, 7): // (deception)
tile = 92;
colour = 6;
animate = 1;
w = 16;
h = 16;
break;
case rn(14, 13): // (chose poorly)
tile = 56;
colour = 6;
animate = 1;
w = 15;
h = 24;
break;
case rn(13, 12): // (backsliders)
tile = 164;
colour = 7;
animate = 1;
w = 16;
h = 16;
break;
case rn(14, 8): // (wheel of fortune room)
tile = 116;
colour = 12;
animate = 1;
w = 32;
h = 32;
break;
case rn(16, 9): // (seeing dollar signs)
tile = 68;
colour = 7;
animate = 1;
w = 16;
h = 16;
break;
case rn(16, 7): // (tomb of mad carew)
tile = 106;
colour = 7;
animate = 2;
w = 24;
h = 25;
break;
//Warp Zone
case rn(15, 2): // (numbers)
tile = 100;
colour = 6;
animate = 1;
w = 32;
h = 14;
yp += 1;
lerpoldyp += 1;
break;
case rn(16, 2): // (Manequins)
tile = 52;
colour = 7;
animate = 5;
w = 16;
h = 25;
yp -= 4;
lerpoldyp -= 4;
break;
case rn(18, 0): // (Obey)
tile = 51;
colour = 11;
animate = 100;
w = 30;
h = 14;
break;
case rn(19, 1): // Ascending and Descending
tile = 48;
colour = 9;
animate = 5;
w = 16;
h = 16;
break;
case rn(19, 2): // Shockwave Rider
tile = 176;
colour = 6;
animate = 1;
w = 16;
h = 16;
break;
case rn(18, 3): // Mind the gap
tile = 168;
colour = 7;
animate = 1;
w = 16;
h = 16;
break;
case rn(17, 3): // Edge Games
if (yp ==96)
{
tile = 160;
colour = 8;
animate = 1;
w = 16;
h = 16;
}
else
{
tile = 156;
colour = 8;
animate = 1;
w = 16;
h = 16;
}
break;
case rn(16, 0): // I love you
tile = 112;
colour = 8;
animate = 5;
w = 16;
h = 16;
break;
case rn(14, 2): // That's why I have to kill you
tile = 114;
colour = 6;
animate = 5;
w = 16;
h = 16;
break;
case rn(18, 2): // Thinking with Portals
//depends on direction
if (xp ==88)
{
tile = 54+12;
colour = 12;
animate = 100;
w = 60;
h = 16;
size = 10;
}
else
{
tile = 54;
colour = 12;
animate = 100;
w = 60;
h = 16;
size = 10;
}
break;
//Final level
case rn(50-100, 53-100): //The Yes Men
tile = 40;
colour = 9;
animate = 1;
w = 20;
h = 20;
break;
case rn(48-100, 51-100): //Wavelengths
tile = 32;
colour = 7;
animate = 1;
w = 32;
break;
case rn(43-100,52-100): // Ascending and Descending
tile = 48;
colour = 9;
animate = 5;
w = 16;
h = 16;
break;
case rn(46-100,51-100): //kids his age
tile = 88;
colour = 11;
animate = 1;
w = 16;
h = 16;
break;
case rn(43-100,51-100): // Mind the gap
tile = 168;
colour = 7;
animate = 1;
w = 16;
h = 16;
break;
case rn(44-100,51-100): // vertigo?
tile = 172;
colour = 7;
animate = 100;
w = 32;
h = 32;
break;
case rn(44-100,52-100): // (backsliders)
tile = 164;
colour = 7;
animate = 1;
w = 16;
h = 16;
break;
case rn(43-100, 56-100): //Intermission 1
tile = 88;
colour = 21;
animate = 1;
w = 16;
h = 16;
break;
case rn(45-100, 56-100): //Intermission 1
tile = 88;
colour = 21;
animate = 1;
w = 16;
h = 16;
break;
//The elephant
case rn(11, 9):
case rn(12, 9):
case rn(11, 8):
case rn(12, 8):
tile = 0;
colour = 102;
animate = 0;
w = 464;
h = 320;
size = 11;
harmful = false;
break;
}
}
void entclass::settreadmillcolour( int rx, int ry )
{
rx -= 100;
ry -= 100;
rx += 50 - 12;
ry += 50 - 14; //Space Station
tile = 20; //default as blue
switch(rn(rx, ry))
{
case rn(52, 48):
tile = 791;
break; //Cyan
case rn(49, 47):
tile = 24;
break; //Yellow
case rn(56, 44):
tile = 24;
break; //Yellow
case rn(54, 49):
tile = 24;
break; //Yellow
case rn(49, 49):
tile = 36;
break; //Green
case rn(55, 44):
tile = 36;
break; //Green
case rn(54, 43):
tile = 36;
break; //Green
case rn(53, 49):
tile = 36;
break; //Green
case rn(54, 45):
tile = 711;
break; //Green (special)
case rn(51, 48):
tile = 711;
break; //Green (special)
case rn(50, 49):
tile = 28;
break; //Purple
case rn(54, 44):
tile = 28;
break; //Purple
case rn(49, 42):
tile = 28;
break; //Purple
case rn(55, 43):
tile = 28;
break; //Purple
case rn(54, 47):
tile = 28;
break; //Purple
case rn(53, 48):
tile = 28;
break; //Purple
case rn(51, 47):
tile = 32;
break; //Red
case rn(52, 49):
tile = 32;
break; //Red
case rn(48, 43):
tile = 32;
break; //Red
case rn(55, 47):
tile = 32;
break; //Red
case rn(54, 48):
tile = 32;
break; //Red
default:
return;
break;
}
}
Fix, for in-GAMEMODE sprites, their colors updating too fast Okay, so the problem here is that Graphics::setcol() is called right before a sprite is drawn in a render function, but render functions are done in deltatime, meaning that the color of a sprite keeps being recalculated every time. This only affects sprites that use fRandom() (the other thing that can dynamically determine a color is help.glow, but that's only updated in the fixed-timestep loop), but is especially noticeable for sprites that flash wildly, like the teleporter, trinket, and elephant. To fix this, we need to make the color be recalculated only in the fixed-timestep loop. However, this means that we MUST store the color of the sprite SOMEWHERE for the delta-timesteps to render it, otherwise the color calculation will just be lost or something. So each entity now has a new attribute, `realcol`, which is the actual raw color used to render the sprite in render functions. This is not to be confused with their `colour` attribute, which is more akin to a color "ID" of sorts, but which isn't an actual color. At the end of gamelogic(), as well as when an entity is first created, the `colour` is given to Graphics::setcol() and then `realcol` gets set to the actual color. Then when it comes time to render the entity, `realcol` gets used instead. Gravitron squares are a somewhat tricky case where there's technically TWO colors for it - one is the actual sprite itself and the other is the indicator. However, usually the indicator and the square aren't both onscreen at the same time, so we can simply switch the realcol between the two as needed. However, we can't use this system for the sprite colors used on the title and map screen, so we'll have to do something else for those.
2020-05-01 02:34:37 +02:00
void entclass::updatecolour()
{
switch (size)
{
case 0: // Sprites
case 7: // Teleporter
case 9: // Really Big Sprite! (2x2)
case 10: // 2x1 Sprite
case 13: // Special for epilogue: huge hero!
graphics.setcol(colour);
realcol = graphics.ct.colour;
break;
case 3: // Big chunky pixels!
realcol = graphics.bigchunkygetcol(colour);
break;
case 4: // Small pickups
graphics.huetilesetcol(colour);
realcol = graphics.ct.colour;
break;
case 11: // The fucking elephant
if (game.noflashingmode)
{
graphics.setcol(22);
}
else
{
graphics.setcol(colour);
}
realcol = graphics.ct.colour;
break;
case 12: // Regular sprites that don't wrap
// if we're outside the screen, we need to draw indicators
if ((xp < -20 && vx > 0) || (xp > 340 && vx < 0))
{
graphics.setcol(23);
}
else
{
graphics.setcol(colour);
}
realcol = graphics.ct.colour;
break;
default:
break;
}
}
bool entclass::ishumanoid()
{
return type == 0
|| type == 12
|| type == 14
|| type == 55;
}