2020-09-28 04:15:06 +02:00
|
|
|
#define OBJ_DEFINITION
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "Entity.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
#include <SDL.h>
|
|
|
|
|
2021-02-20 08:19:09 +01:00
|
|
|
#include "CustomLevels.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "Game.h"
|
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
|
|
|
#include "GlitchrunnerMode.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "Graphics.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "Map.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "Music.h"
|
2020-08-03 06:02:27 +02:00
|
|
|
#include "Script.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "UtilityClass.h"
|
2021-02-24 00:21:29 +01:00
|
|
|
#include "Vlogging.h"
|
2021-08-22 04:51:19 +02:00
|
|
|
#include "Xoshiro.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
bool entityclass::checktowerspikes(int t)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("checktowerspikes() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[t].xp + entities[t].cx;
|
|
|
|
temprect.y = entities[t].yp + entities[t].cy;
|
|
|
|
temprect.w = entities[t].w;
|
|
|
|
temprect.h = entities[t].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int tempx = getgridpoint(temprect.x);
|
|
|
|
int tempy = getgridpoint(temprect.y);
|
|
|
|
int tempw = getgridpoint(temprect.x + temprect.w - 1);
|
|
|
|
int temph = getgridpoint(temprect.y + temprect.h - 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (map.spikecollide(tempx, tempy)) return true;
|
|
|
|
if (map.spikecollide(tempw, tempy)) return true;
|
|
|
|
if (map.spikecollide(tempx, temph)) return true;
|
|
|
|
if (map.spikecollide(tempw, temph)) return true;
|
|
|
|
if (temprect.h >= 12)
|
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int tpy1 = getgridpoint(temprect.y + 6);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (map.spikecollide(tempx, tpy1)) return true;
|
|
|
|
if (map.spikecollide(tempw, tpy1)) return true;
|
|
|
|
if (temprect.h >= 18)
|
|
|
|
{
|
|
|
|
tpy1 = getgridpoint(temprect.y + 12);
|
|
|
|
if (map.spikecollide(tempx, tpy1)) return true;
|
|
|
|
if (map.spikecollide(tempw, tpy1)) return true;
|
|
|
|
if (temprect.h >= 24)
|
|
|
|
{
|
|
|
|
tpy1 = getgridpoint(temprect.y + 18);
|
|
|
|
if (map.spikecollide(tempx, tpy1)) return true;
|
|
|
|
if (map.spikecollide(tempw, tpy1)) return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void entityclass::init(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
platformtile = 0;
|
|
|
|
customplatformtile=0;
|
|
|
|
vertplatforms = false;
|
|
|
|
horplatforms = false;
|
|
|
|
|
|
|
|
nearelephant = false;
|
|
|
|
upsetmode = false;
|
|
|
|
upset = 0;
|
|
|
|
|
2021-04-15 07:09:47 +02:00
|
|
|
customenemy = 0;
|
|
|
|
customwarpmode = false; customwarpmodevon = false; customwarpmodehon = false;
|
|
|
|
customactivitycolour = "";
|
Add `setactivityposition(x,y)`, add new textbox color `transparent` (#847)
* Add `setactivityposition(x,y)`, add new textbox color `transparent`
This commit adds a new internal command as a part of the visual activity zone changes I've been making.
This one allows the user to reposition the activity zone to anywhere on the screen.
In addition, this commit adds the textbox color `transparent`, which just sets r, g and b to 0.
rgb(0, 0, 0) normally creates the color black, however in VVVVVV textboxes, it makes the background
of them invisible, and makes the text the off-white color which the game uses elsewhere.
* add new variables to hardreset
* Fix unwanted text centering; offset position by 16, 4
It makes sense for `setactivityposition(0, 0)` to place the activity zone in the default position,
so the x has been offset by 16, and the y has been offset by 4.
Text was being automatically centered, meaning any activity zone which wasn't centered had misplaced text.
This has been fixed by calculating the center manually, and offsetting it by the passed value.
2021-10-14 00:38:51 +02:00
|
|
|
customactivitypositionx = -1;
|
|
|
|
customactivitypositiony = -1;
|
2021-04-15 07:09:47 +02:00
|
|
|
customactivitytext = "";
|
|
|
|
trophytext = 0;
|
2020-04-30 01:54:36 +02:00
|
|
|
oldtrophytext = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
trophytype = 0;
|
|
|
|
altstates = 0;
|
|
|
|
|
|
|
|
|
2020-07-03 03:10:52 +02:00
|
|
|
SDL_memset(customcrewmoods, true, sizeof(customcrewmoods));
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-07-03 03:22:19 +02:00
|
|
|
resetallflags();
|
2020-07-03 04:17:32 +02:00
|
|
|
SDL_memset(collect, false, sizeof(collect));
|
|
|
|
SDL_memset(customcollect, false, sizeof(customcollect));
|
2020-07-06 22:04:34 +02:00
|
|
|
|
|
|
|
k = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void entityclass::resetallflags(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-07-03 03:22:19 +02:00
|
|
|
SDL_memset(flags, false, sizeof(flags));
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int entityclass::swncolour( int t )
|
|
|
|
{
|
|
|
|
//given colour t, return colour in setcol
|
|
|
|
if (t == 0) return 11;
|
|
|
|
if (t == 1) return 6;
|
|
|
|
if (t == 2) return 8;
|
|
|
|
if (t == 3) return 12;
|
|
|
|
if (t == 4) return 9;
|
|
|
|
if (t == 5) return 7;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void entityclass::swnenemiescol( int t )
|
|
|
|
{
|
|
|
|
//change the colour of all SWN enemies to the current one
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[i].type == 23)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].colour = swncolour(t);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
void entityclass::gravcreate( int ypos, int dir, int xoff /*= 0*/, int yoff /*= 0*/ )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if (dir == 0)
|
|
|
|
{
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(-150 - xoff, 58 + (ypos * 20)+yoff, 23, 0, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(320+150 + xoff, 58 + (ypos * 20)+yoff, 23, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
void entityclass::generateswnwave( int t )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//generate a wave for the SWN game
|
|
|
|
if(game.swndelay<=0)
|
|
|
|
{
|
|
|
|
if (t == 0) //game 0, survive for 30 seconds
|
|
|
|
{
|
|
|
|
switch(game.swnstate)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
//Decide on a wave here
|
|
|
|
//default case
|
|
|
|
game.swnstate = 1;
|
|
|
|
game.swndelay = 5;
|
|
|
|
|
|
|
|
if (game.swntimer <= 150) //less than 5 seconds
|
|
|
|
{
|
|
|
|
game.swnstate = 9;
|
|
|
|
game.swndelay = 8;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 300) //less than 10 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 6;
|
|
|
|
game.swndelay = 12;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 360) //less than 12 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 5+game.swnstate2;
|
|
|
|
game.swndelay = 15;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 420) //less than 14 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 7+game.swnstate2;
|
|
|
|
game.swndelay = 15;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 480) //less than 16 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 5+game.swnstate2;
|
|
|
|
game.swndelay = 15;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 540) //less than 18 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 7+game.swnstate2;
|
|
|
|
game.swndelay = 15;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 600) //less than 20 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 5+game.swnstate2;
|
|
|
|
game.swndelay = 15;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 900) //less than 30 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 4;
|
|
|
|
game.swndelay = 20;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 1050) //less than 35 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 3;
|
|
|
|
game.swndelay = 10;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 1200) //less than 40 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 3;
|
|
|
|
game.swndelay = 20;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 1500) //less than 50 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 2;
|
|
|
|
game.swndelay = 10;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 1650) //less than 55 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 1;
|
|
|
|
game.swndelay = 15;
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (game.swntimer <= 1800) //less than 60 seconds
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
game.swnstate = 1;
|
|
|
|
game.swndelay = 25;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (game.deathcounts - game.swndeaths > 7) game.swndelay += 2;
|
|
|
|
if (game.deathcounts - game.swndeaths > 15) game.swndelay += 2;
|
|
|
|
if (game.deathcounts - game.swndeaths > 25) game.swndelay += 4;
|
|
|
|
break;
|
|
|
|
case 1:
|
2021-08-22 04:51:19 +02:00
|
|
|
createentity(-150, 58 + (int(xoshiro_rand() * 6) * 20), 23, 0, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if(game.swnstate3==0)
|
|
|
|
{
|
|
|
|
game.swnstate2++;
|
|
|
|
if (game.swnstate2 >= 6)
|
|
|
|
{
|
|
|
|
game.swnstate3 = 1;
|
|
|
|
game.swnstate2--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate2--;
|
|
|
|
if (game.swnstate2 < 0)
|
|
|
|
{
|
|
|
|
game.swnstate3 = 0;
|
|
|
|
game.swnstate2++;
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(-150, 58 + (int(game.swnstate2) * 20), 23, 0, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
break;
|
|
|
|
case 3:
|
2021-08-22 04:51:19 +02:00
|
|
|
createentity(320+150, 58 + (int(xoshiro_rand() * 6) * 20), 23, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
//left and right compliments
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 6);
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(-150, 58 + (game.swnstate2 * 20), 23, 0, 0);
|
|
|
|
createentity(320+150, 58 + ((5-game.swnstate2) * 20), 23, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
//Top and bottom
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(-150, 58, 23, 0, 0);
|
|
|
|
createentity(-150, 58 + (5 * 20), 23, 0, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
game.swnstate2 = 1;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
//Middle
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(-150, 58 + (2 * 20), 23, 0, 0);
|
|
|
|
createentity(-150, 58 + (3 * 20), 23, 0, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
//Top and bottom
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(320+150, 58, 23, 1, 0);
|
|
|
|
createentity(320+150, 58 + (5 * 20), 23, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
game.swnstate2 = 1;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
//Middle
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(320+150, 58 + (2 * 20), 23, 1, 0);
|
|
|
|
createentity(320+150, 58 + (3 * 20), 23, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
if(game.swnstate3==0)
|
|
|
|
{
|
|
|
|
game.swnstate2++;
|
|
|
|
if (game.swnstate2 >= 6)
|
|
|
|
{
|
|
|
|
game.swnstate3 = 1;
|
|
|
|
game.swnstate2--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate2--;
|
|
|
|
if (game.swnstate2 < 0)
|
|
|
|
{
|
|
|
|
game.swnstate3 = 0;
|
|
|
|
game.swnstate2++;
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(320 + 150, 58 + (int(game.swnstate2) * 20), 23, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 0; //return to decision state
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t == 1)
|
|
|
|
{
|
|
|
|
//Game 2, super gravitron
|
|
|
|
switch(game.swnstate)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
//Choose either simple or filler
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
game.swnstate3 = 0;
|
|
|
|
game.swnstate4 = 0;
|
|
|
|
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 100);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (game.swnstate2 < 25)
|
|
|
|
{
|
|
|
|
//simple
|
|
|
|
game.swnstate = 2;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//filler
|
|
|
|
game.swnstate = 4;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
//complex chain
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 8);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (game.swnstate2 == 0)
|
|
|
|
{
|
|
|
|
game.swnstate = 10;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 1)
|
|
|
|
{
|
|
|
|
game.swnstate = 12;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 2)
|
|
|
|
{
|
|
|
|
game.swnstate = 14;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 3)
|
|
|
|
{
|
|
|
|
game.swnstate = 20;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 4)
|
|
|
|
{
|
|
|
|
game.swnstate = 21;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 5)
|
|
|
|
{
|
|
|
|
game.swnstate = 22;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 6)
|
|
|
|
{
|
|
|
|
game.swnstate = 22;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 7)
|
|
|
|
{
|
|
|
|
game.swnstate = 14;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
//simple chain
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 6);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (game.swnstate2 == 0)
|
|
|
|
{
|
|
|
|
game.swnstate = 23;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 1)
|
|
|
|
{
|
|
|
|
game.swnstate = 24;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 2)
|
|
|
|
{
|
|
|
|
game.swnstate = 25;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 3)
|
|
|
|
{
|
|
|
|
game.swnstate = 26;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 4)
|
|
|
|
{
|
|
|
|
game.swnstate = 27;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 5)
|
|
|
|
{
|
|
|
|
game.swnstate = 14;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
//Choose a major action
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 100);
|
Fix Super Gravitron pattern oversight
There is a pattern in the Super Gravitron that is meant to "staircase",
similar to the Gravitron in Intermission 2. Something like:
[]
[]
[]
[] []
[] []
Unfortunately, due to an oversight, this pattern can only ever produce 1
square or 4 squares, which look out of place.
Both gravitrons are state machines (of course). States 20 and 21 in the
Super Gravitron are this staircase pattern (state 20 spawns the squares
on the left, state 21 spawns the squares on the right).
The only way states 20 and 21 can be reached is through state 1, and the
only way state 1 can be reached is through state 3. The only way state 3
can be reached is through states 28, 29, 30, and 31.
In states 20 and 21, the variable used to keep track of the amount of
squares spawned is swnstate4. However, states 28, 29, 30, and 31 all end
up using swnstate4, and at the end of states 28 and 29, swnstate4 will
be 7, and at the end of states 30 and 31, swnstate4 will be 3. This
means if we go to states 20 and 21 after coming from states 28 and 29,
we will only get 1 square, and if we go to states 20 and 21 after coming
from states 30 and 31, we will only get 4 squares.
This can be clearly filed under a failure to reset appropriate state.
What's the solution here? Just reset swnstate4 in state 3, so there will
be 7 squares, as intended. This also fixes the bug for state 22 as well,
which is affected in the same manner.
2021-04-30 08:16:31 +02:00
|
|
|
game.swnstate4 = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
if (game.swnstate2 < 25)
|
|
|
|
{
|
|
|
|
//complex
|
|
|
|
game.swnstate = 1;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//simple
|
|
|
|
game.swnstate = 2;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
//filler chain
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 6);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (game.swnstate2 == 0)
|
|
|
|
{
|
|
|
|
game.swnstate = 28;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 1)
|
|
|
|
{
|
|
|
|
game.swnstate = 29;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 2)
|
|
|
|
{
|
|
|
|
game.swnstate = 28;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 3)
|
|
|
|
{
|
|
|
|
game.swnstate = 29;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 4)
|
|
|
|
{
|
|
|
|
game.swnstate = 30;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
else if (game.swnstate2 == 5)
|
|
|
|
{
|
|
|
|
game.swnstate = 31;
|
|
|
|
game.swndelay = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 10:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(0, 0);
|
|
|
|
gravcreate(1, 0);
|
|
|
|
gravcreate(2, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate++;
|
|
|
|
game.swndelay = 10; //return to decision state
|
|
|
|
break;
|
|
|
|
case 11:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(3, 0);
|
|
|
|
gravcreate(4, 0);
|
|
|
|
gravcreate(5, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate2++;
|
|
|
|
if(game.swnstate2==3)
|
|
|
|
{
|
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 30; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate--;
|
|
|
|
game.swndelay = 10; //return to decision state
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 12:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(0, 1);
|
|
|
|
gravcreate(1, 1);
|
|
|
|
gravcreate(2, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate++;
|
|
|
|
game.swndelay = 10; //return to decision state
|
|
|
|
break;
|
|
|
|
case 13:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(3, 1);
|
|
|
|
gravcreate(4, 1);
|
|
|
|
gravcreate(5, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate2++;
|
|
|
|
if(game.swnstate2==3)
|
|
|
|
{
|
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 30; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate--;
|
|
|
|
game.swndelay = 10; //return to decision state
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 14:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(0, 0, 0);
|
|
|
|
gravcreate(5, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
game.swnstate++;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 15:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(1, 0);
|
|
|
|
gravcreate(4, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
game.swnstate++;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 16:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(2, 0);
|
|
|
|
gravcreate(3, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
game.swnstate++;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 17:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(3, 0);
|
|
|
|
gravcreate(2, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
game.swnstate++;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 18:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(4, 0);
|
|
|
|
gravcreate(1, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
game.swnstate++;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 19:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(5, 0);
|
|
|
|
gravcreate(0, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
game.swnstate=0;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 20:
|
|
|
|
game.swnstate4++;
|
|
|
|
if(game.swnstate3==0)
|
|
|
|
{
|
|
|
|
game.swnstate2++;
|
|
|
|
if (game.swnstate2 >= 6)
|
|
|
|
{
|
|
|
|
game.swnstate3 = 1;
|
|
|
|
game.swnstate2--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate2--;
|
|
|
|
if (game.swnstate2 < 0)
|
|
|
|
{
|
|
|
|
game.swnstate3 = 0;
|
|
|
|
game.swnstate2++;
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(-150, 58 + (int(game.swnstate2) * 20), 23, 0, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
if(game.swnstate4<=6)
|
|
|
|
{
|
|
|
|
game.swnstate = 20;
|
|
|
|
game.swndelay = 10; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 10; //return to decision state
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 21:
|
|
|
|
game.swnstate4++;
|
|
|
|
if(game.swnstate3==0)
|
|
|
|
{
|
|
|
|
game.swnstate2++;
|
|
|
|
if (game.swnstate2 >= 6)
|
|
|
|
{
|
|
|
|
game.swnstate3 = 1;
|
|
|
|
game.swnstate2--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate2--;
|
|
|
|
if (game.swnstate2 < 0)
|
|
|
|
{
|
|
|
|
game.swnstate3 = 0;
|
|
|
|
game.swnstate2++;
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(320+150, 58 + (int(game.swnstate2) * 20), 23, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
if(game.swnstate4<=6)
|
|
|
|
{
|
|
|
|
game.swnstate = 21;
|
|
|
|
game.swndelay = 10; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 10; //return to decision state
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 22:
|
|
|
|
game.swnstate4++;
|
|
|
|
//left and right compliments
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 6);
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(-150, 58 + (game.swnstate2 * 20), 23, 0, 0);
|
|
|
|
createentity(320 + 150, 58 + ((5 - game.swnstate2) * 20), 23, 1, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
if(game.swnstate4<=12)
|
|
|
|
{
|
|
|
|
game.swnstate = 22;
|
|
|
|
game.swndelay = 18; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 18; //return to decision state
|
|
|
|
}
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 23:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(1, 0);
|
|
|
|
gravcreate(2, 0, 15);
|
|
|
|
gravcreate(2, 0, -15);
|
|
|
|
gravcreate(3, 0, 15);
|
|
|
|
gravcreate(3, 0, -15);
|
|
|
|
gravcreate(4, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 15; //return to decision state
|
|
|
|
break;
|
|
|
|
case 24:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(1, 1);
|
|
|
|
gravcreate(2, 1, 15);
|
|
|
|
gravcreate(2, 1, -15);
|
|
|
|
gravcreate(3, 1, 15);
|
|
|
|
gravcreate(3, 1, -15);
|
|
|
|
gravcreate(4, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 15; //return to decision state
|
|
|
|
break;
|
|
|
|
case 25:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(0, 0);
|
|
|
|
gravcreate(1, 1,0,10);
|
|
|
|
gravcreate(4, 1,0,-10);
|
|
|
|
gravcreate(5, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 26:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(0, 1, 0);
|
|
|
|
gravcreate(1, 1, 10);
|
|
|
|
gravcreate(4, 1, 40);
|
|
|
|
gravcreate(5, 1, 50);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 27:
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(0, 0, 0);
|
|
|
|
gravcreate(1, 0, 10);
|
|
|
|
gravcreate(4, 0, 40);
|
|
|
|
gravcreate(5, 0, 50);
|
2020-01-01 21:29:24 +01:00
|
|
|
game.swnstate = 0;
|
|
|
|
game.swndelay = 20; //return to decision state
|
|
|
|
break;
|
|
|
|
case 28:
|
|
|
|
game.swnstate4++;
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 6);
|
2020-03-31 02:46:36 +02:00
|
|
|
createentity(-150, 58 + (game.swnstate2 * 20), 23, 0, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
if(game.swnstate4<=6)
|
|
|
|
{
|
|
|
|
game.swnstate = 28;
|
|
|
|
game.swndelay = 8; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate = 3;
|
|
|
|
game.swndelay = 15; //return to decision state
|
|
|
|
}
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 29:
|
|
|
|
game.swnstate4++;
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 6);
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(game.swnstate2, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
if(game.swnstate4<=6)
|
|
|
|
{
|
|
|
|
game.swnstate = 29;
|
|
|
|
game.swndelay = 8; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate = 3;
|
|
|
|
game.swndelay = 15; //return to decision state
|
|
|
|
}
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 30:
|
|
|
|
game.swnstate4++;
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 3);
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(game.swnstate2, 0);
|
|
|
|
gravcreate(5-game.swnstate2, 0);
|
2020-01-01 21:29:24 +01:00
|
|
|
if(game.swnstate4<=2)
|
|
|
|
{
|
|
|
|
game.swnstate = 30;
|
|
|
|
game.swndelay = 14; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate = 3;
|
|
|
|
game.swndelay = 15; //return to decision state
|
|
|
|
}
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
case 31:
|
|
|
|
game.swnstate4++;
|
2021-08-22 04:51:19 +02:00
|
|
|
game.swnstate2 = int(xoshiro_rand() * 3);
|
2020-03-31 02:46:36 +02:00
|
|
|
gravcreate(game.swnstate2, 1);
|
|
|
|
gravcreate(5-game.swnstate2, 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
if(game.swnstate4<=2)
|
|
|
|
{
|
|
|
|
game.swnstate = 31;
|
|
|
|
game.swndelay = 14; //return to decision state
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swnstate = 3;
|
|
|
|
game.swndelay = 15; //return to decision state
|
|
|
|
}
|
|
|
|
game.swnstate2 = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.swndelay--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-15 06:12:37 +02:00
|
|
|
void entityclass::createblock( int t, int xp, int yp, int w, int h, int trig /*= 0*/, const std::string& script /*= ""*/, bool custom /*= false*/)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 01:11:02 +02:00
|
|
|
k = blocks.size();
|
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
|
|
|
blockclass newblock;
|
|
|
|
blockclass* blockptr;
|
|
|
|
|
|
|
|
/* Can we reuse the slot of a disabled block? */
|
|
|
|
bool reuse = false;
|
|
|
|
for (size_t i = 0; i < blocks.size(); ++i)
|
|
|
|
{
|
Remove assigning block type to -1 when disabling them
This fixes a regression where moving platforms had no collision. Because
their width and height would be maintained, but their type would be -1.
(Also because I didn't test enough.)
In #565, I decided to set blocks' types to -1 when disabling them, to be
a bit safer in case there was some code that used block types but not
their width and heights. However, this means that when blocks get
disabled and re-created in the platform update loops, their types get
set to -1, which effectively also disables their collision.
In the end, I'll just have to compromise and remove setting blocks to
type -1. Because in a better world, we shouldn't be destroying and
creating blocks constantly just to move some platforms - however, fixing
such a fundamental problem is beyond the scope of at least 2.3 (there's
also the fact that this problem also results in some bugs that are a
part of compatibility, whether we like it or not). So I'll just remove
the -1.
2021-02-20 00:16:28 +01:00
|
|
|
if (blocks[i].wp == 0
|
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
|
|
|
&& blocks[i].hp == 0
|
|
|
|
&& blocks[i].rect.w == 0
|
|
|
|
&& blocks[i].rect.h == 0)
|
|
|
|
{
|
|
|
|
reuse = true;
|
|
|
|
blockptr = &blocks[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!reuse)
|
|
|
|
{
|
|
|
|
blockptr = &newblock;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
blockptr->clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
blockclass& block = *blockptr;
|
2020-01-01 21:29:24 +01:00
|
|
|
switch(t)
|
|
|
|
{
|
|
|
|
case BLOCK: //Block
|
2020-04-04 01:11:02 +02:00
|
|
|
block.type = BLOCK;
|
|
|
|
block.xp = xp;
|
|
|
|
block.yp = yp;
|
|
|
|
block.wp = w;
|
|
|
|
block.hp = h;
|
|
|
|
block.rectset(xp, yp, w, h);
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case TRIGGER: //Trigger
|
2020-04-04 01:11:02 +02:00
|
|
|
block.type = TRIGGER;
|
|
|
|
block.wp = w;
|
|
|
|
block.hp = h;
|
|
|
|
block.rectset(xp, yp, w, h);
|
|
|
|
block.trigger = trig;
|
2020-07-10 08:01:13 +02:00
|
|
|
block.script = script;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case DAMAGE: //Damage
|
2020-04-04 01:11:02 +02:00
|
|
|
block.type = DAMAGE;
|
|
|
|
block.wp = w;
|
|
|
|
block.hp = h;
|
|
|
|
block.rectset(xp, yp, w, h);
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case DIRECTIONAL: //Directional
|
2020-04-04 01:11:02 +02:00
|
|
|
block.type = DIRECTIONAL;
|
|
|
|
block.wp = w;
|
|
|
|
block.hp = h;
|
|
|
|
block.rectset(xp, yp, w, h);
|
|
|
|
block.trigger = trig;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case SAFE: //Safe block
|
2020-04-04 01:11:02 +02:00
|
|
|
block.type = SAFE;
|
|
|
|
block.xp = xp;
|
|
|
|
block.yp = yp;
|
|
|
|
block.wp = w;
|
|
|
|
block.hp = h;
|
|
|
|
block.rectset(xp, yp, w, h);
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case ACTIVITY: //Activity Zone
|
2020-04-04 01:11:02 +02:00
|
|
|
block.type = ACTIVITY;
|
|
|
|
block.wp = w;
|
|
|
|
block.hp = h;
|
|
|
|
block.rectset(xp, yp, w, h);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Ok, each and every activity zone in the game is initilised here. "Trig" in this case is a variable that
|
|
|
|
//assigns all the details.
|
|
|
|
switch(trig)
|
|
|
|
{
|
|
|
|
case 0: //testing zone
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to explode";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "intro";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=1;
|
|
|
|
break;
|
|
|
|
case 1:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to talk to Violet";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "talkpurple";
|
|
|
|
block.setblockcolour("purple");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 2:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to talk to Vitellary";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "talkyellow";
|
|
|
|
block.setblockcolour("yellow");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 3:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to talk to Vermilion";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "talkred";
|
|
|
|
block.setblockcolour("red");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 4:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to talk to Verdigris";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "talkgreen";
|
|
|
|
block.setblockcolour("green");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 5:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to talk to Victoria";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "talkblue";
|
|
|
|
block.setblockcolour("blue");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 6:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_station_1";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 7:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_outside_1";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 8:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_outside_2";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 9:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_outside_3";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 10:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_outside_4";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 11:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_outside_5";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 12:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_outside_6";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 13:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_finallevel";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 14:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_station_2";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 15:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_station_3";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 16:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_station_4";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 17:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_warp_1";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 18:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_warp_2";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 19:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_lab_1";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 20:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_lab_2";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 21:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_secretlab";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 22:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_shipcomputer";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 23:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminals";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_radio";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 24:
|
2021-04-19 08:23:44 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "terminal_jukebox";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 25:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Passion for Exploring";
|
|
|
|
block.script = "terminal_juke1";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 26:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Pushing Onwards";
|
|
|
|
block.script = "terminal_juke2";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 27:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Positive Force";
|
|
|
|
block.script = "terminal_juke3";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 28:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Presenting VVVVVV";
|
|
|
|
block.script = "terminal_juke4";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 29:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Potential for Anything";
|
|
|
|
block.script = "terminal_juke5";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 30:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Predestined Fate";
|
|
|
|
block.script = "terminal_juke6";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 31:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Pipe Dream";
|
|
|
|
block.script = "terminal_juke7";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 32:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Popular Potpourri";
|
|
|
|
block.script = "terminal_juke8";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 33:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "Pressure Cooker";
|
|
|
|
block.script = "terminal_juke9";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 34:
|
2020-04-04 01:11:02 +02:00
|
|
|
block.prompt = "ecroF evitisoP";
|
|
|
|
block.script = "terminal_juke10";
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
case 35:
|
2021-04-15 06:12:37 +02:00
|
|
|
if (custom)
|
|
|
|
{
|
|
|
|
block.prompt = "Press %s to interact";
|
2021-04-23 03:33:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-15 06:12:37 +02:00
|
|
|
block.prompt = "Press %s to activate terminal";
|
|
|
|
}
|
2020-04-04 01:11:02 +02:00
|
|
|
block.script = "custom_"+customscript;
|
|
|
|
block.setblockcolour("orange");
|
2020-01-01 21:29:24 +01:00
|
|
|
trig=0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-04-04 01:11:02 +02:00
|
|
|
|
2021-04-15 07:09:47 +02:00
|
|
|
if (customactivitytext != "")
|
|
|
|
{
|
|
|
|
block.prompt = customactivitytext;
|
|
|
|
customactivitytext = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (customactivitycolour != "")
|
|
|
|
{
|
2021-09-12 08:15:34 +02:00
|
|
|
block.setblockcolour(customactivitycolour.c_str());
|
2021-04-15 07:09:47 +02:00
|
|
|
customactivitycolour = "";
|
|
|
|
}
|
|
|
|
|
Add `setactivityposition(x,y)`, add new textbox color `transparent` (#847)
* Add `setactivityposition(x,y)`, add new textbox color `transparent`
This commit adds a new internal command as a part of the visual activity zone changes I've been making.
This one allows the user to reposition the activity zone to anywhere on the screen.
In addition, this commit adds the textbox color `transparent`, which just sets r, g and b to 0.
rgb(0, 0, 0) normally creates the color black, however in VVVVVV textboxes, it makes the background
of them invisible, and makes the text the off-white color which the game uses elsewhere.
* add new variables to hardreset
* Fix unwanted text centering; offset position by 16, 4
It makes sense for `setactivityposition(0, 0)` to place the activity zone in the default position,
so the x has been offset by 16, and the y has been offset by 4.
Text was being automatically centered, meaning any activity zone which wasn't centered had misplaced text.
This has been fixed by calculating the center manually, and offsetting it by the passed value.
2021-10-14 00:38:51 +02:00
|
|
|
if (customactivitypositionx != -1)
|
|
|
|
{
|
|
|
|
block.activity_x = customactivitypositionx;
|
|
|
|
block.activity_y = customactivitypositiony;
|
|
|
|
customactivitypositionx = -1;
|
|
|
|
customactivitypositiony = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
block.activity_x = 0;
|
|
|
|
block.activity_y = 0;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
if (!reuse)
|
|
|
|
{
|
|
|
|
blocks.push_back(block);
|
|
|
|
}
|
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
|
|
|
/* Disable entity, and return true if entity was successfully disabled */
|
|
|
|
bool entityclass::disableentity(int t)
|
2020-04-03 22:03:02 +02:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("disableentity() out-of-bounds!");
|
Prevent removing the player entity
Removing the player entity has all sorts of nasty effects, such as
softlocking the game because many inputs require there to be a player
present, such as opening the quit menu.
The most infamous glitch to remove the player entity is the Gravitron
Fling, where the game doesn't see a gravity line at a specific
y-position in the current room, and when it moves the bottom gravity
line it moves the player instead. When the gravity line gets outside the
room, it gets destroyed, so if the player gets dragged outside the room,
they get destroyed, too. (Don't misinterpret this as saying anytime the
player gets dragged outside the room, they get destroyed - it's only the
Gravitron logic that destroys them.)
Also, there are many places in the code that use entity-getting
functions that have a fallback value of 0. If it was possible to remove
the player, then it's possible for this fallback value of 0 to index
obj.entities out-of-bounds, which is not good.
To fix this, entityclass::removeentity() is now a bool that signifies if
the entity was successfully removed or not. If the entity given is the
player (meaning it first checks if it's rule 0, just so in 99% of cases
it'll short-circuit and won't do the next check, which is if
entityclass::getplayer() says the indice to be removed is the player),
then it'll refuse to remove the entity, and return false.
This is a change in behavior where callers might expect
entityclass::removeentity() to always succeed, so I changed the
removeentity_iter() macro to only decrement if removing the entity
succeeded. I also changed entityclass::updateentities() from
'removeentity(i); return true;' to 'return removeentity(i);'.
2020-06-13 21:00:04 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (entities[t].rule == 0 && t == getplayer())
|
|
|
|
{
|
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
|
|
|
/* Don't disable the player entity! */
|
Prevent removing the player entity
Removing the player entity has all sorts of nasty effects, such as
softlocking the game because many inputs require there to be a player
present, such as opening the quit menu.
The most infamous glitch to remove the player entity is the Gravitron
Fling, where the game doesn't see a gravity line at a specific
y-position in the current room, and when it moves the bottom gravity
line it moves the player instead. When the gravity line gets outside the
room, it gets destroyed, so if the player gets dragged outside the room,
they get destroyed, too. (Don't misinterpret this as saying anytime the
player gets dragged outside the room, they get destroyed - it's only the
Gravitron logic that destroys them.)
Also, there are many places in the code that use entity-getting
functions that have a fallback value of 0. If it was possible to remove
the player, then it's possible for this fallback value of 0 to index
obj.entities out-of-bounds, which is not good.
To fix this, entityclass::removeentity() is now a bool that signifies if
the entity was successfully removed or not. If the entity given is the
player (meaning it first checks if it's rule 0, just so in 99% of cases
it'll short-circuit and won't do the next check, which is if
entityclass::getplayer() says the indice to be removed is the player),
then it'll refuse to remove the entity, and return false.
This is a change in behavior where callers might expect
entityclass::removeentity() to always succeed, so I changed the
removeentity_iter() macro to only decrement if removing the entity
succeeded. I also changed entityclass::updateentities() from
'removeentity(i); return true;' to 'return removeentity(i);'.
2020-06-13 21:00:04 +02:00
|
|
|
return false;
|
2020-05-05 01:41:30 +02: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
|
|
|
|
|
|
|
entities[t].invis = true;
|
|
|
|
entities[t].size = -1;
|
|
|
|
entities[t].type = -1;
|
|
|
|
entities[t].rule = -1;
|
2021-09-23 22:16:57 +02:00
|
|
|
entities[t].isplatform = false;
|
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
|
|
|
|
Prevent removing the player entity
Removing the player entity has all sorts of nasty effects, such as
softlocking the game because many inputs require there to be a player
present, such as opening the quit menu.
The most infamous glitch to remove the player entity is the Gravitron
Fling, where the game doesn't see a gravity line at a specific
y-position in the current room, and when it moves the bottom gravity
line it moves the player instead. When the gravity line gets outside the
room, it gets destroyed, so if the player gets dragged outside the room,
they get destroyed, too. (Don't misinterpret this as saying anytime the
player gets dragged outside the room, they get destroyed - it's only the
Gravitron logic that destroys them.)
Also, there are many places in the code that use entity-getting
functions that have a fallback value of 0. If it was possible to remove
the player, then it's possible for this fallback value of 0 to index
obj.entities out-of-bounds, which is not good.
To fix this, entityclass::removeentity() is now a bool that signifies if
the entity was successfully removed or not. If the entity given is the
player (meaning it first checks if it's rule 0, just so in 99% of cases
it'll short-circuit and won't do the next check, which is if
entityclass::getplayer() says the indice to be removed is the player),
then it'll refuse to remove the entity, and return false.
This is a change in behavior where callers might expect
entityclass::removeentity() to always succeed, so I changed the
removeentity_iter() macro to only decrement if removing the entity
succeeded. I also changed entityclass::updateentities() from
'removeentity(i); return true;' to 'return removeentity(i);'.
2020-06-13 21:00:04 +02:00
|
|
|
return true;
|
2020-04-03 22:03:02 +02:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void entityclass::removeallblocks(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 01:11:02 +02:00
|
|
|
blocks.clear();
|
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 entityclass::disableblock( int t )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, blocks))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("disableblock() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
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
|
|
|
blocks[t].wp = 0;
|
|
|
|
blocks[t].hp = 0;
|
|
|
|
|
|
|
|
blocks[t].rect.w = blocks[t].wp;
|
|
|
|
blocks[t].rect.h = blocks[t].hp;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-10-10 02:04:03 +02:00
|
|
|
void entityclass::moveblockto(int x1, int y1, int x2, int y2, int w, int h)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < blocks.size(); i++)
|
|
|
|
{
|
|
|
|
if (blocks[i].xp == x1 && blocks[i].yp == y1)
|
|
|
|
{
|
|
|
|
blocks[i].xp = x2;
|
|
|
|
blocks[i].yp = y2;
|
|
|
|
|
|
|
|
blocks[i].wp = w;
|
|
|
|
blocks[i].hp = h;
|
|
|
|
|
|
|
|
blocks[i].rectset(blocks[i].xp, blocks[i].yp, blocks[i].wp, blocks[i].hp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 entityclass::disableblockat(int x, int y)
|
2020-10-10 00:35:50 +02:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < blocks.size(); i++)
|
|
|
|
{
|
|
|
|
if (blocks[i].xp == x && blocks[i].yp == y)
|
|
|
|
{
|
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
|
|
|
disableblock(i);
|
2020-10-10 00:35:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
void entityclass::removetrigger( int t )
|
|
|
|
{
|
2020-04-04 01:11:02 +02:00
|
|
|
for(size_t i=0; i<blocks.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
if(blocks[i].type == TRIGGER && blocks[i].trigger == 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
|
|
|
disableblock(i);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-17 08:12:32 +02:00
|
|
|
void entityclass::copylinecross(std::vector<entclass>& linecrosskludge, int t)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("copylinecross() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
//Copy entity t into the first free linecrosskludge entity
|
2020-04-03 22:50:16 +02:00
|
|
|
linecrosskludge.push_back(entities[t]);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 08:12:32 +02:00
|
|
|
void entityclass::revertlinecross(std::vector<entclass>& linecrosskludge, int t, int s)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(s, linecrosskludge))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("revertlinecross() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
//Restore entity t info from linecrossing s
|
|
|
|
entities[t].onentity = linecrosskludge[s].onentity;
|
|
|
|
entities[t].state = linecrosskludge[s].state;
|
|
|
|
entities[t].life = linecrosskludge[s].life;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool entityclass::gridmatch( int p1, int p2, int p3, int p4, int p11, int p21, int p31, int p41 )
|
|
|
|
{
|
|
|
|
if (p1 == p11 && p2 == p21 && p3 == p31 && p4 == p41) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Fix enemy movement types 10/12 causing memory leaks
If spawned as a custom enemy (createentity entry 56), or spawned outside
of the rooms they spawn in in the main game, they will repeatedly clone
themselves every frame, which profusely leaks memory. In fact it quickly
causes a crash in 2.2 and previous, but 2.3 fixes that crash, so it just
keeps spawning enemies endlessly, which eventually lags the game, and
eventually can out-of-memory your system (bad!).
The problem is those movement types rely on entclass::setenemyroom() to
change their `behave` to be 11 or 13. Else, the new entity created will
still have `behave` 10 or 12, which will create ANOTHER entity in the
same way, and so on, and so forth.
So to fix this, just make it so if an enemy is still `behave` 10 or 12
by the end, then, just set it to -1. That way it'll stay still and won't
cause any harm.
I considered setting the `behave` to 11 or 13 respectively, but, that's
probably going farther than just fixing a memory leak, and anyways, it's
not that much useful for me as a custom level maker, and the entities
spawned aren't really controllable.
2021-08-20 01:36:31 +02:00
|
|
|
static void entityclonefix(entclass* entity)
|
|
|
|
{
|
|
|
|
if (entity->behave == 10 || entity->behave == 12)
|
|
|
|
{
|
|
|
|
/* Fix memory leak */
|
|
|
|
entity->behave = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1, int p2, int p3, int p4)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
k = entities.size();
|
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
|
|
|
entclass newent;
|
|
|
|
entclass* entptr;
|
|
|
|
|
|
|
|
/* Can we reuse the slot of a disabled entity? */
|
|
|
|
bool reuse = false;
|
|
|
|
for (size_t i = 0; i < entities.size(); ++i)
|
|
|
|
{
|
|
|
|
if (entities[i].invis
|
|
|
|
&& entities[i].size == -1
|
|
|
|
&& entities[i].type == -1
|
2021-09-23 22:16:57 +02:00
|
|
|
&& entities[i].rule == -1
|
|
|
|
&& !entities[i].isplatform)
|
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
|
|
|
{
|
|
|
|
reuse = true;
|
|
|
|
entptr = &entities[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!reuse)
|
|
|
|
{
|
|
|
|
entptr = &newent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entptr->clear();
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
//Size 0 is a sprite
|
|
|
|
//Size 1 is a tile
|
|
|
|
//Beyond that are special cases (to do)
|
|
|
|
//Size 2 is a moving platform of width 4 (32)
|
|
|
|
//Size 3 is apparently a "bug chunky pixel"
|
|
|
|
//Size 4 is a coin/small pickup
|
|
|
|
//Size 5 is a horizontal line, 6 is vertical
|
|
|
|
|
|
|
|
//Rule 0 is the playable character
|
|
|
|
//Rule 1 is anything harmful
|
|
|
|
//Rule 2 is anything decorative (no collisions)
|
|
|
|
//Rule 3 is anything that results in an entity to entity collision and state change
|
|
|
|
//Rule 4 is a horizontal line, 5 is vertical
|
|
|
|
//Rule 6 is a crew member
|
|
|
|
|
2020-06-30 00:27:23 +02:00
|
|
|
#if !defined(NO_CUSTOM_LEVELS)
|
|
|
|
// Special case for gray Warp Zone tileset!
|
2021-02-21 00:45:48 +01:00
|
|
|
const RoomProperty* const room = cl.getroomprop(game.roomx - 100, game.roomy - 100);
|
2021-03-24 19:59:36 +01:00
|
|
|
bool custom_gray = room->tileset == 3 && room->tilecol == 6;
|
2020-06-30 00:27:23 +02:00
|
|
|
#else
|
|
|
|
bool custom_gray = false;
|
|
|
|
#endif
|
|
|
|
|
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
|
|
|
entclass& entity = *entptr;
|
2020-04-29 01:17:10 +02:00
|
|
|
entity.xp = xp;
|
|
|
|
entity.yp = yp;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.type = t;
|
2020-01-01 21:29:24 +01:00
|
|
|
switch(t)
|
|
|
|
{
|
|
|
|
case 0: //Player
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 0; //Playable character
|
|
|
|
entity.tile = 0;
|
|
|
|
entity.colour = 0;
|
|
|
|
entity.cx = 6;
|
|
|
|
entity.cy = 2;
|
|
|
|
entity.w = 12;
|
|
|
|
entity.h = 21;
|
|
|
|
entity.dir = 1;
|
|
|
|
|
2021-05-24 06:57:59 +02:00
|
|
|
/* Fix wrong y-position if spawning in on conveyor */
|
|
|
|
entity.newxp = xp;
|
|
|
|
entity.newyp = yp;
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta1 == 1) entity.invis = true;
|
2020-04-03 22:50:16 +02:00
|
|
|
|
|
|
|
entity.gravity = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
2020-01-16 14:31:01 +01:00
|
|
|
case 1: //Simple enemy, bouncing off the walls
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 1;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.behave = meta1;
|
|
|
|
entity.para = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.cx = 0;
|
|
|
|
entity.cy = 0;
|
|
|
|
|
|
|
|
entity.x1 = p1;
|
|
|
|
entity.y1 = p2;
|
|
|
|
entity.x2 = p3;
|
|
|
|
entity.y2 = p4;
|
|
|
|
|
|
|
|
entity.harmful = true;
|
|
|
|
entity.tile = 24;
|
|
|
|
entity.animate = 0;
|
|
|
|
entity.colour = 8;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
if (game.roomy == 111 && (game.roomx >= 113 && game.roomx <= 117))
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.setenemy(0);
|
|
|
|
entity.setenemyroom(game.roomx, game.roomy); //For colour
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else if (game.roomx == 113 && (game.roomy <= 110 && game.roomy >= 108))
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.setenemy(1);
|
|
|
|
entity.setenemyroom(game.roomx, game.roomy); //For colour
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else if (game.roomx == 113 && game.roomy == 107)
|
|
|
|
{
|
|
|
|
//MAVVERRRICK
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = 96;
|
|
|
|
entity.colour = 6;
|
|
|
|
entity.size = 9;
|
|
|
|
entity.w = 64;
|
|
|
|
entity.h = 44;
|
|
|
|
entity.animate = 4;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.setenemyroom(game.roomx, game.roomy);
|
Fix enemy movement types 10/12 causing memory leaks
If spawned as a custom enemy (createentity entry 56), or spawned outside
of the rooms they spawn in in the main game, they will repeatedly clone
themselves every frame, which profusely leaks memory. In fact it quickly
causes a crash in 2.2 and previous, but 2.3 fixes that crash, so it just
keeps spawning enemies endlessly, which eventually lags the game, and
eventually can out-of-memory your system (bad!).
The problem is those movement types rely on entclass::setenemyroom() to
change their `behave` to be 11 or 13. Else, the new entity created will
still have `behave` 10 or 12, which will create ANOTHER entity in the
same way, and so on, and so forth.
So to fix this, just make it so if an enemy is still `behave` 10 or 12
by the end, then, just set it to -1. That way it'll stay still and won't
cause any harm.
I considered setting the `behave` to 11 or 13 respectively, but, that's
probably going farther than just fixing a memory leak, and anyways, it's
not that much useful for me as a custom level maker, and the entities
spawned aren't really controllable.
2021-08-20 01:36:31 +02:00
|
|
|
entityclonefix(&entity);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2: //A moving platform
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 2;
|
|
|
|
entity.type = 1;
|
|
|
|
entity.size = 2;
|
|
|
|
entity.tile = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
if (customplatformtile > 0){
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = customplatformtile;
|
2020-01-01 21:29:24 +01:00
|
|
|
}else if (platformtile > 0) {
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = platformtile;
|
2020-01-01 21:29:24 +01:00
|
|
|
}else{
|
|
|
|
//appearance again depends on location
|
2020-04-03 22:50:16 +02:00
|
|
|
if (gridmatch(p1, p2, p3, p4, 100, 70, 320, 160)) entity.tile = 616;
|
|
|
|
if (gridmatch(p1, p2, p3, p4, 72, 0, 248, 240)) entity.tile = 610;
|
|
|
|
if (gridmatch(p1, p2, p3, p4, -20, 0, 320, 240)) entity.tile = 413;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
if (gridmatch(p1, p2, p3, p4, -96, -72, 400, 312)) entity.tile = 26;
|
|
|
|
if (gridmatch(p1, p2, p3, p4, -32, -40, 352, 264)) entity.tile = 27;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.w = 32;
|
|
|
|
entity.h = 8;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta1 <= 1) vertplatforms = true;
|
|
|
|
if (meta1 >= 2 && meta1 <= 5) horplatforms = true;
|
|
|
|
if (meta1 == 14 || meta1 == 15) horplatforms = true; //special case for last part of Space Station
|
|
|
|
if (meta1 >= 6 && meta1 <= 7) vertplatforms = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta1 >= 10 && meta1 <= 11)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Double sized threadmills
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.w = 64;
|
|
|
|
entity.h = 8;
|
2021-04-17 10:14:10 +02:00
|
|
|
meta1 -= 2;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.size = 8;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.behave = meta1;
|
|
|
|
entity.para = meta2;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta1 >= 8 && meta1 <= 9)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
horplatforms = true; //threadmill!
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.animate = 10;
|
2020-01-01 21:29:24 +01:00
|
|
|
if(customplatformtile>0){
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = customplatformtile+4;
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta1 == 8) entity.tile += 4;
|
|
|
|
if (meta1 == 9) entity.animate = 11;
|
2020-01-01 21:29:24 +01:00
|
|
|
}else{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.settreadmillcolour(game.roomx, game.roomy);
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta1 == 8) entity.tile += 40;
|
|
|
|
if (meta1 == 9) entity.animate = 11;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.animate = 100;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.x1 = p1;
|
|
|
|
entity.y1 = p2;
|
|
|
|
entity.x2 = p3;
|
|
|
|
entity.y2 = p4;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.isplatform = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
createblock(0, xp, yp, 32, 8);
|
|
|
|
break;
|
|
|
|
case 3: //Disappearing platforms
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 2;
|
|
|
|
entity.size = 2;
|
|
|
|
entity.tile = 2;
|
2020-01-01 21:29:24 +01:00
|
|
|
//appearance again depends on location
|
|
|
|
if(customplatformtile>0)
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile=customplatformtile;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2021-04-17 10:14:10 +02:00
|
|
|
else if (meta1 > 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = meta1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
if(game.roomx==49 && game.roomy==52) entity.tile = 18;
|
|
|
|
if (game.roomx == 50 && game.roomy == 52) entity.tile = 22;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.cy = -1;
|
|
|
|
entity.w = 32;
|
|
|
|
entity.h = 10;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.behave = meta1;
|
|
|
|
entity.para = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
createblock(0, xp, yp, 32, 8);
|
|
|
|
break;
|
|
|
|
case 4: //Breakable blocks
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 6;
|
|
|
|
entity.type = 3;
|
|
|
|
entity.size = 1;
|
|
|
|
entity.tile = 10;
|
|
|
|
entity.cy = -1;
|
|
|
|
entity.w = 8;
|
|
|
|
entity.h = 10;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.behave = meta1;
|
|
|
|
entity.para = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
createblock(0, xp, yp, 8, 8);
|
|
|
|
break;
|
|
|
|
case 5: //Gravity Tokens
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 4;
|
|
|
|
entity.size = 0;
|
|
|
|
entity.tile = 11;
|
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.behave = meta1;
|
|
|
|
entity.para = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 6: //Decorative particles
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 2;
|
|
|
|
entity.type = 5; //Particles
|
|
|
|
entity.colour = 1;
|
|
|
|
entity.size = 3;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.vx = meta1;
|
|
|
|
entity.vy = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
|
|
|
|
entity.life = 12;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 7: //Decorative particles
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 2;
|
|
|
|
entity.type = 5; //Particles
|
|
|
|
entity.colour = 2;
|
|
|
|
entity.size = 3;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.vx = meta1;
|
|
|
|
entity.vy = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
|
|
|
|
entity.life = 12;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 8: //Small collectibles
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 6;
|
|
|
|
entity.size = 4;
|
|
|
|
entity.tile = 48;
|
|
|
|
entity.w = 8;
|
|
|
|
entity.h = 8;
|
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Check if it's already been collected
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta1;
|
|
|
|
if (!INBOUNDS_ARR(meta1, collect) || collect[meta1]) return;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 9: //Something Shiny
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 7;
|
|
|
|
entity.size = 0;
|
|
|
|
entity.tile = 22;
|
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.colour = 3;
|
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Check if it's already been collected
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta1;
|
|
|
|
if (!INBOUNDS_ARR(meta1, collect) || collect[meta1]) return;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 10: //Savepoint
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 8;
|
|
|
|
entity.size = 0;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 20 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.colour = 4;
|
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta2;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
if (game.savepoint == meta2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 5;
|
|
|
|
entity.onentity = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (game.nodeathmode)
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
return;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 11: //Horizontal Gravity Line
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 4;
|
|
|
|
entity.type = 9;
|
|
|
|
entity.size = 5;
|
|
|
|
entity.life = 0;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.w = meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.h = 1;
|
|
|
|
entity.onentity = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 12: //Vertical Gravity Line
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 5;
|
|
|
|
entity.type = 10;
|
|
|
|
entity.size = 6;
|
|
|
|
entity.life = 0;
|
|
|
|
entity.w = 1;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.h = meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
//entity.colour = 0;
|
|
|
|
entity.onentity = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 13: //Warp token
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 11;
|
|
|
|
entity.size = 0;
|
|
|
|
entity.tile = 18;
|
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.colour = 10;
|
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 2;
|
2020-01-01 21:29:24 +01:00
|
|
|
//Added in port, hope it doesn't break anything
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.behave = meta1;
|
|
|
|
entity.para = meta2;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 14: // Teleporter
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 100;
|
|
|
|
entity.size = 7;
|
|
|
|
entity.tile = 1; //inactive
|
|
|
|
entity.w = 96;
|
|
|
|
entity.h = 96;
|
|
|
|
entity.colour = 100;
|
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta2;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 15: // Crew Member (warp zone)
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 6;
|
|
|
|
entity.type = 12; //A special case!
|
|
|
|
entity.tile = 144;
|
|
|
|
entity.colour = 13; //144 for sad :(
|
|
|
|
entity.cx = 6;
|
|
|
|
entity.cy = 2;
|
|
|
|
entity.w = 12;
|
|
|
|
entity.h = 21;
|
|
|
|
entity.dir = 0;
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.state = meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
|
|
|
|
entity.gravity = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 16: // Crew Member, upside down (space station)
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 7;
|
|
|
|
entity.type = 12; //A special case!
|
|
|
|
entity.tile = 144+6;
|
|
|
|
entity.colour = 14; //144 for sad (upside down+12):(
|
|
|
|
entity.cx = 6;
|
|
|
|
entity.cy = 2;
|
|
|
|
entity.w = 12;
|
|
|
|
entity.h = 21;
|
|
|
|
entity.dir = 1;
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.state = meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
|
|
|
|
entity.gravity = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 17: // Crew Member (Lab)
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 6;
|
|
|
|
entity.type = 12; //A special case!
|
|
|
|
entity.tile = 144;
|
|
|
|
entity.colour = 16; //144 for sad :(
|
|
|
|
entity.cx = 6;
|
|
|
|
entity.cy = 2;
|
|
|
|
entity.w = 12;
|
|
|
|
entity.h = 21;
|
|
|
|
entity.dir = 1;
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.state = meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
|
|
|
|
entity.gravity = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 18: // Crew Member (Ship)
|
|
|
|
//This is the scriping crewmember
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 6;
|
|
|
|
entity.type = 12; //A special case!
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.colour = meta1;
|
|
|
|
if (meta2 == 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = 144;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.cx = 6;
|
|
|
|
entity.cy = 2;
|
|
|
|
entity.w = 12;
|
|
|
|
entity.h = 21;
|
|
|
|
entity.dir = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.state = p1;
|
|
|
|
entity.para = p2;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
if (p1 == 17)
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.dir = p2;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.gravity = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 19: // Crew Member (Ship) More tests!
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 6;
|
|
|
|
entity.type = 12; //A special case!
|
|
|
|
entity.tile = 0;
|
|
|
|
entity.colour = 6; //54 for sad :(
|
|
|
|
entity.cx = 6;
|
|
|
|
entity.cy = 2;
|
|
|
|
entity.w = 12;
|
|
|
|
entity.h = 21;
|
|
|
|
entity.dir = 1;
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.state = meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
|
|
|
|
entity.gravity = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 20: //Terminal
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 13;
|
|
|
|
entity.size = 0;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 16 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.colour = 4;
|
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta2;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 21: //as above, except doesn't highlight
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 13;
|
|
|
|
entity.size = 0;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 16 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.colour = 4;
|
|
|
|
entity.onentity = 0;
|
|
|
|
entity.animate = 100;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta2;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 22: //Fake trinkets, only appear if you've collected them
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 7;
|
|
|
|
entity.size = 0;
|
|
|
|
entity.tile = 22;
|
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.colour = 3;
|
|
|
|
entity.onentity = 0;
|
|
|
|
entity.animate = 100;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Check if it's already been collected
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta1;
|
|
|
|
if (INBOUNDS_ARR(meta1, collect) && !collect[meta1]) return;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 23: //SWN Enemies
|
|
|
|
//Given a different behavior, these enemies are especially for SWN mode and disappear outside the screen.
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 1;
|
|
|
|
entity.type = 23;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.behave = meta1;
|
|
|
|
entity.para = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.cx = 0;
|
|
|
|
entity.cy = 0;
|
|
|
|
|
|
|
|
entity.x1 = -2000;
|
|
|
|
entity.y1 = -100;
|
|
|
|
entity.x2 = 5200;
|
|
|
|
entity.y2 = 340;
|
|
|
|
|
|
|
|
entity.harmful = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//initilise tiles here based on behavior
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.size = 12; //don't wrap around
|
|
|
|
entity.colour = 21;
|
|
|
|
entity.tile = 78; //default case
|
|
|
|
entity.animate = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
if (game.swngame == 1)
|
|
|
|
{
|
|
|
|
//set colour based on current state
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = swncolour(game.swncolstate);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 24: // Super Crew Member
|
|
|
|
//This special crewmember is way more advanced than the usual kind, and can interact with game objects
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 6;
|
|
|
|
entity.type = 14; //A special case!
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.colour = meta1;
|
|
|
|
if (meta1 == 16)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//victoria is sad!
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta2 == 2) meta2 = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta2 == 2) meta2 = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2021-04-17 10:14:10 +02:00
|
|
|
if (meta2 == 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = 144;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.cx = 6;
|
|
|
|
entity.cy = 2;
|
|
|
|
entity.w = 12;
|
|
|
|
entity.h = 21;
|
|
|
|
entity.dir = 1;
|
|
|
|
|
|
|
|
entity.x1 = -2000;
|
|
|
|
entity.y1 = -100;
|
|
|
|
entity.x2 = 5200;
|
|
|
|
entity.y2 = 340;
|
|
|
|
|
|
|
|
entity.state = p1;
|
|
|
|
entity.para = p2;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
if (p1 == 17)
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.dir = p2;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.gravity = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 25: //Trophies
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 15;
|
|
|
|
entity.size = 0;
|
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.colour = 4;
|
|
|
|
entity.onentity = 1;
|
|
|
|
entity.animate = 100;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta2;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Decide tile here based on given achievement: both whether you have them and what they are
|
|
|
|
//default is just a trophy base:
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 180 + meta1;
|
|
|
|
switch (meta2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
if(game.bestrank[0]>=3)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 184 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 31;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if(game.bestrank[1]>=3)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 186 + meta1;
|
Fix Secret Lab Time Trial trophies having wrong colors
Ever since 2.0, the colors of some of the Time Trial trophies in the
Secret Lab don't correspond to the crewmate of the given level. The
trophy for the Tower uses Victoria's color, and the Lab trophy uses
Vermilion's color. The Space Station 2 trophy uses Viridian's color, and
the Final Level trophy uses Vitellary's color.
This doesn't appear to be intentional, and it would be odd if it was,
since this game matches the colors everywhere else (each zone on the map
is colored with their respective crewmate in mind, for instance). Also,
the Lab trophy has the sad expression, which is Victoria's trait - it
would be weird if this was intended for Vermilion instead.
But the biggest piece of evidence that this was unintentional is the
corresponding comment for each color in Graphics::setcol(). It mislabels
yellow as cyan, cyan as yellow, blue as red, and red as blue.
To fix this, I simply have to set the correct color for each trophy in
case 25 of entityclass::createentity(). I could fix it in
Graphics::setcol() itself, but custom levels might depend on those
certain colors being the way they are, so it's a safer bet to just fix
it in the trophy creation case itself.
The diff of this might look weird. Even though all I'm doing is changing
some value assignments around, it looks like the "patience" algorithm
thinks I'm moving a whole case of the trophy switch-case around.
2020-11-15 00:43:22 +01:00
|
|
|
entity.colour = 33;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if(game.bestrank[2]>=3)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 184 + meta1;
|
Fix Secret Lab Time Trial trophies having wrong colors
Ever since 2.0, the colors of some of the Time Trial trophies in the
Secret Lab don't correspond to the crewmate of the given level. The
trophy for the Tower uses Victoria's color, and the Lab trophy uses
Vermilion's color. The Space Station 2 trophy uses Viridian's color, and
the Final Level trophy uses Vitellary's color.
This doesn't appear to be intentional, and it would be odd if it was,
since this game matches the colors everywhere else (each zone on the map
is colored with their respective crewmate in mind, for instance). Also,
the Lab trophy has the sad expression, which is Victoria's trait - it
would be weird if this was intended for Vermilion instead.
But the biggest piece of evidence that this was unintentional is the
corresponding comment for each color in Graphics::setcol(). It mislabels
yellow as cyan, cyan as yellow, blue as red, and red as blue.
To fix this, I simply have to set the correct color for each trophy in
case 25 of entityclass::createentity(). I could fix it in
Graphics::setcol() itself, but custom levels might depend on those
certain colors being the way they are, so it's a safer bet to just fix
it in the trophy creation case itself.
The diff of this might look weird. Even though all I'm doing is changing
some value assignments around, it looks like the "patience" algorithm
thinks I'm moving a whole case of the trophy switch-case around.
2020-11-15 00:43:22 +01:00
|
|
|
entity.colour = 35;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
if(game.bestrank[3]>=3)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 184 + meta1;
|
Fix Secret Lab Time Trial trophies having wrong colors
Ever since 2.0, the colors of some of the Time Trial trophies in the
Secret Lab don't correspond to the crewmate of the given level. The
trophy for the Tower uses Victoria's color, and the Lab trophy uses
Vermilion's color. The Space Station 2 trophy uses Viridian's color, and
the Final Level trophy uses Vitellary's color.
This doesn't appear to be intentional, and it would be odd if it was,
since this game matches the colors everywhere else (each zone on the map
is colored with their respective crewmate in mind, for instance). Also,
the Lab trophy has the sad expression, which is Victoria's trait - it
would be weird if this was intended for Vermilion instead.
But the biggest piece of evidence that this was unintentional is the
corresponding comment for each color in Graphics::setcol(). It mislabels
yellow as cyan, cyan as yellow, blue as red, and red as blue.
To fix this, I simply have to set the correct color for each trophy in
case 25 of entityclass::createentity(). I could fix it in
Graphics::setcol() itself, but custom levels might depend on those
certain colors being the way they are, so it's a safer bet to just fix
it in the trophy creation case itself.
The diff of this might look weird. Even though all I'm doing is changing
some value assignments around, it looks like the "patience" algorithm
thinks I'm moving a whole case of the trophy switch-case around.
2020-11-15 00:43:22 +01:00
|
|
|
entity.colour = 30;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
if(game.bestrank[4]>=3)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 184 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 34;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
if(game.bestrank[5]>=3)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 184 + meta1;
|
Fix Secret Lab Time Trial trophies having wrong colors
Ever since 2.0, the colors of some of the Time Trial trophies in the
Secret Lab don't correspond to the crewmate of the given level. The
trophy for the Tower uses Victoria's color, and the Lab trophy uses
Vermilion's color. The Space Station 2 trophy uses Viridian's color, and
the Final Level trophy uses Vitellary's color.
This doesn't appear to be intentional, and it would be odd if it was,
since this game matches the colors everywhere else (each zone on the map
is colored with their respective crewmate in mind, for instance). Also,
the Lab trophy has the sad expression, which is Victoria's trait - it
would be weird if this was intended for Vermilion instead.
But the biggest piece of evidence that this was unintentional is the
corresponding comment for each color in Graphics::setcol(). It mislabels
yellow as cyan, cyan as yellow, blue as red, and red as blue.
To fix this, I simply have to set the correct color for each trophy in
case 25 of entityclass::createentity(). I could fix it in
Graphics::setcol() itself, but custom levels might depend on those
certain colors being the way they are, so it's a safer bet to just fix
it in the trophy creation case itself.
The diff of this might look weird. Even though all I'm doing is changing
some value assignments around, it looks like the "patience" algorithm
thinks I'm moving a whole case of the trophy switch-case around.
2020-11-15 00:43:22 +01:00
|
|
|
entity.colour = 32;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
if(game.unlock[5])
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 188 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 37;
|
|
|
|
entity.h += 3;
|
2020-07-29 02:44:55 +02:00
|
|
|
entity.yp -= 3;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if(game.unlock[19])
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 188 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 37;
|
|
|
|
entity.h += 3;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9:
|
|
|
|
if (game.bestgamedeaths > -1)
|
|
|
|
{
|
|
|
|
if (game.bestgamedeaths <= 50)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 40;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
if (game.bestgamedeaths > -1)
|
|
|
|
{
|
|
|
|
if (game.bestgamedeaths <= 100)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 36;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
if (game.bestgamedeaths > -1)
|
|
|
|
{
|
|
|
|
if (game.bestgamedeaths <= 250)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 38;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
if (game.bestgamedeaths > -1)
|
|
|
|
{
|
|
|
|
if (game.bestgamedeaths <= 500)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 39;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 13:
|
|
|
|
if(game.swnbestrank>=1)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 39;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
if(game.swnbestrank>=2)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 39;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
if(game.swnbestrank>=3)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 39;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
if(game.swnbestrank>=4)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 38;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 17:
|
|
|
|
if(game.swnbestrank>=5)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 36;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 18:
|
|
|
|
if(game.swnbestrank>=6)
|
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.tile = 182 + meta1;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 40;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 19:
|
|
|
|
if(game.unlock[20])
|
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = 3;
|
|
|
|
entity.colour = 102;
|
|
|
|
entity.size = 13;
|
|
|
|
entity.xp -= 64;
|
|
|
|
entity.yp -= 128;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case 26: //Epilogue super warp token
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 11;
|
|
|
|
entity.size = 0;
|
|
|
|
entity.tile = 18;
|
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.colour = 3;
|
|
|
|
entity.onentity = 0;
|
|
|
|
entity.animate = 100;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.size = 13;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
|
2021-03-26 02:57:53 +01:00
|
|
|
/* Warp lines */
|
|
|
|
case 51: /* Vertical */
|
|
|
|
case 52: /* Vertical */
|
|
|
|
case 53: /* Horizontal */
|
|
|
|
case 54: /* Horizontal */
|
|
|
|
entity.type = t;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.onentity = 1;
|
2021-03-26 02:57:53 +01:00
|
|
|
entity.invis = true;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.life = 0;
|
2021-03-26 02:57:53 +01:00
|
|
|
switch (t)
|
|
|
|
{
|
|
|
|
case 51:
|
|
|
|
case 52:
|
|
|
|
entity.rule = 5;
|
|
|
|
entity.size = 6;
|
|
|
|
entity.w = 1;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.h = meta1;
|
2021-03-26 02:57:53 +01:00
|
|
|
break;
|
|
|
|
case 53:
|
|
|
|
case 54:
|
|
|
|
entity.rule = 7;
|
|
|
|
entity.size = 5;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.w = meta1;
|
2021-03-26 02:57:53 +01:00
|
|
|
entity.h = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (map.custommode)
|
|
|
|
{
|
|
|
|
customwarpmode = true;
|
Fix 1-frame flicker entering room with warp lines and entity on edge
Custom levels can have warp lines. If you have a warp line and a warping
background in the same room, the warp line takes precedence over the
warp background.
However, whenever you enter a room with a warp line and warp background,
any entities on the warping edges will be drawn with screenwrapping for
one frame, even though they never wrapped at all.
This is due to frame ordering: when the warp line gets created,
obj.customwarpmode gets set to true. Then when the screen edges and
warping logic gets ran, the very first thing that gets checked is this
exact variable, and map.warpx/map.warpy get set appropriately - so
there's no way the entity could legitimately screenwrap.
However, that happens in gamelogic(). gamelogic() is also the one
responsible for creating entities upon room load, but that happens after
the obj.customwarpmode check - so when the game gets around to rendering
in gamerender(), it sees that map.warpx or map.warpy is on, and draws
the screenwrapping, even though map.warpx/map.warpy aren't really on at
all. Only when gamelogic() is called in the frame later do map.warpx and
map.warpy finally get set to false.
To fix this, just set map.warpx and map.warpy to false when creating
warp lines.
2021-03-26 03:00:31 +01:00
|
|
|
map.warpx = false;
|
|
|
|
map.warpy = false;
|
2021-03-26 02:57:53 +01:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 55: // Crew Member (custom, collectable)
|
|
|
|
//1 - position in array
|
|
|
|
//2 - colour
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 3;
|
|
|
|
entity.type = 55;
|
2021-04-17 10:14:10 +02:00
|
|
|
if(INBOUNDS_ARR(meta2, customcrewmoods)
|
|
|
|
&& customcrewmoods[meta2]==1){
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = 144;
|
2020-01-01 21:29:24 +01:00
|
|
|
}else{
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.tile = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
|
|
|
entity.colour = graphics.crewcolour(meta2);
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.cx = 6;
|
|
|
|
entity.cy = 2;
|
|
|
|
entity.w = 12;
|
|
|
|
entity.h = 21;
|
|
|
|
entity.dir = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.state = 0;
|
|
|
|
entity.onentity = 1;
|
2021-04-17 10:14:10 +02:00
|
|
|
//entity.state = meta1;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.gravity = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Check if it's already been collected
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.para = meta1;
|
|
|
|
if (!INBOUNDS_ARR(meta1, customcollect) || customcollect[meta1]) return;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
case 56: //Custom enemy
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.rule = 1;
|
|
|
|
entity.type = 1;
|
2021-04-17 10:14:10 +02:00
|
|
|
entity.behave = meta1;
|
|
|
|
entity.para = meta2;
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.w = 16;
|
|
|
|
entity.h = 16;
|
|
|
|
entity.cx = 0;
|
|
|
|
entity.cy = 0;
|
|
|
|
|
|
|
|
entity.x1 = p1;
|
|
|
|
entity.y1 = p2;
|
|
|
|
entity.x2 = p3;
|
|
|
|
entity.y2 = p4;
|
|
|
|
|
|
|
|
entity.harmful = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
switch(customenemy){
|
2020-04-03 22:50:16 +02:00
|
|
|
case 0: entity.setenemyroom(4+100, 0+100); break;
|
|
|
|
case 1: entity.setenemyroom(2+100, 0+100); break;
|
|
|
|
case 2: entity.setenemyroom(12+100, 3+100); break;
|
|
|
|
case 3: entity.setenemyroom(13+100, 12+100); break;
|
|
|
|
case 4: entity.setenemyroom(16+100, 9+100); break;
|
|
|
|
case 5: entity.setenemyroom(19+100, 1+100); break;
|
|
|
|
case 6: entity.setenemyroom(19+100, 2+100); break;
|
|
|
|
case 7: entity.setenemyroom(18+100, 3+100); break;
|
|
|
|
case 8: entity.setenemyroom(16+100, 0+100); break;
|
|
|
|
case 9: entity.setenemyroom(14+100, 2+100); break;
|
|
|
|
default: entity.setenemyroom(4+100, 0+100); break;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//Set colour based on room tile
|
|
|
|
//Set custom colours
|
|
|
|
if(customplatformtile>0){
|
|
|
|
int entcol=(customplatformtile/12);
|
|
|
|
switch(entcol){
|
|
|
|
//RED
|
|
|
|
case 3: case 7: case 12: case 23: case 28:
|
|
|
|
case 34: case 42: case 48: case 58:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 6; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
//GREEN
|
|
|
|
case 5: case 9: case 22: case 25: case 29:
|
|
|
|
case 31: case 38: case 46: case 52: case 53:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 7; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
//BLUE
|
|
|
|
case 1: case 6: case 14: case 27: case 33:
|
|
|
|
case 44: case 50: case 57:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 12; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
//YELLOW
|
|
|
|
case 4: case 17: case 24: case 30: case 37:
|
|
|
|
case 45: case 51: case 55:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 9; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
//PURPLE
|
|
|
|
case 2: case 11: case 15: case 19: case 32:
|
|
|
|
case 36: case 49:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 20; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
//CYAN
|
|
|
|
case 8: case 10: case 13: case 18: case 26:
|
|
|
|
case 35: case 41: case 47: case 54:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 11; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
//PINK
|
|
|
|
case 16: case 20: case 39: case 43: case 56:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 8; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
//ORANGE
|
|
|
|
case 21: case 40:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 17; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
default:
|
2020-04-03 22:50:16 +02:00
|
|
|
entity.colour = 6;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-30 00:27:23 +02:00
|
|
|
if(custom_gray){
|
|
|
|
entity.colour = 18;
|
|
|
|
}
|
|
|
|
|
Fix enemy movement types 10/12 causing memory leaks
If spawned as a custom enemy (createentity entry 56), or spawned outside
of the rooms they spawn in in the main game, they will repeatedly clone
themselves every frame, which profusely leaks memory. In fact it quickly
causes a crash in 2.2 and previous, but 2.3 fixes that crash, so it just
keeps spawning enemies endlessly, which eventually lags the game, and
eventually can out-of-memory your system (bad!).
The problem is those movement types rely on entclass::setenemyroom() to
change their `behave` to be 11 or 13. Else, the new entity created will
still have `behave` 10 or 12, which will create ANOTHER entity in the
same way, and so on, and so forth.
So to fix this, just make it so if an enemy is still `behave` 10 or 12
by the end, then, just set it to -1. That way it'll stay still and won't
cause any harm.
I considered setting the `behave` to 11 or 13 respectively, but, that's
probably going farther than just fixing a memory leak, and anyways, it's
not that much useful for me as a custom level maker, and the entities
spawned aren't really controllable.
2021-08-20 01:36:31 +02:00
|
|
|
entityclonefix(&entity);
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
|
|
|
}
|
2020-04-03 22:50:16 +02:00
|
|
|
|
2021-05-17 02:58:34 +02:00
|
|
|
entity.lerpoldxp = entity.xp;
|
|
|
|
entity.lerpoldyp = entity.yp;
|
2020-04-29 01:37:55 +02:00
|
|
|
entity.drawframe = entity.tile;
|
|
|
|
|
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
|
|
|
if (!reuse)
|
|
|
|
{
|
|
|
|
entities.push_back(entity);
|
|
|
|
}
|
Re-fix crewmate directions (without copy-pasting)
This once again fixes the facing directions of crewmates upon room load,
except now it covers more cases.
So, here is the saga so far:
- 2.0 (presumably) to 2.2: crewmate direction fix is special-cased at
the end of mapclass::loadlevel(). Only covers crewmates created during
the room load, does not cover crewmates created from scripts, only
covers state 18 of crewmates.
- 2.3 currently (after #220): crewmate direction fix is moved to
entityclass::createentity(), which covers every avenue of crewmate
creation (including from scripts), but still only covers state 18.
- This commit: crewmate direction fix now covers every possible state of
the crewmate, also does not copy-paste any code.
What I've done instead is to make it so createentity() will immediately
call updateentities() on the pushed-back entity. This is kludge-y, but
is completely okay to do, because unlike other entities, crewmate
entities never change their state or have any side-effects from
double-evaluation, meaning calling updateentities() on them is
idempotent and it's okay to call their updateentities() more than once.
This does have the slight danger that if the states of crewmates were to
change in the future to no longer be idempotent, this would end up
resulting in a somewhat hard-to-track-down double-evaluation bug, but
it's worth taking that risk.
This fix is not applied to entity 14 (the supercrewmate) because it is
possible that calling updateentities() on it will immediately remove the
entity, which is not idempotent (it's changing the state of something
outside the object). Supercrewmates are a bit difficult to work with
outside of the main game anyways, and if you spawn them you could
probably just use the changedir() script command to fix their direction,
so I'm not inclined to fix this for them anyway.
2021-01-18 21:44:22 +01:00
|
|
|
|
|
|
|
/* Fix crewmate facing directions
|
|
|
|
* This is a bit kludge-y but it's better than copy-pasting
|
|
|
|
* and is okay to do because entity 12 does not change state on its own
|
|
|
|
*/
|
|
|
|
if (entity.type == 12)
|
|
|
|
{
|
|
|
|
size_t indice;
|
|
|
|
if (reuse)
|
|
|
|
{
|
|
|
|
indice = entptr - entities.data();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
indice = entities.size() - 1;
|
|
|
|
}
|
|
|
|
updateentities(indice);
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1, int p2)
|
2021-04-17 10:08:51 +02:00
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
createentity(xp, yp, t, meta1, meta2, p1, p2, 320, 240);
|
2021-04-17 10:08:51 +02:00
|
|
|
}
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1)
|
2021-04-17 10:08:51 +02:00
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
createentity(xp, yp, t, meta1, meta2, p1, 0);
|
2021-04-17 10:08:51 +02:00
|
|
|
}
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2)
|
2021-04-17 10:08:51 +02:00
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
createentity(xp, yp, t, meta1, meta2, 0);
|
2021-04-17 10:08:51 +02:00
|
|
|
}
|
|
|
|
|
2021-04-17 10:14:10 +02:00
|
|
|
void entityclass::createentity(int xp, int yp, int t, int meta1)
|
2021-04-17 10:08:51 +02:00
|
|
|
{
|
2021-04-17 10:14:10 +02:00
|
|
|
createentity(xp, yp, t, meta1, 0);
|
2021-04-17 10:08:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void entityclass::createentity(int xp, int yp, int t)
|
|
|
|
{
|
|
|
|
createentity(xp, yp, t, 0);
|
|
|
|
}
|
|
|
|
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
//Returns true if entity is removed
|
|
|
|
bool entityclass::updateentities( int i )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(i, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("updateentities() out-of-bounds!");
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
return true;
|
2020-05-05 01:41:30 +02:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
if(entities[i].statedelay<=0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
switch(entities[i].type)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
case 0: //Player
|
|
|
|
break;
|
|
|
|
case 1: //Movement behaviors
|
|
|
|
//Enemies can have a number of different behaviors:
|
|
|
|
switch(entities[i].behave)
|
|
|
|
{
|
|
|
|
case 0: //Bounce, Start moving down
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 3;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
bool entitygone = updateentities(i);
|
|
|
|
if (entitygone) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].state == 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[i].outside()) entities[i].state = entities[i].onwall;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
entities[i].vy = -entities[i].para;
|
|
|
|
entities[i].onwall = 3;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 3)
|
|
|
|
{
|
|
|
|
entities[i].vy = entities[i].para;
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
entities[i].state = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 1: //Bounce, Start moving up
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
entities[i].state = 2;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
bool entitygone = updateentities(i);
|
|
|
|
if (entitygone) return true;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
if (entities[i].outside()) entities[i].state = entities[i].onwall;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vy = -entities[i].para;
|
|
|
|
entities[i].onwall = 3;
|
|
|
|
entities[i].state = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 3)
|
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vy = entities[i].para;
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
entities[i].state = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
break;
|
|
|
|
case 2: //Bounce, Start moving left
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 3;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
bool entitygone = updateentities(i);
|
|
|
|
if (entitygone) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].state == 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[i].outside()) entities[i].state = entities[i].onwall;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
entities[i].vx = entities[i].para;
|
|
|
|
entities[i].onwall = 3;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 3)
|
|
|
|
{
|
|
|
|
entities[i].vx = -entities[i].para;
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
entities[i].state = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 3: //Bounce, Start moving right
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 3;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
bool entitygone = updateentities(i);
|
|
|
|
if (entitygone) return true;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
if (entities[i].outside()) entities[i].state = entities[i].onwall;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vx = -entities[i].para;
|
|
|
|
entities[i].onwall = 3;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 3)
|
|
|
|
{
|
|
|
|
entities[i].vx = entities[i].para;
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
entities[i].state = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 4: //Always move left
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vx = entities[i].para;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 5: //Always move right
|
2020-01-01 21:29:24 +01:00
|
|
|
if (entities[i].state == 0)
|
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//Init
|
|
|
|
entities[i].vx = static_cast<int>(entities[i].para);
|
|
|
|
entities[i].state = 1;
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
entities[i].vx = 0;
|
|
|
|
entities[i].onwall = 0;
|
|
|
|
entities[i].xp -= static_cast<int>(entities[i].para);
|
|
|
|
entities[i].statedelay=8;
|
|
|
|
entities[i].state=0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 6: //Always move up
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vy = static_cast<int>(entities[i].para);
|
|
|
|
entities[i].state = 1;
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
entities[i].vy = static_cast<int>(-entities[i].para);
|
|
|
|
entities[i].onwall = 0;
|
|
|
|
entities[i].yp -= (entities[i].para);
|
|
|
|
entities[i].statedelay=8;
|
|
|
|
entities[i].state=0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 7: //Always move down
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vx = static_cast<int>(entities[i].para);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 8:
|
|
|
|
case 9:
|
|
|
|
//Threadmill: don't move, just impart velocity
|
|
|
|
if (entities[i].state == 0) //Init
|
|
|
|
{
|
|
|
|
entities[i].vx = 0;
|
|
|
|
entities[i].state = 1;
|
|
|
|
entities[i].onwall = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
//Emitter: shoot an enemy every so often
|
|
|
|
if (entities[i].state == 0)
|
|
|
|
{
|
|
|
|
createentity(entities[i].xp+28, entities[i].yp, 1, 10, 1);
|
|
|
|
entities[i].state = 1;
|
|
|
|
entities[i].statedelay = 12;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
entities[i].state = 0;
|
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 11: //Always move right, destroy when outside screen
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vx = entities[i].para;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 1)
|
|
|
|
{
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
if (entities[i].xp >= 335)
|
|
|
|
{
|
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
|
|
|
return disableentity(i);
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
if (game.roomx == 117)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
if (entities[i].xp >= (33*8)-32)
|
|
|
|
{
|
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
|
|
|
return disableentity(i);
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
//collector for LIES
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 12:
|
|
|
|
//Emitter: shoot an enemy every so often (up)
|
|
|
|
if (entities[i].state == 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
createentity(entities[i].xp, entities[i].yp, 1, 12, 1);
|
|
|
|
entities[i].state = 1;
|
|
|
|
entities[i].statedelay = 16;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].state = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 13: //Always move up, destroy when outside screen
|
|
|
|
if (entities[i].state == 0) //Init
|
|
|
|
{
|
|
|
|
entities[i].vy = entities[i].para;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 1)
|
|
|
|
{
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
if (entities[i].yp <= -60)
|
|
|
|
{
|
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
|
|
|
return disableentity(i);
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
}
|
2020-11-06 20:58:18 +01:00
|
|
|
if (game.roomx == 113 && game.roomy == 108)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
if (entities[i].yp <= 60)
|
|
|
|
{
|
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
|
|
|
return disableentity(i);
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
//collector for factory
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
break;
|
|
|
|
case 14: //Very special hack: as two, but doesn't move in specific circumstances
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
for (size_t j = 0; j < entities.size(); j++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[j].type == 2 && entities[j].state== 3 && entities[j].xp == (entities[i].xp-32) )
|
|
|
|
{
|
|
|
|
entities[i].state = 3;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
bool entitygone = updateentities(i);
|
|
|
|
if (entitygone) return true;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].state == 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[i].outside()) entities[i].state = entities[i].onwall;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].state == 2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vx = entities[i].para;
|
|
|
|
entities[i].onwall = 3;
|
|
|
|
entities[i].state = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].state == 3)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vx = -entities[i].para;
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
entities[i].state = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 15: //As above, but for 3!
|
|
|
|
if (entities[i].state == 0) //Init
|
|
|
|
{
|
|
|
|
for (size_t j = 0; j < entities.size(); j++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[j].type == 2 && entities[j].state==3 && entities[j].xp==entities[i].xp+32)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 3;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
bool entitygone = updateentities(i);
|
|
|
|
if (entitygone) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
if (entities[i].outside()) entities[i].state = entities[i].onwall;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vx = -entities[i].para;
|
|
|
|
entities[i].onwall = 3;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 3)
|
|
|
|
{
|
|
|
|
entities[i].vx = entities[i].para;
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 16: //MAVERICK BUS FOLLOWS HIS OWN RULES
|
|
|
|
if (entities[i].state == 0) //Init
|
|
|
|
{
|
2020-06-13 05:36:08 +02:00
|
|
|
int player = getplayer();
|
2020-04-03 22:57:02 +02:00
|
|
|
//first, y position
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(player, entities) && entities[player].yp > 14 * 8)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].tile = 120;
|
|
|
|
entities[i].yp = (28*8)-62;
|
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
|
|
|
entities[i].lerpoldyp = (28*8)-62;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].tile = 96;
|
|
|
|
entities[i].yp = 24;
|
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
|
|
|
entities[i].lerpoldyp = 24;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
//now, x position
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(player, entities) && entities[player].xp > 20 * 8)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//approach from the left
|
|
|
|
entities[i].xp = -64;
|
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
|
|
|
entities[i].lerpoldxp = -64;
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 2;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
bool entitygone = updateentities(i); //right
|
|
|
|
if (entitygone) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//approach from the left
|
|
|
|
entities[i].xp = 320;
|
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
|
|
|
entities[i].lerpoldxp = 320;
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 3;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
bool entitygone = updateentities(i); //left
|
|
|
|
if (entitygone) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].state == 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[i].outside()) entities[i].state = entities[i].onwall;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
entities[i].vx = int(entities[i].para);
|
|
|
|
entities[i].onwall = 3;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 3)
|
|
|
|
{
|
|
|
|
entities[i].vx = int(-entities[i].para);
|
|
|
|
entities[i].onwall = 2;
|
|
|
|
entities[i].state = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 17: //Special for ASCII Snake (left)
|
|
|
|
if (entities[i].state == 0) //Init
|
|
|
|
{
|
|
|
|
entities[i].statedelay = 6;
|
|
|
|
entities[i].xp -= int(entities[i].para);
|
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
|
|
|
entities[i].lerpoldxp -= int(entities[i].para);
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 18: //Special for ASCII Snake (right)
|
|
|
|
if (entities[i].state == 0) //Init
|
|
|
|
{
|
|
|
|
entities[i].statedelay = 6;
|
|
|
|
entities[i].xp += int(entities[i].para);
|
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
|
|
|
entities[i].lerpoldxp += int(entities[i].para);
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2: //Disappearing platforms
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].life = 12;
|
|
|
|
entities[i].state = 2;
|
|
|
|
entities[i].onentity = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
music.playef(7);
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
entities[i].life--;
|
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.
2021-01-09 23:50:06 +01:00
|
|
|
if (entities[i].life % 3 == 0) entities[i].walkingframe++;
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[i].life <= 0)
|
|
|
|
{
|
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
|
|
|
disableblockat(entities[i].xp, entities[i].yp);
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 3;// = false;
|
|
|
|
entities[i].invis = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 3)
|
|
|
|
{
|
|
|
|
//wait until recharged!
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 4)
|
|
|
|
{
|
|
|
|
//restart!
|
|
|
|
createblock(0, entities[i].xp, entities[i].yp, 32, 8);
|
|
|
|
entities[i].state = 4;
|
|
|
|
entities[i].invis = false;
|
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.
2021-01-09 23:50:06 +01:00
|
|
|
entities[i].walkingframe--;
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state++;
|
|
|
|
entities[i].onentity = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 5)
|
|
|
|
{
|
|
|
|
entities[i].life+=3;
|
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.
2021-01-09 23:50:06 +01:00
|
|
|
if (entities[i].life % 3 == 0) entities[i].walkingframe--;
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[i].life >= 12)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].life = 12;
|
|
|
|
entities[i].state = 0;
|
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.
2021-01-09 23:50:06 +01:00
|
|
|
entities[i].walkingframe++;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3: //Breakable blocks
|
|
|
|
//Only counts if vy of player entity is non zero
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].life = 4;
|
|
|
|
entities[i].state = 2;
|
|
|
|
entities[i].onentity = 0;
|
|
|
|
music.playef(6);
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
entities[i].life--;
|
|
|
|
entities[i].tile++;
|
|
|
|
if (entities[i].life <= 0)
|
|
|
|
{
|
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
|
|
|
disableblockat(entities[i].xp, entities[i].yp);
|
|
|
|
return disableentity(i);
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4: //Gravity token
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
game.gravitycontrol = (game.gravitycontrol + 1) % 2;
|
2021-08-23 05:39:05 +02:00
|
|
|
++game.totalflips;
|
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
|
|
|
return disableentity(i);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 5: //Particle sprays
|
|
|
|
if (entities[i].state == 0)
|
|
|
|
{
|
|
|
|
entities[i].life--;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
if (entities[i].life < 0)
|
|
|
|
{
|
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
|
|
|
return disableentity(i);
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 6: //Small pickup
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
music.playef(4);
|
2021-04-14 04:58:26 +02:00
|
|
|
if (INBOUNDS_ARR(entities[i].para, collect))
|
2020-09-10 07:40:15 +02:00
|
|
|
{
|
|
|
|
collect[(int) entities[i].para] = true;
|
|
|
|
}
|
2020-04-03 22:57:02 +02: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
|
|
|
return disableentity(i);
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 7: //Found a trinket
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
2020-07-03 04:17:32 +02:00
|
|
|
if (INBOUNDS_ARR(entities[i].para, collect))
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
2020-07-03 04:17:32 +02:00
|
|
|
collect[(int) entities[i].para] = true;
|
Prevent out-of-bounds indexing with trinket/crewmate IDs
If a trinket or crewmate ID is out-of-bounds, it will not be created.
This is not only because the `collect`/`customcollect` check in
entityclass::createentity() would then be out-of-bounds, but also
touching it would also be out-of-bounds, too.
Display trinkets will always be created if the ID is out-of-bounds.
Apparently some people (@AllyTally) have been creating display trinkets
with IDs of -1 in order to get a display trinket that always shows up,
which is rather horrifying. But it makes sense, there's a lot more
nonzero int values than there are the amount of int values that are
zero, so it's fairly likely that the `collect` check will always come up
to be true (nonzero). Also, it's more useful to be able to have a
display trinket that always shows up without having to collect a trinket
beforehand, than it is to have it not be created (because technically by
default, you're already in the world where you don't have it created).
Display trinkets still have their `para` set to their ID, though, and if
they managed to gain an `onentity` of 1, bad things could happen... So
just to be sure, I added INBOUNDS checks to crewmates and trinkets in
entityclass::updateentities() so no UB will happen if you collect a
crewmate/trinket with an out-of-bounds ID. Also, I de-duplicated the
`collect`/`customcollect` setting, too.
2020-06-15 23:20:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (game.intimetrial)
|
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
music.playef(25);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
game.state = 1000;
|
|
|
|
if(music.currentsong!=-1) music.silencedasmusik();
|
|
|
|
music.playef(3);
|
2020-04-07 08:46:27 +02:00
|
|
|
if (game.trinkets() > game.stat_trinkets && !map.custommode)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-07 08:46:27 +02:00
|
|
|
game.stat_trinkets = game.trinkets();
|
2021-08-05 23:20:32 +02:00
|
|
|
game.savestatsandsettings();
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
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
|
|
|
return disableentity(i);
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8: //Savepoints
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
//First, deactivate all other savepoints
|
|
|
|
for (size_t j = 0; j < entities.size(); j++)
|
|
|
|
{
|
|
|
|
if (entities[j].type == 8)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[j].colour = 4;
|
|
|
|
entities[j].onentity = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].colour = 5;
|
|
|
|
entities[i].onentity = 0;
|
|
|
|
game.savepoint = entities[i].para;
|
|
|
|
music.playef(5);
|
|
|
|
|
|
|
|
game.savex = entities[i].xp - 4;
|
|
|
|
|
|
|
|
if (entities[i].tile == 20)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-10-06 08:13:07 +02:00
|
|
|
game.savey = entities[i].yp - 2;
|
2020-04-03 22:57:02 +02:00
|
|
|
game.savegc = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].tile == 21)
|
|
|
|
{
|
2020-10-06 08:13:07 +02:00
|
|
|
game.savey = entities[i].yp - 7;
|
2020-04-03 22:57:02 +02:00
|
|
|
game.savegc = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
game.saverx = game.roomx;
|
|
|
|
game.savery = game.roomy;
|
2020-06-13 05:36:08 +02:00
|
|
|
int player = getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(player, entities))
|
2020-06-13 05:36:08 +02:00
|
|
|
{
|
|
|
|
game.savedir = entities[player].dir;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 9: //Gravity Lines
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].life--;
|
|
|
|
entities[i].onentity = 0;
|
|
|
|
|
|
|
|
if (entities[i].life <= 0)
|
|
|
|
{
|
|
|
|
entities[i].state = 0;
|
|
|
|
entities[i].onentity = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 10: //Vertical gravity Lines
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].onentity = 3;
|
|
|
|
entities[i].state = 2;
|
|
|
|
|
|
|
|
|
|
|
|
music.playef(8);
|
|
|
|
game.gravitycontrol = (game.gravitycontrol + 1) % 2;
|
|
|
|
game.totalflips++;
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int temp = getplayer();
|
2020-04-03 22:57:02 +02:00
|
|
|
if (game.gravitycontrol == 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(temp, entities) && entities[temp].vy < 3) entities[temp].vy = 3;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(temp, entities) && entities[temp].vy > -3) entities[temp].vy = -3;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
entities[i].life--;
|
|
|
|
if (entities[i].life <= 0)
|
|
|
|
{
|
|
|
|
entities[i].state = 0;
|
|
|
|
entities[i].onentity = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 3)
|
|
|
|
{
|
|
|
|
entities[i].state = 2;
|
|
|
|
entities[i].life = 4;
|
|
|
|
entities[i].onentity = 3;
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 4)
|
|
|
|
{
|
|
|
|
//Special case for room initilisations: As state one, except without the reversal
|
|
|
|
entities[i].onentity = 3;
|
|
|
|
entities[i].state = 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 11: //Warp point
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
//Depending on the room the warp point is in, teleport to a new location!
|
|
|
|
entities[i].onentity = 0;
|
|
|
|
//play a sound or somefink
|
|
|
|
music.playef(10);
|
|
|
|
game.teleport = true;
|
|
|
|
|
|
|
|
game.edteleportent = i;
|
|
|
|
//for the multiple room:
|
|
|
|
if (int(entities[i].xp) == 12*8) game.teleportxpos = 1;
|
|
|
|
if (int(entities[i].xp) == 5*8) game.teleportxpos = 2;
|
|
|
|
if (int(entities[i].xp) == 28*8) game.teleportxpos = 3;
|
|
|
|
if (int(entities[i].xp) == 21*8) game.teleportxpos = 4;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 12: //Crew member
|
|
|
|
//Somewhat complex AI: exactly what they do depends on room, location, state etc
|
|
|
|
//At state 0, do nothing at all.
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
//happy!
|
2021-09-07 03:56:39 +02:00
|
|
|
if (INBOUNDS_VEC(k, entities) && entities[k].rule == 6) entities[k].tile = 0;
|
|
|
|
if (INBOUNDS_VEC(k, entities) && entities[k].rule == 7) entities[k].tile = 6;
|
2020-04-03 22:57:02 +02:00
|
|
|
//Stay close to the hero!
|
|
|
|
int j = getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 45)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 45)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
//Special rules:
|
2020-11-02 05:04:54 +01:00
|
|
|
if (game.roomx == 110 && game.roomy == 105 && !map.custommode)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
if (entities[i].xp < 155)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[i].ax < 0) entities[i].ax = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 2)
|
|
|
|
{
|
|
|
|
//Basic rules, don't change expression
|
|
|
|
int j = getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 45)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 45)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].ax = -3;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 10)
|
|
|
|
{
|
|
|
|
//Everything from 10 on is for cutscenes
|
|
|
|
//Basic rules, don't change expression
|
|
|
|
int j = getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 45)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 45)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 11)
|
|
|
|
{
|
|
|
|
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
|
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
|
|
|
int j=getcrewman(PURPLE);
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities))
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 12)
|
|
|
|
{
|
|
|
|
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
|
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
|
|
|
int j=getcrewman(YELLOW);
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities))
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 13)
|
|
|
|
{
|
|
|
|
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
|
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
|
|
|
int j=getcrewman(RED);
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities))
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 14)
|
|
|
|
{
|
|
|
|
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
|
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
|
|
|
int j=getcrewman(GREEN);
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities))
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 15)
|
|
|
|
{
|
|
|
|
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
|
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
|
|
|
int j=getcrewman(BLUE);
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities))
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().
Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.
Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.
Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-10 07:31:09 +02:00
|
|
|
if (entities[j].xp > entities[i].xp + 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 16)
|
|
|
|
{
|
|
|
|
//Follow a position: given an x coordinate, seek it out.
|
|
|
|
if (entities[i].para > entities[i].xp + 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
|
|
|
else if (entities[i].para < entities[i].xp - 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entities[i].para > entities[i].xp + 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
|
|
|
else if (entities[i].para < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 17)
|
|
|
|
{
|
|
|
|
//stand still
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 18)
|
|
|
|
{
|
|
|
|
//Stand still and face the player
|
|
|
|
int j = getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 19)
|
|
|
|
{
|
|
|
|
//Walk right off the screen after time t
|
|
|
|
if (entities[i].para <= 0)
|
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
entities[i].ax = 3;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].para--;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else if (entities[i].state == 20)
|
|
|
|
{
|
|
|
|
//Panic! For briefing script
|
|
|
|
if (entities[i].life == 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//walk left for a bit
|
|
|
|
entities[i].ax = 0;
|
|
|
|
if (40 > entities[i].xp + 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (40 < entities[i].xp - 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
if (40 > entities[i].xp + 45)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (40 < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
|
|
|
if ( (entities[i].ax) == 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].life = 1;
|
|
|
|
entities[i].para = 30;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (entities[i].life == 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//Stand around for a bit
|
|
|
|
entities[i].para--;
|
|
|
|
if (entities[i].para <= 0)
|
|
|
|
{
|
|
|
|
entities[i].life++;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].life == 2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//walk right for a bit
|
|
|
|
entities[i].ax = 0;
|
|
|
|
if (280 > entities[i].xp + 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (280 < entities[i].xp - 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
if (280 > entities[i].xp + 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
|
|
|
else if (280 < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
|
|
|
if ( (entities[i].ax) == 0)
|
|
|
|
{
|
|
|
|
entities[i].life = 3;
|
|
|
|
entities[i].para = 30;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
else if (entities[i].life == 3)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//Stand around for a bit
|
|
|
|
entities[i].para--;
|
|
|
|
if (entities[i].para <= 0)
|
|
|
|
{
|
|
|
|
entities[i].life=0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 13: //Terminals (very similar to savepoints)
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].colour = 5;
|
|
|
|
entities[i].onentity = 0;
|
|
|
|
music.playef(17);
|
|
|
|
|
|
|
|
entities[i].state = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 14: //Super Crew member
|
|
|
|
//Actually needs less complex AI than the scripting crewmember
|
|
|
|
if (entities[i].state == 0)
|
|
|
|
{
|
|
|
|
//follow player, but only if he's on the floor!
|
|
|
|
int j = getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if(INBOUNDS_VEC(j, entities) && entities[j].onground>0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if (entities[j].xp > entities[i].xp + 5)
|
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[j].xp>15 && entities[j].xp < entities[i].xp - 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
|
|
|
if (entities[j].xp > entities[i].xp + 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
}
|
|
|
|
else if (entities[j].xp < entities[i].xp - 45)
|
|
|
|
{
|
|
|
|
entities[i].ax = -3;
|
|
|
|
}
|
|
|
|
if (entities[i].ax < 0 && entities[i].xp < 60)
|
|
|
|
{
|
|
|
|
entities[i].ax = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].dir = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].ax = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entities[i].xp > 240)
|
|
|
|
{
|
|
|
|
entities[i].ax = 3;
|
|
|
|
entities[i].dir = 1;
|
|
|
|
}
|
|
|
|
if (entities[i].xp >= 310)
|
|
|
|
{
|
|
|
|
game.scmprogress++;
|
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
|
|
|
return disableentity(i);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 15: //Trophy
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
2020-08-03 06:02:27 +02:00
|
|
|
if (!script.running) trophytext+=2;
|
2020-04-03 22:57:02 +02:00
|
|
|
if (trophytext > 30) trophytext = 30;
|
|
|
|
trophytype = entities[i].para;
|
|
|
|
|
|
|
|
entities[i].state = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 23:
|
|
|
|
//swn game!
|
|
|
|
switch(entities[i].behave)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
if (entities[i].state == 0) //Init
|
|
|
|
{
|
|
|
|
entities[i].vx = 7;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
if (entities[i].xp > 320)
|
|
|
|
{
|
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
|
|
|
return disableentity(i);
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 1:
|
|
|
|
if (entities[i].state == 0) //Init
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].vx = -7;
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
if (entities[i].xp <-20)
|
|
|
|
{
|
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
|
|
|
return disableentity(i);
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
case 51: //Vertical warp line
|
|
|
|
if (entities[i].state == 2){
|
|
|
|
int j=getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if(INBOUNDS_VEC(j, entities) && entities[j].xp<=307){
|
2020-04-03 22:57:02 +02:00
|
|
|
customwarpmodevon=false;
|
|
|
|
entities[i].state = 0;
|
|
|
|
}
|
|
|
|
}else if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].state = 2;
|
|
|
|
entities[i].statedelay = 2;
|
|
|
|
entities[i].onentity = 1;
|
|
|
|
customwarpmodevon=true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 52: //Vertical warp line
|
|
|
|
if (entities[i].state == 2){
|
|
|
|
int j=getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if(INBOUNDS_VEC(j, entities) && entities[j].xp<=307){
|
2020-04-03 22:57:02 +02:00
|
|
|
customwarpmodevon=false;
|
|
|
|
entities[i].state = 0;
|
|
|
|
}
|
|
|
|
}else if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].state = 2;
|
|
|
|
entities[i].statedelay = 2;
|
|
|
|
entities[i].onentity = 1;
|
|
|
|
customwarpmodevon=true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 53: //Warp lines Horizonal
|
|
|
|
if (entities[i].state == 2){
|
|
|
|
customwarpmodehon=false;
|
|
|
|
entities[i].state = 0;
|
|
|
|
}else if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].state = 2;
|
|
|
|
entities[i].statedelay = 2;
|
|
|
|
entities[i].onentity = 1;
|
|
|
|
customwarpmodehon=true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 54: //Warp lines Horizonal
|
|
|
|
if (entities[i].state == 2){
|
|
|
|
customwarpmodehon=false;
|
|
|
|
entities[i].state = 0;
|
|
|
|
}else if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
entities[i].state = 2;
|
|
|
|
entities[i].statedelay = 2;
|
|
|
|
entities[i].onentity = 1;
|
|
|
|
customwarpmodehon=true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 55: //Collectable crewmate
|
|
|
|
//wait for collision
|
|
|
|
if (entities[i].state == 0)
|
|
|
|
{
|
|
|
|
//Basic rules, don't change expression
|
|
|
|
int j = getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].dir = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
entities[i].dir = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[i].state == 1)
|
|
|
|
{
|
2020-07-03 04:17:32 +02:00
|
|
|
if (INBOUNDS_ARR(entities[i].para, customcollect))
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
2020-07-03 04:17:32 +02:00
|
|
|
customcollect[(int) entities[i].para] = true;
|
Prevent out-of-bounds indexing with trinket/crewmate IDs
If a trinket or crewmate ID is out-of-bounds, it will not be created.
This is not only because the `collect`/`customcollect` check in
entityclass::createentity() would then be out-of-bounds, but also
touching it would also be out-of-bounds, too.
Display trinkets will always be created if the ID is out-of-bounds.
Apparently some people (@AllyTally) have been creating display trinkets
with IDs of -1 in order to get a display trinket that always shows up,
which is rather horrifying. But it makes sense, there's a lot more
nonzero int values than there are the amount of int values that are
zero, so it's fairly likely that the `collect` check will always come up
to be true (nonzero). Also, it's more useful to be able to have a
display trinket that always shows up without having to collect a trinket
beforehand, than it is to have it not be created (because technically by
default, you're already in the world where you don't have it created).
Display trinkets still have their `para` set to their ID, though, and if
they managed to gain an `onentity` of 1, bad things could happen... So
just to be sure, I added INBOUNDS checks to crewmates and trinkets in
entityclass::updateentities() so no UB will happen if you collect a
crewmate/trinket with an out-of-bounds ID. Also, I de-duplicated the
`collect`/`customcollect` setting, too.
2020-06-15 23:20:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (game.intimetrial)
|
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
music.playef(27);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.state = 1010;
|
|
|
|
//music.haltdasmusik();
|
|
|
|
if(music.currentsong!=-1) music.silencedasmusik();
|
|
|
|
music.playef(27);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
return disableentity(i);
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 100: //The teleporter
|
|
|
|
if (entities[i].state == 1)
|
|
|
|
{
|
|
|
|
//if inactive, activate!
|
|
|
|
if (entities[i].tile == 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
music.playef(18);
|
|
|
|
entities[i].tile = 2;
|
|
|
|
entities[i].colour = 101;
|
|
|
|
if(!game.intimetrial && !game.nodeathmode)
|
|
|
|
{
|
|
|
|
game.state = 2000;
|
|
|
|
game.statedelay = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
game.activetele = true;
|
|
|
|
game.teleblock.x = entities[i].xp - 32;
|
|
|
|
game.teleblock.y = entities[i].yp - 32;
|
|
|
|
game.teleblock.w = 160;
|
|
|
|
game.teleblock.h = 160;
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
|
|
|
|
//Alright, let's set this as our savepoint too
|
|
|
|
//First, deactivate all other savepoints
|
|
|
|
for (size_t j = 0; j < entities.size(); j++)
|
|
|
|
{
|
|
|
|
if (entities[j].type == 8)
|
|
|
|
{
|
|
|
|
entities[j].colour = 4;
|
|
|
|
entities[j].onentity = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
game.savepoint = static_cast<int>(entities[i].para);
|
|
|
|
game.savex = entities[i].xp + 44;
|
|
|
|
game.savey = entities[i].yp + 44;
|
|
|
|
game.savegc = 0;
|
|
|
|
|
|
|
|
game.saverx = game.roomx;
|
|
|
|
game.savery = game.roomy;
|
2020-06-13 05:36:08 +02:00
|
|
|
int player = getplayer();
|
Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.
'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.
Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.
It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-09 13:15:14 +02:00
|
|
|
if (INBOUNDS_VEC(player, entities))
|
2020-06-13 05:36:08 +02:00
|
|
|
{
|
|
|
|
game.savedir = entities[player].dir;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
2020-07-17 22:29:22 +02:00
|
|
|
entities[i].onentity = 0;
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[i].state = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else if (entities[i].state == 2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//Initilise the teleporter without changing the game state or playing sound
|
|
|
|
entities[i].onentity = 0;
|
|
|
|
entities[i].tile = 6;
|
|
|
|
entities[i].colour = 102;
|
|
|
|
|
|
|
|
game.activetele = true;
|
|
|
|
game.teleblock.x = entities[i].xp - 32;
|
|
|
|
game.teleblock.y = entities[i].yp - 32;
|
|
|
|
game.teleblock.w = 160;
|
|
|
|
game.teleblock.h = 160;
|
|
|
|
|
|
|
|
entities[i].state = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[i].statedelay--;
|
|
|
|
if (entities[i].statedelay < 0)
|
|
|
|
{
|
|
|
|
entities[i].statedelay = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
Prevent updating an entity if updateentities() removed it
Otherwise, this would result in the game updating an entity twice, which
isn't good. This is most noticeable in the Gravitron, where many
Gravitron squares are created and destroyed at a time, and it's
especially noticeable during the part near the end of the Gravitron
where the pattern is two Gravitron squares, one at the top and bottom,
and then two Gravitron squares in the middle afterwards. The timing is
just right such that the top one of the two middle ones would be
misaligned with the bottom one of the two when a Gravitron square gets
outside the screen.
To do this, I changed entityclass::updateentities() into a bool, and
made every single caller check its return value. I only needed to do
this for the ones preceding updateentitylogic() and
entitymapcollision(), but I wanted to play it safe and be defensive, so
I did it for the disappearing platform kludge, as well as the
updateentities() within the updateentities() function.
2020-05-05 22:49:12 +02:00
|
|
|
|
|
|
|
return false;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
void entityclass::animateentities( int _i )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(_i, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("animateentities() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
if(entities[_i].statedelay < 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
switch(entities[_i].type)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
case 0:
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].dir==1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe=entities[_i].tile;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[_i].drawframe=entities[_i].tile+3;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-03-18 21:18:33 +01:00
|
|
|
if(entities[_i].visualonground>0 || entities[_i].visualonroof>0)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
if(entities[_i].vx > 0.00f || entities[_i].vx < -0.00f)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
//Walking
|
|
|
|
if(entities[_i].framedelay<=1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay=4;
|
|
|
|
entities[_i].walkingframe++;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[_i].walkingframe >=2) entities[_i].walkingframe=0;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe + 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2021-03-18 21:18:33 +01:00
|
|
|
if (entities[_i].visualonroof > 0) entities[_i].drawframe += 6;
|
2020-09-06 01:55:34 +02:00
|
|
|
// Stuck in a wall? Then default to gravitycontrol
|
2021-03-18 21:18:33 +01:00
|
|
|
if (entities[_i].visualonground > 0 && entities[_i].visualonroof > 0
|
2020-09-06 01:55:34 +02:00
|
|
|
&& game.gravitycontrol == 0)
|
|
|
|
{
|
|
|
|
entities[_i].drawframe -= 6;
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[_i].drawframe ++;
|
|
|
|
if (game.gravitycontrol == 1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe += 6;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
if (game.deathseq > -1)
|
|
|
|
{
|
|
|
|
entities[_i].drawframe=13;
|
|
|
|
if (entities[_i].dir == 1) entities[_i].drawframe = 12;
|
|
|
|
if (game.gravitycontrol == 1) entities[_i].drawframe += 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
case 23:
|
|
|
|
//Variable animation
|
|
|
|
switch(entities[_i].animate)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
//Simple oscilation
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
|
|
|
{
|
|
|
|
entities[_i].framedelay = 8;
|
|
|
|
if(entities[_i].actionframe==0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
entities[_i].walkingframe++;
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[_i].walkingframe == 4)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = 2;
|
|
|
|
entities[_i].actionframe = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
else
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe--;
|
|
|
|
if (entities[_i].walkingframe == -1)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = 1;
|
|
|
|
entities[_i].actionframe = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
//Simple Loop
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
|
|
|
{
|
|
|
|
entities[_i].framedelay = 8;
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 4)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
//Simpler Loop (just two frames)
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
|
|
|
{
|
|
|
|
entities[_i].framedelay = 2;
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
//Simpler Loop (just two frames, but double sized)
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
|
|
|
{
|
|
|
|
entities[_i].framedelay = 2;
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += (entities[_i].walkingframe*2);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
//Simpler Loop (just two frames, but double sized) (as above but slower)
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
|
|
|
{
|
|
|
|
entities[_i].framedelay = 6;
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe += (entities[_i].walkingframe*2);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
//Simpler Loop (just two frames) (slower)
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay = 6;
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 6:
|
|
|
|
//Normal Loop (four frames, double sized)
|
2020-01-01 21:29:24 +01:00
|
|
|
entities[_i].framedelay--;
|
2020-04-03 22:57:02 +02:00
|
|
|
if(entities[_i].framedelay<=0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay = 4;
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 4)
|
|
|
|
{
|
|
|
|
entities[_i].walkingframe = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += (entities[_i].walkingframe*2);
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
//Simpler Loop (just two frames) (slower) (with directions!)
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay = 6;
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 2)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
|
|
|
|
|
|
|
if (entities[_i].vx > 0.000f ) entities[_i].drawframe += 2;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
//Threadmill left
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay = 3;//(6-entities[_i].para);
|
|
|
|
entities[_i].walkingframe--;
|
|
|
|
if (entities[_i].walkingframe == -1)
|
|
|
|
{
|
|
|
|
entities[_i].walkingframe = 3;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
//Threadmill right
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay = 3;//(6-entities[_i].para);
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 4)
|
|
|
|
{
|
|
|
|
entities[_i].walkingframe = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
|
|
|
break;
|
|
|
|
case 100:
|
|
|
|
//Simple case for no animation (platforms, etc)
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
2020-01-01 21:29:24 +01:00
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
break;
|
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.
2021-01-09 23:50:06 +01:00
|
|
|
case 2: //Disappearing platforms
|
|
|
|
entities[_i].drawframe = entities[_i].tile + entities[_i].walkingframe;
|
|
|
|
break;
|
2020-04-03 22:57:02 +02:00
|
|
|
case 11:
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
if(entities[_i].animate==2)
|
|
|
|
{
|
|
|
|
//Simpler Loop (just two frames)
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay = 10;
|
|
|
|
entities[_i].walkingframe++;
|
|
|
|
if (entities[_i].walkingframe == 2)
|
|
|
|
{
|
|
|
|
entities[_i].walkingframe = 0;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
case 55:
|
|
|
|
case 14: //Crew member! Very similar to hero
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].dir==1)
|
|
|
|
{
|
|
|
|
entities[_i].drawframe=entities[_i].tile;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[_i].drawframe=entities[_i].tile+3;
|
|
|
|
}
|
|
|
|
|
2021-03-18 21:18:33 +01:00
|
|
|
if(entities[_i].visualonground>0 || entities[_i].visualonroof>0)
|
2020-04-03 22:57:02 +02:00
|
|
|
{
|
|
|
|
if(entities[_i].vx > 0.0000f || entities[_i].vx < -0.000f)
|
|
|
|
{
|
|
|
|
//Walking
|
2020-01-01 21:29:24 +01:00
|
|
|
if(entities[_i].framedelay<=0)
|
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay=4;
|
|
|
|
entities[_i].walkingframe++;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
if (entities[_i].walkingframe >=2) entities[_i].walkingframe=0;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe + 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2021-03-18 21:18:33 +01:00
|
|
|
//if (entities[_i].visualonroof > 0) entities[_i].drawframe += 6;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[_i].drawframe ++;
|
|
|
|
//if (game.gravitycontrol == 1) {
|
2021-09-07 03:56:39 +02:00
|
|
|
// entities[_i].drawframe += 6;
|
2020-04-03 22:57:02 +02:00
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (game.deathseq > -1)
|
|
|
|
{
|
|
|
|
entities[_i].drawframe=13;
|
|
|
|
if (entities[_i].dir == 1) entities[_i].drawframe = 12;
|
|
|
|
if (entities[_i].rule == 7) entities[_i].drawframe += 2;
|
|
|
|
//if (game.gravitycontrol == 1) entities[_i].drawframe += 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 100: //the teleporter!
|
|
|
|
if (entities[_i].tile == 1)
|
|
|
|
{
|
|
|
|
//it's inactive
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
}
|
|
|
|
else if (entities[_i].tile == 2)
|
|
|
|
{
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
|
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
|
|
|
{
|
|
|
|
entities[_i].framedelay = 1;
|
|
|
|
entities[_i].walkingframe = int(fRandom() * 6);
|
|
|
|
if (entities[_i].walkingframe >= 4)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].walkingframe = -1;
|
|
|
|
entities[_i].framedelay = 4;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
|
|
|
}
|
|
|
|
else if (entities[_i].tile == 6)
|
|
|
|
{
|
|
|
|
//faster!
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].framedelay--;
|
|
|
|
if(entities[_i].framedelay<=0)
|
|
|
|
{
|
|
|
|
entities[_i].framedelay = 2;
|
|
|
|
entities[_i].walkingframe = int(fRandom() * 6);
|
|
|
|
if (entities[_i].walkingframe >= 4)
|
|
|
|
{
|
|
|
|
entities[_i].walkingframe = -5;
|
|
|
|
entities[_i].framedelay = 4;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
2020-04-03 22:57:02 +02:00
|
|
|
entities[_i].drawframe += entities[_i].walkingframe;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
entities[_i].drawframe = entities[_i].tile;
|
|
|
|
break;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//entities[_i].statedelay--;
|
|
|
|
if (entities[_i].statedelay < 0) entities[_i].statedelay = 0;
|
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 entityclass::animatehumanoidcollision(const int i)
|
|
|
|
{
|
|
|
|
/* For some awful reason, drawframe is used for actual collision.
|
|
|
|
* And removing the input delay changes collision drawframe
|
|
|
|
* because vx is checked in animateentities().
|
|
|
|
* So we need to separate the collision drawframe from the visual drawframe
|
|
|
|
* and update it separately in gamelogic.
|
|
|
|
* Hence this function.
|
|
|
|
*/
|
|
|
|
entclass* entity;
|
|
|
|
|
|
|
|
if (!INBOUNDS_VEC(i, entities))
|
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("animatehumanoidcollision() out-of-bounds!");
|
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
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
entity = &entities[i];
|
|
|
|
|
|
|
|
if (!entity->ishumanoid())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entity->statedelay > 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
--entity->collisionframedelay;
|
|
|
|
|
|
|
|
if (entity->dir == 1)
|
|
|
|
{
|
|
|
|
entity->collisiondrawframe = entity->tile;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entity->collisiondrawframe = entity->tile + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entity->visualonground > 0 || entity->visualonroof > 0)
|
|
|
|
{
|
|
|
|
if (entity->vx > 0.0f || entity->vx < -0.0f)
|
|
|
|
{
|
|
|
|
/* Walking */
|
|
|
|
if (entity->collisionframedelay <= 1)
|
|
|
|
{
|
|
|
|
entity->collisionframedelay = 4;
|
|
|
|
++entity->collisionwalkingframe;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entity->collisionwalkingframe >= 2)
|
|
|
|
{
|
|
|
|
entity->collisionwalkingframe = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
entity->collisiondrawframe += entity->collisionwalkingframe + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entity->visualonroof > 0)
|
|
|
|
{
|
|
|
|
entity->collisiondrawframe += 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++entity->collisiondrawframe;
|
|
|
|
|
|
|
|
if (entity->type == 0 && game.gravitycontrol == 1)
|
|
|
|
{
|
|
|
|
entity->collisiondrawframe += 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* deathseq shouldn't matter, but handling it anyway just in case */
|
|
|
|
if (game.deathseq > -1)
|
|
|
|
{
|
|
|
|
entity->collisiondrawframe = 13;
|
|
|
|
|
|
|
|
if (entity->dir == 1)
|
|
|
|
{
|
|
|
|
entity->collisiondrawframe = 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((entity->type == 0 && game.gravitycontrol == 1)
|
|
|
|
|| (entity->type != 0 && entity->rule == 7))
|
|
|
|
{
|
|
|
|
entity->collisiondrawframe += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
entity->framedelay = entity->collisionframedelay;
|
|
|
|
entity->drawframe = entity->collisiondrawframe;
|
|
|
|
entity->walkingframe = entity->collisionwalkingframe;
|
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
int entityclass::getcompanion(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Returns the index of the companion with rule t
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if(entities[i].rule==6 || entities[i].rule==7)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
int entityclass::getplayer(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Returns the index of the first player entity
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if(entities[i].type==0)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
int entityclass::getscm(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Returns the supercrewmate
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if(entities[i].type==14)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int entityclass::getlineat( int t )
|
|
|
|
{
|
|
|
|
//Get the entity which is a horizontal line at height t (for SWN game)
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if (entities[i].size == 5)
|
|
|
|
{
|
|
|
|
if (entities[i].yp == t)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
|
|
|
int entityclass::getcrewman( int t, int fallback /*= 0*/ )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Returns the index of the crewman with colour index given by t
|
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
Prevent the game from thinking horizontal warp lines are cyan crewmates (#87)
* Add entity type attribute checks to getcrewman()
This means that the game is no longer able to target other non-crewmate
entities when it looks for a crewmate.
This has actually happened in practice. A command like
position(cyan,above) could position the text box above a horizontal warp
line instead of the intended crewmate.
This is because getcrewman() previously only checked the rule attributes
of entities, and if their rule happened to be 6 or 7. Usually this
corresponds with crewmates being unflipped or flipped, respectively.
But warp lines' rules overlap with rules 6 and 7. If a warp line is
vertical, its rule is 5, and if it is horizontal, its rule is 7.
Now, usually, this wouldn't be an issue, but getcrewman() does a color
check as well. And for cyan, that is color 0. However, if an entity
doesn't use a color, it just defaults to color 0. So, getcrewman() found
an entity with a rule of 7 and a color of 0. This must mean it's a cyan
crewmate! But, well, it's actually just a horizontal warp line.
This commit prevents the above from happening.
2020-01-16 05:55:53 +01:00
|
|
|
if ((entities[i].type == 12 || entities[i].type == 14)
|
|
|
|
&& (entities[i].rule == 6 || entities[i].rule == 7))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if(entities[i].colour==t)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
|
|
|
return fallback;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int entityclass::getcustomcrewman( int t )
|
|
|
|
{
|
|
|
|
//Returns the index of the crewman with colour index given by t
|
|
|
|
if (t == 0) t = 0;
|
|
|
|
if (t == 1) t = 20;
|
|
|
|
if (t == 2) t = 14;
|
|
|
|
if (t == 3) t = 15;
|
|
|
|
if (t == 4) t = 13;
|
|
|
|
if (t == 5) t = 16;
|
|
|
|
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if (entities[i].type == 55)
|
|
|
|
{
|
|
|
|
if(entities[i].colour==t)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
int entityclass::getteleporter(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
if(entities[i].type==100)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool entityclass::entitycollide( int a, int b )
|
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(a, entities) || !INBOUNDS_VEC(b, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entitycollide() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
//Do entities a and b collide?
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[a].xp + entities[a].cx;
|
|
|
|
temprect.y = entities[a].yp + entities[a].cy;
|
|
|
|
temprect.w = entities[a].w;
|
|
|
|
temprect.h = entities[a].h;
|
|
|
|
|
|
|
|
SDL_Rect temprect2;
|
|
|
|
temprect2.x = entities[b].xp + entities[b].cx;
|
|
|
|
temprect2.y = entities[b].yp + entities[b].cy;
|
|
|
|
temprect2.w = entities[b].w;
|
|
|
|
temprect2.h = entities[b].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-02 21:30:37 +02:00
|
|
|
if (help.intersects(temprect, temprect2)) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-06 02:23:09 +02:00
|
|
|
bool entityclass::checkdamage(bool scm /*= false*/)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-06 02:23:09 +02:00
|
|
|
//Returns true if player (or supercrewmate) collides with a damagepoint
|
2020-04-03 22:50:16 +02:00
|
|
|
for(size_t i=0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-06 02:23:09 +02:00
|
|
|
if((scm && entities[i].type == 14) || (!scm && entities[i].rule == 0))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[i].xp + entities[i].cx;
|
|
|
|
temprect.y = entities[i].yp + entities[i].cy;
|
|
|
|
temprect.w = entities[i].w;
|
|
|
|
temprect.h = entities[i].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-04 01:11:02 +02:00
|
|
|
for (size_t j=0; j<blocks.size(); j++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
if (blocks[j].type == DAMAGE && help.intersects(blocks[j].rect, temprect))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-10 08:20:03 +02:00
|
|
|
int entityclass::checktrigger(int* block_idx)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Returns an int player entity (rule 0) collides with a trigger
|
2020-07-10 08:20:03 +02:00
|
|
|
//Also returns the index of the block
|
|
|
|
*block_idx = -1;
|
2020-04-03 22:50:16 +02:00
|
|
|
for(size_t i=0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if(entities[i].rule==0)
|
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[i].xp + entities[i].cx;
|
|
|
|
temprect.y = entities[i].yp + entities[i].cy;
|
|
|
|
temprect.w = entities[i].w;
|
|
|
|
temprect.h = entities[i].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-04 01:11:02 +02:00
|
|
|
for (size_t j=0; j<blocks.size(); j++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
if (blocks[j].type == TRIGGER && help.intersects(blocks[j].rect, temprect))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-07-10 08:20:03 +02:00
|
|
|
*block_idx = j;
|
2020-04-04 02:06:07 +02:00
|
|
|
return blocks[j].trigger;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
int entityclass::checkactivity(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Returns an int player entity (rule 0) collides with an activity
|
2020-04-03 22:50:16 +02:00
|
|
|
for(size_t i=0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if(entities[i].rule==0)
|
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[i].xp + entities[i].cx;
|
|
|
|
temprect.y = entities[i].yp + entities[i].cy;
|
|
|
|
temprect.w = entities[i].w;
|
|
|
|
temprect.h = entities[i].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-04 01:11:02 +02:00
|
|
|
for (size_t j=0; j<blocks.size(); j++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
if (blocks[j].type == ACTIVITY && help.intersects(blocks[j].rect, temprect))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
return j;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int entityclass::getgridpoint( int t )
|
|
|
|
{
|
Remove overcomplicated integer divisions
Believe it or not, there are still some remnants of the ActionScript
coding standards in the codebase! And one of them sometimes pops up
whenever an integer division happens.
As it so happens, it seems like division in ActionScript automatically
produces a decimal number. So to prevent that, the game sometimes
subtracts off the remainder of the number to be divided before
performing the division on it.
Thus, we get statements that look like
(a - (a % b)) / b
And probably more parentheses surrounding it too, since it would be
copy-pasted into yet another larger expression, because of course it
would.
`(a % b)` here is subtracting the remainder of `a` divided by `b`, using
the modulo operator, before it gets divided by `b`. Thus, the number
will always be divisible by `b`, so dividing it will mathematically not
produce a decimal number.
Needless to say, this is unnecessary, and very unreadable. In fact, when
I saw these for the first time, I thought they were overcomplicated
_modulos_, _not_ integer division! In C and C++, dividing an integer by
an integer will always result in an integer, so there's no need to do
all this runaround just to divide two integers.
To find all of these, I used the command
rg --pcre2 '(.+?).+?-.+?(?=\1).+?%.+?([\d]+?).+?\/.+?(?=\2)'
which basically matches expressions of the form 'a - a % b / b', where
'a' and 'b' are identical and there could be any characters in the
spaces.
2021-09-25 02:21:46 +02:00
|
|
|
return t / 8;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
bool entityclass::checkplatform(const SDL_Rect& temprect, int* px, int* py)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Return true if rectset intersects a moving platform, setups px & py to the platform x & y
|
2020-04-04 01:11:02 +02:00
|
|
|
for (size_t i = 0; i < blocks.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
if (blocks[i].type == BLOCK && help.intersects(blocks[i].rect, temprect))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
*px = blocks[i].xp;
|
|
|
|
*py = blocks[i].yp;
|
2020-04-04 02:06:07 +02:00
|
|
|
return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
bool entityclass::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
|
|
|
{
|
2020-04-04 01:11:02 +02:00
|
|
|
for (size_t i = 0; i < blocks.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
if(!skipdirblocks && blocks[i].type == DIRECTIONAL)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-04 02:06:07 +02:00
|
|
|
if (dy > 0 && blocks[i].trigger == 0) if (help.intersects(blocks[i].rect, temprect)) return true;
|
|
|
|
if (dy <= 0 && blocks[i].trigger == 1) if (help.intersects(blocks[i].rect, temprect)) return true;
|
|
|
|
if (dx > 0 && blocks[i].trigger == 2) if (help.intersects(blocks[i].rect, temprect)) return true;
|
|
|
|
if (dx <= 0 && blocks[i].trigger == 3) if (help.intersects(blocks[i].rect, temprect)) return true;
|
|
|
|
}
|
|
|
|
if (blocks[i].type == BLOCK && help.intersects(blocks[i].rect, temprect))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (blocks[i].type == SAFE && (dr)==1 && help.intersects(blocks[i].rect, temprect))
|
|
|
|
{
|
|
|
|
return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
bool entityclass::checkwall(const SDL_Rect& temprect, const float dx, const float dy, const float dr, const bool skipblocks, const bool skipdirblocks)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Returns true if entity setup in temprect collides with a wall
|
|
|
|
if(skipblocks)
|
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
if (checkblocks(temprect, dx, dy, dr, skipdirblocks)) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int tempx = getgridpoint(temprect.x);
|
|
|
|
int tempy = getgridpoint(temprect.y);
|
|
|
|
int tempw = getgridpoint(temprect.x + temprect.w - 1);
|
|
|
|
int temph = getgridpoint(temprect.y + temprect.h - 1);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (map.collide(tempx, tempy)) return true;
|
|
|
|
if (map.collide(tempw, tempy)) return true;
|
|
|
|
if (map.collide(tempx, temph)) return true;
|
|
|
|
if (map.collide(tempw, temph)) return true;
|
|
|
|
if (temprect.h >= 12)
|
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int tpy1 = getgridpoint(temprect.y + 6);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (map.collide(tempx, tpy1)) return true;
|
|
|
|
if (map.collide(tempw, tpy1)) return true;
|
|
|
|
if (temprect.h >= 18)
|
|
|
|
{
|
|
|
|
tpy1 = getgridpoint(temprect.y + 12);
|
|
|
|
if (map.collide(tempx, tpy1)) return true;
|
|
|
|
if (map.collide(tempw, tpy1)) return true;
|
|
|
|
if (temprect.h >= 24)
|
|
|
|
{
|
|
|
|
tpy1 = getgridpoint(temprect.y + 18);
|
|
|
|
if (map.collide(tempx, tpy1)) return true;
|
|
|
|
if (map.collide(tempw, tpy1)) return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (temprect.w >= 12)
|
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int tpx1 = getgridpoint(temprect.x + 6);
|
2020-01-01 21:29:24 +01:00
|
|
|
if (map.collide(tpx1, tempy)) return true;
|
|
|
|
if (map.collide(tpx1, temph)) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
bool entityclass::checkwall(const SDL_Rect& temprect)
|
|
|
|
{
|
|
|
|
// Same as above but use default arguments for blocks
|
|
|
|
return checkwall(temprect, 0, 0, 0, true, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
float entityclass::hplatformat(const int px, const int py)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//Returns first entity of horizontal platform at (px, py), -1000 otherwise.
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 23:48:27 +02:00
|
|
|
if (entities[i].rule == 2 && entities[i].behave >= 2
|
|
|
|
&& entities[i].xp == px && entities[i].yp == py)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 23:48:27 +02:00
|
|
|
if (entities[i].behave == 8) //threadmill!
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 23:48:27 +02:00
|
|
|
return entities[i].para;
|
|
|
|
}
|
|
|
|
else if(entities[i].behave == 9) //threadmill!
|
|
|
|
{
|
|
|
|
return -entities[i].para;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return entities[i].vx;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
int entityclass::yline( int a, int b )
|
|
|
|
{
|
|
|
|
if (a < b) return -1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool entityclass::entityhlinecollide( int t, int l )
|
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entityhlinecollide() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
//Returns true is entity t collided with the horizontal line l.
|
|
|
|
if(entities[t].xp + entities[t].cx+entities[t].w>=entities[l].xp)
|
|
|
|
{
|
|
|
|
if(entities[t].xp + entities[t].cx<=entities[l].xp+entities[l].w)
|
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int linetemp = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
linetemp += yline(entities[t].yp, entities[l].yp);
|
|
|
|
linetemp += yline(entities[t].yp + entities[t].h, entities[l].yp);
|
|
|
|
linetemp += yline(entities[t].oldyp, entities[l].yp);
|
|
|
|
linetemp += yline(entities[t].oldyp + entities[t].h, entities[l].yp);
|
|
|
|
|
|
|
|
if (linetemp > -4 && linetemp < 4) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool entityclass::entityvlinecollide( int t, int l )
|
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entityvlinecollide() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
//Returns true is entity t collided with the vertical line l.
|
2020-04-03 23:48:27 +02:00
|
|
|
if(entities[t].yp + entities[t].cy+entities[t].h>=entities[l].yp
|
|
|
|
&& entities[t].yp + entities[t].cy<=entities[l].yp+entities[l].h)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int linetemp = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 23:48:27 +02:00
|
|
|
linetemp += yline(entities[t].xp + entities[t].cx+1, entities[l].xp);
|
|
|
|
linetemp += yline(entities[t].xp + entities[t].cx+1 + entities[t].w, entities[l].xp);
|
|
|
|
linetemp += yline(entities[t].oldxp + entities[t].cx+1, entities[l].xp);
|
|
|
|
linetemp += yline(entities[t].oldxp + entities[t].cx+1 + entities[t].w, entities[l].xp);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-03 23:48:27 +02:00
|
|
|
if (linetemp > -4 && linetemp < 4) return true;
|
|
|
|
return false;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool entityclass::entitywarphlinecollide(int t, int l) {
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entitywarphlinecollide() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-02 22:42:50 +02:00
|
|
|
//Returns true is entity t collided with the horizontal line l.
|
2020-04-03 23:48:27 +02:00
|
|
|
if(entities[t].xp + entities[t].cx+entities[t].w>=entities[l].xp
|
|
|
|
&&entities[t].xp + entities[t].cx<=entities[l].xp+entities[l].w){
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int linetemp = 0;
|
2020-04-03 23:48:27 +02:00
|
|
|
if (entities[l].yp < 120) {
|
|
|
|
//Top line
|
|
|
|
if (entities[t].vy < 0) {
|
|
|
|
if (entities[t].yp < entities[l].yp + 10) linetemp++;
|
|
|
|
if (entities[t].yp + entities[t].h < entities[l].yp + 10) linetemp++;
|
|
|
|
if (entities[t].oldyp < entities[l].yp + 10) linetemp++;
|
|
|
|
if (entities[t].oldyp + entities[t].h < entities[l].yp + 10) linetemp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (linetemp > 0) return true;
|
|
|
|
return false;
|
|
|
|
}else {
|
|
|
|
//Bottom line
|
|
|
|
if (entities[t].vy > 0) {
|
|
|
|
if (entities[t].yp > entities[l].yp - 10) linetemp++;
|
|
|
|
if (entities[t].yp + entities[t].h > entities[l].yp - 10) linetemp++;
|
|
|
|
if (entities[t].oldyp > entities[l].yp - 10) linetemp++;
|
|
|
|
if (entities[t].oldyp + entities[t].h > entities[l].yp - 10) linetemp++;
|
2020-04-02 22:42:50 +02:00
|
|
|
}
|
2020-04-03 23:48:27 +02:00
|
|
|
|
|
|
|
if (linetemp > 0) return true;
|
|
|
|
return false;
|
2020-04-02 22:42:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-02 21:41:33 +02:00
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
bool entityclass::entitywarpvlinecollide(int t, int l) {
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entitywarpvlinecollide() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-02 22:42:50 +02:00
|
|
|
//Returns true is entity t collided with the vertical warp line l.
|
2020-04-03 23:48:27 +02:00
|
|
|
if(entities[t].yp + entities[t].cy+entities[t].h>=entities[l].yp
|
|
|
|
&& entities[t].yp + entities[t].cy <= entities[l].yp + entities[l].h) {
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int linetemp = 0;
|
2020-04-03 23:48:27 +02:00
|
|
|
if (entities[l].xp < 160) {
|
|
|
|
//Left hand line
|
|
|
|
if (entities[t].xp + entities[t].cx + 1 < entities[l].xp + 10) linetemp++;
|
|
|
|
if (entities[t].xp + entities[t].cx+1 + entities[t].w < entities[l].xp + 10) linetemp++;
|
|
|
|
if (entities[t].oldxp + entities[t].cx + 1 < entities[l].xp + 10) linetemp++;
|
|
|
|
if (entities[t].oldxp + entities[t].cx + 1 + entities[t].w < entities[l].xp + 10) linetemp++;
|
|
|
|
|
|
|
|
if (linetemp > 0) return true;
|
|
|
|
return false;
|
|
|
|
}else {
|
|
|
|
//Right hand line
|
|
|
|
if (entities[t].xp + entities[t].cx + 1 > entities[l].xp - 10) linetemp++;
|
|
|
|
if (entities[t].xp + entities[t].cx+1 + entities[t].w > entities[l].xp - 10) linetemp++;
|
|
|
|
if (entities[t].oldxp + entities[t].cx + 1 > entities[l].xp - 10) linetemp++;
|
|
|
|
if (entities[t].oldxp + entities[t].cx + 1 + entities[t].w > entities[l].xp - 10) linetemp++;
|
|
|
|
|
|
|
|
if (linetemp > 0) return true;
|
|
|
|
return false;
|
2020-04-02 22:42:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
float entityclass::entitycollideplatformroof( int t )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entitycollideplatformroof() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return -1000;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[t].xp + entities[t].cx;
|
|
|
|
temprect.y = entities[t].yp + entities[t].cy -1;
|
|
|
|
temprect.w = entities[t].w;
|
|
|
|
temprect.h = entities[t].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int px = 0, py = 0;
|
|
|
|
if (checkplatform(temprect, &px, &py))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//px and py now contain an x y coordinate for a platform, find it
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
return hplatformat(px, py);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
return -1000;
|
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
float entityclass::entitycollideplatformfloor( int t )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entitycollideplatformfloor() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return -1000;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[t].xp + entities[t].cx;
|
|
|
|
temprect.y = entities[t].yp + entities[t].cy + 1;
|
|
|
|
temprect.w = entities[t].w;
|
|
|
|
temprect.h = entities[t].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int px = 0, py = 0;
|
|
|
|
if (checkplatform(temprect, &px, &py))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//px and py now contain an x y coordinate for a platform, find it
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
return hplatformat(px, py);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
return -1000;
|
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
bool entityclass::entitycollidefloor( int t )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entitycollidefloor() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[t].xp + entities[t].cx;
|
|
|
|
temprect.y = entities[t].yp + entities[t].cy + 1;
|
|
|
|
temprect.w = entities[t].w;
|
|
|
|
temprect.h = entities[t].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
if (checkwall(temprect)) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
bool entityclass::entitycollideroof( int t )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entitycollideroof() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = entities[t].xp + entities[t].cx;
|
|
|
|
temprect.y = entities[t].yp + entities[t].cy - 1;
|
|
|
|
temprect.w = entities[t].w;
|
|
|
|
temprect.h = entities[t].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
if (checkwall(temprect)) return true;
|
2020-01-01 21:29:24 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
bool entityclass::testwallsx( int t, int tx, int ty, const bool skipdirblocks )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("testwallsx() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = tx + entities[t].cx;
|
|
|
|
temprect.y = ty + entities[t].cy;
|
|
|
|
temprect.w = entities[t].w;
|
|
|
|
temprect.h = entities[t].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
bool skipblocks = entities[t].rule < 2 || entities[t].type == 14;
|
|
|
|
float dx = 0;
|
|
|
|
float dy = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
if (entities[t].rule == 0) dx = entities[t].vx;
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
float dr = entities[t].rule;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Ok, now we check walls
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
if (checkwall(temprect, dx, dy, dr, skipblocks, skipdirblocks))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if (entities[t].vx > 1.0f)
|
|
|
|
{
|
|
|
|
entities[t].vx--;
|
|
|
|
entities[t].newxp = entities[t].xp + entities[t].vx;
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
return testwallsx(t, entities[t].newxp, entities[t].yp, skipdirblocks);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else if (entities[t].vx < -1.0f)
|
|
|
|
{
|
|
|
|
entities[t].vx++;
|
|
|
|
entities[t].newxp = entities[t].xp + entities[t].vx;
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
return testwallsx(t, entities[t].newxp, entities[t].yp, skipdirblocks);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[t].vx=0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
bool entityclass::testwallsy( int t, float tx, float ty )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("testwallsy() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
SDL_Rect temprect;
|
|
|
|
temprect.x = static_cast<int>(tx) + entities[t].cx;
|
|
|
|
temprect.y = static_cast<int>(ty) + entities[t].cy;
|
|
|
|
temprect.w = entities[t].w;
|
|
|
|
temprect.h = entities[t].h;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
bool skipblocks = entities[t].rule < 2 || entities[t].type == 14;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
float dx = 0;
|
|
|
|
float dy = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
if (entities[t].rule == 0) dy = entities[t].vy;
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
float dr = entities[t].rule;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Ok, now we check walls
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
if (checkwall(temprect, dx, dy, dr, skipblocks, false))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
if (entities[t].vy > 1)
|
|
|
|
{
|
|
|
|
entities[t].vy--;
|
|
|
|
entities[t].newyp = int(entities[t].yp + entities[t].vy);
|
2020-03-31 02:46:36 +02:00
|
|
|
return testwallsy(t, entities[t].xp, entities[t].newyp);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else if (entities[t].vy < -1)
|
|
|
|
{
|
|
|
|
entities[t].vy++;
|
|
|
|
entities[t].newyp = int(entities[t].yp + entities[t].vy);
|
2020-03-31 02:46:36 +02:00
|
|
|
return testwallsy(t, entities[t].xp, entities[t].newyp);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[t].vy=0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void entityclass::applyfriction( int t, float xrate, float yrate )
|
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("applyfriction() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
if (entities[t].vx > 0.00f) entities[t].vx -= xrate;
|
|
|
|
if (entities[t].vx < 0.00f) entities[t].vx += xrate;
|
|
|
|
if (entities[t].vy > 0.00f) entities[t].vy -= yrate;
|
|
|
|
if (entities[t].vy < 0.00f) entities[t].vy += yrate;
|
|
|
|
if (entities[t].vy > 10.00f) entities[t].vy = 10.0f;
|
|
|
|
if (entities[t].vy < -10.00f) entities[t].vy = -10.0f;
|
|
|
|
if (entities[t].vx > 6.00f) entities[t].vx = 6.0f;
|
|
|
|
if (entities[t].vx < -6.00f) entities[t].vx = -6.0f;
|
|
|
|
|
2021-01-02 03:38:55 +01:00
|
|
|
if (SDL_fabsf(entities[t].vx) < xrate) entities[t].vx = 0.0f;
|
|
|
|
if (SDL_fabsf(entities[t].vy) < yrate) entities[t].vy = 0.0f;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
void entityclass::updateentitylogic( int t )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("updateentitylogic() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
entities[t].oldxp = entities[t].xp;
|
|
|
|
entities[t].oldyp = entities[t].yp;
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
entities[t].vx = entities[t].vx + entities[t].ax;
|
|
|
|
entities[t].vy = entities[t].vy + entities[t].ay;
|
|
|
|
entities[t].ax = 0;
|
|
|
|
|
|
|
|
if (entities[t].gravity)
|
|
|
|
{
|
|
|
|
if (entities[t].rule == 0)
|
|
|
|
{
|
|
|
|
if(game.gravitycontrol==0)
|
|
|
|
{
|
|
|
|
entities[t].ay = 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[t].ay = -3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (entities[t].rule == 7)
|
|
|
|
{
|
|
|
|
entities[t].ay = -3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[t].ay = 3;
|
|
|
|
}
|
|
|
|
applyfriction(t, game.inertia, 0.25f);
|
|
|
|
}
|
|
|
|
|
|
|
|
entities[t].newxp = entities[t].xp + entities[t].vx;
|
|
|
|
entities[t].newyp = entities[t].yp + entities[t].vy;
|
|
|
|
}
|
|
|
|
|
2020-03-31 02:46:36 +02:00
|
|
|
void entityclass::entitymapcollision( int t )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("entitymapcollision() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
if (testwallsx(t, entities[t].newxp, entities[t].yp, false))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
entities[t].xp = entities[t].newxp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (entities[t].onwall > 0) entities[t].state = entities[t].onwall;
|
|
|
|
if (entities[t].onxwall > 0) entities[t].state = entities[t].onxwall;
|
|
|
|
}
|
2020-03-31 02:46:36 +02:00
|
|
|
if (testwallsy(t, entities[t].xp, entities[t].newyp))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
entities[t].yp = entities[t].newyp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (entities[t].onwall > 0) entities[t].state = entities[t].onwall;
|
|
|
|
if (entities[t].onywall > 0) entities[t].state = entities[t].onywall;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 02:25:49 +02:00
|
|
|
void entityclass::movingplatformfix( int t, int j )
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-08 09:31:44 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(j, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("movingplatformfix() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-06 02:25:49 +02:00
|
|
|
//If this intersects the entity, then we move them along it
|
|
|
|
if (entitycollide(t, j))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//ok, bollox, let's make sure
|
|
|
|
entities[j].yp = entities[j].yp + int(entities[j].vy);
|
|
|
|
if (entitycollide(t, j))
|
|
|
|
{
|
|
|
|
entities[j].yp = entities[j].yp - int(entities[j].vy);
|
|
|
|
entities[j].vy = entities[t].vy;
|
|
|
|
entities[j].newyp = entities[j].yp + int(entities[j].vy);
|
2020-03-31 02:46:36 +02:00
|
|
|
if (testwallsy(j, entities[j].xp, entities[j].newyp))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-06 02:25:49 +02:00
|
|
|
if (entities[t].vy > 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
entities[j].yp = entities[t].yp + entities[t].h;
|
|
|
|
entities[j].vy = 0;
|
2020-10-06 07:53:10 +02:00
|
|
|
entities[j].onroof = 2;
|
2021-06-14 20:44:00 +02:00
|
|
|
entities[j].visualonroof = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[j].yp = entities[t].yp - entities[j].h-entities[j].cy;
|
|
|
|
entities[j].vy = 0;
|
2020-10-06 07:53:10 +02:00
|
|
|
entities[j].onground = 2;
|
2021-06-14 20:44:00 +02:00
|
|
|
entities[j].visualonground = 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[t].state = entities[t].onwall;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void entityclass::customwarplinecheck(int i) {
|
2020-09-10 06:58:59 +02:00
|
|
|
if (!INBOUNDS_VEC(i, entities))
|
2020-05-05 01:41:30 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("customwarplinecheck() out-of-bounds!");
|
2020-05-05 01:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-02 22:42:50 +02:00
|
|
|
//Turns on obj.customwarpmodevon and obj.customwarpmodehon if player collides
|
|
|
|
//with warp lines
|
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
//We test entity to entity
|
|
|
|
for (int j = 0; j < (int) entities.size(); j++) {
|
2020-04-03 23:50:08 +02:00
|
|
|
if (i != j) {
|
2020-04-03 23:48:27 +02:00
|
|
|
if (entities[i].rule == 0 && entities[j].rule == 5 //Player vs vertical line!
|
|
|
|
&& (entities[j].type == 51 || entities[j].type == 52)
|
|
|
|
&& entitywarpvlinecollide(i, j)) {
|
|
|
|
customwarpmodevon = true;
|
2020-04-03 22:57:02 +02:00
|
|
|
}
|
2020-04-02 22:42:50 +02:00
|
|
|
|
2020-04-03 23:48:27 +02:00
|
|
|
if (entities[i].rule == 0 && entities[j].rule == 7 //Player vs horizontal WARP line
|
|
|
|
&& (entities[j].type == 53 || entities[j].type == 54)
|
|
|
|
&& entitywarphlinecollide(i, j)) {
|
|
|
|
customwarpmodehon = true;
|
2020-04-02 22:42:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void entityclass::entitycollisioncheck(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-04-03 22:50:16 +02:00
|
|
|
for (size_t i = 0; i < entities.size(); i++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-06 02:35:44 +02:00
|
|
|
bool player = entities[i].rule == 0;
|
|
|
|
bool scm = game.supercrewmate && entities[i].type == 14;
|
|
|
|
if (!player && !scm)
|
Optimize entity collision checking to O(n)
I noticed that if I have a large amount of entities in the room, the
game starts to freeze and one frame would take a very long time. I
identified an obvious cause of this, which is that the entity collision
checking in entityclass::entitycollisioncheck() is O(n²), n being the
number of entities in the room.
But it doesn't need to be O(n²). The only entities you need to check
against all other entities are the player and the supercrewmate. You
don't need to "test entity to entity" if 99% of the pairs of entities
you're checking don't involve the player or supercrewmate.
How do we make it O(n)? Well, just hoist the rule 0 and type 14 checks
out of the inner for-loop. That way, the inner for-loop won't be
unconditionally ran, meaning that in most cases it will always be O(n).
However, if you start having large amounts of duplicate player or
supercrewmate entities (I don't know why you would), it would start
approaching O(n²), but I feel like that's fair in that case. But most of
the time, it will be O(n).
So that's how collision checking is now O(n) instead.
2020-09-08 01:36:38 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-06 02:35:44 +02:00
|
|
|
|
2020-04-03 22:57:02 +02:00
|
|
|
//We test entity to entity
|
|
|
|
for (size_t j = 0; j < entities.size(); j++)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-06 02:35:44 +02:00
|
|
|
if (i == j)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-09-06 02:35:44 +02:00
|
|
|
continue;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-09-06 02:35:44 +02:00
|
|
|
|
|
|
|
collisioncheck(i, j, scm);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//can't have the player being stuck...
|
2020-09-06 02:38:38 +02:00
|
|
|
stuckprevention(getplayer());
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Can't have the supercrewmate getting stuck either!
|
|
|
|
if (game.supercrewmate)
|
|
|
|
{
|
2020-09-06 02:38:38 +02:00
|
|
|
stuckprevention(getscm());
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//Is the player colliding with any damageblocks?
|
|
|
|
if (checkdamage() && !map.invincibility)
|
|
|
|
{
|
|
|
|
//usual player dead stuff
|
|
|
|
game.deathseq = 30;
|
|
|
|
}
|
|
|
|
|
|
|
|
//how about the supercrewmate?
|
|
|
|
if (game.supercrewmate)
|
|
|
|
{
|
2020-09-06 02:23:09 +02:00
|
|
|
if (checkdamage(true) && !map.invincibility)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
//usual player dead stuff
|
|
|
|
game.scmhurt = true;
|
|
|
|
game.deathseq = 30;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Fix the two-frame-delay when entering a room with an "init" script
This patch is very kludge-y, but at least it fixes a semi-noticeable
visual issue in custom levels that use internal scripts to spawn
entities when loading a room.
Basically, the problem here is that when the game checks for script
boxes and sets newscript, newscript has already been processed for that
frame, and when the game does load a script, script.run() has already
been processed for that frame.
That issue can be fixed, but it turns out that due to my over-30-FPS
game loop changes, there's now ANOTHER visible frame of delay between
room load and entity creation, because the render function gets called
in between the script being loaded at the end of gamelogic() and the
script actually getting run.
So... I have to temporary move script.run() to the end of gamelogic()
(in map.twoframedelayfix()), and make sure it doesn't get run next
frame, because double-evaluations are bad. To do that, I have to
introduce the kludge variable script.dontrunnextframe, which does
exactly as it says.
And with all that work, the two-frame (now three-frame) delay is fixed.
2020-06-28 02:08:57 +02:00
|
|
|
// WARNING: If updating this code, don't forget to update Map.cpp mapclass::twoframedelayfix()
|
2020-07-10 08:20:03 +02:00
|
|
|
int block_idx = -1;
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int activetrigger = checktrigger(&block_idx);
|
|
|
|
if (activetrigger > -1 && INBOUNDS_VEC(block_idx, blocks))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-08-17 21:08:32 +02:00
|
|
|
// Load the block's script if its gamestate is out of range
|
|
|
|
if (blocks[block_idx].script != "" && (activetrigger < 300 || activetrigger > 336))
|
2020-07-10 08:20:03 +02:00
|
|
|
{
|
|
|
|
game.startscript = true;
|
|
|
|
game.newscript = blocks[block_idx].script;
|
|
|
|
removetrigger(activetrigger);
|
|
|
|
game.state = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.state = activetrigger;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
game.statedelay = 0;
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 02:35:44 +02:00
|
|
|
|
|
|
|
void entityclass::collisioncheck(int i, int j, bool scm /*= false*/)
|
|
|
|
{
|
2020-09-08 09:31:44 +02:00
|
|
|
if (!INBOUNDS_VEC(i, entities) || !INBOUNDS_VEC(j, entities))
|
2020-09-06 02:35:44 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("collisioncheck() out-of-bounds!");
|
2020-09-06 02:35:44 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-06 02:37:57 +02:00
|
|
|
switch (entities[j].rule)
|
2020-09-06 02:35:44 +02:00
|
|
|
{
|
2020-09-06 02:37:57 +02:00
|
|
|
case 1:
|
|
|
|
if (!entities[j].harmful)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-06 02:35:44 +02:00
|
|
|
//person i hits enemy or enemy bullet j
|
|
|
|
if (entitycollide(i, j) && !map.invincibility)
|
|
|
|
{
|
|
|
|
if (entities[i].size == 0 && (entities[j].size == 0 || entities[j].size == 12))
|
|
|
|
{
|
|
|
|
//They're both sprites, so do a per pixel collision
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
point colpoint1;
|
2020-09-06 02:35:44 +02:00
|
|
|
colpoint1.x = entities[i].xp;
|
|
|
|
colpoint1.y = entities[i].yp;
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
point colpoint2;
|
2020-09-06 02:35:44 +02:00
|
|
|
colpoint2.x = entities[j].xp;
|
|
|
|
colpoint2.y = entities[j].yp;
|
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
|
|
|
int drawframe1 = entities[i].collisiondrawframe;
|
2020-09-06 02:35:44 +02:00
|
|
|
int drawframe2 = entities[j].drawframe;
|
|
|
|
std::vector<SDL_Surface*>& spritesvec = graphics.flipmode ? graphics.flipsprites : graphics.sprites;
|
2020-09-08 09:31:44 +02:00
|
|
|
if (INBOUNDS_VEC(drawframe1, spritesvec) && INBOUNDS_VEC(drawframe2, spritesvec)
|
2020-09-06 02:35:44 +02:00
|
|
|
&& graphics.Hitest(spritesvec[drawframe1],
|
|
|
|
colpoint1, spritesvec[drawframe2], colpoint2))
|
|
|
|
{
|
|
|
|
//Do the collision stuff
|
|
|
|
game.deathseq = 30;
|
|
|
|
game.scmhurt = scm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Ok, then we just assume a normal bounding box collision
|
|
|
|
game.deathseq = 30;
|
|
|
|
game.scmhurt = scm;
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 02:37:57 +02:00
|
|
|
break;
|
|
|
|
case 2: //Moving platforms
|
2020-11-04 09:42:42 +01:00
|
|
|
if (entities[j].behave >= 8 && entities[j].behave < 10)
|
Disable nocollisionat() for conveyors
While my previous commit fixes the glitchy y-position when you get stuck
inside a conveyor, I noticed that getting inside a conveyor seems to
always push the player out, despite conveyors sharing the same code with
moving platforms, which has code to temporarily disable their own
collision when the player gets stuck inside them, so that the player
DOESN'T get pushed out.
Well, it turns out that the reason this happens is because conveyors in
a room that get placed during mapclass::loadlevel() get tile 1 placed
underneath them. This is mostly so moving platforms will collide with
them, because otherwise platforms don't collide with other platforms,
and a conveyor is considered a platform.
This means that a conveyor without any tiles behind it will simply get
the player stuck if they get inside it, and the player won't be pushed
out. This is bad, because conveyors don't move, so they'll be stuck
there forever until they press R (or save, quit, and load). This
situation doesn't come up in the main game, but it COULD come up in
custom levels that use the internal createentity() command to create
conveyors that don't have any tiles behind them.
It seems good to fix this as well, while we're at it fixing conveyor
physics, so I'm fixing this as well.
2020-10-31 21:44:37 +01:00
|
|
|
{
|
|
|
|
//We don't want conveyors, moving platforms only
|
|
|
|
break;
|
|
|
|
}
|
Restore platform evaluation order to 2.2
This commit restores the evaluation order of moving platforms and conveyors to
be what it was in 2.2. The evaluation order changed in 2.3 after the patchset
to improve the handling of the `obj.entities` and `obj.blocks` vectors (#191).
By evaluation order, I'm talking about the order in which platforms and
conveyors will be evaluated (and thus will take priority) if Viridian stands
on both a conveyor or platform at once, and they either have different speeds
or are pointing in different directions. Nowhere in the main game is there a
place where you can stand on two different conveyors/platforms at once, so
this is solely within the territory of custom levels, which is my specialty.
So what caused this evaluation order to change? Well, every moving platform
and conveyor in the game is actually made up of two objects: an entity, and a
block. The entity is the part that moves around, and the block is the part
that actually has the collision.
But if the entity is the part that moves around, and entities and blocks are
in entirely separate vectors, how is the block part going to move along with
it? Well, maybe you'd guess some sort of unique ID system, but spend some time
digging around the code and you won't find any trace of any (there's no
attribute on an entity to store such an ID, for starters).
Instead, what the game does is actually remove all blocks that coincide with
the exact top-left corner of the entity, and then create a new one. Destroying
and creating blocks like this all the time is hugely wasteful, but hey, it
worked.
So why did the evaluation order change in 2.3? Well, to understand that,
you'll need to understand 2.2's `active` system. Instead of having an object
be real simply by virtue of it existing, 2.2 had this system where the object
was only real if it had its `active` attribute set to true. In other words,
you would be looking at a fake object that didn't actually exist if its
`active` attribute was false.
On the surface, this doesn't seem that bad. But this can lead to "holes" in a
given vector of objects. A hole is simply an inactive object neighbored by
active objects (or the inactive object could be the first one in the vector,
but then have an active object immediately following it).
If you have a vector of 3 objects, all of them active, then removing the
second one will result in the vector containing an active object, followed by
an inactive object, followed by an active one. However, since the switch to
more properly use vectors instead of relying on this `active` system, there's
no longer any way for holes to exist in a vector. Properly removing an object
from a vector will just shift the rest of the objects down, so if we remove
the second object after the vector fix, then this will simply move the third
object into the slot of where the second object used to be.
So, what happens if you destroy a block and then create a new one in the
`active` system? Let's say that your `obj.blocks` looks like this, and here
I'm denoting each block by writing out its coordinates:
[30,60] [70,90] [80,100]
and that you want to update the position of the second one, because the entity
that that blocks belongs to has been updated. Okay, so, you delete that block,
which then makes things look like this:
[30,60] [-] [80,100]
and then afterwards, you create a new block with the updated position,
resulting in this:
[30,60] [74,90] [80,100]
Since `entityclass::createblock()` will find the first block slot that has a
false `active` attribute, it puts the new object in the same slot as the old
one. What has been essentially done here is that the slot of the block has
basically been reserved for the new block with the new position. Here, the
evaluation order of each block will stay the same.
But then 2.3 comes along and changes things up. So we start with an
`obj.blocks` like this again:
[30,60] [70,90] [80,100]
and we want to update the second block, like before. So we remove the second
block, resulting in this:
[30,60] [80,100]
It should be obvious that unlike before, where the third block stayed in the
third slot, the third block has now been moved to the second slot. But
continuing on; we are now going to create the new block with its updated
position, resulting in this:
[30,60] [80,100] [70,90]
At this point, we can see that the evaluation order of these blocks has been
changed due to the fact that the third block has now been moved to the slot
that was previously the slot of the second block.
So what can we do about this? Well, we can basically emulate what VVVVVV did
in 2.2, which is disable a block without actually removing it - except I'm not
going to reintroduce an `active` attribute or anything. I'll disable the
collision of all blocks at a certain position by setting their widths and
heights to 0, and then re-enable them later by finding the first block at that
same position, updating its position, and re-assigning its width and height
again.
The former is what `entityclass::nocollisionat()` does; the latter is what
`entityclass::moveblockto()` does. The former mimicks turning off the `active`
attribute of all blocks sharing a certain top-left corner; the latter mimicks
creating a new block - and it will only do this for one block, because
`entityclass::createblock()` in 2.2 only looked for the first block with a
false `active` attribute.
Now, some quirks relied on the previous behavior of destroying and creating
blocks, but all of these quirks have been preserved with the way I implemented
this fix.
The first quirk is that platforms passing through 0,0 will destroy all spike
hitboxes, script boxes, activity zones, and one-way hitboxes in the room. The
hitboxes of moving platforms, disappearing platforms, 1x1 quicksand, and
conveyors will not be affected.
This is a consequence of the fact that the former group uses the `x` and `y`
of their `rect`, while the latter group uses the `xp` and `yp` attributes. So
the `xp` and `yp` of the former are both 0. Meaning, a platform passing
through 0,0 destroys them all.
Having these separate coordinates seems like an artifact from the Flash days.
(And furthermore, there's an unused `x` and `y` attribute on all blocks,
making for technically three separate sets of coordinates! This should
probably be cleaned up, except for what I'm about to say...) But actually, if
you merge both sets of coordinates into one, this lets moving platforms
destroy script boxes and activity zones if it passes through the top-left
corner of them, which is probably far worse than the destruction being
localized to a specific coordinate that would never likely be reached
normally.
This quirk is preserved just fine without any special-casing, because instead
of destroying all blocks at 0,0, they just get disabled, which does the same
job. This quirk seems trivial to fix if I made it so that the position of a
platform's block was updated instantaneously instead of having one step to
disable it and another step to re-enable it, but I aim to preserve as much
quirks as possible.
The second quirk is that a moving platform passing through the top-left corner
of a disappearing platform or 1x1 quicksand will destroy the block of that
disappearing platform. This is because, again, when a moving platform updates,
it destroys all blocks at its previous position, not just only one block. This
is automatically preserved because this commit just disables the block of the
disappearing platform instead of removing it. Just like the last one, this
quirk seems extremely trivial to fix, and this time by simply making it so
`entityclass::nocollisionat()` would have a `break` statement, i.e. only
disabling the first block it finds instead of all blocks it finds, but I want
to keep all quirks that are possible to keep.
The last quirk is that, apparently, in order to prevent pushing the player
vertically out of a moving platform if they get inside of one, the game
destroys the block of the moving platform. If I had missed this edge case,
then the block would've been destroyed, leaving the moving platform with no
collision. But I caught it in my testing, so the block gets disabled instead
of destroyed. Also, it seems obtuse for those who don't understand why a
platform's block gets destroyed or disabled whenever the player collides with
it in `entityclass::collisioncheck()`, so I've put up a comment documenting it
as well.
The different platform evaluation order desyncs my Nova TAS, but after
applying this patchset and #504, my TAS syncs fine (save for the different
walkingframe from starting immediately on the ground instead of in the air
(#502), but that's minor and can be easily fixed).
I've attached a test level to the pull request for this commit (#503) to
demonstrate that this patchset not only fixes platform evaluation order, but
preserves some bugs and quirks with the existing block system.
The first room demonstrates the fixed platform evaluation order, by stepping
on the conveyors that both point into each other. In 2.2, Viridian will move
to the right of the background pillar, but in 2.3, Viridian will move to the
left of the pillar. However, after applying this patch, Viridian will now move
to the right of the pillar once again.
The second room demonstrates that the platform-passing-through-0,0 trick still
works (as explained above).
The last room demonstrates that platforms passing through the top-left corners
of disappearing platforms or 1x1 quicksand will remove the blocks of those
entities, causing Viridian to immediately pass through them. This trick is
still preserved after my patchset is applied.
2020-10-10 02:09:11 +02:00
|
|
|
if (entitycollide(i, j))
|
|
|
|
{
|
|
|
|
//Disable collision temporarily so we don't push the person out!
|
|
|
|
//Collision will be restored at end of platform update loop in gamelogic
|
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
|
|
|
disableblockat(entities[j].xp, entities[j].yp);
|
Restore platform evaluation order to 2.2
This commit restores the evaluation order of moving platforms and conveyors to
be what it was in 2.2. The evaluation order changed in 2.3 after the patchset
to improve the handling of the `obj.entities` and `obj.blocks` vectors (#191).
By evaluation order, I'm talking about the order in which platforms and
conveyors will be evaluated (and thus will take priority) if Viridian stands
on both a conveyor or platform at once, and they either have different speeds
or are pointing in different directions. Nowhere in the main game is there a
place where you can stand on two different conveyors/platforms at once, so
this is solely within the territory of custom levels, which is my specialty.
So what caused this evaluation order to change? Well, every moving platform
and conveyor in the game is actually made up of two objects: an entity, and a
block. The entity is the part that moves around, and the block is the part
that actually has the collision.
But if the entity is the part that moves around, and entities and blocks are
in entirely separate vectors, how is the block part going to move along with
it? Well, maybe you'd guess some sort of unique ID system, but spend some time
digging around the code and you won't find any trace of any (there's no
attribute on an entity to store such an ID, for starters).
Instead, what the game does is actually remove all blocks that coincide with
the exact top-left corner of the entity, and then create a new one. Destroying
and creating blocks like this all the time is hugely wasteful, but hey, it
worked.
So why did the evaluation order change in 2.3? Well, to understand that,
you'll need to understand 2.2's `active` system. Instead of having an object
be real simply by virtue of it existing, 2.2 had this system where the object
was only real if it had its `active` attribute set to true. In other words,
you would be looking at a fake object that didn't actually exist if its
`active` attribute was false.
On the surface, this doesn't seem that bad. But this can lead to "holes" in a
given vector of objects. A hole is simply an inactive object neighbored by
active objects (or the inactive object could be the first one in the vector,
but then have an active object immediately following it).
If you have a vector of 3 objects, all of them active, then removing the
second one will result in the vector containing an active object, followed by
an inactive object, followed by an active one. However, since the switch to
more properly use vectors instead of relying on this `active` system, there's
no longer any way for holes to exist in a vector. Properly removing an object
from a vector will just shift the rest of the objects down, so if we remove
the second object after the vector fix, then this will simply move the third
object into the slot of where the second object used to be.
So, what happens if you destroy a block and then create a new one in the
`active` system? Let's say that your `obj.blocks` looks like this, and here
I'm denoting each block by writing out its coordinates:
[30,60] [70,90] [80,100]
and that you want to update the position of the second one, because the entity
that that blocks belongs to has been updated. Okay, so, you delete that block,
which then makes things look like this:
[30,60] [-] [80,100]
and then afterwards, you create a new block with the updated position,
resulting in this:
[30,60] [74,90] [80,100]
Since `entityclass::createblock()` will find the first block slot that has a
false `active` attribute, it puts the new object in the same slot as the old
one. What has been essentially done here is that the slot of the block has
basically been reserved for the new block with the new position. Here, the
evaluation order of each block will stay the same.
But then 2.3 comes along and changes things up. So we start with an
`obj.blocks` like this again:
[30,60] [70,90] [80,100]
and we want to update the second block, like before. So we remove the second
block, resulting in this:
[30,60] [80,100]
It should be obvious that unlike before, where the third block stayed in the
third slot, the third block has now been moved to the second slot. But
continuing on; we are now going to create the new block with its updated
position, resulting in this:
[30,60] [80,100] [70,90]
At this point, we can see that the evaluation order of these blocks has been
changed due to the fact that the third block has now been moved to the slot
that was previously the slot of the second block.
So what can we do about this? Well, we can basically emulate what VVVVVV did
in 2.2, which is disable a block without actually removing it - except I'm not
going to reintroduce an `active` attribute or anything. I'll disable the
collision of all blocks at a certain position by setting their widths and
heights to 0, and then re-enable them later by finding the first block at that
same position, updating its position, and re-assigning its width and height
again.
The former is what `entityclass::nocollisionat()` does; the latter is what
`entityclass::moveblockto()` does. The former mimicks turning off the `active`
attribute of all blocks sharing a certain top-left corner; the latter mimicks
creating a new block - and it will only do this for one block, because
`entityclass::createblock()` in 2.2 only looked for the first block with a
false `active` attribute.
Now, some quirks relied on the previous behavior of destroying and creating
blocks, but all of these quirks have been preserved with the way I implemented
this fix.
The first quirk is that platforms passing through 0,0 will destroy all spike
hitboxes, script boxes, activity zones, and one-way hitboxes in the room. The
hitboxes of moving platforms, disappearing platforms, 1x1 quicksand, and
conveyors will not be affected.
This is a consequence of the fact that the former group uses the `x` and `y`
of their `rect`, while the latter group uses the `xp` and `yp` attributes. So
the `xp` and `yp` of the former are both 0. Meaning, a platform passing
through 0,0 destroys them all.
Having these separate coordinates seems like an artifact from the Flash days.
(And furthermore, there's an unused `x` and `y` attribute on all blocks,
making for technically three separate sets of coordinates! This should
probably be cleaned up, except for what I'm about to say...) But actually, if
you merge both sets of coordinates into one, this lets moving platforms
destroy script boxes and activity zones if it passes through the top-left
corner of them, which is probably far worse than the destruction being
localized to a specific coordinate that would never likely be reached
normally.
This quirk is preserved just fine without any special-casing, because instead
of destroying all blocks at 0,0, they just get disabled, which does the same
job. This quirk seems trivial to fix if I made it so that the position of a
platform's block was updated instantaneously instead of having one step to
disable it and another step to re-enable it, but I aim to preserve as much
quirks as possible.
The second quirk is that a moving platform passing through the top-left corner
of a disappearing platform or 1x1 quicksand will destroy the block of that
disappearing platform. This is because, again, when a moving platform updates,
it destroys all blocks at its previous position, not just only one block. This
is automatically preserved because this commit just disables the block of the
disappearing platform instead of removing it. Just like the last one, this
quirk seems extremely trivial to fix, and this time by simply making it so
`entityclass::nocollisionat()` would have a `break` statement, i.e. only
disabling the first block it finds instead of all blocks it finds, but I want
to keep all quirks that are possible to keep.
The last quirk is that, apparently, in order to prevent pushing the player
vertically out of a moving platform if they get inside of one, the game
destroys the block of the moving platform. If I had missed this edge case,
then the block would've been destroyed, leaving the moving platform with no
collision. But I caught it in my testing, so the block gets disabled instead
of destroyed. Also, it seems obtuse for those who don't understand why a
platform's block gets destroyed or disabled whenever the player collides with
it in `entityclass::collisioncheck()`, so I've put up a comment documenting it
as well.
The different platform evaluation order desyncs my Nova TAS, but after
applying this patchset and #504, my TAS syncs fine (save for the different
walkingframe from starting immediately on the ground instead of in the air
(#502), but that's minor and can be easily fixed).
I've attached a test level to the pull request for this commit (#503) to
demonstrate that this patchset not only fixes platform evaluation order, but
preserves some bugs and quirks with the existing block system.
The first room demonstrates the fixed platform evaluation order, by stepping
on the conveyors that both point into each other. In 2.2, Viridian will move
to the right of the background pillar, but in 2.3, Viridian will move to the
left of the pillar. However, after applying this patch, Viridian will now move
to the right of the pillar once again.
The second room demonstrates that the platform-passing-through-0,0 trick still
works (as explained above).
The last room demonstrates that platforms passing through the top-left corners
of disappearing platforms or 1x1 quicksand will remove the blocks of those
entities, causing Viridian to immediately pass through them. This trick is
still preserved after my patchset is applied.
2020-10-10 02:09:11 +02:00
|
|
|
}
|
2020-09-06 02:37:57 +02:00
|
|
|
break;
|
|
|
|
case 3: //Entity to entity
|
2020-09-06 02:35:44 +02:00
|
|
|
if(entities[j].onentity>0)
|
|
|
|
{
|
|
|
|
if (entitycollide(i, j)) entities[j].state = entities[j].onentity;
|
|
|
|
}
|
2020-09-06 02:37:57 +02:00
|
|
|
break;
|
|
|
|
case 4: //Person vs horizontal line!
|
2020-09-06 02:35:44 +02:00
|
|
|
if(game.deathseq==-1)
|
|
|
|
{
|
|
|
|
//Here we compare the person's old position versus his new one versus the line.
|
|
|
|
//All points either be above or below it. Otherwise, there was a collision this frame.
|
|
|
|
if (entities[j].onentity > 0)
|
|
|
|
{
|
|
|
|
if (entityhlinecollide(i, j))
|
|
|
|
{
|
|
|
|
music.playef(8);
|
|
|
|
game.gravitycontrol = (game.gravitycontrol + 1) % 2;
|
|
|
|
game.totalflips++;
|
|
|
|
if (game.gravitycontrol == 0)
|
|
|
|
{
|
|
|
|
if (entities[i].vy < 1) entities[i].vy = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (entities[i].vy > -1) entities[i].vy = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
entities[j].state = entities[j].onentity;
|
|
|
|
entities[j].life = 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 02:37:57 +02:00
|
|
|
break;
|
|
|
|
case 5: //Person vs vertical gravity/warp line!
|
2020-09-06 02:35:44 +02:00
|
|
|
if(game.deathseq==-1)
|
|
|
|
{
|
|
|
|
if(entities[j].onentity>0)
|
|
|
|
{
|
|
|
|
if (entityvlinecollide(i, j))
|
|
|
|
{
|
|
|
|
entities[j].state = entities[j].onentity;
|
|
|
|
entities[j].life = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 02:37:57 +02:00
|
|
|
break;
|
|
|
|
case 6: //Person versus crumbly blocks! Special case
|
2020-09-06 02:35:44 +02:00
|
|
|
if (entities[j].onentity > 0)
|
|
|
|
{
|
|
|
|
//ok; only check the actual collision if they're in a close proximity
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
int temp = entities[i].yp - entities[j].yp;
|
2020-09-06 02:35:44 +02:00
|
|
|
if (temp > -30 && temp < 30)
|
|
|
|
{
|
|
|
|
temp = entities[i].xp - entities[j].xp;
|
|
|
|
if (temp > -30 && temp < 30)
|
|
|
|
{
|
|
|
|
if (entitycollide(i, j)) entities[j].state = entities[j].onentity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 02:37:57 +02:00
|
|
|
break;
|
|
|
|
case 7: // Person versus horizontal warp line, pre-2.1
|
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
|
|
|
if (GlitchrunnerMode_less_than_or_equal(Glitchrunner2_0)
|
2020-09-06 02:35:44 +02:00
|
|
|
&& game.deathseq == -1
|
|
|
|
&& entities[j].onentity > 0
|
|
|
|
&& entityhlinecollide(i, j))
|
|
|
|
{
|
|
|
|
entities[j].state = entities[j].onentity;
|
|
|
|
}
|
2020-09-06 02:37:57 +02:00
|
|
|
break;
|
2020-09-06 02:35:44 +02:00
|
|
|
}
|
|
|
|
}
|
2020-09-06 02:38:38 +02:00
|
|
|
|
|
|
|
void entityclass::stuckprevention(int t)
|
|
|
|
{
|
2020-09-08 09:31:44 +02:00
|
|
|
if (!INBOUNDS_VEC(t, entities))
|
2020-09-06 02:38:38 +02:00
|
|
|
{
|
2021-02-24 00:21:29 +01:00
|
|
|
vlog_error("stuckprevention() out-of-bounds!");
|
2020-09-06 02:38:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can't have this entity (player or supercrewmate) being stuck...
|
Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.
These attributes were:
- colpoint1
- colpoint2
- tempx
- tempy
- tempw
- temph
- temp
- temp2
- tpx1
- tpy1
- tpx2
- tpy2
- temprect
- temprect2
- x (actually unused)
- dx
- dy
- dr
- px
- py
- linetemp
- activetrigger
- skipblocks
- skipdirblocks
Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.
Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-26 21:38:57 +02:00
|
|
|
if (!testwallsx(t, entities[t].xp, entities[t].yp, true))
|
2020-09-06 02:38:38 +02:00
|
|
|
{
|
|
|
|
// Let's try to get out...
|
|
|
|
if (game.gravitycontrol == 0)
|
|
|
|
{
|
|
|
|
entities[t].yp -= 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entities[t].yp += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|