VVVVVV/desktop_version/src/Entity.h

205 lines
4.3 KiB
C
Raw Normal View History

2020-01-01 21:29:24 +01:00
#ifndef ENTITY_H
#define ENTITY_H
#include <SDL.h>
#include <string>
#include <vector>
2020-01-01 21:29:24 +01:00
#include "Maths.h"
#include "Ent.h"
#include "BlockV.h"
#include "Game.h"
2020-01-01 21:29:24 +01:00
enum
{
BLOCK = 0,
TRIGGER = 1,
DAMAGE = 2,
DIRECTIONAL = 3,
SAFE = 4,
ACTIVITY = 5
};
class entityclass
{
public:
void init(void);
2020-01-01 21:29:24 +01:00
void resetallflags(void);
2020-01-01 21:29:24 +01:00
void fatal_top(void)
2020-01-01 21:29:24 +01:00
{
createblock(DAMAGE, -8, -8, 384, 16);
}
void fatal_bottom(void)
2020-01-01 21:29:24 +01:00
{
createblock(DAMAGE, -8, 224, 384, 16);
}
void fatal_left(void)
2020-01-01 21:29:24 +01:00
{
createblock(DAMAGE, -8, -8, 16, 260);
}
void fatal_right(void)
2020-01-01 21:29:24 +01:00
{
createblock(DAMAGE, 312, -8, 16, 260);
}
int swncolour(int t );
void swnenemiescol(int t);
void gravcreate(int ypos, int dir, int xoff = 0, int yoff = 0);
2020-01-01 21:29:24 +01:00
void generateswnwave(int t);
2020-01-01 21:29:24 +01:00
void createblock(int t, int xp, int yp, int w, int h, int trig = 0, const std::string& script = "");
2020-01-01 21:29:24 +01:00
Fix entity and block indices after destroying them This patch restores some 2.2 behavior, fixing a regression caused by the refactor of properly using std::vectors. In 2.2, the game allocated 200 items in obj.entities, but used a system where each entity had an `active` attribute to signify if the entity actually existed or not. When dealing with entities, you would have to check this `active` flag, or else you'd be dealing with an entity that didn't actually exist. (By the way, what I'm saying applies to blocks and obj.blocks as well, except for some small differing details like the game allocating 500 block slots versus obj.entities's 200.) As a consequence, the game had to use a separate tracking variable, obj.nentity, because obj.entities.size() would just report 200, instead of the actual amount of entities. Needless to say, having to check for `active` and use `obj.nentity` is a bit error-prone, and it's messier than simply using the std::vector the way it was intended. Also, this resulted in a hard limit of 200 entities, which custom level makers ran into surprisingly quite often. 2.3 comes along, and removes the whole system. Now, std::vectors are properly being used, and obj.entities.size() reports the actual number of entities in the vector; you no longer have to check for `active` when dealing with entities of any sort. But there was one previous behavior of 2.2 that this system kind of forgets about - namely, the ability to have holes in between entities. You see, when an entity got disabled in 2.2 (which just meant turning its `active` off), the indices of all other entities stayed the same; the indice of the entity that got disabled stays there as a hole in the array. But when an entity gets removed in 2.3 (previous to this patch), the indices of every entity afterwards in the array get shifted down by one. std::vector isn't really meant to be able to contain holes. Do the indices of entities and blocks matter? Yes; they determine the order in which entities and blocks get evaluated (the highest indice gets evaluated first), and I had to fix some block evaluation order stuff in previous PRs. And in the case of entities, they matter hugely when using the recently-discovered Arbitrary Entity Manipulation glitch (where crewmate script commands are used on arbitrary entities by setting the `i` attribute of `scriptclass` and passing invalid crewmate identifiers to the commands). If you use Arbitrary Entity Manipulation after destroying some entities, there is a chance that your script won't work between 2.2 and 2.3. The indices also still determine the rendering order of entities (highest indice gets drawn first, which means lowest indice gets drawn in front of other entities). As an example: let's say we have the player at 0, a gravity line at 1, and a checkpoint at 2; then we destroy the gravity line and create a crewmate (let's do Violet). If we're able to have holes, then after removing the gravity line, none of the other indices shift. Then Violet will be created at indice 1, and will be drawn in front of the checkpoint. But if we can't have holes, then removing the gravity line results in the indice of the checkpoint shifting down to indice 1. Then Violet is created at indice 2, and gets drawn behind the checkpoint! This is a clear illustration of changing the behavior that existed in 2.2. However, I also don't want to go back to the `active` system of having to check an attribute before operating on an entity. So... what do we do to restore the holes? Well, we don't need to have an `active` attribute, or modify any existing code that operates on entities. Instead, we can just set the attributes of the entities so that they naturally get ignored by everything that comes into contact with it. For entities, we set their invis to true, and their size, type, and rule to -1 (the game never uses a size, type, or rule of -1 anywhere); for blocks, we set their type to -1, and their width and height to 0. obj.entities.size() will no longer necessarily equal the amount of entities in the room; rather, it will be the amount of entity SLOTS that have been allocated. But nothing that uses obj.entities.size() needs to actually know the amount of entities; it's mostly used for iterating over every entity in the vector. Excess entity slots get cleaned up upon every call of mapclass::gotoroom(), which will now deallocate entity slots starting from the end until it hits a player, at which point it will switch to disabling entity slots instead of removing them entirely. The entclass::clear() and blockclass::clear() functions have been restored because we need to call their initialization functions when reusing a block/entity slot; it's possible to create an entity with an invalid type number (it creates a glitchy Viridian), and without calling the initialization function again, it would simply not create anything. After this patch is applied, entity and block indices will be restored to how they behaved in 2.2.
2020-12-27 07:11:34 +01:00
bool disableentity(int t);
void removeallblocks(void);
2020-01-01 21:29:24 +01:00
Fix entity and block indices after destroying them This patch restores some 2.2 behavior, fixing a regression caused by the refactor of properly using std::vectors. In 2.2, the game allocated 200 items in obj.entities, but used a system where each entity had an `active` attribute to signify if the entity actually existed or not. When dealing with entities, you would have to check this `active` flag, or else you'd be dealing with an entity that didn't actually exist. (By the way, what I'm saying applies to blocks and obj.blocks as well, except for some small differing details like the game allocating 500 block slots versus obj.entities's 200.) As a consequence, the game had to use a separate tracking variable, obj.nentity, because obj.entities.size() would just report 200, instead of the actual amount of entities. Needless to say, having to check for `active` and use `obj.nentity` is a bit error-prone, and it's messier than simply using the std::vector the way it was intended. Also, this resulted in a hard limit of 200 entities, which custom level makers ran into surprisingly quite often. 2.3 comes along, and removes the whole system. Now, std::vectors are properly being used, and obj.entities.size() reports the actual number of entities in the vector; you no longer have to check for `active` when dealing with entities of any sort. But there was one previous behavior of 2.2 that this system kind of forgets about - namely, the ability to have holes in between entities. You see, when an entity got disabled in 2.2 (which just meant turning its `active` off), the indices of all other entities stayed the same; the indice of the entity that got disabled stays there as a hole in the array. But when an entity gets removed in 2.3 (previous to this patch), the indices of every entity afterwards in the array get shifted down by one. std::vector isn't really meant to be able to contain holes. Do the indices of entities and blocks matter? Yes; they determine the order in which entities and blocks get evaluated (the highest indice gets evaluated first), and I had to fix some block evaluation order stuff in previous PRs. And in the case of entities, they matter hugely when using the recently-discovered Arbitrary Entity Manipulation glitch (where crewmate script commands are used on arbitrary entities by setting the `i` attribute of `scriptclass` and passing invalid crewmate identifiers to the commands). If you use Arbitrary Entity Manipulation after destroying some entities, there is a chance that your script won't work between 2.2 and 2.3. The indices also still determine the rendering order of entities (highest indice gets drawn first, which means lowest indice gets drawn in front of other entities). As an example: let's say we have the player at 0, a gravity line at 1, and a checkpoint at 2; then we destroy the gravity line and create a crewmate (let's do Violet). If we're able to have holes, then after removing the gravity line, none of the other indices shift. Then Violet will be created at indice 1, and will be drawn in front of the checkpoint. But if we can't have holes, then removing the gravity line results in the indice of the checkpoint shifting down to indice 1. Then Violet is created at indice 2, and gets drawn behind the checkpoint! This is a clear illustration of changing the behavior that existed in 2.2. However, I also don't want to go back to the `active` system of having to check an attribute before operating on an entity. So... what do we do to restore the holes? Well, we don't need to have an `active` attribute, or modify any existing code that operates on entities. Instead, we can just set the attributes of the entities so that they naturally get ignored by everything that comes into contact with it. For entities, we set their invis to true, and their size, type, and rule to -1 (the game never uses a size, type, or rule of -1 anywhere); for blocks, we set their type to -1, and their width and height to 0. obj.entities.size() will no longer necessarily equal the amount of entities in the room; rather, it will be the amount of entity SLOTS that have been allocated. But nothing that uses obj.entities.size() needs to actually know the amount of entities; it's mostly used for iterating over every entity in the vector. Excess entity slots get cleaned up upon every call of mapclass::gotoroom(), which will now deallocate entity slots starting from the end until it hits a player, at which point it will switch to disabling entity slots instead of removing them entirely. The entclass::clear() and blockclass::clear() functions have been restored because we need to call their initialization functions when reusing a block/entity slot; it's possible to create an entity with an invalid type number (it creates a glitchy Viridian), and without calling the initialization function again, it would simply not create anything. After this patch is applied, entity and block indices will be restored to how they behaved in 2.2.
2020-12-27 07:11:34 +01:00
void disableblock(int t);
2020-01-01 21:29:24 +01:00
Fix entity and block indices after destroying them This patch restores some 2.2 behavior, fixing a regression caused by the refactor of properly using std::vectors. In 2.2, the game allocated 200 items in obj.entities, but used a system where each entity had an `active` attribute to signify if the entity actually existed or not. When dealing with entities, you would have to check this `active` flag, or else you'd be dealing with an entity that didn't actually exist. (By the way, what I'm saying applies to blocks and obj.blocks as well, except for some small differing details like the game allocating 500 block slots versus obj.entities's 200.) As a consequence, the game had to use a separate tracking variable, obj.nentity, because obj.entities.size() would just report 200, instead of the actual amount of entities. Needless to say, having to check for `active` and use `obj.nentity` is a bit error-prone, and it's messier than simply using the std::vector the way it was intended. Also, this resulted in a hard limit of 200 entities, which custom level makers ran into surprisingly quite often. 2.3 comes along, and removes the whole system. Now, std::vectors are properly being used, and obj.entities.size() reports the actual number of entities in the vector; you no longer have to check for `active` when dealing with entities of any sort. But there was one previous behavior of 2.2 that this system kind of forgets about - namely, the ability to have holes in between entities. You see, when an entity got disabled in 2.2 (which just meant turning its `active` off), the indices of all other entities stayed the same; the indice of the entity that got disabled stays there as a hole in the array. But when an entity gets removed in 2.3 (previous to this patch), the indices of every entity afterwards in the array get shifted down by one. std::vector isn't really meant to be able to contain holes. Do the indices of entities and blocks matter? Yes; they determine the order in which entities and blocks get evaluated (the highest indice gets evaluated first), and I had to fix some block evaluation order stuff in previous PRs. And in the case of entities, they matter hugely when using the recently-discovered Arbitrary Entity Manipulation glitch (where crewmate script commands are used on arbitrary entities by setting the `i` attribute of `scriptclass` and passing invalid crewmate identifiers to the commands). If you use Arbitrary Entity Manipulation after destroying some entities, there is a chance that your script won't work between 2.2 and 2.3. The indices also still determine the rendering order of entities (highest indice gets drawn first, which means lowest indice gets drawn in front of other entities). As an example: let's say we have the player at 0, a gravity line at 1, and a checkpoint at 2; then we destroy the gravity line and create a crewmate (let's do Violet). If we're able to have holes, then after removing the gravity line, none of the other indices shift. Then Violet will be created at indice 1, and will be drawn in front of the checkpoint. But if we can't have holes, then removing the gravity line results in the indice of the checkpoint shifting down to indice 1. Then Violet is created at indice 2, and gets drawn behind the checkpoint! This is a clear illustration of changing the behavior that existed in 2.2. However, I also don't want to go back to the `active` system of having to check an attribute before operating on an entity. So... what do we do to restore the holes? Well, we don't need to have an `active` attribute, or modify any existing code that operates on entities. Instead, we can just set the attributes of the entities so that they naturally get ignored by everything that comes into contact with it. For entities, we set their invis to true, and their size, type, and rule to -1 (the game never uses a size, type, or rule of -1 anywhere); for blocks, we set their type to -1, and their width and height to 0. obj.entities.size() will no longer necessarily equal the amount of entities in the room; rather, it will be the amount of entity SLOTS that have been allocated. But nothing that uses obj.entities.size() needs to actually know the amount of entities; it's mostly used for iterating over every entity in the vector. Excess entity slots get cleaned up upon every call of mapclass::gotoroom(), which will now deallocate entity slots starting from the end until it hits a player, at which point it will switch to disabling entity slots instead of removing them entirely. The entclass::clear() and blockclass::clear() functions have been restored because we need to call their initialization functions when reusing a block/entity slot; it's possible to create an entity with an invalid type number (it creates a glitchy Viridian), and without calling the initialization function again, it would simply not create anything. After this patch is applied, entity and block indices will be restored to how they behaved in 2.2.
2020-12-27 07:11:34 +01:00
void disableblockat(int x, int y);
2020-01-01 21:29:24 +01:00
void moveblockto(int x1, int y1, int x2, int y2, int w, int h);
2020-01-01 21:29:24 +01:00
void removetrigger(int t);
void copylinecross(int t);
void revertlinecross(int t, int s);
bool gridmatch(int p1, int p2, int p3, int p4, int p11, int p21, int p31, int p41);
int crewcolour(int t);
void createentity(int xp, int yp, int t, int meta1, int meta2,
int p1, int p2, int p3, int p4);
void createentity(int xp, int yp, int t, int meta1, int meta2,
int p1, int p2);
void createentity(int xp, int yp, int t, int meta1, int meta2,
int p1);
void createentity(int xp, int yp, int t, int meta1, int meta2);
void createentity(int xp, int yp, int t, int meta1);
void createentity(int xp, int yp, int t);
2020-01-01 21:29:24 +01:00
bool updateentities(int i);
2020-01-01 21:29:24 +01:00
void animateentities(int i);
2020-01-01 21:29:24 +01:00
Fix regression: quick stopping changing drawframe This fixes a regression that desyncs my Nova TAS after re-removing the 1-frame input delay. Quick stopping is simply holding left/right but for less than 5 frames. Viridian doesn't decelerate when you let go and they immediately stop in place. (The code calls this tapping, but "quick stopping" is a better name because you can immediately counter-strafe to stop yourself from decelrating in the first place, and that works because of this same code.) So, the sequence of events in 2.2 and previous looks like this: - gameinput() - If quick stopping, set vx to 0 - gamerender() - Change drawframe depending on vx - gamelogic() - Use drawframe for collision (whyyyyyyyyyyyyyyyyyyyyyyyyyyy) And now (ignoring the intermediate period where the whole loop order was wrong), the sequence of events in 2.3 looks like this: - gamerenderfixed() - Change drawframe depending on vx - gamerender() - gameinput() - If quick stopping, set vx to 0 - gamelogic() - Use drawframe for collision (my mind has become numb to pain) So, this means that all the player movement stuff is completely the same. Except their drawframe is going to be different. Unfortunately, I had overlooked that gameinput() sets vx and that animateentities() (in gamerenderfixed()) checks vx. Although, to be fair, it's a pretty dumb decision to make collision detection be based on the actual sprites' pixels themselves, instead of a hitbox, in the first place, so you'd expect THAT to be the end of the dumb parade. Or maybe you shouldn't, I don't know. So, what's the solution? What I've done here is added duplicates of framedelay, drawframe, and walkingframe, for collision use only. They get updated in gamelogic(), after gameinput(), which is after when vx could be set to 0. I've kept the original framedelay, drawframe, and walkingframe around, to keep the same visuals as closely as possible. However, due to the removal of the input delay, whenever you quick stop, your sprite will be wrong for just 1 frame - because when you let go of the direction key, the game will set your vx to 0 and the logical drawframe will update to reflect that, but the previous frame cannot know in advance that you'll release the key on the next frame, and so the visual drawframe will assume that you keep holding the key. Whereas in 2.2 and below, when you release a direction key, the player's position will only update to reflect that on the next frame, but the current frame can immediately recognize that and update the drawframe now, instead of retconning it later. Basically the visual drawframe assumes that you keep holding the key, and if you don't, then it takes on the value of the collision drawframe anyway, so it's okay. And it's only visual, anyway - the collision drawframe of the next frame (when you release the key) will be the same as the drawframe of the frame you release the key in 2.2 and below. But I really don't care to try and fix this for if you re-enable the input delay because it's minor and it'd be more complicated.
2021-06-16 04:18:58 +02:00
void animatehumanoidcollision(const int i);
int getcompanion(void);
2020-01-01 21:29:24 +01:00
int getplayer(void);
2020-01-01 21:29:24 +01:00
int getscm(void);
2020-01-01 21:29:24 +01:00
int getlineat(int t);
int getcrewman(int t);
int getcustomcrewman(int t);
int getteleporter(void);
2020-01-01 21:29:24 +01:00
bool entitycollide(int a, int b);
bool checkdamage(bool scm = false);
2020-01-01 21:29:24 +01:00
int checktrigger(int* block_idx);
2020-01-01 21:29:24 +01:00
int checkactivity(void);
2020-01-01 21:29:24 +01:00
int getgridpoint(int t);
bool checkplatform(const SDL_Rect& temprect, int* px, int* py);
2020-01-01 21:29:24 +01:00
bool checkblocks(const SDL_Rect& temprect, const float dx, const float dy, const float dr, const bool skipdirblocks);
2020-01-01 21:29:24 +01:00
bool checktowerspikes(int t);
2020-01-01 21:29:24 +01:00
bool checkwall(const SDL_Rect& temprect, const float dx, const float dy, const float dr, const bool skipblocks, const bool skipdirblocks);
bool checkwall(const SDL_Rect& temprect);
2020-01-01 21:29:24 +01:00
float hplatformat(const int px, const int py);
2020-01-01 21:29:24 +01:00
int yline(int a, int b);
bool entityhlinecollide(int t, int l);
bool entityvlinecollide(int t, int l);
bool entitywarphlinecollide(int t, int l);
bool entitywarpvlinecollide(int t, int l);
2020-01-01 21:29:24 +01:00
void customwarplinecheck(int i);
2020-01-01 21:29:24 +01:00
float entitycollideplatformroof(int t);
2020-01-01 21:29:24 +01:00
float entitycollideplatformfloor(int t);
2020-01-01 21:29:24 +01:00
bool entitycollidefloor(int t);
2020-01-01 21:29:24 +01:00
bool entitycollideroof(int t);
2020-01-01 21:29:24 +01:00
bool testwallsx(int t, int tx, int ty, const bool skipdirblocks);
2020-01-01 21:29:24 +01:00
bool testwallsy(int t, float tx, float ty);
2020-01-01 21:29:24 +01:00
void applyfriction(int t, float xrate, float yrate);
void updateentitylogic(int t);
2020-01-01 21:29:24 +01:00
void entitymapcollision(int t);
2020-01-01 21:29:24 +01:00
void movingplatformfix(int t, int j);
2020-01-01 21:29:24 +01:00
void entitycollisioncheck(void);
2020-01-01 21:29:24 +01:00
void collisioncheck(int i, int j, bool scm = false);
void stuckprevention(int t);
2020-01-01 21:29:24 +01:00
std::vector<entclass> entities;
std::vector<entclass> linecrosskludge;
int k;
2020-01-01 21:29:24 +01:00
std::vector<blockclass> blocks;
bool flags[100];
bool collect[100];
bool customcollect[100];
2020-01-01 21:29:24 +01:00
int platformtile;
bool vertplatforms, horplatforms;
// :(
bool nearelephant, upsetmode;
int upset;
//Trophy Text
int trophytext, trophytype;
int oldtrophytext;
2020-01-01 21:29:24 +01:00
//Secret lab scripts
int altstates;
//Custom stuff
int customenemy;
int customplatformtile;
bool customwarpmode, customwarpmodevon, customwarpmodehon;
std::string customscript;
bool customcrewmoods[Game::numcrew];
2020-01-01 21:29:24 +01:00
};
#ifndef OBJ_DEFINITION
extern entityclass obj;
#endif
2020-01-01 21:29:24 +01:00
#endif /* ENTITY_H */