1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-23 10:09:43 +01:00
VVVVVV/desktop_version/src/Entity.cpp
Misa d549a535e0 Rename edlevelclass to RoomProperty
That's what edlevelclass is... so that's what it should be named. (Also
removes that "ed", too, making this less coupled to the in-game editor.)

Unfortunately, for compatibility reasons, the name of the XML element
will still remain the same.
2021-09-01 15:30:02 -07:00

4883 lines
149 KiB
C++

#define OBJ_DEFINITION
#include "Entity.h"
#include <SDL.h>
#include "CustomLevels.h"
#include "Game.h"
#include "GlitchrunnerMode.h"
#include "Graphics.h"
#include "Map.h"
#include "Music.h"
#include "Script.h"
#include "UtilityClass.h"
#include "Vlogging.h"
#include "Xoshiro.h"
bool entityclass::checktowerspikes(int t)
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("checktowerspikes() out-of-bounds!");
return false;
}
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;
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);
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)
{
int tpy1 = getgridpoint(temprect.y + 6);
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;
}
void entityclass::init(void)
{
platformtile = 0;
customplatformtile=0;
vertplatforms = false;
horplatforms = false;
nearelephant = false;
upsetmode = false;
upset = 0;
customenemy = 0;
customwarpmode = false; customwarpmodevon = false; customwarpmodehon = false;
customactivitycolour = "";
customactivitytext = "";
trophytext = 0;
oldtrophytext = 0;
trophytype = 0;
altstates = 0;
SDL_memset(customcrewmoods, true, sizeof(customcrewmoods));
resetallflags();
SDL_memset(collect, false, sizeof(collect));
SDL_memset(customcollect, false, sizeof(customcollect));
k = 0;
}
void entityclass::resetallflags(void)
{
SDL_memset(flags, false, sizeof(flags));
}
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
for (size_t i = 0; i < entities.size(); i++)
{
if (entities[i].type == 23)
{
entities[i].colour = swncolour(t);
}
}
}
void entityclass::gravcreate( int ypos, int dir, int xoff /*= 0*/, int yoff /*= 0*/ )
{
if (dir == 0)
{
createentity(-150 - xoff, 58 + (ypos * 20)+yoff, 23, 0, 0);
}
else
{
createentity(320+150 + xoff, 58 + (ypos * 20)+yoff, 23, 1, 0);
}
}
void entityclass::generateswnwave( int t )
{
//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;
}
else if (game.swntimer <= 300) //less than 10 seconds
{
game.swnstate = 6;
game.swndelay = 12;
}
else if (game.swntimer <= 360) //less than 12 seconds
{
game.swnstate = 5+game.swnstate2;
game.swndelay = 15;
}
else if (game.swntimer <= 420) //less than 14 seconds
{
game.swnstate = 7+game.swnstate2;
game.swndelay = 15;
}
else if (game.swntimer <= 480) //less than 16 seconds
{
game.swnstate = 5+game.swnstate2;
game.swndelay = 15;
}
else if (game.swntimer <= 540) //less than 18 seconds
{
game.swnstate = 7+game.swnstate2;
game.swndelay = 15;
}
else if (game.swntimer <= 600) //less than 20 seconds
{
game.swnstate = 5+game.swnstate2;
game.swndelay = 15;
}
else if (game.swntimer <= 900) //less than 30 seconds
{
game.swnstate = 4;
game.swndelay = 20;
}
else if (game.swntimer <= 1050) //less than 35 seconds
{
game.swnstate = 3;
game.swndelay = 10;
}
else if (game.swntimer <= 1200) //less than 40 seconds
{
game.swnstate = 3;
game.swndelay = 20;
}
else if (game.swntimer <= 1500) //less than 50 seconds
{
game.swnstate = 2;
game.swndelay = 10;
}
else if (game.swntimer <= 1650) //less than 55 seconds
{
game.swnstate = 1;
game.swndelay = 15;
}
else if (game.swntimer <= 1800) //less than 60 seconds
{
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:
createentity(-150, 58 + (int(xoshiro_rand() * 6) * 20), 23, 0, 0);
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++;
}
}
createentity(-150, 58 + (int(game.swnstate2) * 20), 23, 0, 0);
game.swnstate = 0;
game.swndelay = 0; //return to decision state
break;
case 3:
createentity(320+150, 58 + (int(xoshiro_rand() * 6) * 20), 23, 1, 0);
game.swnstate = 0;
game.swndelay = 0; //return to decision state
break;
case 4:
//left and right compliments
game.swnstate2 = int(xoshiro_rand() * 6);
createentity(-150, 58 + (game.swnstate2 * 20), 23, 0, 0);
createentity(320+150, 58 + ((5-game.swnstate2) * 20), 23, 1, 0);
game.swnstate = 0;
game.swndelay = 0; //return to decision state
game.swnstate2 = 0;
break;
case 5:
//Top and bottom
createentity(-150, 58, 23, 0, 0);
createentity(-150, 58 + (5 * 20), 23, 0, 0);
game.swnstate = 0;
game.swndelay = 0; //return to decision state
game.swnstate2 = 1;
break;
case 6:
//Middle
createentity(-150, 58 + (2 * 20), 23, 0, 0);
createentity(-150, 58 + (3 * 20), 23, 0, 0);
game.swnstate = 0;
game.swndelay = 0; //return to decision state
game.swnstate2 = 0;
break;
case 7:
//Top and bottom
createentity(320+150, 58, 23, 1, 0);
createentity(320+150, 58 + (5 * 20), 23, 1, 0);
game.swnstate = 0;
game.swndelay = 0; //return to decision state
game.swnstate2 = 1;
break;
case 8:
//Middle
createentity(320+150, 58 + (2 * 20), 23, 1, 0);
createentity(320+150, 58 + (3 * 20), 23, 1, 0);
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++;
}
}
createentity(320 + 150, 58 + (int(game.swnstate2) * 20), 23, 1, 0);
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;
game.swnstate2 = int(xoshiro_rand() * 100);
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
game.swnstate2 = int(xoshiro_rand() * 8);
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
game.swnstate2 = int(xoshiro_rand() * 6);
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
game.swnstate2 = int(xoshiro_rand() * 100);
game.swnstate4 = 0;
if (game.swnstate2 < 25)
{
//complex
game.swnstate = 1;
game.swndelay = 0;
}
else
{
//simple
game.swnstate = 2;
game.swndelay = 0;
}
break;
case 4:
//filler chain
game.swnstate2 = int(xoshiro_rand() * 6);
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:
gravcreate(0, 0);
gravcreate(1, 0);
gravcreate(2, 0);
game.swnstate++;
game.swndelay = 10; //return to decision state
break;
case 11:
gravcreate(3, 0);
gravcreate(4, 0);
gravcreate(5, 0);
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:
gravcreate(0, 1);
gravcreate(1, 1);
gravcreate(2, 1);
game.swnstate++;
game.swndelay = 10; //return to decision state
break;
case 13:
gravcreate(3, 1);
gravcreate(4, 1);
gravcreate(5, 1);
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:
gravcreate(0, 0, 0);
gravcreate(5, 1, 0);
game.swnstate++;
game.swndelay = 20; //return to decision state
break;
case 15:
gravcreate(1, 0);
gravcreate(4, 1);
game.swnstate++;
game.swndelay = 20; //return to decision state
break;
case 16:
gravcreate(2, 0);
gravcreate(3, 1);
game.swnstate++;
game.swndelay = 20; //return to decision state
break;
case 17:
gravcreate(3, 0);
gravcreate(2, 1);
game.swnstate++;
game.swndelay = 20; //return to decision state
break;
case 18:
gravcreate(4, 0);
gravcreate(1, 1);
game.swnstate++;
game.swndelay = 20; //return to decision state
break;
case 19:
gravcreate(5, 0);
gravcreate(0, 1);
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++;
}
}
createentity(-150, 58 + (int(game.swnstate2) * 20), 23, 0, 0);
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++;
}
}
createentity(320+150, 58 + (int(game.swnstate2) * 20), 23, 1, 0);
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
game.swnstate2 = int(xoshiro_rand() * 6);
createentity(-150, 58 + (game.swnstate2 * 20), 23, 0, 0);
createentity(320 + 150, 58 + ((5 - game.swnstate2) * 20), 23, 1, 0);
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:
gravcreate(1, 0);
gravcreate(2, 0, 15);
gravcreate(2, 0, -15);
gravcreate(3, 0, 15);
gravcreate(3, 0, -15);
gravcreate(4, 0);
game.swnstate = 0;
game.swndelay = 15; //return to decision state
break;
case 24:
gravcreate(1, 1);
gravcreate(2, 1, 15);
gravcreate(2, 1, -15);
gravcreate(3, 1, 15);
gravcreate(3, 1, -15);
gravcreate(4, 1);
game.swnstate = 0;
game.swndelay = 15; //return to decision state
break;
case 25:
gravcreate(0, 0);
gravcreate(1, 1,0,10);
gravcreate(4, 1,0,-10);
gravcreate(5, 0);
game.swnstate = 0;
game.swndelay = 20; //return to decision state
break;
case 26:
gravcreate(0, 1, 0);
gravcreate(1, 1, 10);
gravcreate(4, 1, 40);
gravcreate(5, 1, 50);
game.swnstate = 0;
game.swndelay = 20; //return to decision state
break;
case 27:
gravcreate(0, 0, 0);
gravcreate(1, 0, 10);
gravcreate(4, 0, 40);
gravcreate(5, 0, 50);
game.swnstate = 0;
game.swndelay = 20; //return to decision state
break;
case 28:
game.swnstate4++;
game.swnstate2 = int(xoshiro_rand() * 6);
createentity(-150, 58 + (game.swnstate2 * 20), 23, 0, 0);
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++;
game.swnstate2 = int(xoshiro_rand() * 6);
gravcreate(game.swnstate2, 1);
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++;
game.swnstate2 = int(xoshiro_rand() * 3);
gravcreate(game.swnstate2, 0);
gravcreate(5-game.swnstate2, 0);
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++;
game.swnstate2 = int(xoshiro_rand() * 3);
gravcreate(game.swnstate2, 1);
gravcreate(5-game.swnstate2, 1);
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--;
}
}
void entityclass::createblock( int t, int xp, int yp, int w, int h, int trig /*= 0*/, const std::string& script /*= ""*/, bool custom /*= false*/)
{
k = blocks.size();
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)
{
if (blocks[i].wp == 0
&& 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;
switch(t)
{
case BLOCK: //Block
block.type = BLOCK;
block.xp = xp;
block.yp = yp;
block.wp = w;
block.hp = h;
block.rectset(xp, yp, w, h);
break;
case TRIGGER: //Trigger
block.type = TRIGGER;
block.wp = w;
block.hp = h;
block.rectset(xp, yp, w, h);
block.trigger = trig;
block.script = script;
break;
case DAMAGE: //Damage
block.type = DAMAGE;
block.wp = w;
block.hp = h;
block.rectset(xp, yp, w, h);
break;
case DIRECTIONAL: //Directional
block.type = DIRECTIONAL;
block.wp = w;
block.hp = h;
block.rectset(xp, yp, w, h);
block.trigger = trig;
break;
case SAFE: //Safe block
block.type = SAFE;
block.xp = xp;
block.yp = yp;
block.wp = w;
block.hp = h;
block.rectset(xp, yp, w, h);
break;
case ACTIVITY: //Activity Zone
block.type = ACTIVITY;
block.wp = w;
block.hp = h;
block.rectset(xp, yp, w, h);
//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
block.prompt = "Press %s to explode";
block.script = "intro";
block.setblockcolour("orange");
trig=1;
break;
case 1:
block.prompt = "Press %s to talk to Violet";
block.script = "talkpurple";
block.setblockcolour("purple");
trig=0;
break;
case 2:
block.prompt = "Press %s to talk to Vitellary";
block.script = "talkyellow";
block.setblockcolour("yellow");
trig=0;
break;
case 3:
block.prompt = "Press %s to talk to Vermilion";
block.script = "talkred";
block.setblockcolour("red");
trig=0;
break;
case 4:
block.prompt = "Press %s to talk to Verdigris";
block.script = "talkgreen";
block.setblockcolour("green");
trig=0;
break;
case 5:
block.prompt = "Press %s to talk to Victoria";
block.script = "talkblue";
block.setblockcolour("blue");
trig=0;
break;
case 6:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_station_1";
block.setblockcolour("orange");
trig=0;
break;
case 7:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_outside_1";
block.setblockcolour("orange");
trig=0;
break;
case 8:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_outside_2";
block.setblockcolour("orange");
trig=0;
break;
case 9:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_outside_3";
block.setblockcolour("orange");
trig=0;
break;
case 10:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_outside_4";
block.setblockcolour("orange");
trig=0;
break;
case 11:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_outside_5";
block.setblockcolour("orange");
trig=0;
break;
case 12:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_outside_6";
block.setblockcolour("orange");
trig=0;
break;
case 13:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_finallevel";
block.setblockcolour("orange");
trig=0;
break;
case 14:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_station_2";
block.setblockcolour("orange");
trig=0;
break;
case 15:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_station_3";
block.setblockcolour("orange");
trig=0;
break;
case 16:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_station_4";
block.setblockcolour("orange");
trig=0;
break;
case 17:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_warp_1";
block.setblockcolour("orange");
trig=0;
break;
case 18:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_warp_2";
block.setblockcolour("orange");
trig=0;
break;
case 19:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_lab_1";
block.setblockcolour("orange");
trig=0;
break;
case 20:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_lab_2";
block.setblockcolour("orange");
trig=0;
break;
case 21:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_secretlab";
block.setblockcolour("orange");
trig=0;
break;
case 22:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_shipcomputer";
block.setblockcolour("orange");
trig=0;
break;
case 23:
block.prompt = "Press %s to activate terminals";
block.script = "terminal_radio";
block.setblockcolour("orange");
trig=0;
break;
case 24:
block.prompt = "Press %s to activate terminal";
block.script = "terminal_jukebox";
block.setblockcolour("orange");
trig=0;
break;
case 25:
block.prompt = "Passion for Exploring";
block.script = "terminal_juke1";
block.setblockcolour("orange");
trig=0;
break;
case 26:
block.prompt = "Pushing Onwards";
block.script = "terminal_juke2";
block.setblockcolour("orange");
trig=0;
break;
case 27:
block.prompt = "Positive Force";
block.script = "terminal_juke3";
block.setblockcolour("orange");
trig=0;
break;
case 28:
block.prompt = "Presenting VVVVVV";
block.script = "terminal_juke4";
block.setblockcolour("orange");
trig=0;
break;
case 29:
block.prompt = "Potential for Anything";
block.script = "terminal_juke5";
block.setblockcolour("orange");
trig=0;
break;
case 30:
block.prompt = "Predestined Fate";
block.script = "terminal_juke6";
block.setblockcolour("orange");
trig=0;
break;
case 31:
block.prompt = "Pipe Dream";
block.script = "terminal_juke7";
block.setblockcolour("orange");
trig=0;
break;
case 32:
block.prompt = "Popular Potpourri";
block.script = "terminal_juke8";
block.setblockcolour("orange");
trig=0;
break;
case 33:
block.prompt = "Pressure Cooker";
block.script = "terminal_juke9";
block.setblockcolour("orange");
trig=0;
break;
case 34:
block.prompt = "ecroF evitisoP";
block.script = "terminal_juke10";
block.setblockcolour("orange");
trig=0;
break;
case 35:
if (custom)
{
block.prompt = "Press %s to interact";
}
else
{
block.prompt = "Press %s to activate terminal";
}
block.script = "custom_"+customscript;
block.setblockcolour("orange");
trig=0;
break;
}
break;
}
if (customactivitytext != "")
{
block.prompt = customactivitytext;
customactivitytext = "";
}
if (customactivitycolour != "")
{
block.setblockcolour(customactivitycolour);
customactivitycolour = "";
}
if (!reuse)
{
blocks.push_back(block);
}
}
/* Disable entity, and return true if entity was successfully disabled */
bool entityclass::disableentity(int t)
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("disableentity() out-of-bounds!");
return true;
}
if (entities[t].rule == 0 && t == getplayer())
{
/* Don't disable the player entity! */
return false;
}
entities[t].invis = true;
entities[t].size = -1;
entities[t].type = -1;
entities[t].rule = -1;
return true;
}
void entityclass::removeallblocks(void)
{
blocks.clear();
}
void entityclass::disableblock( int t )
{
if (!INBOUNDS_VEC(t, blocks))
{
vlog_error("disableblock() out-of-bounds!");
return;
}
blocks[t].wp = 0;
blocks[t].hp = 0;
blocks[t].rect.w = blocks[t].wp;
blocks[t].rect.h = blocks[t].hp;
}
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;
}
}
}
void entityclass::disableblockat(int x, int y)
{
for (size_t i = 0; i < blocks.size(); i++)
{
if (blocks[i].xp == x && blocks[i].yp == y)
{
disableblock(i);
}
}
}
void entityclass::removetrigger( int t )
{
for(size_t i=0; i<blocks.size(); i++)
{
if(blocks[i].type == TRIGGER && blocks[i].trigger == t)
{
disableblock(i);
}
}
}
void entityclass::copylinecross(std::vector<entclass>& linecrosskludge, int t)
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("copylinecross() out-of-bounds!");
return;
}
//Copy entity t into the first free linecrosskludge entity
linecrosskludge.push_back(entities[t]);
}
void entityclass::revertlinecross(std::vector<entclass>& linecrosskludge, int t, int s)
{
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(s, linecrosskludge))
{
vlog_error("revertlinecross() out-of-bounds!");
return;
}
//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;
}
static void entityclonefix(entclass* entity)
{
if (entity->behave == 10 || entity->behave == 12)
{
/* Fix memory leak */
entity->behave = -1;
}
}
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1, int p2, int p3, int p4)
{
k = entities.size();
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
&& entities[i].rule == -1)
{
reuse = true;
entptr = &entities[i];
break;
}
}
if (!reuse)
{
entptr = &newent;
}
else
{
entptr->clear();
}
//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
#if !defined(NO_CUSTOM_LEVELS)
// Special case for gray Warp Zone tileset!
const RoomProperty* const room = cl.getroomprop(game.roomx - 100, game.roomy - 100);
bool custom_gray = room->tileset == 3 && room->tilecol == 6;
#else
bool custom_gray = false;
#endif
entclass& entity = *entptr;
entity.xp = xp;
entity.yp = yp;
entity.type = t;
switch(t)
{
case 0: //Player
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;
/* Fix wrong y-position if spawning in on conveyor */
entity.newxp = xp;
entity.newyp = yp;
if (meta1 == 1) entity.invis = true;
entity.gravity = true;
break;
case 1: //Simple enemy, bouncing off the walls
entity.rule = 1;
entity.behave = meta1;
entity.para = meta2;
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;
if (game.roomy == 111 && (game.roomx >= 113 && game.roomx <= 117))
{
entity.setenemy(0);
entity.setenemyroom(game.roomx, game.roomy); //For colour
}
else if (game.roomx == 113 && (game.roomy <= 110 && game.roomy >= 108))
{
entity.setenemy(1);
entity.setenemyroom(game.roomx, game.roomy); //For colour
}
else if (game.roomx == 113 && game.roomy == 107)
{
//MAVVERRRICK
entity.tile = 96;
entity.colour = 6;
entity.size = 9;
entity.w = 64;
entity.h = 44;
entity.animate = 4;
}
else
{
entity.setenemyroom(game.roomx, game.roomy);
entityclonefix(&entity);
}
break;
case 2: //A moving platform
entity.rule = 2;
entity.type = 1;
entity.size = 2;
entity.tile = 1;
if (customplatformtile > 0){
entity.tile = customplatformtile;
}else if (platformtile > 0) {
entity.tile = platformtile;
}else{
//appearance again depends on location
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;
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;
}
entity.w = 32;
entity.h = 8;
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;
if (meta1 >= 10 && meta1 <= 11)
{
//Double sized threadmills
entity.w = 64;
entity.h = 8;
meta1 -= 2;
entity.size = 8;
}
entity.behave = meta1;
entity.para = meta2;
if (meta1 >= 8 && meta1 <= 9)
{
horplatforms = true; //threadmill!
entity.animate = 10;
if(customplatformtile>0){
entity.tile = customplatformtile+4;
if (meta1 == 8) entity.tile += 4;
if (meta1 == 9) entity.animate = 11;
}else{
entity.settreadmillcolour(game.roomx, game.roomy);
if (meta1 == 8) entity.tile += 40;
if (meta1 == 9) entity.animate = 11;
}
}
else
{
entity.animate = 100;
}
entity.x1 = p1;
entity.y1 = p2;
entity.x2 = p3;
entity.y2 = p4;
entity.isplatform = true;
createblock(0, xp, yp, 32, 8);
break;
case 3: //Disappearing platforms
entity.rule = 3;
entity.type = 2;
entity.size = 2;
entity.tile = 2;
//appearance again depends on location
if(customplatformtile>0)
{
entity.tile=customplatformtile;
}
else if (meta1 > 0)
{
entity.tile = meta1;
}
else
{
if(game.roomx==49 && game.roomy==52) entity.tile = 18;
if (game.roomx == 50 && game.roomy == 52) entity.tile = 22;
}
entity.cy = -1;
entity.w = 32;
entity.h = 10;
entity.behave = meta1;
entity.para = meta2;
entity.onentity = 1;
entity.animate = 100;
createblock(0, xp, yp, 32, 8);
break;
case 4: //Breakable blocks
entity.rule = 6;
entity.type = 3;
entity.size = 1;
entity.tile = 10;
entity.cy = -1;
entity.w = 8;
entity.h = 10;
entity.behave = meta1;
entity.para = meta2;
entity.onentity = 1;
entity.animate = 100;
createblock(0, xp, yp, 8, 8);
break;
case 5: //Gravity Tokens
entity.rule = 3;
entity.type = 4;
entity.size = 0;
entity.tile = 11;
entity.w = 16;
entity.h = 16;
entity.behave = meta1;
entity.para = meta2;
entity.onentity = 1;
entity.animate = 100;
break;
case 6: //Decorative particles
entity.rule = 2;
entity.type = 5; //Particles
entity.colour = 1;
entity.size = 3;
entity.vx = meta1;
entity.vy = meta2;
entity.life = 12;
break;
case 7: //Decorative particles
entity.rule = 2;
entity.type = 5; //Particles
entity.colour = 2;
entity.size = 3;
entity.vx = meta1;
entity.vy = meta2;
entity.life = 12;
break;
case 8: //Small collectibles
entity.rule = 3;
entity.type = 6;
entity.size = 4;
entity.tile = 48;
entity.w = 8;
entity.h = 8;
entity.onentity = 1;
entity.animate = 100;
//Check if it's already been collected
entity.para = meta1;
if (!INBOUNDS_ARR(meta1, collect) || collect[meta1]) return;
break;
case 9: //Something Shiny
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;
//Check if it's already been collected
entity.para = meta1;
if (!INBOUNDS_ARR(meta1, collect) || collect[meta1]) return;
break;
case 10: //Savepoint
entity.rule = 3;
entity.type = 8;
entity.size = 0;
entity.tile = 20 + meta1;
entity.w = 16;
entity.h = 16;
entity.colour = 4;
entity.onentity = 1;
entity.animate = 100;
entity.para = meta2;
if (game.savepoint == meta2)
{
entity.colour = 5;
entity.onentity = 0;
}
if (game.nodeathmode)
{
return;
}
break;
case 11: //Horizontal Gravity Line
entity.rule = 4;
entity.type = 9;
entity.size = 5;
entity.life = 0;
entity.w = meta1;
entity.h = 1;
entity.onentity = 1;
break;
case 12: //Vertical Gravity Line
entity.rule = 5;
entity.type = 10;
entity.size = 6;
entity.life = 0;
entity.w = 1;
entity.h = meta1;
//entity.colour = 0;
entity.onentity = 1;
break;
case 13: //Warp token
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;
//Added in port, hope it doesn't break anything
entity.behave = meta1;
entity.para = meta2;
break;
case 14: // Teleporter
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;
entity.para = meta2;
break;
case 15: // Crew Member (warp zone)
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;
entity.state = meta1;
entity.gravity = true;
break;
case 16: // Crew Member, upside down (space station)
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;
entity.state = meta1;
entity.gravity = true;
break;
case 17: // Crew Member (Lab)
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;
entity.state = meta1;
entity.gravity = true;
break;
case 18: // Crew Member (Ship)
//This is the scriping crewmember
entity.rule = 6;
entity.type = 12; //A special case!
entity.colour = meta1;
if (meta2 == 0)
{
entity.tile = 0;
}
else
{
entity.tile = 144;
}
entity.cx = 6;
entity.cy = 2;
entity.w = 12;
entity.h = 21;
entity.dir = 0;
entity.state = p1;
entity.para = p2;
if (p1 == 17)
{
entity.dir = p2;
}
entity.gravity = true;
break;
case 19: // Crew Member (Ship) More tests!
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;
entity.state = meta1;
entity.gravity = true;
break;
case 20: //Terminal
entity.rule = 3;
entity.type = 13;
entity.size = 0;
entity.tile = 16 + meta1;
entity.w = 16;
entity.h = 16;
entity.colour = 4;
entity.onentity = 1;
entity.animate = 100;
entity.para = meta2;
break;
case 21: //as above, except doesn't highlight
entity.rule = 3;
entity.type = 13;
entity.size = 0;
entity.tile = 16 + meta1;
entity.w = 16;
entity.h = 16;
entity.colour = 4;
entity.onentity = 0;
entity.animate = 100;
entity.para = meta2;
break;
case 22: //Fake trinkets, only appear if you've collected them
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;
//Check if it's already been collected
entity.para = meta1;
if (INBOUNDS_ARR(meta1, collect) && !collect[meta1]) return;
break;
case 23: //SWN Enemies
//Given a different behavior, these enemies are especially for SWN mode and disappear outside the screen.
entity.rule = 1;
entity.type = 23;
entity.behave = meta1;
entity.para = meta2;
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;
//initilise tiles here based on behavior
entity.size = 12; //don't wrap around
entity.colour = 21;
entity.tile = 78; //default case
entity.animate = 1;
if (game.swngame == 1)
{
//set colour based on current state
entity.colour = swncolour(game.swncolstate);
}
break;
case 24: // Super Crew Member
//This special crewmember is way more advanced than the usual kind, and can interact with game objects
entity.rule = 6;
entity.type = 14; //A special case!
entity.colour = meta1;
if (meta1 == 16)
{
//victoria is sad!
if (meta2 == 2) meta2 = 1;
}
else
{
if (meta2 == 2) meta2 = 0;
}
if (meta2 == 0)
{
entity.tile = 0;
}
else
{
entity.tile = 144;
}
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;
if (p1 == 17)
{
entity.dir = p2;
}
entity.gravity = true;
break;
case 25: //Trophies
entity.rule = 3;
entity.type = 15;
entity.size = 0;
entity.w = 16;
entity.h = 16;
entity.colour = 4;
entity.onentity = 1;
entity.animate = 100;
entity.para = meta2;
//Decide tile here based on given achievement: both whether you have them and what they are
//default is just a trophy base:
entity.tile = 180 + meta1;
switch (meta2)
{
case 1:
if(game.bestrank[0]>=3)
{
entity.tile = 184 + meta1;
entity.colour = 31;
}
break;
case 2:
if(game.bestrank[1]>=3)
{
entity.tile = 186 + meta1;
entity.colour = 33;
}
break;
case 3:
if(game.bestrank[2]>=3)
{
entity.tile = 184 + meta1;
entity.colour = 35;
}
break;
case 4:
if(game.bestrank[3]>=3)
{
entity.tile = 184 + meta1;
entity.colour = 30;
}
break;
case 5:
if(game.bestrank[4]>=3)
{
entity.tile = 184 + meta1;
entity.colour = 34;
}
break;
case 6:
if(game.bestrank[5]>=3)
{
entity.tile = 184 + meta1;
entity.colour = 32;
}
break;
case 7:
if(game.unlock[5])
{
entity.tile = 188 + meta1;
entity.colour = 37;
entity.h += 3;
entity.yp -= 3;
}
break;
case 8:
if(game.unlock[19])
{
entity.tile = 188 + meta1;
entity.colour = 37;
entity.h += 3;
}
break;
case 9:
if (game.bestgamedeaths > -1)
{
if (game.bestgamedeaths <= 50)
{
entity.tile = 182 + meta1;
entity.colour = 40;
}
}
break;
case 10:
if (game.bestgamedeaths > -1)
{
if (game.bestgamedeaths <= 100)
{
entity.tile = 182 + meta1;
entity.colour = 36;
}
}
break;
case 11:
if (game.bestgamedeaths > -1)
{
if (game.bestgamedeaths <= 250)
{
entity.tile = 182 + meta1;
entity.colour = 38;
}
}
break;
case 12:
if (game.bestgamedeaths > -1)
{
if (game.bestgamedeaths <= 500)
{
entity.tile = 182 + meta1;
entity.colour = 39;
}
}
break;
case 13:
if(game.swnbestrank>=1)
{
entity.tile = 182 + meta1;
entity.colour = 39;
}
break;
case 14:
if(game.swnbestrank>=2)
{
entity.tile = 182 + meta1;
entity.colour = 39;
}
break;
case 15:
if(game.swnbestrank>=3)
{
entity.tile = 182 + meta1;
entity.colour = 39;
}
break;
case 16:
if(game.swnbestrank>=4)
{
entity.tile = 182 + meta1;
entity.colour = 38;
}
break;
case 17:
if(game.swnbestrank>=5)
{
entity.tile = 182 + meta1;
entity.colour = 36;
}
break;
case 18:
if(game.swnbestrank>=6)
{
entity.tile = 182 + meta1;
entity.colour = 40;
}
break;
case 19:
if(game.unlock[20])
{
entity.tile = 3;
entity.colour = 102;
entity.size = 13;
entity.xp -= 64;
entity.yp -= 128;
}
break;
}
break;
case 26: //Epilogue super warp token
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;
entity.para = meta2;
entity.size = 13;
break;
/* Warp lines */
case 51: /* Vertical */
case 52: /* Vertical */
case 53: /* Horizontal */
case 54: /* Horizontal */
entity.type = t;
entity.onentity = 1;
entity.invis = true;
entity.life = 0;
switch (t)
{
case 51:
case 52:
entity.rule = 5;
entity.size = 6;
entity.w = 1;
entity.h = meta1;
break;
case 53:
case 54:
entity.rule = 7;
entity.size = 5;
entity.w = meta1;
entity.h = 1;
break;
}
if (map.custommode)
{
customwarpmode = true;
map.warpx = false;
map.warpy = false;
}
break;
case 55: // Crew Member (custom, collectable)
//1 - position in array
//2 - colour
entity.rule = 3;
entity.type = 55;
if(INBOUNDS_ARR(meta2, customcrewmoods)
&& customcrewmoods[meta2]==1){
entity.tile = 144;
}else{
entity.tile = 0;
}
entity.colour = graphics.crewcolour(meta2);
entity.cx = 6;
entity.cy = 2;
entity.w = 12;
entity.h = 21;
entity.dir = 0;
entity.state = 0;
entity.onentity = 1;
//entity.state = meta1;
entity.gravity = true;
//Check if it's already been collected
entity.para = meta1;
if (!INBOUNDS_ARR(meta1, customcollect) || customcollect[meta1]) return;
break;
case 56: //Custom enemy
entity.rule = 1;
entity.type = 1;
entity.behave = meta1;
entity.para = meta2;
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;
switch(customenemy){
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;
}
//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:
entity.colour = 6; break;
//GREEN
case 5: case 9: case 22: case 25: case 29:
case 31: case 38: case 46: case 52: case 53:
entity.colour = 7; break;
//BLUE
case 1: case 6: case 14: case 27: case 33:
case 44: case 50: case 57:
entity.colour = 12; break;
//YELLOW
case 4: case 17: case 24: case 30: case 37:
case 45: case 51: case 55:
entity.colour = 9; break;
//PURPLE
case 2: case 11: case 15: case 19: case 32:
case 36: case 49:
entity.colour = 20; break;
//CYAN
case 8: case 10: case 13: case 18: case 26:
case 35: case 41: case 47: case 54:
entity.colour = 11; break;
//PINK
case 16: case 20: case 39: case 43: case 56:
entity.colour = 8; break;
//ORANGE
case 21: case 40:
entity.colour = 17; break;
default:
entity.colour = 6;
break;
}
}
if(custom_gray){
entity.colour = 18;
}
entityclonefix(&entity);
break;
}
entity.lerpoldxp = entity.xp;
entity.lerpoldyp = entity.yp;
entity.drawframe = entity.tile;
if (!reuse)
{
entities.push_back(entity);
}
/* 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);
}
}
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1, int p2)
{
createentity(xp, yp, t, meta1, meta2, p1, p2, 320, 240);
}
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1)
{
createentity(xp, yp, t, meta1, meta2, p1, 0);
}
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2)
{
createentity(xp, yp, t, meta1, meta2, 0);
}
void entityclass::createentity(int xp, int yp, int t, int meta1)
{
createentity(xp, yp, t, meta1, 0);
}
void entityclass::createentity(int xp, int yp, int t)
{
createentity(xp, yp, t, 0);
}
//Returns true if entity is removed
bool entityclass::updateentities( int i )
{
if (!INBOUNDS_VEC(i, entities))
{
vlog_error("updateentities() out-of-bounds!");
return true;
}
if(entities[i].statedelay<=0)
{
switch(entities[i].type)
{
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
{
entities[i].state = 3;
bool entitygone = updateentities(i);
if (entitygone) return true;
}
else if (entities[i].state == 1)
{
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;
}
break;
case 1: //Bounce, Start moving up
if (entities[i].state == 0) //Init
{
entities[i].state = 2;
bool entitygone = updateentities(i);
if (entitygone) return true;
}
else if (entities[i].state == 1)
{
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;
}
break;
case 2: //Bounce, Start moving left
if (entities[i].state == 0) //Init
{
entities[i].state = 3;
bool entitygone = updateentities(i);
if (entitygone) return true;
}
else if (entities[i].state == 1)
{
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;
}
break;
case 3: //Bounce, Start moving right
if (entities[i].state == 0) //Init
{
entities[i].state = 3;
bool entitygone = updateentities(i);
if (entitygone) return true;
}
else if (entities[i].state == 1)
{
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;
}
break;
case 4: //Always move left
if (entities[i].state == 0) //Init
{
entities[i].vx = entities[i].para;
}
break;
case 5: //Always move right
if (entities[i].state == 0)
{
//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;
}
break;
case 6: //Always move up
if (entities[i].state == 0) //Init
{
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;
}
break;
case 7: //Always move down
if (entities[i].state == 0) //Init
{
entities[i].vx = static_cast<int>(entities[i].para);
}
break;
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)
{
entities[i].state = 0;
}
break;
case 11: //Always move right, destroy when outside screen
if (entities[i].state == 0) //Init
{
entities[i].vx = entities[i].para;
entities[i].state = 1;
}
else if (entities[i].state == 1)
{
if (entities[i].xp >= 335)
{
return disableentity(i);
}
if (game.roomx == 117)
{
if (entities[i].xp >= (33*8)-32)
{
return disableentity(i);
}
//collector for LIES
}
}
break;
case 12:
//Emitter: shoot an enemy every so often (up)
if (entities[i].state == 0)
{
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)
{
if (entities[i].yp <= -60)
{
return disableentity(i);
}
if (game.roomx == 113 && game.roomy == 108)
{
if (entities[i].yp <= 60)
{
return disableentity(i);
}
//collector for factory
}
}
break;
case 14: //Very special hack: as two, but doesn't move in specific circumstances
if (entities[i].state == 0) //Init
{
for (size_t j = 0; j < entities.size(); j++)
{
if (entities[j].type == 2 && entities[j].state== 3 && entities[j].xp == (entities[i].xp-32) )
{
entities[i].state = 3;
bool entitygone = updateentities(i);
if (entitygone) return true;
}
}
}
else if (entities[i].state == 1)
{
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;
}
break;
case 15: //As above, but for 3!
if (entities[i].state == 0) //Init
{
for (size_t j = 0; j < entities.size(); j++)
{
if (entities[j].type == 2 && entities[j].state==3 && entities[j].xp==entities[i].xp+32)
{
entities[i].state = 3;
bool entitygone = updateentities(i);
if (entitygone) return true;
}
}
}
else if (entities[i].state == 1)
{
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;
}
break;
case 16: //MAVERICK BUS FOLLOWS HIS OWN RULES
if (entities[i].state == 0) //Init
{
int player = getplayer();
//first, y position
if (INBOUNDS_VEC(player, entities) && entities[player].yp > 14 * 8)
{
entities[i].tile = 120;
entities[i].yp = (28*8)-62;
entities[i].lerpoldyp = (28*8)-62;
}
else
{
entities[i].tile = 96;
entities[i].yp = 24;
entities[i].lerpoldyp = 24;
}
//now, x position
if (INBOUNDS_VEC(player, entities) && entities[player].xp > 20 * 8)
{
//approach from the left
entities[i].xp = -64;
entities[i].lerpoldxp = -64;
entities[i].state = 2;
bool entitygone = updateentities(i); //right
if (entitygone) return true;
}
else
{
//approach from the left
entities[i].xp = 320;
entities[i].lerpoldxp = 320;
entities[i].state = 3;
bool entitygone = updateentities(i); //left
if (entitygone) return true;
}
}
else if (entities[i].state == 1)
{
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);
entities[i].lerpoldxp -= int(entities[i].para);
}
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);
entities[i].lerpoldxp += int(entities[i].para);
}
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;
music.playef(7);
}
else if (entities[i].state == 2)
{
entities[i].life--;
if (entities[i].life % 3 == 0) entities[i].walkingframe++;
if (entities[i].life <= 0)
{
disableblockat(entities[i].xp, entities[i].yp);
entities[i].state = 3;// = false;
entities[i].invis = true;
}
}
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;
entities[i].walkingframe--;
entities[i].state++;
entities[i].onentity = 1;
}
else if (entities[i].state == 5)
{
entities[i].life+=3;
if (entities[i].life % 3 == 0) entities[i].walkingframe--;
if (entities[i].life >= 12)
{
entities[i].life = 12;
entities[i].state = 0;
entities[i].walkingframe++;
}
}
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)
{
disableblockat(entities[i].xp, entities[i].yp);
return disableentity(i);
}
}
break;
case 4: //Gravity token
//wait for collision
if (entities[i].state == 1)
{
game.gravitycontrol = (game.gravitycontrol + 1) % 2;
++game.totalflips;
return disableentity(i);
}
break;
case 5: //Particle sprays
if (entities[i].state == 0)
{
entities[i].life--;
if (entities[i].life < 0)
{
return disableentity(i);
}
}
break;
case 6: //Small pickup
//wait for collision
if (entities[i].state == 1)
{
music.playef(4);
if (INBOUNDS_ARR(entities[i].para, collect))
{
collect[(int) entities[i].para] = true;
}
return disableentity(i);
}
break;
case 7: //Found a trinket
//wait for collision
if (entities[i].state == 1)
{
if (INBOUNDS_ARR(entities[i].para, collect))
{
collect[(int) entities[i].para] = true;
}
if (game.intimetrial)
{
music.playef(25);
}
else
{
game.state = 1000;
if(music.currentsong!=-1) music.silencedasmusik();
music.playef(3);
if (game.trinkets() > game.stat_trinkets && !map.custommode)
{
game.stat_trinkets = game.trinkets();
game.savestatsandsettings();
}
}
return disableentity(i);
}
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)
{
entities[j].colour = 4;
entities[j].onentity = 1;
}
}
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)
{
game.savey = entities[i].yp - 2;
game.savegc = 1;
}
else if (entities[i].tile == 21)
{
game.savey = entities[i].yp - 7;
game.savegc = 0;
}
game.saverx = game.roomx;
game.savery = game.roomy;
int player = getplayer();
if (INBOUNDS_VEC(player, entities))
{
game.savedir = entities[player].dir;
}
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;
}
}
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++;
int temp = getplayer();
if (game.gravitycontrol == 0)
{
if (INBOUNDS_VEC(temp, entities) && entities[temp].vy < 3) entities[temp].vy = 3;
}
else
{
if (INBOUNDS_VEC(temp, entities) && entities[temp].vy > -3) entities[temp].vy = -3;
}
}
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!
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;
//Stay close to the hero!
int j = getplayer();
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
{
entities[i].dir = 0;
}
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 45)
{
entities[i].ax = 3;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 45)
{
entities[i].ax = -3;
}
//Special rules:
if (game.roomx == 110 && game.roomy == 105 && !map.custommode)
{
if (entities[i].xp < 155)
{
if (entities[i].ax < 0) entities[i].ax = 0;
}
}
}
else if (entities[i].state == 2)
{
//Basic rules, don't change expression
int j = getplayer();
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
{
entities[i].dir = 0;
}
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 45)
{
entities[i].ax = 3;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 45)
{
entities[i].ax = -3;
}
}
else if (entities[i].state == 10)
{
//Everything from 10 on is for cutscenes
//Basic rules, don't change expression
int j = getplayer();
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
{
entities[i].dir = 0;
}
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 45)
{
entities[i].ax = 3;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 45)
{
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)
int j=getcrewman(PURPLE);
if (INBOUNDS_VEC(j, entities))
{
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;
}
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;
}
}
}
else if (entities[i].state == 12)
{
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
int j=getcrewman(YELLOW);
if (INBOUNDS_VEC(j, entities))
{
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;
}
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;
}
}
}
else if (entities[i].state == 13)
{
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
int j=getcrewman(RED);
if (INBOUNDS_VEC(j, entities))
{
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;
}
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;
}
}
}
else if (entities[i].state == 14)
{
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
int j=getcrewman(GREEN);
if (INBOUNDS_VEC(j, entities))
{
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;
}
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;
}
}
}
else if (entities[i].state == 15)
{
//11-15 means to follow a specific character, in crew order (cyan, purple, yellow, red, green, blue)
int j=getcrewman(BLUE);
if (INBOUNDS_VEC(j, entities))
{
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;
}
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;
}
}
}
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();
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
{
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;
}
else
{
entities[i].para--;
}
}
else if (entities[i].state == 20)
{
//Panic! For briefing script
if (entities[i].life == 0)
{
//walk left for a bit
entities[i].ax = 0;
if (40 > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (40 < entities[i].xp - 5)
{
entities[i].dir = 0;
}
if (40 > entities[i].xp + 45)
{
entities[i].ax = 3;
}
else if (40 < entities[i].xp - 45)
{
entities[i].ax = -3;
}
if ( (entities[i].ax) == 0)
{
entities[i].life = 1;
entities[i].para = 30;
}
}
else if (entities[i].life == 1)
{
//Stand around for a bit
entities[i].para--;
if (entities[i].para <= 0)
{
entities[i].life++;
}
}
else if (entities[i].life == 2)
{
//walk right for a bit
entities[i].ax = 0;
if (280 > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (280 < entities[i].xp - 5)
{
entities[i].dir = 0;
}
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;
}
}
else if (entities[i].life == 3)
{
//Stand around for a bit
entities[i].para--;
if (entities[i].para <= 0)
{
entities[i].life=0;
}
}
}
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();
if(INBOUNDS_VEC(j, entities) && entities[j].onground>0)
{
if (entities[j].xp > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (entities[j].xp>15 && entities[j].xp < entities[i].xp - 5)
{
entities[i].dir = 0;
}
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;
}
}
else
{
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
{
entities[i].dir = 0;
}
entities[i].ax = 0;
}
if (entities[i].xp > 240)
{
entities[i].ax = 3;
entities[i].dir = 1;
}
if (entities[i].xp >= 310)
{
game.scmprogress++;
return disableentity(i);
}
}
break;
case 15: //Trophy
//wait for collision
if (entities[i].state == 1)
{
if (!script.running) trophytext+=2;
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;
if (entities[i].xp > 320)
{
return disableentity(i);
}
}
break;
case 1:
if (entities[i].state == 0) //Init
{
entities[i].vx = -7;
if (entities[i].xp <-20)
{
return disableentity(i);
}
}
break;
}
break;
case 51: //Vertical warp line
if (entities[i].state == 2){
int j=getplayer();
if(INBOUNDS_VEC(j, entities) && entities[j].xp<=307){
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();
if(INBOUNDS_VEC(j, entities) && entities[j].xp<=307){
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();
if (INBOUNDS_VEC(j, entities) && entities[j].xp > entities[i].xp + 5)
{
entities[i].dir = 1;
}
else if (INBOUNDS_VEC(j, entities) && entities[j].xp < entities[i].xp - 5)
{
entities[i].dir = 0;
}
}
else if (entities[i].state == 1)
{
if (INBOUNDS_ARR(entities[i].para, customcollect))
{
customcollect[(int) entities[i].para] = true;
}
if (game.intimetrial)
{
music.playef(27);
}
else
{
game.state = 1010;
//music.haltdasmusik();
if(music.currentsong!=-1) music.silencedasmusik();
music.playef(27);
}
return disableentity(i);
}
break;
case 100: //The teleporter
if (entities[i].state == 1)
{
//if inactive, activate!
if (entities[i].tile == 1)
{
music.playef(18);
entities[i].tile = 2;
entities[i].colour = 101;
if(!game.intimetrial && !game.nodeathmode)
{
game.state = 2000;
game.statedelay = 0;
}
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;
//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;
int player = getplayer();
if (INBOUNDS_VEC(player, entities))
{
game.savedir = entities[player].dir;
}
}
entities[i].onentity = 0;
entities[i].state = 0;
}
else if (entities[i].state == 2)
{
//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;
}
break;
}
}
else
{
entities[i].statedelay--;
if (entities[i].statedelay < 0)
{
entities[i].statedelay = 0;
}
}
return false;
}
void entityclass::animateentities( int _i )
{
if (!INBOUNDS_VEC(_i, entities))
{
vlog_error("animateentities() out-of-bounds!");
return;
}
if(entities[_i].statedelay < 1)
{
switch(entities[_i].type)
{
case 0:
entities[_i].framedelay--;
if(entities[_i].dir==1)
{
entities[_i].drawframe=entities[_i].tile;
}
else
{
entities[_i].drawframe=entities[_i].tile+3;
}
if(entities[_i].visualonground>0 || entities[_i].visualonroof>0)
{
if(entities[_i].vx > 0.00f || entities[_i].vx < -0.00f)
{
//Walking
if(entities[_i].framedelay<=1)
{
entities[_i].framedelay=4;
entities[_i].walkingframe++;
}
if (entities[_i].walkingframe >=2) entities[_i].walkingframe=0;
entities[_i].drawframe += entities[_i].walkingframe + 1;
}
if (entities[_i].visualonroof > 0) entities[_i].drawframe += 6;
// Stuck in a wall? Then default to gravitycontrol
if (entities[_i].visualonground > 0 && entities[_i].visualonroof > 0
&& game.gravitycontrol == 0)
{
entities[_i].drawframe -= 6;
}
}
else
{
entities[_i].drawframe ++;
if (game.gravitycontrol == 1)
{
entities[_i].drawframe += 6;
}
}
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)
{
entities[_i].walkingframe++;
if (entities[_i].walkingframe == 4)
{
entities[_i].walkingframe = 2;
entities[_i].actionframe = 1;
}
}
else
{
entities[_i].walkingframe--;
if (entities[_i].walkingframe == -1)
{
entities[_i].walkingframe = 1;
entities[_i].actionframe = 0;
}
}
}
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)
{
entities[_i].walkingframe = 0;
}
}
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)
{
entities[_i].walkingframe = 0;
}
}
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)
{
entities[_i].walkingframe = 0;
}
}
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)
{
entities[_i].walkingframe = 0;
}
}
entities[_i].drawframe = entities[_i].tile;
entities[_i].drawframe += (entities[_i].walkingframe*2);
break;
case 5:
//Simpler Loop (just two frames) (slower)
entities[_i].framedelay--;
if(entities[_i].framedelay<=0)
{
entities[_i].framedelay = 6;
entities[_i].walkingframe++;
if (entities[_i].walkingframe == 2)
{
entities[_i].walkingframe = 0;
}
}
entities[_i].drawframe = entities[_i].tile;
entities[_i].drawframe += entities[_i].walkingframe;
break;
case 6:
//Normal Loop (four frames, double sized)
entities[_i].framedelay--;
if(entities[_i].framedelay<=0)
{
entities[_i].framedelay = 4;
entities[_i].walkingframe++;
if (entities[_i].walkingframe == 4)
{
entities[_i].walkingframe = 0;
}
}
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)
{
entities[_i].framedelay = 6;
entities[_i].walkingframe++;
if (entities[_i].walkingframe == 2)
{
entities[_i].walkingframe = 0;
}
}
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)
{
entities[_i].framedelay = 3;//(6-entities[_i].para);
entities[_i].walkingframe--;
if (entities[_i].walkingframe == -1)
{
entities[_i].walkingframe = 3;
}
}
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)
{
entities[_i].framedelay = 3;//(6-entities[_i].para);
entities[_i].walkingframe++;
if (entities[_i].walkingframe == 4)
{
entities[_i].walkingframe = 0;
}
}
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;
break;
}
break;
case 2: //Disappearing platforms
entities[_i].drawframe = entities[_i].tile + entities[_i].walkingframe;
break;
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)
{
entities[_i].framedelay = 10;
entities[_i].walkingframe++;
if (entities[_i].walkingframe == 2)
{
entities[_i].walkingframe = 0;
}
}
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;
}
if(entities[_i].visualonground>0 || entities[_i].visualonroof>0)
{
if(entities[_i].vx > 0.0000f || entities[_i].vx < -0.000f)
{
//Walking
if(entities[_i].framedelay<=0)
{
entities[_i].framedelay=4;
entities[_i].walkingframe++;
}
if (entities[_i].walkingframe >=2) entities[_i].walkingframe=0;
entities[_i].drawframe += entities[_i].walkingframe + 1;
}
//if (entities[_i].visualonroof > 0) entities[_i].drawframe += 6;
}
else
{
entities[_i].drawframe ++;
//if (game.gravitycontrol == 1) {
// entities[_i].drawframe += 6;
//}
}
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)
{
entities[_i].walkingframe = -1;
entities[_i].framedelay = 4;
}
}
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;
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;
}
}
entities[_i].drawframe = entities[_i].tile;
entities[_i].drawframe += entities[_i].walkingframe;
}
break;
default:
entities[_i].drawframe = entities[_i].tile;
break;
}
}
else
{
//entities[_i].statedelay--;
if (entities[_i].statedelay < 0) entities[_i].statedelay = 0;
}
}
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))
{
vlog_error("animatehumanoidcollision() out-of-bounds!");
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;
}
int entityclass::getcompanion(void)
{
//Returns the index of the companion with rule t
for (size_t i = 0; i < entities.size(); i++)
{
if(entities[i].rule==6 || entities[i].rule==7)
{
return i;
}
}
return -1;
}
int entityclass::getplayer(void)
{
//Returns the index of the first player entity
for (size_t i = 0; i < entities.size(); i++)
{
if(entities[i].type==0)
{
return i;
}
}
return -1;
}
int entityclass::getscm(void)
{
//Returns the supercrewmate
for (size_t i = 0; i < entities.size(); i++)
{
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)
for (size_t i = 0; i < entities.size(); i++)
{
if (entities[i].size == 5)
{
if (entities[i].yp == t)
{
return i;
}
}
}
return 0;
}
int entityclass::getcrewman( int t, int fallback /*= 0*/ )
{
//Returns the index of the crewman with colour index given by t
for (size_t i = 0; i < entities.size(); i++)
{
if ((entities[i].type == 12 || entities[i].type == 14)
&& (entities[i].rule == 6 || entities[i].rule == 7))
{
if(entities[i].colour==t)
{
return i;
}
}
}
return fallback;
}
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;
for (size_t i = 0; i < entities.size(); i++)
{
if (entities[i].type == 55)
{
if(entities[i].colour==t)
{
return i;
}
}
}
return 0;
}
int entityclass::getteleporter(void)
{
for (size_t i = 0; i < entities.size(); i++)
{
if(entities[i].type==100)
{
return i;
}
}
return -1;
}
bool entityclass::entitycollide( int a, int b )
{
if (!INBOUNDS_VEC(a, entities) || !INBOUNDS_VEC(b, entities))
{
vlog_error("entitycollide() out-of-bounds!");
return false;
}
//Do entities a and b collide?
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;
if (help.intersects(temprect, temprect2)) return true;
return false;
}
bool entityclass::checkdamage(bool scm /*= false*/)
{
//Returns true if player (or supercrewmate) collides with a damagepoint
for(size_t i=0; i < entities.size(); i++)
{
if((scm && entities[i].type == 14) || (!scm && entities[i].rule == 0))
{
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;
for (size_t j=0; j<blocks.size(); j++)
{
if (blocks[j].type == DAMAGE && help.intersects(blocks[j].rect, temprect))
{
return true;
}
}
}
}
return false;
}
int entityclass::checktrigger(int* block_idx)
{
//Returns an int player entity (rule 0) collides with a trigger
//Also returns the index of the block
*block_idx = -1;
for(size_t i=0; i < entities.size(); i++)
{
if(entities[i].rule==0)
{
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;
for (size_t j=0; j<blocks.size(); j++)
{
if (blocks[j].type == TRIGGER && help.intersects(blocks[j].rect, temprect))
{
*block_idx = j;
return blocks[j].trigger;
}
}
}
}
return -1;
}
int entityclass::checkactivity(void)
{
//Returns an int player entity (rule 0) collides with an activity
for(size_t i=0; i < entities.size(); i++)
{
if(entities[i].rule==0)
{
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;
for (size_t j=0; j<blocks.size(); j++)
{
if (blocks[j].type == ACTIVITY && help.intersects(blocks[j].rect, temprect))
{
return j;
}
}
}
}
return -1;
}
int entityclass::getgridpoint( int t )
{
t = (t - (t % 8)) / 8;
return t;
}
bool entityclass::checkplatform(const SDL_Rect& temprect, int* px, int* py)
{
//Return true if rectset intersects a moving platform, setups px & py to the platform x & y
for (size_t i = 0; i < blocks.size(); i++)
{
if (blocks[i].type == BLOCK && help.intersects(blocks[i].rect, temprect))
{
*px = blocks[i].xp;
*py = blocks[i].yp;
return true;
}
}
return false;
}
bool entityclass::checkblocks(const SDL_Rect& temprect, const float dx, const float dy, const float dr, const bool skipdirblocks)
{
for (size_t i = 0; i < blocks.size(); i++)
{
if(!skipdirblocks && blocks[i].type == DIRECTIONAL)
{
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;
}
}
return false;
}
bool entityclass::checkwall(const SDL_Rect& temprect, const float dx, const float dy, const float dr, const bool skipblocks, const bool skipdirblocks)
{
//Returns true if entity setup in temprect collides with a wall
if(skipblocks)
{
if (checkblocks(temprect, dx, dy, dr, skipdirblocks)) return true;
}
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);
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)
{
int tpy1 = getgridpoint(temprect.y + 6);
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)
{
int tpx1 = getgridpoint(temprect.x + 6);
if (map.collide(tpx1, tempy)) return true;
if (map.collide(tpx1, temph)) return true;
}
return false;
}
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)
{
//Returns first entity of horizontal platform at (px, py), -1000 otherwise.
for (size_t i = 0; i < entities.size(); i++)
{
if (entities[i].rule == 2 && entities[i].behave >= 2
&& entities[i].xp == px && entities[i].yp == py)
{
if (entities[i].behave == 8) //threadmill!
{
return entities[i].para;
}
else if(entities[i].behave == 9) //threadmill!
{
return -entities[i].para;
}
else
{
return entities[i].vx;
}
}
}
return -1000;
}
int entityclass::yline( int a, int b )
{
if (a < b) return -1;
return 1;
}
bool entityclass::entityhlinecollide( int t, int l )
{
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities))
{
vlog_error("entityhlinecollide() out-of-bounds!");
return false;
}
//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)
{
int linetemp = 0;
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 )
{
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities))
{
vlog_error("entityvlinecollide() out-of-bounds!");
return false;
}
//Returns true is entity t collided with the vertical line l.
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)
{
int linetemp = 0;
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);
if (linetemp > -4 && linetemp < 4) return true;
return false;
}
return false;
}
bool entityclass::entitywarphlinecollide(int t, int l) {
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities))
{
vlog_error("entitywarphlinecollide() out-of-bounds!");
return false;
}
//Returns true is entity t collided with the horizontal line l.
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){
int linetemp = 0;
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++;
}
if (linetemp > 0) return true;
return false;
}
}
return false;
}
bool entityclass::entitywarpvlinecollide(int t, int l) {
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities))
{
vlog_error("entitywarpvlinecollide() out-of-bounds!");
return false;
}
//Returns true is entity t collided with the vertical warp line l.
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) {
int linetemp = 0;
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;
}
}
return false;
}
float entityclass::entitycollideplatformroof( int t )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("entitycollideplatformroof() out-of-bounds!");
return -1000;
}
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;
int px = 0, py = 0;
if (checkplatform(temprect, &px, &py))
{
//px and py now contain an x y coordinate for a platform, find it
return hplatformat(px, py);
}
return -1000;
}
float entityclass::entitycollideplatformfloor( int t )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("entitycollideplatformfloor() out-of-bounds!");
return -1000;
}
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;
int px = 0, py = 0;
if (checkplatform(temprect, &px, &py))
{
//px and py now contain an x y coordinate for a platform, find it
return hplatformat(px, py);
}
return -1000;
}
bool entityclass::entitycollidefloor( int t )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("entitycollidefloor() out-of-bounds!");
return false;
}
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;
if (checkwall(temprect)) return true;
return false;
}
bool entityclass::entitycollideroof( int t )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("entitycollideroof() out-of-bounds!");
return false;
}
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;
if (checkwall(temprect)) return true;
return false;
}
bool entityclass::testwallsx( int t, int tx, int ty, const bool skipdirblocks )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("testwallsx() out-of-bounds!");
return false;
}
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;
bool skipblocks = entities[t].rule < 2 || entities[t].type == 14;
float dx = 0;
float dy = 0;
if (entities[t].rule == 0) dx = entities[t].vx;
float dr = entities[t].rule;
//Ok, now we check walls
if (checkwall(temprect, dx, dy, dr, skipblocks, skipdirblocks))
{
if (entities[t].vx > 1.0f)
{
entities[t].vx--;
entities[t].newxp = entities[t].xp + entities[t].vx;
return testwallsx(t, entities[t].newxp, entities[t].yp, skipdirblocks);
}
else if (entities[t].vx < -1.0f)
{
entities[t].vx++;
entities[t].newxp = entities[t].xp + entities[t].vx;
return testwallsx(t, entities[t].newxp, entities[t].yp, skipdirblocks);
}
else
{
entities[t].vx=0;
return false;
}
}
return true;
}
bool entityclass::testwallsy( int t, float tx, float ty )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("testwallsy() out-of-bounds!");
return false;
}
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;
bool skipblocks = entities[t].rule < 2 || entities[t].type == 14;
float dx = 0;
float dy = 0;
if (entities[t].rule == 0) dy = entities[t].vy;
float dr = entities[t].rule;
//Ok, now we check walls
if (checkwall(temprect, dx, dy, dr, skipblocks, false))
{
if (entities[t].vy > 1)
{
entities[t].vy--;
entities[t].newyp = int(entities[t].yp + entities[t].vy);
return testwallsy(t, entities[t].xp, entities[t].newyp);
}
else if (entities[t].vy < -1)
{
entities[t].vy++;
entities[t].newyp = int(entities[t].yp + entities[t].vy);
return testwallsy(t, entities[t].xp, entities[t].newyp);
}
else
{
entities[t].vy=0;
return false;
}
}
return true;
}
void entityclass::applyfriction( int t, float xrate, float yrate )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("applyfriction() out-of-bounds!");
return;
}
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;
if (SDL_fabsf(entities[t].vx) < xrate) entities[t].vx = 0.0f;
if (SDL_fabsf(entities[t].vy) < yrate) entities[t].vy = 0.0f;
}
void entityclass::updateentitylogic( int t )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("updateentitylogic() out-of-bounds!");
return;
}
entities[t].oldxp = entities[t].xp;
entities[t].oldyp = entities[t].yp;
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;
}
void entityclass::entitymapcollision( int t )
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("entitymapcollision() out-of-bounds!");
return;
}
if (testwallsx(t, entities[t].newxp, entities[t].yp, false))
{
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;
}
if (testwallsy(t, entities[t].xp, entities[t].newyp))
{
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;
}
}
void entityclass::movingplatformfix( int t, int j )
{
if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(j, entities))
{
vlog_error("movingplatformfix() out-of-bounds!");
return;
}
//If this intersects the entity, then we move them along it
if (entitycollide(t, j))
{
//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);
if (testwallsy(j, entities[j].xp, entities[j].newyp))
{
if (entities[t].vy > 0)
{
entities[j].yp = entities[t].yp + entities[t].h;
entities[j].vy = 0;
entities[j].onroof = 2;
entities[j].visualonroof = 1;
}
else
{
entities[j].yp = entities[t].yp - entities[j].h-entities[j].cy;
entities[j].vy = 0;
entities[j].onground = 2;
entities[j].visualonground = 1;
}
}
else
{
entities[t].state = entities[t].onwall;
}
}
}
}
void entityclass::customwarplinecheck(int i) {
if (!INBOUNDS_VEC(i, entities))
{
vlog_error("customwarplinecheck() out-of-bounds!");
return;
}
//Turns on obj.customwarpmodevon and obj.customwarpmodehon if player collides
//with warp lines
//We test entity to entity
for (int j = 0; j < (int) entities.size(); j++) {
if (i != j) {
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;
}
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;
}
}
}
}
void entityclass::entitycollisioncheck(void)
{
for (size_t i = 0; i < entities.size(); i++)
{
bool player = entities[i].rule == 0;
bool scm = game.supercrewmate && entities[i].type == 14;
if (!player && !scm)
{
continue;
}
//We test entity to entity
for (size_t j = 0; j < entities.size(); j++)
{
if (i == j)
{
continue;
}
collisioncheck(i, j, scm);
}
}
//can't have the player being stuck...
stuckprevention(getplayer());
//Can't have the supercrewmate getting stuck either!
if (game.supercrewmate)
{
stuckprevention(getscm());
}
//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)
{
if (checkdamage(true) && !map.invincibility)
{
//usual player dead stuff
game.scmhurt = true;
game.deathseq = 30;
}
}
// WARNING: If updating this code, don't forget to update Map.cpp mapclass::twoframedelayfix()
int block_idx = -1;
int activetrigger = checktrigger(&block_idx);
if (activetrigger > -1 && INBOUNDS_VEC(block_idx, blocks))
{
// Load the block's script if its gamestate is out of range
if (blocks[block_idx].script != "" && (activetrigger < 300 || activetrigger > 336))
{
game.startscript = true;
game.newscript = blocks[block_idx].script;
removetrigger(activetrigger);
game.state = 0;
}
else
{
game.state = activetrigger;
}
game.statedelay = 0;
}
}
void entityclass::collisioncheck(int i, int j, bool scm /*= false*/)
{
if (!INBOUNDS_VEC(i, entities) || !INBOUNDS_VEC(j, entities))
{
vlog_error("collisioncheck() out-of-bounds!");
return;
}
switch (entities[j].rule)
{
case 1:
if (!entities[j].harmful)
{
break;
}
//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
point colpoint1;
colpoint1.x = entities[i].xp;
colpoint1.y = entities[i].yp;
point colpoint2;
colpoint2.x = entities[j].xp;
colpoint2.y = entities[j].yp;
int drawframe1 = entities[i].collisiondrawframe;
int drawframe2 = entities[j].drawframe;
std::vector<SDL_Surface*>& spritesvec = graphics.flipmode ? graphics.flipsprites : graphics.sprites;
if (INBOUNDS_VEC(drawframe1, spritesvec) && INBOUNDS_VEC(drawframe2, spritesvec)
&& 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;
}
}
break;
case 2: //Moving platforms
if (entities[j].behave >= 8 && entities[j].behave < 10)
{
//We don't want conveyors, moving platforms only
break;
}
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
disableblockat(entities[j].xp, entities[j].yp);
}
break;
case 3: //Entity to entity
if(entities[j].onentity>0)
{
if (entitycollide(i, j)) entities[j].state = entities[j].onentity;
}
break;
case 4: //Person vs horizontal line!
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;
}
}
}
break;
case 5: //Person vs vertical gravity/warp line!
if(game.deathseq==-1)
{
if(entities[j].onentity>0)
{
if (entityvlinecollide(i, j))
{
entities[j].state = entities[j].onentity;
entities[j].life = 4;
}
}
}
break;
case 6: //Person versus crumbly blocks! Special case
if (entities[j].onentity > 0)
{
//ok; only check the actual collision if they're in a close proximity
int temp = entities[i].yp - entities[j].yp;
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;
}
}
}
break;
case 7: // Person versus horizontal warp line, pre-2.1
if (GlitchrunnerMode_less_than_or_equal(Glitchrunner2_0)
&& game.deathseq == -1
&& entities[j].onentity > 0
&& entityhlinecollide(i, j))
{
entities[j].state = entities[j].onentity;
}
break;
}
}
void entityclass::stuckprevention(int t)
{
if (!INBOUNDS_VEC(t, entities))
{
vlog_error("stuckprevention() out-of-bounds!");
return;
}
// Can't have this entity (player or supercrewmate) being stuck...
if (!testwallsx(t, entities[t].xp, entities[t].yp, true))
{
// Let's try to get out...
if (game.gravitycontrol == 0)
{
entities[t].yp -= 3;
}
else
{
entities[t].yp += 3;
}
}
}