2020-09-28 04:15:06 +02:00
|
|
|
#define MAP_DEFINITION
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "Map.h"
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
#include "Alloc.h"
|
2021-09-25 01:37:27 +02:00
|
|
|
#include "Constants.h"
|
2021-02-20 08:19:09 +01:00
|
|
|
#include "CustomLevels.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "Entity.h"
|
|
|
|
#include "Game.h"
|
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
|
|
|
#include "GlitchrunnerMode.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "Graphics.h"
|
2022-12-31 02:16:24 +01:00
|
|
|
#include "Localization.h"
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "MakeAndPlay.h"
|
2023-01-29 08:32:14 +01:00
|
|
|
#include "Maths.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "Music.h"
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "Script.h"
|
2023-02-04 09:14:04 +01:00
|
|
|
#include "Unused.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "UtilityClass.h"
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
mapclass::mapclass(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
//Start here!
|
|
|
|
colstatedelay = 0;
|
|
|
|
colsuperstate = 0;
|
|
|
|
spikeleveltop = 0;
|
|
|
|
spikelevelbottom = 0;
|
|
|
|
oldspikeleveltop = 0;
|
|
|
|
oldspikelevelbottom = 0;
|
|
|
|
warpx = false;
|
|
|
|
warpy = false;
|
|
|
|
extrarow = 0;
|
|
|
|
|
|
|
|
showteleporters = false;
|
|
|
|
showtargets = false;
|
|
|
|
showtrinkets = false;
|
|
|
|
|
|
|
|
finalmode = false;
|
|
|
|
finalstretch = false;
|
|
|
|
|
|
|
|
cursorstate = 0;
|
|
|
|
cursordelay = 0;
|
|
|
|
|
|
|
|
towermode = false;
|
|
|
|
cameraseekframe = 0;
|
|
|
|
resumedelay = 0;
|
|
|
|
|
|
|
|
final_colormode = false;
|
|
|
|
final_colorframe = 0;
|
|
|
|
final_colorframedelay = 0;
|
|
|
|
final_mapcol = 0;
|
|
|
|
final_aniframe = 0;
|
|
|
|
final_aniframedelay = 0;
|
|
|
|
|
|
|
|
custommode=false;
|
|
|
|
custommodeforreal=false;
|
|
|
|
customshowmm=true;
|
2023-08-23 22:03:23 +02:00
|
|
|
revealmap = true;
|
2021-09-07 03:56:39 +02:00
|
|
|
|
|
|
|
rcol = 0;
|
|
|
|
|
|
|
|
//This needs to be in map instead!
|
|
|
|
invincibility = false;
|
|
|
|
|
|
|
|
//We create a blank map
|
|
|
|
SDL_memset(contents, 0, sizeof(contents));
|
|
|
|
|
|
|
|
SDL_memset(roomdeaths, 0, sizeof(roomdeaths));
|
|
|
|
SDL_memset(roomdeathsfinal, 0, sizeof(roomdeathsfinal));
|
|
|
|
resetmap();
|
|
|
|
|
2023-02-18 00:52:57 +01:00
|
|
|
setroomname("");
|
|
|
|
hiddenname = "";
|
|
|
|
|
|
|
|
roomname_special = false;
|
|
|
|
specialroomnames.clear();
|
|
|
|
roomnameset = false;
|
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 0;
|
|
|
|
initmapdata();
|
|
|
|
|
|
|
|
ypos = 0;
|
|
|
|
oldypos = 0;
|
|
|
|
|
|
|
|
background = 0;
|
|
|
|
cameramode = 0;
|
|
|
|
cameraseek = 0;
|
|
|
|
minitowermode = false;
|
|
|
|
roomtexton = false;
|
|
|
|
|
|
|
|
nexttowercolour_set = false;
|
2024-11-04 00:20:33 +01:00
|
|
|
|
|
|
|
currentregion = 0;
|
|
|
|
SDL_zeroa(region);
|
2020-07-03 06:13:23 +02:00
|
|
|
}
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
static char roomname_static[SCREEN_WIDTH_CHARS];
|
|
|
|
static char* roomname_heap;
|
|
|
|
|
|
|
|
void mapclass::destroy(void)
|
|
|
|
{
|
|
|
|
VVV_free(roomname_heap);
|
|
|
|
}
|
|
|
|
|
2020-07-03 06:13:23 +02:00
|
|
|
//Areamap starts at 100,100 and extends 20x20
|
|
|
|
const int mapclass::areamap[] = {
|
2021-09-07 03:56:39 +02:00
|
|
|
1,2,2,2,2,2,2,2,0,3,0,0,0,4,4,4,4,4,4,4,
|
|
|
|
1,2,2,2,2,2,2,0,0,3,0,0,0,0,4,4,4,4,4,4,
|
|
|
|
0,1,0,0,2,0,0,0,0,3,0,0,0,0,4,4,4,4,4,4,
|
|
|
|
0,0,0,0,2,0,0,0,0,3,0,0,5,5,5,5,4,4,4,4,
|
|
|
|
0,0,2,2,2,0,0,0,0,3,11,11,5,5,5,5,0,0,0,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,5,5,5,5,5,5,0,0,0,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,5,5,5,5,5,5,5,0,0,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,5,5,5,5,5,5,5,5,5,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,0,0,0,5,5,5,5,5,5,0,
|
|
|
|
0,0,0,0,0,0,0,0,11,3,0,0,0,5,5,5,5,5,5,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,0,0,0,5,5,5,5,5,5,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,0,5,5,5,5,5,5,5,5,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,0,5,5,5,5,5,5,0,5,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,0,5,5,5,5,5,5,0,5,0,
|
|
|
|
0,0,0,0,0,0,0,0,0,3,0,5,5,0,0,0,0,0,5,0,
|
|
|
|
0,0,0,0,0,0,0,2,0,3,0,0,0,0,0,0,0,0,0,0,
|
|
|
|
0,0,2,2,2,2,2,2,0,3,0,0,0,0,0,0,0,0,0,0,
|
|
|
|
0,2,2,2,2,2,2,2,0,3,0,0,0,0,0,0,0,0,0,0,
|
|
|
|
2,2,2,2,2,0,0,2,0,3,0,0,0,0,0,0,0,0,0,0,
|
|
|
|
2,2,2,2,2,0,0,2,0,3,0,0,0,0,0,0,0,0,0,0,
|
2020-07-03 06:13:23 +02:00
|
|
|
};
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Unify all queries to map size to `map.getwidth` and `map.getheight`
It's becoming pretty clear that the size of the map is important enough
to be queried a lot, but each time it's something like `map.custommode ?
map.customwidth : 20` and `map.custommode ? map.customheight : 20` which
is not ideal because of copy-pasting.
Furthermore, even `map.customwidth` and `map.customheight` are just
duplicates of `cl.mapwidth` and `cl.mapheight`, which are only set in
`customlevelclass::generatecustomminimap`. This is a bit annoying if you
want to, say, add checks that depend on the width and height of the
custom map in `mapclass::initcustommapdata`, but `map.customwidth` and
`map.customheight` are out of date because `generatecustomminimap`
hasn't been called yet. And doing the ternary there requires a `#ifndef
NO_CUSTOM_LEVELS` to reference `cl.mapwidth` and `cl.mapheight` which is
just awful.
So I'm axing `map.customwidth` and `map.customheight`, and I'm axing all
the ternaries that are duplicating the source of truth in
`MapRenderData`. Instead, there will just be one function to call for
the width and height, `mapclass::getwidth` and `mapclass::getheight`,
and everyone can simply call those without needing to do ternaries or
duplication.
2022-11-30 22:35:14 +01:00
|
|
|
int mapclass::getwidth(void)
|
|
|
|
{
|
|
|
|
if (custommode)
|
|
|
|
{
|
|
|
|
return cl.mapwidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 20;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mapclass::getheight(void)
|
|
|
|
{
|
|
|
|
if (custommode)
|
|
|
|
{
|
|
|
|
return cl.mapheight;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 20;
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
int mapclass::intpol(int a, int b, float c)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
return static_cast<int>(a + ((b - a) * c));
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-15 04:29:19 +02:00
|
|
|
void mapclass::setteleporter(int x, int y)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2022-11-30 22:42:10 +01:00
|
|
|
if (x < 0 || x >= getwidth() || y < 0 || y >= getheight())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-01-29 08:32:14 +01:00
|
|
|
SDL_Point temp;
|
2021-09-07 03:56:39 +02:00
|
|
|
temp.x = x;
|
|
|
|
temp.y = y;
|
|
|
|
teleporters.push_back(temp);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-15 04:37:00 +02:00
|
|
|
void mapclass::settrinket(int x, int y)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2022-11-30 22:42:10 +01:00
|
|
|
if (x < 0 || x >= getwidth() || y < 0 || y >= getheight())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-01-29 08:32:14 +01:00
|
|
|
SDL_Point temp;
|
2021-09-07 03:56:39 +02:00
|
|
|
temp.x = x;
|
|
|
|
temp.y = y;
|
|
|
|
shinytrinkets.push_back(temp);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
void mapclass::setroomname(const char* name)
|
|
|
|
{
|
|
|
|
VVV_free(roomname_heap);
|
|
|
|
|
|
|
|
const size_t size = SDL_strlcpy(
|
|
|
|
roomname_static, name, sizeof(roomname_static)
|
|
|
|
) + 1;
|
|
|
|
roomname = roomname_static;
|
|
|
|
|
|
|
|
if (size > sizeof(roomname_static))
|
|
|
|
{
|
|
|
|
roomname_heap = SDL_strdup(name);
|
|
|
|
if (roomname_heap != NULL)
|
|
|
|
{
|
|
|
|
roomname = roomname_heap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::resetmap(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
//clear the explored area of the map
|
|
|
|
SDL_memset(explored, 0, sizeof(explored));
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2023-08-23 22:03:23 +02:00
|
|
|
void mapclass::fullmap(void)
|
|
|
|
{
|
|
|
|
//mark the whole map as explored
|
|
|
|
SDL_memset(explored, 1, sizeof(explored));
|
|
|
|
}
|
|
|
|
|
2022-12-12 00:05:20 +01:00
|
|
|
void mapclass::updateroomnames(void)
|
|
|
|
{
|
|
|
|
if (roomnameset)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-18 00:52:57 +01:00
|
|
|
const int rx = game.roomx;
|
|
|
|
const int ry = game.roomy;
|
2022-12-12 00:05:20 +01:00
|
|
|
|
|
|
|
for (int i = specialroomnames.size() - 1; i >= 0; i--)
|
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
Roomname* roomname = &specialroomnames[i];
|
|
|
|
if (rx == roomname->x && ry == roomname->y && (roomname->flag == -1 || (INBOUNDS_ARR(roomname->flag, obj.flags) && obj.flags[roomname->flag])))
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
|
|
|
roomname_special = true;
|
2023-02-18 00:52:57 +01:00
|
|
|
if (roomname->type == RoomnameType_STATIC)
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
setroomname(roomname->text[0].c_str());
|
2022-12-12 00:05:20 +01:00
|
|
|
}
|
2023-02-18 00:52:57 +01:00
|
|
|
if (roomname->type == RoomnameType_GLITCH)
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
roomname->delay--;
|
|
|
|
if (roomname->delay <= 0)
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
roomname->progress = (roomname->progress + 1) % 2;
|
|
|
|
roomname->delay = 5;
|
|
|
|
if (roomname->progress == 0)
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
roomname->delay = 25 + (int) (fRandom() * 10);
|
2022-12-12 00:05:20 +01:00
|
|
|
}
|
|
|
|
}
|
2023-02-18 00:52:57 +01:00
|
|
|
setroomname(roomname->text[roomname->progress].c_str());
|
2022-12-12 00:05:20 +01:00
|
|
|
}
|
2023-02-18 00:52:57 +01:00
|
|
|
if (roomname->type == RoomnameType_TRANSFORM)
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
roomname->delay--;
|
|
|
|
if (roomname->delay <= 0)
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
roomname->progress++;
|
|
|
|
roomname->delay = 2;
|
|
|
|
if ((size_t) roomname->progress >= roomname->text.size())
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
roomname->progress = roomname->loop ? 0 : roomname->text.size() - 1;
|
2022-12-12 00:05:20 +01:00
|
|
|
}
|
|
|
|
}
|
2023-02-18 00:52:57 +01:00
|
|
|
setroomname(roomname->text[roomname->progress].c_str());
|
2022-12-12 00:05:20 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::initmapdata(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
if (custommode)
|
|
|
|
{
|
|
|
|
initcustommapdata();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
teleporters.clear();
|
|
|
|
shinytrinkets.clear();
|
|
|
|
|
|
|
|
//Set up static map information like teleporters and shiny trinkets.
|
|
|
|
setteleporter(0, 0);
|
|
|
|
setteleporter(0, 16);
|
|
|
|
setteleporter(2, 4);
|
|
|
|
setteleporter(2, 11);
|
|
|
|
setteleporter(7, 9);
|
|
|
|
setteleporter(7, 15);
|
|
|
|
setteleporter(8, 11);
|
|
|
|
setteleporter(10, 5);
|
|
|
|
setteleporter(11, 4);
|
|
|
|
setteleporter(13, 2);
|
|
|
|
setteleporter(13, 8);
|
|
|
|
setteleporter(14, 19);
|
|
|
|
setteleporter(15, 0);
|
|
|
|
setteleporter(17, 12);
|
|
|
|
setteleporter(17, 17);
|
|
|
|
setteleporter(18, 1);
|
|
|
|
setteleporter(18, 7);
|
|
|
|
|
|
|
|
settrinket(14, 4);
|
|
|
|
settrinket(13, 6);
|
|
|
|
settrinket(11, 12);
|
|
|
|
settrinket(15, 12);
|
|
|
|
settrinket(14, 11);
|
|
|
|
settrinket(18, 14);
|
|
|
|
settrinket(11, 7);
|
|
|
|
settrinket(9, 2);
|
|
|
|
settrinket(9, 16);
|
|
|
|
settrinket(2, 18);
|
|
|
|
settrinket(7, 18);
|
|
|
|
settrinket(6, 1);
|
|
|
|
settrinket(17, 3);
|
|
|
|
settrinket(10, 19);
|
|
|
|
settrinket(5, 15);
|
|
|
|
settrinket(1, 10);
|
|
|
|
settrinket(3, 2);
|
|
|
|
settrinket(10, 8);
|
2023-02-18 00:52:57 +01:00
|
|
|
|
|
|
|
//Special room names
|
|
|
|
specialroomnames.clear();
|
|
|
|
|
|
|
|
{
|
|
|
|
static const char* lines[] = {
|
|
|
|
"Television Newsvel",
|
|
|
|
"TelevisvonvNewsvel",
|
|
|
|
"TvlvvvsvonvNevsvel",
|
|
|
|
"vvvvvvsvovvNe svel",
|
|
|
|
"vhv vvv'vvovv vevl",
|
|
|
|
"vhv V v'Cvovv vewv",
|
|
|
|
"vhe 9 v'Cvovv vewv",
|
|
|
|
"vhe 9 v'Cvovv Newv",
|
|
|
|
"The 9 O'Cvovk Newv",
|
|
|
|
"The 9 O'Clock News"
|
|
|
|
};
|
|
|
|
|
|
|
|
roomnamechange(45, 51, lines, SDL_arraysize(lines));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
static const char* lines[] = {
|
|
|
|
"Vwitvhed",
|
|
|
|
"vVwivcvedv",
|
|
|
|
"vvvwMvcvMdvv",
|
|
|
|
"DvvvwMvfvvMdvvv",
|
|
|
|
"Dvav Mvfvr Mdvvvv",
|
|
|
|
"Diav M for Mdrver",
|
|
|
|
"Dial M for Murder"
|
|
|
|
};
|
|
|
|
|
|
|
|
roomnamechange(46, 51, lines, SDL_arraysize(lines));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
static const char* lines[] = {
|
|
|
|
"Gvnsmove",
|
|
|
|
"Gvnvmovevv",
|
|
|
|
"Gunvmove1vv6",
|
|
|
|
"Vunsmoke 19v6",
|
|
|
|
"Gunsmoke 1966"
|
|
|
|
};
|
|
|
|
|
|
|
|
roomnamechange(47, 51, lines, SDL_arraysize(lines));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
static const char* lines[] = {
|
|
|
|
"Please envoy theve repeats",
|
|
|
|
"Plse envoy tse rvpvas",
|
|
|
|
"Plse envoy tse rvpvas",
|
|
|
|
"Vl envoy te rvevs",
|
|
|
|
"Vv evo tv vevs",
|
|
|
|
"Iv vhv Mvrvivs",
|
|
|
|
"In the Margins"
|
|
|
|
};
|
|
|
|
|
|
|
|
roomnamechange(50, 53, lines, SDL_arraysize(lines));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
static const char* lines[] = {
|
|
|
|
"Try Viggling the Antenna",
|
|
|
|
"TryJivglvng theAvtevna",
|
|
|
|
"Tvvivglvng thAvtvvv",
|
|
|
|
"Vvvgglvnv tvnvva",
|
|
|
|
"Vvavvnvs vvtv",
|
|
|
|
"Veavvn's Gvte",
|
|
|
|
"Heaven's Gate"
|
|
|
|
};
|
|
|
|
|
|
|
|
roomnamechange(50, 54, lines, SDL_arraysize(lines));
|
|
|
|
}
|
|
|
|
|
|
|
|
roomnameglitch(42, 51, "Rear Window", "Rear Vindow");
|
|
|
|
roomnameglitch(48, 51, "On the Waterfront", "On the Vaterfront");
|
|
|
|
roomnameglitch(49, 51, "The Untouchables", "The Untouchavles");
|
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::roomnameglitch(int x, int y, const char* name, const char* text)
|
|
|
|
{
|
|
|
|
Roomname roomname;
|
|
|
|
roomname.x = x;
|
|
|
|
roomname.y = y;
|
|
|
|
roomname.type = RoomnameType_GLITCH;
|
|
|
|
roomname.flag = -1;
|
|
|
|
roomname.loop = false;
|
|
|
|
roomname.progress = 1;
|
|
|
|
roomname.delay = -1;
|
|
|
|
|
|
|
|
roomname.text.push_back(name);
|
|
|
|
roomname.text.push_back(text);
|
|
|
|
|
|
|
|
specialroomnames.push_back(roomname);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::roomnamechange(const int x, const int y, const char** lines, const size_t size)
|
|
|
|
{
|
|
|
|
Roomname roomname;
|
|
|
|
roomname.x = x;
|
|
|
|
roomname.y = y;
|
|
|
|
roomname.type = RoomnameType_TRANSFORM;
|
|
|
|
roomname.flag = 72; // Flag 72 is synced with finalstretch
|
|
|
|
roomname.loop = false;
|
|
|
|
roomname.progress = 0;
|
|
|
|
roomname.delay = 2;
|
|
|
|
|
|
|
|
roomname.text.insert(roomname.text.end(), lines, lines + size);
|
|
|
|
|
|
|
|
specialroomnames.push_back(roomname);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::initcustommapdata(void)
|
2020-06-30 21:30:19 +02:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
shinytrinkets.clear();
|
2020-06-30 21:30:19 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
for (size_t i = 0; i < customentities.size(); i++)
|
|
|
|
{
|
|
|
|
const CustomEntity& ent = customentities[i];
|
|
|
|
if (ent.t != 9)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-03-29 09:18:42 +02:00
|
|
|
settrinket(ent.rx, ent.ry);
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
2020-06-30 21:30:19 +02:00
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
int mapclass::finalat(int x, int y)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
//return the tile index of the final stretch tiles offset by the colour difference
|
2021-09-25 01:37:27 +02:00
|
|
|
const int tile = contents[TILE_IDX(x, y)];
|
|
|
|
if (tile == 740)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
//Special case: animated tiles
|
|
|
|
if (final_mapcol == 1)
|
|
|
|
{
|
|
|
|
int offset;
|
|
|
|
if (game.noflashingmode)
|
|
|
|
{
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
offset = int(fRandom() * 11) * 40;
|
|
|
|
}
|
|
|
|
return 737 + offset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int offset;
|
|
|
|
if (game.noflashingmode)
|
|
|
|
{
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
offset = final_aniframe * 40;
|
|
|
|
}
|
2021-09-25 01:37:27 +02:00
|
|
|
return tile - (final_mapcol * 3) + offset;
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
2021-09-25 01:37:27 +02:00
|
|
|
else if (tile >= 80)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
return tile - (final_mapcol * 3);
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
return tile;
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int mapclass::maptiletoenemycol(int t)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
//returns the colour index for enemies that matches the map colour t
|
|
|
|
switch(t)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return 11;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
return 8;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
return 12;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
return 9;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
return 7;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
return 18;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 11;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 10:09:42 +02:00
|
|
|
void mapclass::changefinalcol(int t)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
//change the map to colour t - for the game's final stretch.
|
|
|
|
//First up, the tiles. This is just a setting:
|
|
|
|
final_mapcol = t;
|
2022-12-30 07:23:48 +01:00
|
|
|
const int temp = 6 - t;
|
2021-09-07 03:56:39 +02:00
|
|
|
//Next, entities
|
|
|
|
for (size_t i = 0; i < obj.entities.size(); i++)
|
|
|
|
{
|
|
|
|
if (obj.entities[i].type == 1) //something with a movement behavior
|
|
|
|
{
|
|
|
|
if (obj.entities[i].animate == 10 || obj.entities[i].animate == 11) //treadmill
|
|
|
|
{
|
|
|
|
if(temp<3)
|
|
|
|
{
|
|
|
|
obj.entities[i].tile = 907 + (temp * 80);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
obj.entities[i].tile = 911 + ((temp-3) * 80);
|
|
|
|
}
|
|
|
|
if(obj.entities[i].animate == 10) obj.entities[i].tile += 40;
|
|
|
|
}
|
|
|
|
else if (obj.entities[i].isplatform)
|
|
|
|
{
|
|
|
|
obj.entities[i].tile = 915+(temp*40);
|
|
|
|
}
|
|
|
|
else //just an enemy
|
|
|
|
{
|
|
|
|
obj.entities[i].colour = maptiletoenemycol(temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (obj.entities[i].type == 2) //disappearing platforms
|
|
|
|
{
|
|
|
|
obj.entities[i].tile = 915+(temp*40);
|
|
|
|
}
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2021-01-08 02:37:38 +01:00
|
|
|
void mapclass::setcol(TowerBG& bg_obj, const int r1, const int g1, const int b1 , const int r2, const int g2, const int b2, const int c)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
bg_obj.r = intpol(r1, r2, c / 5);
|
|
|
|
bg_obj.g = intpol(g1, g2, c / 5);
|
|
|
|
bg_obj.b = intpol(b1, b2, c / 5);
|
2021-01-08 02:37:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::updatebgobj(TowerBG& bg_obj)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
const int check = bg_obj.colstate % 5; //current state of phase
|
|
|
|
const int cmode = (bg_obj.colstate - check) / 5; // current colour transition;
|
|
|
|
|
|
|
|
switch(cmode)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
setcol(bg_obj, 255, 93, 107, 255, 255, 93, check);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
setcol(bg_obj, 255, 255, 93, 159, 255, 93, check);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
setcol(bg_obj, 159, 255, 93, 93, 245, 255, check);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
setcol(bg_obj, 93, 245, 255, 177, 93, 255, check);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
setcol(bg_obj, 177, 93, 255, 255, 93, 255, check);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
setcol(bg_obj, 255, 93, 255, 255, 93, 107, check);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bg_obj.tdrawback = true;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Consolidate tower BG bypos and bscroll assignments
Tower backgrounds have a bypos and bscroll. bypos is just the y-position
of the background, and bscroll is the amount of pixels to scroll the
background by on each frame, which is used to scroll it (if it's not
being redrawn) and for linear interpolation.
For the tower background (and not the title background), bypos is
map.ypos / 2, and bscroll is (map.ypos - map.oldypos) / 2. However,
usually bscroll gets assigned at the same time bypos is incremented or
decremented, so you never see that calculation explicitly - except in
the previous commit, where I worked out the calculation because the
change in y-position isn't a known constant.
Having to do all these calculations every time introduces the
possibility of errors where you forget to do it, or you do it wrongly.
But that's not even the worst; you could cause a linear interpolation
glitch if you decide to overwrite bscroll without taking into account
map.oldypos and map.ypos.
So that's why I'm adding a function that automatically updates the tower
background, using the values of map.oldypos and map.ypos, that is used
every time map.ypos is assigned. That way, we have to write less code,
you can be sure that there's no place where we forget to do the
calculations (or at least it will be glaringly obvious) or we do it
wrongly, and it plays nicely with linear interpolation. This also
replaces every instance where the manual calculations are done with the
new function.
2021-04-23 03:47:07 +02:00
|
|
|
void mapclass::setbgobjlerp(TowerBG& bg_obj)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
bg_obj.bypos = ypos / 2;
|
|
|
|
bg_obj.bscroll = (ypos - oldypos) / 2;
|
Consolidate tower BG bypos and bscroll assignments
Tower backgrounds have a bypos and bscroll. bypos is just the y-position
of the background, and bscroll is the amount of pixels to scroll the
background by on each frame, which is used to scroll it (if it's not
being redrawn) and for linear interpolation.
For the tower background (and not the title background), bypos is
map.ypos / 2, and bscroll is (map.ypos - map.oldypos) / 2. However,
usually bscroll gets assigned at the same time bypos is incremented or
decremented, so you never see that calculation explicitly - except in
the previous commit, where I worked out the calculation because the
change in y-position isn't a known constant.
Having to do all these calculations every time introduces the
possibility of errors where you forget to do it, or you do it wrongly.
But that's not even the worst; you could cause a linear interpolation
glitch if you decide to overwrite bscroll without taking into account
map.oldypos and map.ypos.
So that's why I'm adding a function that automatically updates the tower
background, using the values of map.oldypos and map.ypos, that is used
every time map.ypos is assigned. That way, we have to write less code,
you can be sure that there's no place where we forget to do the
calculations (or at least it will be glaringly obvious) or we do it
wrongly, and it plays nicely with linear interpolation. This also
replaces every instance where the manual calculations are done with the
new function.
2021-04-23 03:47:07 +02:00
|
|
|
}
|
|
|
|
|
2020-11-03 00:05:24 +01:00
|
|
|
void mapclass::updatetowerglow(TowerBG& bg_obj)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
if (colstatedelay <= 0 || colsuperstate > 0)
|
|
|
|
{
|
|
|
|
if (colsuperstate > 0) bg_obj.colstate--;
|
|
|
|
bg_obj.colstate++;
|
|
|
|
if (bg_obj.colstate >= 30) bg_obj.colstate = 0;
|
|
|
|
|
|
|
|
const int check = bg_obj.colstate % 5;
|
|
|
|
updatebgobj(bg_obj);
|
|
|
|
|
|
|
|
if (check == 0)
|
|
|
|
{
|
|
|
|
colstatedelay = 45;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
colstatedelay = 0;
|
|
|
|
}
|
|
|
|
if (colsuperstate > 0) colstatedelay = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
colstatedelay--;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::nexttowercolour(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
/* Prevent cycling title BG more than once per frame. */
|
|
|
|
if (nexttowercolour_set)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nexttowercolour_set = true;
|
2021-08-06 01:38:06 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
graphics.titlebg.colstate+=5;
|
|
|
|
if (graphics.titlebg.colstate >= 30) graphics.titlebg.colstate = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
updatebgobj(graphics.titlebg);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::settowercolour(int t)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
graphics.titlebg.colstate=t*5;
|
|
|
|
if (graphics.titlebg.colstate >= 30) graphics.titlebg.colstate = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
updatebgobj(graphics.titlebg);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2022-06-06 05:17:26 +02:00
|
|
|
bool mapclass::towerspikecollide(int x, int y)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
if (tower.at(x,y,0)>= 6 && tower.at(x,y,0) <= 11) return true;
|
|
|
|
return false;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2022-06-06 05:17:26 +02:00
|
|
|
bool mapclass::collide(int x, int y, const bool invincible)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
if (towermode)
|
|
|
|
{
|
|
|
|
if (tower.at(x, y, 0) >= 12 && tower.at(x, y, 0) <= 27) return true;
|
2022-06-06 05:17:26 +02:00
|
|
|
if (invincible)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
if (tower.at(x, y, 0) >= 6 && tower.at(x, y, 0) <= 11) return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tileset == 2)
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
int tile;
|
2022-06-06 05:17:26 +02:00
|
|
|
if (y == -1) return collide(x, y + 1, invincible);
|
|
|
|
if (y == 29+extrarow) return collide(x, y - 1, invincible);
|
|
|
|
if (x == -1) return collide(x + 1, y, invincible);
|
|
|
|
if (x == 40) return collide(x - 1, y, invincible);
|
2021-09-07 03:56:39 +02:00
|
|
|
if (x < 0 || y < 0 || x >= 40 || y >= 29 + extrarow) return false;
|
2021-09-25 01:37:27 +02:00
|
|
|
tile = contents[TILE_IDX(x, y)];
|
|
|
|
if (tile >= 12 && tile <= 27) return true;
|
2022-06-06 05:17:26 +02:00
|
|
|
if (invincible)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile >= 6 && tile <= 11) return true;
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
int tile;
|
2022-06-06 05:17:26 +02:00
|
|
|
if (y == -1) return collide(x, y + 1, invincible);
|
|
|
|
if (y == 29+extrarow) return collide(x, y - 1, invincible);
|
|
|
|
if (x == -1) return collide(x + 1, y, invincible);
|
|
|
|
if (x == 40) return collide(x - 1, y, invincible);
|
2021-09-07 03:56:39 +02:00
|
|
|
if (x < 0 || y < 0 || x >= 40 || y >= 29+extrarow) return false;
|
2021-09-25 01:37:27 +02:00
|
|
|
tile = contents[TILE_IDX(x, y)];
|
|
|
|
if (tile == 1) return true;
|
|
|
|
if (tileset==0 && tile == 59) return true;
|
|
|
|
if (tile>= 80 && tile < 680) return true;
|
|
|
|
if (tile == 740 && tileset==1) return true;
|
2022-06-06 05:17:26 +02:00
|
|
|
if (invincible)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile>= 6 && tile <= 9) return true;
|
|
|
|
if (tile>= 49 && tile <= 50) return true;
|
2021-09-07 03:56:39 +02:00
|
|
|
if (tileset == 1)
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile>= 49 && tile < 80) return true;
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::settile(int xp, int yp, int t)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
if (xp >= 0 && xp < 40 && yp >= 0 && yp < 29+extrarow)
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
contents[TILE_IDX(xp, yp)] = t;
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int mapclass::area(int _rx, int _ry)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
//THIS IS THE BUG
|
|
|
|
if (finalmode)
|
|
|
|
{
|
|
|
|
return 6;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int lookup = (_rx - 100) + ((_ry - 100) * 20);
|
|
|
|
if(_rx-100>=0 && _rx-100<20 && _ry-100>=0 && _ry-100<20){
|
|
|
|
return areamap[lookup];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 6;
|
|
|
|
}
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 20:12:39 +01:00
|
|
|
bool mapclass::isexplored(const int rx, const int ry)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
const int roomnum = rx + ry*20;
|
|
|
|
if (INBOUNDS_ARR(roomnum, explored))
|
|
|
|
{
|
|
|
|
return explored[roomnum];
|
|
|
|
}
|
|
|
|
return false;
|
2021-03-24 20:12:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::setexplored(const int rx, const int ry, const bool status)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
const int roomnum = rx + ry*20;
|
|
|
|
if (INBOUNDS_ARR(roomnum, explored))
|
|
|
|
{
|
|
|
|
explored[roomnum] = status;
|
|
|
|
}
|
2021-03-24 20:12:39 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::exploretower(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
for (int i = 0; i < 20; i++)
|
|
|
|
{
|
|
|
|
setexplored(9, i, true);
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::hideship(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
//remove the ship from the explored areas
|
|
|
|
setexplored(2, 10, false);
|
|
|
|
setexplored(3, 10, false);
|
|
|
|
setexplored(4, 10, false);
|
|
|
|
setexplored(2, 11, false);
|
|
|
|
setexplored(3, 11, false);
|
|
|
|
setexplored(4, 11, false);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::showship(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
//show the ship in the explored areas
|
|
|
|
setexplored(2, 10, true);
|
|
|
|
setexplored(3, 10, true);
|
|
|
|
setexplored(4, 10, true);
|
|
|
|
setexplored(2, 11, true);
|
|
|
|
setexplored(3, 11, true);
|
|
|
|
setexplored(4, 11, true);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::resetplayer(void)
|
2020-12-28 22:16:16 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
resetplayer(false);
|
2020-12-28 22:16:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::resetplayer(const bool player_died)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
bool was_in_tower = towermode;
|
|
|
|
if (game.roomx != game.saverx || game.roomy != game.savery)
|
|
|
|
{
|
|
|
|
gotoroom(game.saverx, game.savery);
|
|
|
|
}
|
|
|
|
|
|
|
|
game.deathseq = -1;
|
|
|
|
int i = obj.getplayer();
|
|
|
|
if(INBOUNDS_VEC(i, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[i].vx = 0;
|
|
|
|
obj.entities[i].vy = 0;
|
|
|
|
obj.entities[i].ax = 0;
|
|
|
|
obj.entities[i].ay = 0;
|
|
|
|
obj.entities[i].xp = game.savex;
|
|
|
|
obj.entities[i].yp = game.savey;
|
|
|
|
|
|
|
|
//Fix conveyor death loop glitch
|
|
|
|
obj.entities[i].newxp = obj.entities[i].xp;
|
|
|
|
obj.entities[i].newyp = obj.entities[i].yp;
|
|
|
|
|
|
|
|
obj.entities[i].dir = game.savedir;
|
|
|
|
obj.entities[i].colour = game.savecolour;
|
|
|
|
if (player_died)
|
|
|
|
{
|
|
|
|
game.lifeseq = 10;
|
|
|
|
obj.entities[i].invis = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
obj.entities[i].invis = false;
|
|
|
|
}
|
|
|
|
if (!GlitchrunnerMode_less_than_or_equal(Glitchrunner2_2))
|
|
|
|
{
|
|
|
|
obj.entities[i].size = 0;
|
|
|
|
obj.entities[i].cx = 6;
|
|
|
|
obj.entities[i].cy = 2;
|
|
|
|
obj.entities[i].w = 12;
|
|
|
|
obj.entities[i].h = 21;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we entered a tower as part of respawn, reposition camera
|
|
|
|
if (!was_in_tower && towermode)
|
|
|
|
{
|
|
|
|
ypos = obj.entities[i].yp - 120;
|
|
|
|
if (ypos < 0)
|
|
|
|
{
|
|
|
|
ypos = 0;
|
|
|
|
}
|
|
|
|
oldypos = ypos;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Fix dying during collection prompt persisting effects
In the main game, if you press R during the trinket collection prompt
after collecting a trinket, AND you have never entered Comms Relay, and
you respawn in a different room, the trinket collection gamestate will
be interrupted, but you will still be left with the advance text prompt,
cutscene bars, and muted music.
The previous workaround to fix the music would be to mute and then
unmute the game, but due to the new music changes, this workaround
(which in and of itself is a bug) no longer works. Instead, the music
would have to be restarted by going into another zone on the map.
Having an advance text prompt outside of a cutscene results in the
player being unable to flip, but they can still move around left and
right.
Speedrunners previously used the no-Comms-Relay interrupting behavior to
skip certain trinket collection prompts entirely with a frame-perfect R
press, so I can't patch that out. Having an advance text prompt outside
of a cutscene is (ab)used in custom levels to intentionally prevent the
player from flipping, and furthermore, it's also used in credits warp
runs of the main game to increment the gamestate; so I cannot patch that
out. The ability to press R everywhere even during cutscenes was added
for good reason - to make it less likely that a softlock can happen - so
I don't want to revert it.
But I still think this is worth fixing because previously, the
punishment for missing the frame-perfect window late was simply not
skipping the trinket prompt (since the R-press would be ignored), but
now the punishment is basically having to reset because of the advance
text prompt.
I would usually handle this in gamestate 0, but awful custom levels
might want to intentionally interrupt the gamestate to do, I don't know,
something. No level does that so far, but I'd like to do the least
invasive thing.
So what I've done is made it so the effects of interruption are undone
if you press R and the gamestate is interrupted. This is handled in
mapclass::resetplayer().
2021-09-10 20:49:46 +02:00
|
|
|
if (game.state == 0 && !script.running && game.completestop)
|
|
|
|
{
|
|
|
|
/* Looks like a collection dialogue was interrupted.
|
|
|
|
* Undo its effects! */
|
|
|
|
game.advancetext = false;
|
|
|
|
graphics.showcutscenebars = false;
|
|
|
|
if (music.currentsong > -1)
|
|
|
|
{
|
|
|
|
music.fadeMusicVolumeIn(3000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
game.scmhurt = false; //Just in case the supercrewmate is fucking this up!
|
|
|
|
if (game.supercrewmate)
|
|
|
|
{
|
|
|
|
if (game.roomx == game.scmprogress + 41)
|
|
|
|
{
|
|
|
|
game.scmprogress = game.roomx - 41;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.scmprogress = game.roomx - 40;
|
|
|
|
}
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 10:09:42 +02:00
|
|
|
void mapclass::warpto(int rx, int ry , int t, int tx, int ty)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
gotoroom(rx, ry);
|
|
|
|
game.teleport = false;
|
|
|
|
if (INBOUNDS_VEC(t, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[t].xp = tx * 8;
|
|
|
|
obj.entities[t].yp = (ty * 8) - obj.entities[t].h;
|
|
|
|
obj.entities[t].lerpoldxp = obj.entities[t].xp;
|
|
|
|
obj.entities[t].lerpoldyp = obj.entities[t].yp;
|
|
|
|
}
|
|
|
|
game.gravitycontrol = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 10:09:42 +02:00
|
|
|
void mapclass::gotoroom(int rx, int ry)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
int roomchangedir;
|
|
|
|
std::vector<entclass> linecrosskludge;
|
|
|
|
|
|
|
|
//First, destroy the current room
|
|
|
|
obj.removeallblocks();
|
|
|
|
game.activetele = false;
|
|
|
|
game.readytotele = 0;
|
|
|
|
game.oldreadytotele = 0;
|
|
|
|
|
|
|
|
//Ok, let's save the position of all lines on the screen
|
|
|
|
for (size_t i = 0; i < obj.entities.size(); i++)
|
|
|
|
{
|
|
|
|
if (obj.entities[i].type == 9)
|
|
|
|
{
|
|
|
|
//It's a horizontal line
|
|
|
|
if (obj.entities[i].xp <= 0 || (obj.entities[i].xp + obj.entities[i].w) >= 312)
|
|
|
|
{
|
|
|
|
//it's on a screen edge
|
|
|
|
obj.copylinecross(linecrosskludge, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable all entities in the room, and deallocate any unnecessary entity slots. */
|
|
|
|
/* However don't disable player entities, but do preserve holes between them (if any). */
|
|
|
|
bool player_found = false;
|
|
|
|
for (int i = obj.entities.size() - 1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
/* Iterate in reverse order to prevent unnecessary indice shifting */
|
|
|
|
if (obj.entities[i].rule == 0)
|
|
|
|
{
|
|
|
|
player_found = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!player_found)
|
|
|
|
{
|
|
|
|
obj.entities.erase(obj.entities.begin() + i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
obj.disableentity(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx < game.roomx)
|
|
|
|
{
|
|
|
|
roomchangedir = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
roomchangedir = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (finalmode)
|
|
|
|
{
|
|
|
|
//Ok, what way are we moving?
|
|
|
|
game.roomx = rx;
|
|
|
|
game.roomy = ry;
|
|
|
|
|
|
|
|
if (game.roomy < 10)
|
|
|
|
{
|
|
|
|
game.roomy = 11;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(game.roomx>=41 && game.roomy>=48 && game.roomx<61 && game.roomy<68 )
|
|
|
|
{
|
|
|
|
game.currentroomdeaths = roomdeathsfinal[game.roomx - 41 + (20 * (game.roomy - 48))];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.currentroomdeaths = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Final level for time trial
|
2023-05-24 03:37:32 +02:00
|
|
|
if (game.intimetrial && game.roomx == 46 && game.roomy == 54)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2023-05-24 03:37:32 +02:00
|
|
|
music.niceplay(Music_PREDESTINEDFATEREMIX);
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (custommode)
|
|
|
|
{
|
|
|
|
game.roomx = rx;
|
|
|
|
game.roomy = ry;
|
|
|
|
if (game.roomx < 100) game.roomx = 100 + cl.mapwidth-1;
|
|
|
|
if (game.roomy < 100) game.roomy = 100 + cl.mapheight-1;
|
|
|
|
if (game.roomx > 100 + cl.mapwidth-1) game.roomx = 100;
|
|
|
|
if (game.roomy > 100 + cl.mapheight-1) game.roomy = 100;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game.roomx = rx;
|
|
|
|
game.roomy = ry;
|
|
|
|
if (game.roomx < 100) game.roomx = 119;
|
|
|
|
if (game.roomy < 100) game.roomy = 119;
|
|
|
|
if (game.roomx > 119) game.roomx = 100;
|
|
|
|
if (game.roomy > 119) game.roomy = 100;
|
|
|
|
|
|
|
|
game.currentroomdeaths = roomdeaths[game.roomx - 100 + (20 * (game.roomy - 100))];
|
|
|
|
|
|
|
|
//Alright, change music depending on where we are:
|
|
|
|
music.changemusicarea(game.roomx - 100, game.roomy - 100);
|
|
|
|
}
|
|
|
|
loadlevel(game.roomx, game.roomy);
|
|
|
|
|
|
|
|
|
|
|
|
//Do we need to reload the background?
|
|
|
|
bool redrawbg = game.roomx != game.prevroomx || game.roomy != game.prevroomy;
|
|
|
|
|
2023-01-07 19:28:07 +01:00
|
|
|
if (redrawbg)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
graphics.backgrounddrawn = false; //Used for background caching speedup
|
|
|
|
}
|
|
|
|
graphics.foregrounddrawn = false; //Used for background caching speedup
|
|
|
|
|
|
|
|
game.prevroomx = game.roomx;
|
|
|
|
game.prevroomy = game.roomy;
|
|
|
|
|
|
|
|
//a very special case: if entering the communication room, room 13,4 before tag 5 is set, set the game state to a background
|
|
|
|
//textbox thingy. if tag five is not set when changing room, reset the game state. (tag 5 is set when you get back to the ship)
|
|
|
|
if(!game.intimetrial && !custommode)
|
|
|
|
{
|
|
|
|
if (!obj.flags[5] && !finalmode)
|
|
|
|
{
|
2022-12-07 00:20:48 +01:00
|
|
|
game.setstate(0);
|
2021-09-07 03:56:39 +02:00
|
|
|
if (game.roomx == 113 && game.roomy == 104)
|
|
|
|
{
|
2022-12-07 00:20:48 +01:00
|
|
|
game.setstate(50);
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Ok, kludge to fix lines in crossing rooms - if we're intersecting a gravity line right now, let's
|
|
|
|
//set it to an inactive state.
|
|
|
|
|
|
|
|
//Alright! So, let's look at our lines from the previous rooms, and determine if any of them are actually
|
|
|
|
//continuations!
|
|
|
|
|
2022-12-30 07:23:48 +01:00
|
|
|
const int player_idx = obj.getplayer();
|
|
|
|
if (INBOUNDS_VEC(player_idx, obj.entities))
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2022-12-30 07:23:48 +01:00
|
|
|
entclass* player = &obj.entities[player_idx];
|
|
|
|
player->oldxp = player->xp;
|
|
|
|
player->oldyp = player->yp;
|
|
|
|
player->lerpoldxp = player->xp - int(player->vx);
|
|
|
|
player->lerpoldyp = player->yp - int(player->vy);
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < obj.entities.size(); i++)
|
|
|
|
{
|
|
|
|
if (obj.entities[i].type == 9)
|
|
|
|
{
|
|
|
|
//It's a horizontal line
|
|
|
|
if (obj.entities[i].xp <= 0 || obj.entities[i].xp + obj.entities[i].w >= 312)
|
|
|
|
{
|
|
|
|
//it's on a screen edge
|
|
|
|
for (size_t j = 0; j < linecrosskludge.size(); j++)
|
|
|
|
{
|
|
|
|
if (obj.entities[i].yp == linecrosskludge[j].yp)
|
|
|
|
{
|
|
|
|
//y's match, how about x's?
|
|
|
|
//we're moving left:
|
|
|
|
if (roomchangedir == 0)
|
|
|
|
{
|
|
|
|
if (obj.entities[i].xp + obj.entities[i].w >= 312 && linecrosskludge[j].xp <= 0)
|
|
|
|
{
|
|
|
|
obj.revertlinecross(linecrosskludge, i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (obj.entities[i].xp <= 0 && linecrosskludge[j].xp + linecrosskludge[j].w >= 312)
|
|
|
|
{
|
|
|
|
obj.revertlinecross(linecrosskludge, i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-12 07:53:07 +02:00
|
|
|
|
|
|
|
if (game.companion > 0)
|
|
|
|
{
|
|
|
|
//We've changed room? Let's bring our companion along!
|
|
|
|
spawncompanion();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::spawncompanion(void)
|
|
|
|
{
|
|
|
|
int i = obj.getplayer();
|
|
|
|
if (!INBOUNDS_VEC(i, obj.entities))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//ok, we'll presume our companion has been destroyed in the room change. So:
|
|
|
|
switch(game.companion)
|
|
|
|
{
|
|
|
|
case 6:
|
|
|
|
{
|
|
|
|
obj.createentity(obj.entities[i].xp, 121.0f, 15.0f,1); //Y=121, the floor in that particular place!
|
|
|
|
int j = obj.getcompanion();
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].vx = obj.entities[i].vx;
|
|
|
|
obj.entities[j].dir = obj.entities[i].dir;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 7:
|
|
|
|
if (game.roomy <= 105) //don't jump after him!
|
|
|
|
{
|
|
|
|
if (game.roomx == 110)
|
|
|
|
{
|
|
|
|
obj.createentity(320, 86, 16, 1); //Y=86, the ROOF in that particular place!
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
obj.createentity(obj.entities[i].xp, 86.0f, 16.0f, 1); //Y=86, the ROOF in that particular place!
|
|
|
|
}
|
|
|
|
int j = obj.getcompanion();
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].vx = obj.entities[i].vx;
|
|
|
|
obj.entities[j].dir = obj.entities[i].dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (game.roomy >= 104) //don't jump after him!
|
|
|
|
{
|
|
|
|
if (game.roomx == 102)
|
|
|
|
{
|
|
|
|
obj.createentity(310, 177, 17, 1);
|
|
|
|
int j = obj.getcompanion();
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].vx = obj.entities[i].vx;
|
|
|
|
obj.entities[j].dir = obj.entities[i].dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
obj.createentity(obj.entities[i].xp, 177.0f, 17.0f, 1);
|
|
|
|
int j = obj.getcompanion();
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].vx = obj.entities[i].vx;
|
|
|
|
obj.entities[j].dir = obj.entities[i].dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
if (!towermode) //don't go back into the tower!
|
|
|
|
{
|
|
|
|
if (game.roomx == 110 && obj.entities[i].xp<20)
|
|
|
|
{
|
|
|
|
obj.createentity(100, 185, 18, 15, 0, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
obj.createentity(obj.entities[i].xp, 185.0f, 18.0f, 15, 0, 1);
|
|
|
|
}
|
|
|
|
int j = obj.getcompanion();
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].vx = obj.entities[i].vx;
|
|
|
|
obj.entities[j].dir = obj.entities[i].dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
//intermission 2, choose colour based on lastsaved
|
|
|
|
if (game.roomy == 51)
|
|
|
|
{
|
|
|
|
if (!obj.flags[59])
|
|
|
|
{
|
|
|
|
obj.createentity(225.0f, 169.0f, 18, graphics.crewcolour(game.lastsaved), 0, 10);
|
|
|
|
int j = obj.getcompanion();
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].vx = obj.entities[i].vx;
|
|
|
|
obj.entities[j].dir = obj.entities[i].dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (game.roomy >= 52)
|
|
|
|
{
|
|
|
|
if (obj.flags[59])
|
|
|
|
{
|
|
|
|
obj.createentity(160.0f, 177.0f, 18, graphics.crewcolour(game.lastsaved), 0, 18, 1);
|
|
|
|
int j = obj.getcompanion();
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].vx = obj.entities[i].vx;
|
|
|
|
obj.entities[j].dir = obj.entities[i].dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
obj.flags[59] = true;
|
|
|
|
obj.createentity(obj.entities[i].xp, -20.0f, 18.0f, graphics.crewcolour(game.lastsaved), 0, 10, 0);
|
|
|
|
int j = obj.getcompanion();
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].vx = obj.entities[i].vx;
|
|
|
|
obj.entities[j].dir = obj.entities[i].dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
//Intermission 1: We're using the SuperCrewMate instead!
|
|
|
|
if(game.roomx-41==game.scmprogress)
|
|
|
|
{
|
|
|
|
switch(game.scmprogress)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
obj.createentity(76, 161, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
obj.createentity(10, 169, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
obj.createentity(10, 177, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
obj.createentity(10, 177, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
obj.createentity(10, 185, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
obj.createentity(10, 185, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
obj.createentity(10, 185, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
obj.createentity(10, 41, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
obj.createentity(10, 169, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
obj.createentity(10, 169, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
obj.createentity(10, 129, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
obj.createentity(10, 129, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
obj.createentity(10, 65, 24, graphics.crewcolour(game.lastsaved), 2);
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
obj.createentity(10, 177, 24, graphics.crewcolour(game.lastsaved));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2023-09-12 00:07:10 +02:00
|
|
|
const char* mapclass::currentarea(const int roomx, const int roomy)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2023-09-13 18:47:20 +02:00
|
|
|
/* For translation, the returned value is passed to loc::gettext_roomname_special().
|
|
|
|
* Returned strings must therefore be found in roomnames_special.xml! */
|
|
|
|
|
2023-09-12 00:07:10 +02:00
|
|
|
if (roomx >= 102 && roomx <= 104 && roomy >= 110 && roomy <= 111)
|
|
|
|
{
|
2023-09-13 18:47:20 +02:00
|
|
|
return "The Ship";
|
2023-09-12 00:07:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (area(roomx, roomy))
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
case 0:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Dimension VVVVVV";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 1:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Dimension VVVVVV";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 2:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Laboratory";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 3:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "The Tower";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 4:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Warp Zone";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 5:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Space Station";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 6:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Outside Dimension VVVVVV";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 7:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Outside Dimension VVVVVV";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 8:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Outside Dimension VVVVVV";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 9:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Outside Dimension VVVVVV";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 10:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "Outside Dimension VVVVVV";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 11:
|
2023-09-13 18:47:20 +02:00
|
|
|
return "The Tower";
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-09-13 18:47:20 +02:00
|
|
|
return "???";
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2021-08-23 06:30:53 +02:00
|
|
|
static void copy_short_to_int(int* dest, const short* src, const size_t size)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < size; ++i)
|
|
|
|
{
|
|
|
|
dest[i] = src[i];
|
|
|
|
}
|
2021-08-23 06:30:53 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 10:09:42 +02:00
|
|
|
void mapclass::loadlevel(int rx, int ry)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
int t;
|
2023-08-23 22:03:23 +02:00
|
|
|
if (revealmap)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2023-08-23 22:03:23 +02:00
|
|
|
if (!finalmode)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2023-08-23 22:03:23 +02:00
|
|
|
setexplored(rx - 100, ry - 100, true);
|
|
|
|
if (rx == 109 && !custommode)
|
|
|
|
{
|
|
|
|
exploretower();
|
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
roomtexton = false;
|
|
|
|
roomtext.clear();
|
2022-12-12 00:05:20 +01:00
|
|
|
roomnameset = false;
|
2021-09-07 03:56:39 +02:00
|
|
|
|
|
|
|
obj.platformtile = 0;
|
|
|
|
obj.customplatformtile=0;
|
|
|
|
obj.vertplatforms = false;
|
|
|
|
obj.horplatforms = false;
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname("");
|
2021-09-07 03:56:39 +02:00
|
|
|
hiddenname = "";
|
|
|
|
background = 1;
|
|
|
|
warpx = false;
|
|
|
|
warpy = false;
|
|
|
|
|
|
|
|
towermode = false;
|
|
|
|
ypos = 0;
|
|
|
|
oldypos = 0;
|
|
|
|
extrarow = 0;
|
|
|
|
spikeleveltop = 0;
|
|
|
|
spikelevelbottom = 0;
|
|
|
|
oldspikeleveltop = 0;
|
|
|
|
oldspikelevelbottom = 0;
|
|
|
|
|
|
|
|
//Custom stuff for warplines
|
|
|
|
obj.customwarpmode=false;
|
|
|
|
obj.customwarpmodevon=false;
|
|
|
|
obj.customwarpmodehon=false;
|
|
|
|
|
|
|
|
if (finalmode)
|
|
|
|
{
|
|
|
|
t = 6;
|
|
|
|
//check if we're in the towers
|
|
|
|
if (rx == 49 && ry == 52)
|
|
|
|
{
|
|
|
|
//entered tower 1
|
|
|
|
t = 7;
|
|
|
|
}
|
|
|
|
else if (rx == 49 && ry == 53)
|
|
|
|
{
|
|
|
|
//re entered tower 1
|
|
|
|
t = 8;
|
|
|
|
}
|
|
|
|
else if (rx == 51 && ry == 54)
|
|
|
|
{
|
|
|
|
//entered tower 2
|
|
|
|
t = 9;
|
|
|
|
}
|
|
|
|
else if (rx == 51 && ry == 53)
|
|
|
|
{
|
|
|
|
//re entered tower 2
|
|
|
|
t = 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (custommode)
|
|
|
|
{
|
|
|
|
t= 12;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
t = area(rx, ry);
|
|
|
|
|
|
|
|
if (t == 3)
|
|
|
|
{
|
|
|
|
//correct position for tower
|
|
|
|
if (ry == 109)
|
|
|
|
{
|
|
|
|
//entered from ground floor
|
|
|
|
int player = obj.getplayer();
|
|
|
|
if (INBOUNDS_VEC(player, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[player].yp += (671 * 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
ypos = (700-29) * 8;
|
|
|
|
oldypos = ypos;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
cameramode = 0;
|
|
|
|
graphics.towerbg.colstate = 0;
|
|
|
|
colsuperstate = 0;
|
|
|
|
}
|
|
|
|
else if (ry == 104)
|
|
|
|
{
|
|
|
|
//you've entered from the top floor
|
|
|
|
ypos = 0;
|
|
|
|
oldypos = ypos;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
cameramode = 0;
|
|
|
|
graphics.towerbg.colstate = 0;
|
|
|
|
colsuperstate = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t < 2) //on the world map, want to test if we're in the secret lab
|
|
|
|
{
|
|
|
|
if (rx >= 116)
|
|
|
|
{
|
|
|
|
if (ry >= 105)
|
|
|
|
{
|
|
|
|
if (ry <= 107)
|
|
|
|
{
|
|
|
|
if (rx == 119 && ry == 105)
|
|
|
|
{
|
|
|
|
//Ah, this is just a normal area
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//in the secret lab! Crazy background!
|
|
|
|
background = 2;
|
|
|
|
if (rx == 116 && ry == 105) graphics.rcol = 1;
|
|
|
|
if (rx == 117 && ry == 105) graphics.rcol = 5;
|
|
|
|
if (rx == 118 && ry == 105) graphics.rcol = 4;
|
|
|
|
if (rx == 117 && ry == 106) graphics.rcol = 2;
|
|
|
|
if (rx == 118 && ry == 106) graphics.rcol = 0;
|
|
|
|
if (rx == 119 && ry == 106) graphics.rcol = 3;
|
|
|
|
if (rx == 119 && ry == 107) graphics.rcol = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx == 119 && ry == 108 && !custommode)
|
|
|
|
{
|
|
|
|
background = 5;
|
|
|
|
graphics.rcol = 3;
|
|
|
|
warpx = true;
|
|
|
|
warpy = true;
|
|
|
|
}
|
|
|
|
|
2022-12-29 04:58:49 +01:00
|
|
|
roomname_special = false;
|
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
switch(t)
|
|
|
|
{
|
2023-02-04 09:14:04 +01:00
|
|
|
#ifdef MAKEANDPLAY
|
|
|
|
UNUSED(copy_short_to_int);
|
|
|
|
#else
|
2021-09-07 03:56:39 +02:00
|
|
|
case 0:
|
|
|
|
case 1: //World Map
|
|
|
|
{
|
|
|
|
tileset = 1;
|
|
|
|
extrarow = 1;
|
|
|
|
const short* tmap = otherlevel.loadlevel(rx, ry);
|
|
|
|
copy_short_to_int(contents, tmap, SDL_arraysize(contents));
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname(otherlevel.roomname);
|
2022-12-29 04:58:49 +01:00
|
|
|
roomname_special = otherlevel.roomname_special;
|
2021-09-07 03:56:39 +02:00
|
|
|
hiddenname = otherlevel.hiddenname;
|
|
|
|
tileset = otherlevel.roomtileset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: //The Lab
|
|
|
|
{
|
|
|
|
const short* tmap = lablevel.loadlevel(rx, ry);
|
|
|
|
copy_short_to_int(contents, tmap, SDL_arraysize(contents));
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname(lablevel.roomname);
|
2022-12-29 04:58:49 +01:00
|
|
|
roomname_special = lablevel.roomname_special;
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 1;
|
|
|
|
background = 2;
|
|
|
|
graphics.rcol = lablevel.rcol;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3: //The Tower
|
|
|
|
graphics.towerbg.tdrawback = true;
|
|
|
|
minitowermode = false;
|
|
|
|
tower.minitowermode = false;
|
|
|
|
graphics.towerbg.scrolldir = 0;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname("The Tower");
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 1;
|
|
|
|
background = 3;
|
|
|
|
towermode = true;
|
|
|
|
|
|
|
|
//All the entities for here are just loaded here; it's essentially one room after all
|
|
|
|
|
|
|
|
|
|
|
|
obj.createentity(48, 5456, 10, 1, 505007); // (savepoint)
|
|
|
|
obj.createentity(224, 4528, 10, 1, 505017); // (savepoint)
|
|
|
|
obj.createentity(232, 4168, 10, 0, 505027); // (savepoint)
|
|
|
|
obj.createentity(280, 3816, 10, 1, 505037); // (savepoint)
|
|
|
|
obj.createentity(152, 3552, 10, 1, 505047); // (savepoint)
|
|
|
|
obj.createentity(216, 3280, 10, 0, 505057); // (savepoint)
|
|
|
|
obj.createentity(216, 4808, 10, 1, 505067); // (savepoint)
|
|
|
|
obj.createentity(72, 3096, 10, 0, 505077); // (savepoint)
|
|
|
|
obj.createentity(176, 2600, 10, 0, 505087); // (savepoint)
|
|
|
|
obj.createentity(216, 2392, 10, 0, 505097); // (savepoint)
|
|
|
|
obj.createentity(152, 1184, 10, 1, 505107); // (savepoint)
|
|
|
|
obj.createentity(152, 912, 10, 1, 505117); // (savepoint)
|
|
|
|
obj.createentity(152, 536, 10, 1, 505127); // (savepoint)
|
|
|
|
obj.createentity(120, 5136, 10, 0, 505137); // (savepoint)
|
|
|
|
obj.createentity(144, 1824, 10, 0, 505147); // (savepoint)
|
|
|
|
obj.createentity(72, 2904, 10, 0, 505157); // (savepoint)
|
|
|
|
obj.createentity(224, 1648, 10, 1, 505167); // (savepoint)
|
|
|
|
obj.createentity(112, 5280, 10, 1, 50517); // (savepoint)
|
|
|
|
|
|
|
|
obj.createentity(24, 4216, 9, 7); // (shiny trinket)
|
|
|
|
obj.createentity(280, 3216, 9, 8); // (shiny trinket)
|
|
|
|
break;
|
|
|
|
case 4: //The Warpzone
|
|
|
|
{
|
|
|
|
const short* tmap = warplevel.loadlevel(rx, ry);
|
|
|
|
copy_short_to_int(contents, tmap, SDL_arraysize(contents));
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname(warplevel.roomname);
|
2022-12-29 04:58:49 +01:00
|
|
|
roomname_special = warplevel.roomname_special;
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 1;
|
|
|
|
background = 3;
|
|
|
|
graphics.rcol = warplevel.rcol;
|
|
|
|
graphics.backgrounddrawn = false;
|
|
|
|
|
|
|
|
warpx = warplevel.warpx;
|
|
|
|
warpy = warplevel.warpy;
|
|
|
|
background = 5;
|
|
|
|
if (warpy) background = 4;
|
|
|
|
if (warpx) background = 3;
|
|
|
|
if (warpx && warpy) background = 5;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 5: //Space station
|
|
|
|
{
|
|
|
|
const short* tmap = spacestation2.loadlevel(rx, ry);
|
|
|
|
copy_short_to_int(contents, tmap, SDL_arraysize(contents));
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname(spacestation2.roomname);
|
2022-12-29 04:58:49 +01:00
|
|
|
roomname_special = spacestation2.roomname_special;
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 6: //final level
|
|
|
|
{
|
|
|
|
const short* tmap = finallevel.loadlevel(rx, ry);
|
|
|
|
copy_short_to_int(contents, tmap, SDL_arraysize(contents));
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname(finallevel.roomname);
|
2022-12-29 04:58:49 +01:00
|
|
|
roomname_special = finallevel.roomname_special;
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 1;
|
|
|
|
background = 3;
|
|
|
|
graphics.backgrounddrawn = false;
|
|
|
|
|
|
|
|
if (finalstretch)
|
|
|
|
{
|
|
|
|
background = 6;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
warpx = finallevel.warpx;
|
|
|
|
warpy = finallevel.warpy;
|
|
|
|
background = 5;
|
|
|
|
if (warpy) background = 4;
|
|
|
|
if (warpx) background = 3;
|
|
|
|
if (warpx && warpy) background = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
graphics.rcol = 6;
|
|
|
|
changefinalcol(final_mapcol);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 7: //Final Level, Tower 1
|
|
|
|
graphics.towerbg.tdrawback = true;
|
|
|
|
minitowermode = true;
|
|
|
|
tower.minitowermode = true;
|
|
|
|
graphics.towerbg.scrolldir = 1;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname("Panic Room");
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 1;
|
|
|
|
background = 3;
|
|
|
|
towermode = true;
|
|
|
|
|
|
|
|
tower.loadminitower1();
|
|
|
|
|
|
|
|
ypos = 0;
|
|
|
|
oldypos = 0;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
cameramode = 0;
|
|
|
|
graphics.towerbg.colstate = 0;
|
|
|
|
colsuperstate = 0;
|
|
|
|
break;
|
|
|
|
case 8: //Final Level, Tower 1 (reentered from below)
|
|
|
|
{
|
|
|
|
graphics.towerbg.tdrawback = true;
|
|
|
|
minitowermode = true;
|
|
|
|
tower.minitowermode = true;
|
|
|
|
graphics.towerbg.scrolldir = 1;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname("Panic Room");
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 1;
|
|
|
|
background = 3;
|
|
|
|
towermode = true;
|
|
|
|
|
|
|
|
tower.loadminitower1();
|
|
|
|
|
|
|
|
int i = obj.getplayer();
|
|
|
|
if (INBOUNDS_VEC(i, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[i].yp += (71 * 8);
|
|
|
|
}
|
|
|
|
game.roomy--;
|
|
|
|
|
|
|
|
ypos = (100-29) * 8;
|
|
|
|
oldypos = ypos;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
cameramode = 0;
|
|
|
|
graphics.towerbg.colstate = 0;
|
|
|
|
colsuperstate = 0;}
|
|
|
|
break;
|
|
|
|
case 9: //Final Level, Tower 2
|
|
|
|
{
|
|
|
|
graphics.towerbg.tdrawback = true;
|
|
|
|
minitowermode = true;
|
|
|
|
tower.minitowermode = true;
|
|
|
|
graphics.towerbg.scrolldir = 0;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname("The Final Challenge");
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 1;
|
|
|
|
background = 3;
|
|
|
|
towermode = true;
|
|
|
|
|
|
|
|
tower.loadminitower2();
|
|
|
|
|
|
|
|
obj.createentity(56, 556, 11, 136); // (horizontal gravity line)
|
|
|
|
obj.createentity(184, 592, 10, 0, 50500); // (savepoint)
|
|
|
|
obj.createentity(184, 644, 11, 88); // (horizontal gravity line)
|
|
|
|
obj.createentity(56, 460, 11, 136); // (horizontal gravity line)
|
|
|
|
obj.createentity(216, 440, 10, 0, 50501); // (savepoint)
|
|
|
|
obj.createentity(104, 508, 11, 168); // (horizontal gravity line)
|
|
|
|
obj.createentity(219, 264, 12, 56); // (vertical gravity line)
|
|
|
|
obj.createentity(120, 332, 11, 96); // (horizontal gravity line)
|
|
|
|
obj.createentity(219, 344, 12, 56); // (vertical gravity line)
|
|
|
|
obj.createentity(224, 332, 11, 48); // (horizontal gravity line)
|
|
|
|
obj.createentity(56, 212, 11, 144); // (horizontal gravity line)
|
|
|
|
obj.createentity(32, 20, 11, 96); // (horizontal gravity line)
|
|
|
|
obj.createentity(72, 156, 11, 200); // (horizontal gravity line)
|
|
|
|
|
|
|
|
int i = obj.getplayer();
|
|
|
|
if (INBOUNDS_VEC(i, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[i].yp += (71 * 8);
|
|
|
|
}
|
|
|
|
game.roomy--;
|
|
|
|
|
|
|
|
ypos = (100-29) * 8;
|
|
|
|
oldypos = ypos;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
cameramode = 0;
|
|
|
|
graphics.towerbg.colstate = 0;
|
|
|
|
colsuperstate = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 10: //Final Level, Tower 2
|
|
|
|
{
|
|
|
|
|
|
|
|
graphics.towerbg.tdrawback = true;
|
|
|
|
minitowermode = true;
|
|
|
|
tower.minitowermode = true;
|
|
|
|
graphics.towerbg.scrolldir = 0;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname("The Final Challenge");
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 1;
|
|
|
|
background = 3;
|
|
|
|
towermode = true;
|
|
|
|
|
|
|
|
tower.loadminitower2();
|
|
|
|
|
|
|
|
obj.createentity(56, 556, 11, 136); // (horizontal gravity line)
|
|
|
|
obj.createentity(184, 592, 10, 0, 50500); // (savepoint)
|
|
|
|
obj.createentity(184, 644, 11, 88); // (horizontal gravity line)
|
|
|
|
obj.createentity(56, 460, 11, 136); // (horizontal gravity line)
|
|
|
|
obj.createentity(216, 440, 10, 0, 50501); // (savepoint)
|
|
|
|
obj.createentity(104, 508, 11, 168); // (horizontal gravity line)
|
|
|
|
obj.createentity(219, 264, 12, 56); // (vertical gravity line)
|
|
|
|
obj.createentity(120, 332, 11, 96); // (horizontal gravity line)
|
|
|
|
obj.createentity(219, 344, 12, 56); // (vertical gravity line)
|
|
|
|
obj.createentity(224, 332, 11, 48); // (horizontal gravity line)
|
|
|
|
obj.createentity(56, 212, 11, 144); // (horizontal gravity line)
|
|
|
|
obj.createentity(32, 20, 11, 96); // (horizontal gravity line)
|
|
|
|
obj.createentity(72, 156, 11, 200); // (horizontal gravity line)
|
|
|
|
|
|
|
|
ypos = 0;
|
|
|
|
oldypos = 0;
|
|
|
|
setbgobjlerp(graphics.towerbg);
|
|
|
|
cameramode = 0;
|
|
|
|
graphics.towerbg.colstate = 0;
|
|
|
|
colsuperstate = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 11: //Tower Hallways //Content is held in final level routine
|
|
|
|
{
|
|
|
|
const short* tmap = finallevel.loadlevel(rx, ry);
|
|
|
|
copy_short_to_int(contents, tmap, SDL_arraysize(contents));
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname(finallevel.roomname);
|
2022-12-29 04:58:49 +01:00
|
|
|
roomname_special = finallevel.roomname_special;
|
2021-09-07 03:56:39 +02:00
|
|
|
tileset = 2;
|
|
|
|
if (rx == 108)
|
|
|
|
{
|
|
|
|
background = 7;
|
|
|
|
rcol = 15;
|
|
|
|
}
|
|
|
|
if (rx == 110)
|
|
|
|
{
|
|
|
|
background = 8;
|
|
|
|
rcol = 10;
|
|
|
|
}
|
|
|
|
if (rx == 111)
|
|
|
|
{
|
|
|
|
background = 9;
|
|
|
|
rcol = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-04-02 22:09:22 +02:00
|
|
|
#endif
|
2021-09-07 03:56:39 +02:00
|
|
|
case 12: //Custom level
|
|
|
|
{
|
|
|
|
const RoomProperty* const room = cl.getroomprop(rx - 100, ry - 100);
|
|
|
|
game.customcol = cl.getlevelcol(room->tileset, room->tilecol) + 1;
|
|
|
|
obj.customplatformtile = game.customcol * 12;
|
|
|
|
|
|
|
|
switch (room->tileset)
|
|
|
|
{
|
|
|
|
case 0: // Space Station
|
|
|
|
tileset = 0;
|
|
|
|
background = 1;
|
|
|
|
break;
|
|
|
|
case 1: // Outside
|
|
|
|
tileset = 1;
|
|
|
|
background = 1;
|
|
|
|
break;
|
|
|
|
case 2: // Lab
|
|
|
|
tileset = 1;
|
|
|
|
background = 2;
|
|
|
|
graphics.rcol = room->tilecol;
|
|
|
|
break;
|
|
|
|
case 3: // Warp Zone/intermission
|
|
|
|
tileset = 1;
|
|
|
|
background = 6;
|
|
|
|
break;
|
|
|
|
case 4: // Ship
|
|
|
|
tileset = 1;
|
|
|
|
background = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tileset = 1;
|
|
|
|
background = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If screen warping, then override all that:
|
|
|
|
bool redrawbg = game.roomx != game.prevroomx || game.roomy != game.prevroomy;
|
|
|
|
if (redrawbg)
|
|
|
|
{
|
|
|
|
graphics.backgrounddrawn = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (room->warpdir)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
warpx = true;
|
|
|
|
background = 3;
|
|
|
|
graphics.rcol = cl.getwarpbackground(rx - 100, ry - 100);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
warpy = true;
|
|
|
|
background = 4;
|
|
|
|
graphics.rcol = cl.getwarpbackground(rx - 100, ry - 100);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
warpx = true;
|
|
|
|
warpy = true;
|
|
|
|
background = 5;
|
|
|
|
graphics.rcol = cl.getwarpbackground(rx - 100, ry - 100);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-12-01 07:35:42 +01:00
|
|
|
setroomname(room->roomname.c_str());
|
2021-09-07 03:56:39 +02:00
|
|
|
extrarow = 1;
|
|
|
|
const int* tmap = cl.loadlevel(rx, ry);
|
|
|
|
SDL_memcpy(contents, tmap, sizeof(contents));
|
|
|
|
|
|
|
|
|
|
|
|
roomtexton = false;
|
|
|
|
roomtext.clear();
|
|
|
|
|
|
|
|
// Entities have to be created HERE, akwardly
|
|
|
|
int tempcheckpoints = 0;
|
|
|
|
int tempscriptbox = 0;
|
|
|
|
for (size_t edi = 0; edi < customentities.size(); edi++)
|
|
|
|
{
|
|
|
|
// If entity is in this room, create it
|
|
|
|
const CustomEntity& ent = customentities[edi];
|
2023-03-29 09:18:42 +02:00
|
|
|
if (ent.rx != rx - 100 || ent.ry != ry - 100)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-03-29 09:18:42 +02:00
|
|
|
const int ex = ent.x * 8;
|
|
|
|
const int ey = ent.y * 8;
|
2021-09-07 03:56:39 +02:00
|
|
|
|
|
|
|
// Platform and enemy bounding boxes
|
|
|
|
int bx1 = 0, by1 = 0, bx2 = 0, by2 = 0;
|
|
|
|
|
|
|
|
bool enemy = ent.t == 1;
|
|
|
|
bool moving_plat = ent.t == 2 && ent.p1 <= 4;
|
|
|
|
if (enemy || moving_plat)
|
|
|
|
{
|
|
|
|
if (enemy)
|
|
|
|
{
|
|
|
|
bx1 = room->enemyx1;
|
|
|
|
by1 = room->enemyy1;
|
|
|
|
bx2 = room->enemyx2;
|
|
|
|
by2 = room->enemyy2;
|
|
|
|
}
|
|
|
|
else if (moving_plat)
|
|
|
|
{
|
|
|
|
bx1 = room->platx1;
|
|
|
|
by1 = room->platy1;
|
|
|
|
bx2 = room->platx2;
|
|
|
|
by2 = room->platy2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enlarge bounding boxes to fix warping entities
|
|
|
|
if (warpx && bx1 == 0 && bx2 == 320)
|
|
|
|
{
|
|
|
|
bx1 -= 100;
|
|
|
|
bx2 += 100;
|
|
|
|
}
|
|
|
|
if (warpy && by1 == 0 && by2 == 240)
|
|
|
|
{
|
|
|
|
by1 -= 100;
|
|
|
|
by2 += 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ent.t)
|
|
|
|
{
|
|
|
|
case 1: // Enemies
|
|
|
|
obj.customenemy = room->enemytype;
|
2024-08-26 01:19:07 +02:00
|
|
|
obj.createentity(ex, ey, 56, ent.p1, 4 + room->enemyv, bx1, by1, bx2, by2);
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
case 2: // Platforms and conveyors
|
|
|
|
if (ent.p1 <= 4)
|
|
|
|
{
|
|
|
|
obj.createentity(ex, ey, 2, ent.p1, room->platv, bx1, by1, bx2, by2);
|
|
|
|
}
|
|
|
|
else if (ent.p1 >= 5 && ent.p1 <= 8) // Conveyor
|
|
|
|
{
|
|
|
|
obj.createentity(ex, ey, 2, ent.p1 + 3, 4);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3: // Disappearing platforms
|
|
|
|
obj.createentity(ex, ey, 3);
|
|
|
|
break;
|
|
|
|
case 9: // Trinkets
|
|
|
|
obj.createentity(ex, ey, 9, cl.findtrinket(edi));
|
|
|
|
break;
|
|
|
|
case 10: // Checkpoints
|
|
|
|
obj.createentity(ex, ey, 10, ent.p1, (rx + ry*100) * 20 + tempcheckpoints);
|
|
|
|
tempcheckpoints++;
|
|
|
|
break;
|
|
|
|
case 11: // Gravity Lines
|
|
|
|
if (ent.p1 == 0) //Horizontal
|
|
|
|
{
|
|
|
|
obj.createentity(ent.p2 * 8, ey + 4, 11, ent.p3);
|
|
|
|
}
|
|
|
|
else //Vertical
|
|
|
|
{
|
|
|
|
obj.createentity(ex + 3, ent.p2 * 8, 12, ent.p3);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 13: // Warp Tokens
|
|
|
|
obj.createentity(ex, ey, 13, ent.p1, ent.p2);
|
|
|
|
break;
|
|
|
|
case 15: // Collectable crewmate
|
|
|
|
obj.createentity(ex - 4, ey + 1, 55, cl.findcrewmate(edi), ent.p1, ent.p2);
|
|
|
|
break;
|
|
|
|
case 17: // Roomtext!
|
|
|
|
{
|
|
|
|
roomtexton = true;
|
|
|
|
Roomtext text;
|
|
|
|
text.x = ex / 8;
|
|
|
|
text.y = ey / 8;
|
2021-09-12 21:48:15 +02:00
|
|
|
text.text = ent.scriptname.c_str();
|
2024-01-09 21:50:54 +01:00
|
|
|
text.rtl = ent.p1;
|
2021-09-07 03:56:39 +02:00
|
|
|
roomtext.push_back(text);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 18: // Terminals
|
|
|
|
{
|
|
|
|
obj.customscript = ent.scriptname;
|
|
|
|
|
|
|
|
int usethistile = ent.p1;
|
|
|
|
int usethisy = ey;
|
|
|
|
|
|
|
|
// This isn't a boolean: we just swap 0 and 1 around and leave the rest alone
|
|
|
|
if (usethistile == 0)
|
|
|
|
{
|
|
|
|
usethistile = 1; // Unflipped
|
|
|
|
}
|
|
|
|
else if (usethistile == 1)
|
|
|
|
{
|
|
|
|
usethistile = 0; // Flipped;
|
|
|
|
usethisy -= 8;
|
|
|
|
}
|
|
|
|
|
2022-11-20 05:13:03 +01:00
|
|
|
obj.createentity(ex, usethisy + 8, 20 + SDL_clamp(ent.p2, 0, 1), usethistile);
|
2023-08-23 03:23:08 +02:00
|
|
|
|
|
|
|
for (size_t i = 0; i < script.customscripts.size(); i++)
|
2022-11-20 05:13:03 +01:00
|
|
|
{
|
2023-08-23 03:23:08 +02:00
|
|
|
if (script.customscripts[i].name == obj.customscript)
|
|
|
|
{
|
|
|
|
obj.createblock(ACTIVITY, ex - 8, usethisy + 8, 20, 16, 35);
|
|
|
|
break;
|
|
|
|
}
|
2022-11-20 05:13:03 +01:00
|
|
|
}
|
2021-09-07 03:56:39 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 19: //Script Box
|
|
|
|
if (INBOUNDS_ARR(tempscriptbox, game.customscript))
|
|
|
|
{
|
|
|
|
game.customscript[tempscriptbox] = ent.scriptname;
|
|
|
|
}
|
|
|
|
obj.createblock(TRIGGER, ex, ey, ent.p1 * 8, ent.p2 * 8, 300 + tempscriptbox, "custom_" + ent.scriptname);
|
|
|
|
tempscriptbox++;
|
|
|
|
break;
|
|
|
|
case 50: // Warp Lines
|
|
|
|
obj.customwarpmode=true;
|
|
|
|
switch (ent.p1)
|
|
|
|
{
|
|
|
|
case 0: // Vertical, left
|
|
|
|
obj.createentity(ex + 4, ent.p2 * 8, 51, ent.p3);
|
|
|
|
break;
|
|
|
|
case 1: //Horizontal, right
|
|
|
|
obj.createentity(ex + 4, ent.p2 * 8, 52, ent.p3);
|
|
|
|
break;
|
|
|
|
case 2: //Vertical, top
|
|
|
|
obj.createentity(ent.p2 * 8, ey + 7, 53, ent.p3);
|
|
|
|
break;
|
|
|
|
case 3: // Horizontal, bottom
|
|
|
|
obj.createentity(ent.p2 * 8, ey, 54, ent.p3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//do the appear/remove roomname here
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//The room's loaded: now we fill out damage blocks based on the tiles.
|
|
|
|
if (towermode)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (int j = 0; j < 29 + extrarow; j++)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 40; i++)
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
int tile = contents[TILE_IDX(i, j)];
|
2021-09-07 03:56:39 +02:00
|
|
|
//Damage blocks
|
|
|
|
if(tileset==0)
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile == 6 || tile == 8)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
//sticking up
|
|
|
|
obj.createblock(2, (i * 8), (j * 8)+4, 8, 4);
|
|
|
|
}
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile == 7 || tile == 9)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
//Sticking down
|
|
|
|
obj.createblock(2, (i * 8), (j * 8), 8, 4);
|
|
|
|
}
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile == 49 || tile == 50)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
//left or right
|
|
|
|
obj.createblock(2, (i * 8), (j * 8)+3, 8, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(tileset==1)
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
if ((tile >= 63 && tile <= 74) ||
|
|
|
|
(tile >= 6 && tile <= 9))
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
//sticking up) {
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile < 10) tile++;
|
2021-09-07 03:56:39 +02:00
|
|
|
//sticking up
|
2021-09-25 01:37:27 +02:00
|
|
|
if(tile%2==0)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
obj.createblock(2, (i * 8), (j * 8), 8, 4);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Sticking down
|
|
|
|
obj.createblock(2, (i * 8), (j * 8) + 4, 8, 4);
|
|
|
|
}
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile < 11) tile--;
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile >= 49 && tile <= 62)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
//left or right
|
|
|
|
obj.createblock(2, (i * 8), (j * 8)+3, 8, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(tileset==2)
|
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile == 6 || tile == 8)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
//sticking up
|
|
|
|
obj.createblock(2, (i * 8), (j * 8)+4, 8, 4);
|
|
|
|
}
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile == 7 || tile == 9)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
//Sticking down
|
|
|
|
obj.createblock(2, (i * 8), (j * 8), 8, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Breakable blocks
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile == 10)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2021-10-07 02:00:50 +02:00
|
|
|
settile(i, j, 0);
|
2021-09-07 03:56:39 +02:00
|
|
|
obj.createentity(i * 8, j * 8, 4);
|
|
|
|
}
|
|
|
|
//Directional blocks
|
2021-09-25 01:37:27 +02:00
|
|
|
if (tile >= 14 && tile <= 17)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
obj.createblock(3, i * 8, j * 8, 8, 8, tile-14);
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < obj.entities.size(); i++)
|
|
|
|
{
|
|
|
|
if (obj.entities[i].type == 1 && obj.entities[i].behave >= 8 && obj.entities[i].behave < 10)
|
|
|
|
{
|
|
|
|
//put a block underneath
|
|
|
|
int temp = obj.entities[i].xp / 8.0f;
|
|
|
|
int temp2 = obj.entities[i].yp / 8.0f;
|
|
|
|
settile(temp, temp2, 1);
|
|
|
|
settile(temp+1, temp2, 1);
|
|
|
|
settile(temp+2, temp2, 1);
|
|
|
|
settile(temp+3, temp2, 1);
|
|
|
|
if (obj.entities[i].w == 64)
|
|
|
|
{
|
|
|
|
settile(temp+4, temp2, 1);
|
|
|
|
settile(temp+5, temp2, 1);
|
|
|
|
settile(temp+6, temp2, 1);
|
|
|
|
settile(temp+7, temp2, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Special scripting: Create objects and triggers based on what crewmembers are rescued.
|
|
|
|
if (!finalmode && !custommode)
|
|
|
|
{
|
|
|
|
//First up: the extra bits:
|
|
|
|
//Vermilion's quest:
|
|
|
|
if (rx == 100 && ry == 105) //On path to verdigris
|
|
|
|
{
|
|
|
|
if (game.crewstats[3] && !game.crewstats[4])
|
|
|
|
{
|
|
|
|
obj.createentity(87, 105, 18, 15, 0, 18);
|
|
|
|
obj.createblock(5, 87-32, 0, 32+32+32, 240, 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rx == 107 && ry == 100) //victoria
|
|
|
|
{
|
|
|
|
if (game.crewstats[3] && !game.crewstats[5])
|
|
|
|
{
|
|
|
|
obj.createentity(140, 137, 18, 15, 0, 18);
|
|
|
|
obj.createblock(5, 140-32, 0, 32+32+32, 240, 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rx == 114 && ry == 109)
|
|
|
|
{
|
|
|
|
if (game.crewstats[3] && !game.crewstats[2])
|
|
|
|
{
|
|
|
|
obj.createentity(235, 81, 18, 15, 0, 18);
|
|
|
|
obj.createblock(5, 235-32, 0, 32+32+32, 240, 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Verdigris fixing the ship
|
|
|
|
if (rx == 101 && ry == 109)
|
|
|
|
{
|
|
|
|
if (game.crewstats[4])
|
|
|
|
{
|
|
|
|
if(game.crewrescued()>4 && game.crewrescued()!=6)
|
|
|
|
{
|
|
|
|
obj.createentity(175, 121, 18, 13, 0, 18);
|
|
|
|
obj.createblock(5, 175-32, 0, 32+32+32, 240, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rx == 103 && ry == 109)
|
|
|
|
{
|
|
|
|
if (game.crewstats[4])
|
|
|
|
{
|
|
|
|
if(game.crewrescued()<=4 && game.crewrescued()!=6)
|
|
|
|
{
|
|
|
|
obj.createentity(53, 161, 18, 13, 1, 18);
|
|
|
|
obj.createblock(5, 53-32, 0, 32+32+32, 240, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx == 104 && ry == 111)
|
|
|
|
{
|
|
|
|
//Red
|
|
|
|
//First: is he rescued?
|
|
|
|
if (game.crewstats[3])
|
|
|
|
{
|
|
|
|
//If so, red will always be at his post
|
|
|
|
obj.createentity(107, 121, 18, 15, 0, 18);
|
|
|
|
//What script do we use?
|
|
|
|
obj.createblock(5, 107-32, 0, 32+32+32, 240, 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rx == 103 && ry == 111)
|
|
|
|
{
|
|
|
|
//Yellow
|
|
|
|
//First: is he rescued?
|
|
|
|
if (game.crewstats[2])
|
|
|
|
{
|
|
|
|
obj.createentity(198, 105, 18, 14, 0, 18);
|
|
|
|
//What script do we use?
|
|
|
|
obj.createblock(5, 198-32, 0, 32+32+32, 240, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rx == 103 && ry == 110)
|
|
|
|
{
|
|
|
|
//Green
|
|
|
|
//First: is he rescued?
|
|
|
|
if (game.crewstats[4])
|
|
|
|
{
|
|
|
|
obj.createentity(242, 177, 18, 13, 0, 18);
|
|
|
|
//What script do we use?
|
|
|
|
obj.createblock(5, 242-32, 177-20, 32+32+32, 40, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rx == 104 && ry == 110)
|
|
|
|
{
|
|
|
|
//Purple
|
|
|
|
//First: is she rescued?
|
|
|
|
if (game.crewstats[1])
|
|
|
|
{
|
|
|
|
obj.createentity(140, 177, 18, 20, 0, 18);
|
|
|
|
//What script do we use?
|
|
|
|
obj.createblock(5, 140-32, 0, 32+32+32, 240, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rx == 102 && ry == 110)
|
|
|
|
{
|
|
|
|
//Blue
|
|
|
|
//First: is she rescued?
|
|
|
|
if (game.crewstats[5])
|
|
|
|
{
|
|
|
|
//A slight varation - she's upside down
|
|
|
|
obj.createentity(249, 62, 18, 16, 0, 18);
|
|
|
|
int j = obj.getcrewman(BLUE);
|
|
|
|
if (INBOUNDS_VEC(j, obj.entities))
|
|
|
|
{
|
|
|
|
obj.entities[j].rule = 7;
|
|
|
|
obj.entities[j].tile +=6;
|
|
|
|
}
|
|
|
|
//What script do we use?
|
|
|
|
obj.createblock(5, 249-32, 0, 32+32+32, 240, 5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
Fix the two-frame-delay when entering a room with an "init" script
This patch is very kludge-y, but at least it fixes a semi-noticeable
visual issue in custom levels that use internal scripts to spawn
entities when loading a room.
Basically, the problem here is that when the game checks for script
boxes and sets newscript, newscript has already been processed for that
frame, and when the game does load a script, script.run() has already
been processed for that frame.
That issue can be fixed, but it turns out that due to my over-30-FPS
game loop changes, there's now ANOTHER visible frame of delay between
room load and entity creation, because the render function gets called
in between the script being loaded at the end of gamelogic() and the
script actually getting run.
So... I have to temporary move script.run() to the end of gamelogic()
(in map.twoframedelayfix()), and make sure it doesn't get run next
frame, because double-evaluations are bad. To do that, I have to
introduce the kludge variable script.dontrunnextframe, which does
exactly as it says.
And with all that work, the two-frame (now three-frame) delay is fixed.
2020-06-28 02:08:57 +02:00
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void mapclass::twoframedelayfix(void)
|
Fix the two-frame-delay when entering a room with an "init" script
This patch is very kludge-y, but at least it fixes a semi-noticeable
visual issue in custom levels that use internal scripts to spawn
entities when loading a room.
Basically, the problem here is that when the game checks for script
boxes and sets newscript, newscript has already been processed for that
frame, and when the game does load a script, script.run() has already
been processed for that frame.
That issue can be fixed, but it turns out that due to my over-30-FPS
game loop changes, there's now ANOTHER visible frame of delay between
room load and entity creation, because the render function gets called
in between the script being loaded at the end of gamelogic() and the
script actually getting run.
So... I have to temporary move script.run() to the end of gamelogic()
(in map.twoframedelayfix()), and make sure it doesn't get run next
frame, because double-evaluations are bad. To do that, I have to
introduce the kludge variable script.dontrunnextframe, which does
exactly as it says.
And with all that work, the two-frame (now three-frame) delay is fixed.
2020-06-28 02:08:57 +02:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
// Fixes the two-frame delay in custom levels that use scripts to spawn an entity upon room load.
|
|
|
|
// Because when the room loads and newscript is set to run, newscript has already ran for that frame,
|
|
|
|
// and when the script gets loaded script.run() has already ran for that frame, too.
|
|
|
|
// A bit kludge-y, but it's the least we can do without changing the frame ordering.
|
|
|
|
|
|
|
|
if (GlitchrunnerMode_less_than_or_equal(Glitchrunner2_2)
|
2024-07-05 23:59:15 +02:00
|
|
|
|| !custommode
|
|
|
|
|| game.deathseq != -1)
|
2021-09-07 03:56:39 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
int block_idx = -1;
|
|
|
|
// obj.checktrigger() sets block_idx
|
|
|
|
int activetrigger = obj.checktrigger(&block_idx);
|
|
|
|
if (activetrigger <= -1
|
2024-07-05 23:59:15 +02:00
|
|
|
|| !INBOUNDS_VEC(block_idx, obj.blocks)
|
|
|
|
|| activetrigger < 300)
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
game.newscript = obj.blocks[block_idx].script;
|
|
|
|
obj.removetrigger(activetrigger);
|
2022-12-07 00:20:48 +01:00
|
|
|
game.setstate(0);
|
2022-12-07 00:35:06 +01:00
|
|
|
game.setstatedelay(0);
|
2021-09-07 03:56:39 +02:00
|
|
|
script.load(game.newscript);
|
Fix the two-frame-delay when entering a room with an "init" script
This patch is very kludge-y, but at least it fixes a semi-noticeable
visual issue in custom levels that use internal scripts to spawn
entities when loading a room.
Basically, the problem here is that when the game checks for script
boxes and sets newscript, newscript has already been processed for that
frame, and when the game does load a script, script.run() has already
been processed for that frame.
That issue can be fixed, but it turns out that due to my over-30-FPS
game loop changes, there's now ANOTHER visible frame of delay between
room load and entity creation, because the render function gets called
in between the script being loaded at the end of gamelogic() and the
script actually getting run.
So... I have to temporary move script.run() to the end of gamelogic()
(in map.twoframedelayfix()), and make sure it doesn't get run next
frame, because double-evaluations are bad. To do that, I have to
introduce the kludge variable script.dontrunnextframe, which does
exactly as it says.
And with all that work, the two-frame (now three-frame) delay is fixed.
2020-06-28 02:08:57 +02:00
|
|
|
}
|
2024-07-05 23:59:15 +02:00
|
|
|
|
|
|
|
MapRenderData mapclass::get_render_data(void)
|
|
|
|
{
|
|
|
|
MapRenderData data;
|
|
|
|
data.width = getwidth();
|
|
|
|
data.height = getheight();
|
|
|
|
|
|
|
|
data.startx = 0;
|
|
|
|
data.starty = 0;
|
|
|
|
|
|
|
|
// Region handling
|
|
|
|
if (region[currentregion].isvalid)
|
|
|
|
{
|
|
|
|
data.startx = region[currentregion].rx;
|
|
|
|
data.starty = region[currentregion].ry;
|
|
|
|
data.width = ((region[currentregion].rx2 - data.startx) + 1);
|
|
|
|
data.height = ((region[currentregion].ry2 - data.starty) + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
data.zoom = 1;
|
|
|
|
|
|
|
|
if (data.width <= 10 && data.height <= 10)
|
|
|
|
{
|
|
|
|
data.zoom = 2;
|
|
|
|
}
|
|
|
|
if (data.width <= 5 && data.height <= 5)
|
|
|
|
{
|
|
|
|
data.zoom = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.xoff = 0;
|
|
|
|
data.yoff = 0;
|
|
|
|
|
|
|
|
// Set minimap offsets
|
|
|
|
switch (data.zoom)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
data.xoff = 24 * (5 - data.width);
|
|
|
|
data.yoff = 18 * (5 - data.height);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
data.xoff = 12 * (10 - data.width);
|
|
|
|
data.yoff = 9 * (10 - data.height);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
data.xoff = 6 * (20 - data.width);
|
|
|
|
data.yoff = (int)(4.5 * (20 - data.height));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.pixelsx = 240 - (data.xoff * 2);
|
|
|
|
data.pixelsy = 180 - (data.yoff * 2);
|
|
|
|
|
|
|
|
data.legendxoff = 40 + data.xoff;
|
|
|
|
data.legendyoff = 21 + data.yoff;
|
|
|
|
|
|
|
|
// Magic numbers for centering legend tiles.
|
|
|
|
switch (data.zoom)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
data.legendxoff += 21;
|
|
|
|
data.legendyoff += 16;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
data.legendxoff += 9;
|
|
|
|
data.legendyoff += 5;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
data.legendxoff += 3;
|
|
|
|
data.legendyoff += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::setregion(int id, int rx, int ry, int rx2, int ry2)
|
|
|
|
{
|
2024-11-04 00:20:33 +01:00
|
|
|
if (INBOUNDS_ARR(id, region) && id > 0)
|
2024-07-05 23:59:15 +02:00
|
|
|
{
|
2024-11-04 02:48:11 +01:00
|
|
|
// swap the variables if they're entered in the wrong order
|
2024-11-04 06:16:26 +01:00
|
|
|
if (rx2 < rx)
|
2024-11-04 02:48:11 +01:00
|
|
|
{
|
|
|
|
int temp = rx;
|
|
|
|
rx = rx2;
|
|
|
|
rx2 = temp;
|
|
|
|
}
|
2024-11-04 06:16:33 +01:00
|
|
|
if (ry2 < ry)
|
2024-11-04 02:48:11 +01:00
|
|
|
{
|
|
|
|
int temp = ry;
|
|
|
|
ry = ry2;
|
|
|
|
ry2 = temp;
|
|
|
|
}
|
|
|
|
|
2024-07-05 23:59:15 +02:00
|
|
|
region[id].isvalid = true;
|
|
|
|
region[id].rx = SDL_clamp(rx, 0, cl.mapwidth - 1);
|
|
|
|
region[id].ry = SDL_clamp(ry, 0, cl.mapheight - 1);
|
|
|
|
region[id].rx2 = SDL_clamp(rx2, 0, cl.mapwidth - 1);
|
|
|
|
region[id].ry2 = SDL_clamp(ry2, 0, cl.mapheight - 1);
|
2024-11-04 00:20:33 +01:00
|
|
|
|
|
|
|
if (id == currentregion)
|
|
|
|
{
|
|
|
|
cl.generatecustomminimap();
|
|
|
|
}
|
2024-07-05 23:59:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::removeregion(int id)
|
|
|
|
{
|
2024-11-04 00:20:33 +01:00
|
|
|
if (INBOUNDS_ARR(id, region) && id > 0)
|
2024-07-05 23:59:15 +02:00
|
|
|
{
|
2024-11-03 23:16:57 +01:00
|
|
|
SDL_zero(region[id]);
|
2024-11-04 00:20:33 +01:00
|
|
|
|
|
|
|
if (id == currentregion)
|
|
|
|
{
|
|
|
|
cl.generatecustomminimap();
|
|
|
|
}
|
2024-07-05 23:59:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mapclass::changeregion(int id)
|
|
|
|
{
|
|
|
|
if (INBOUNDS_ARR(id, region))
|
|
|
|
{
|
|
|
|
currentregion = id;
|
|
|
|
cl.generatecustomminimap();
|
|
|
|
}
|
2024-11-04 00:20:33 +01:00
|
|
|
}
|