1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-22 17:49:43 +01:00

Teleporter edentities with custom curves

Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
This commit is contained in:
AllyTally 2022-11-03 17:09:50 -03:00 committed by NyakoFox
parent 6174d62f6f
commit 53619f191d
9 changed files with 396 additions and 18 deletions

View file

@ -599,6 +599,7 @@
<string english="I: Warp Lines" translation="" explanation="editor tool. Makes a room edge be connected to its opposite edge" max="32"/>
<string english="O: Crewmates" translation="" explanation="editor tool. Crewmate that can be rescued" max="32"/>
<string english="P: Start Point" translation="" explanation="editor tool" max="32"/>
<string english="^2: Teleporters" translation="" explanation="editor tool. Large round teleporter which can teleport the player to other teleporters" max="32"/>
<string english="START" translation="" explanation="start point in level editor" max="10"/>
<string english="SPACE ^ SHIFT ^" translation="" explanation="editor, indicates both SPACE key and SHIFT key open up menus. ^ is rendered as up arrow" max="32"/>
<string english="F1: Change Tileset" translation="" explanation="editor shortcut, switch to different tileset" max="25"/>

View file

@ -26,6 +26,10 @@
#include "Vlogging.h"
#define SCRIPT_LINE_PADDING 6
#define LERP(a, b, t) ((a) + (t) * ((b) - (a)))
#define POINT_OFFSET 12
#define POINT_SIZE 6
#define TELEPORTER_ARC_SMOOTHNESS 255
editorclass::editorclass(void)
{
@ -48,6 +52,7 @@ editorclass::editorclass(void)
register_tool(EditorTool_WARP_LINES, "Warp Lines", "I", SDLK_i, false);
register_tool(EditorTool_CREWMATES, "Crewmates", "O", SDLK_o, false);
register_tool(EditorTool_START_POINT, "Start Point", "P", SDLK_p, false);
register_tool(EditorTool_TELEPORTERS, "Teleporters", "^2", SDLK_2, true);
static const short basic[] = {
121, 121, 121, 121, 121, 121, 121, 160, 121, 121, 121, 121, 121, 121, 121,
@ -379,11 +384,18 @@ void editorclass::reset(void)
levy = 0;
keydelay = 0;
lclickdelay = 0;
rclickdelay = 0;
savekey = false;
loadkey = false;
updatetiles = true;
changeroom = true;
dragging = false;
dragging_entity = -1;
dragging_point = 1;
drag_offset_x = 0;
drag_offset_y = 0;
entframe = 0;
entframedelay = 0;
@ -1038,6 +1050,73 @@ static void draw_entities(void)
font::print(PR_BOR | PR_CJK_HIGH, x, y - 8, text, 210, 210, 255);
break;
}
case 14: // Teleporters
{
graphics.drawtele(x, y, 1, graphics.getcol(100));
graphics.draw_rect(x, y, 8 * 12, 8 * 12, graphics.getRGB(164, 164, 255));
int sprite = 0;
if (customentities[i].p5 % 2 == 0)
{
sprite += 3;
}
if (customentities[i].p5 >= 2)
{
sprite += 6;
}
graphics.draw_sprite(customentities[i].p3, customentities[i].p4, sprite, graphics.crewcolourreal(0));
SDL_Point triangle[4] = {
{ x + 37 + POINT_OFFSET, y + 37 + POINT_OFFSET },
{ customentities[i].p1 + POINT_OFFSET, customentities[i].p2 + POINT_OFFSET },
{ customentities[i].p3 + POINT_OFFSET, customentities[i].p4 + POINT_OFFSET },
{ x + 37 + POINT_OFFSET, y + 37 + POINT_OFFSET}
};
SDL_SetRenderDrawColor(gameScreen.m_renderer, 164, 255, 255, 255);
SDL_RenderDrawLines(gameScreen.m_renderer, triangle, SDL_arraysize(triangle));
SDL_Point points[TELEPORTER_ARC_SMOOTHNESS + 1];
for (int j = 0; j <= TELEPORTER_ARC_SMOOTHNESS; j++)
{
float progress = (float)j / TELEPORTER_ARC_SMOOTHNESS;
float left_line_x = LERP(x + 37 + POINT_OFFSET, customentities[i].p1 + POINT_OFFSET, progress);
float left_line_y = LERP(y + 37 + POINT_OFFSET, customentities[i].p2 + POINT_OFFSET, progress);
float right_line_x = LERP(customentities[i].p1 + POINT_OFFSET, customentities[i].p3 + POINT_OFFSET, progress);
float right_line_y = LERP(customentities[i].p2 + POINT_OFFSET, customentities[i].p4 + POINT_OFFSET, progress);
SDL_Point coords;
coords.x = LERP(left_line_x, right_line_x, progress);
coords.y = LERP(left_line_y, right_line_y, progress);
points[j] = coords;
}
SDL_SetRenderDrawColor(gameScreen.m_renderer, 164, 255, 164, 255);
SDL_RenderDrawLines(gameScreen.m_renderer, points, SDL_arraysize(points));
int offset = POINT_OFFSET - (POINT_SIZE / 2);
SDL_Color point1 = graphics.getRGB(255, 255, 255);
SDL_Color point2 = graphics.getRGB(255, 255, 255);
if (ed.dragging_entity == i && ed.dragging_point == 1)
{
point1 = graphics.getRGB(164, 164, 255);
}
else if (ed.dragging_entity == i && ed.dragging_point == 2)
{
point2 = graphics.getRGB(164, 164, 255);
}
graphics.draw_rect(customentities[i].p1 + offset, customentities[i].p2 + offset, POINT_SIZE, POINT_SIZE, point1);
graphics.draw_rect(customentities[i].p3 + offset, customentities[i].p4 + offset, POINT_SIZE, POINT_SIZE, point2);
break;
}
case 15: // Crewmates
graphics.draw_sprite(x - 4, y, 144, graphics.crewcolourreal(entity->p1));
graphics.draw_rect(x, y, 16, 24, graphics.getRGB(164, 164, 164));
@ -1383,6 +1462,10 @@ static void draw_cursor(void)
// 2x3
graphics.draw_rect(x, y, 16, 24, blue);
break;
case EditorTool_TELEPORTERS:
// 12x12
graphics.draw_rect(x, y, 96, 96, blue);
break;
default:
break;
}
@ -1757,6 +1840,15 @@ void editorclass::draw_tool(EditorTools tool, int x, int y)
case EditorTool_START_POINT:
graphics.draw_sprite(x, y, 184, graphics.col_crewcyan);
break;
case EditorTool_TELEPORTERS:
{
graphics.fill_rect(x, y, 16, 16, graphics.getRGB(16, 16, 16));
SDL_Color color = graphics.getcol(100);
graphics.set_texture_color_mod(graphics.grphx.im_teleporter, color.r, color.g, color.b);
graphics.draw_texture_part(graphics.grphx.im_teleporter, x, y, 136, 40, 16, 16, 1, 1);
graphics.set_texture_color_mod(graphics.grphx.im_teleporter, 255, 255, 255);
break;
}
default:
break;
}
@ -2646,6 +2738,18 @@ void editorclass::tool_place()
add_entity(levx, levy, tilex, tiley, 16, 0);
lclickdelay = 1;
break;
case EditorTool_TELEPORTERS:
{
lclickdelay = 1;
int point1x = SDL_clamp((tilex + 9) * 8, -POINT_OFFSET, SCREEN_WIDTH_PIXELS - POINT_OFFSET);
int point1y = SDL_clamp((tiley - 4) * 8 + 37, -POINT_OFFSET, SCREEN_HEIGHT_PIXELS - POINT_OFFSET);
int point2x = SDL_clamp((tilex + 16) * 8 + 38, -POINT_OFFSET, SCREEN_WIDTH_PIXELS - POINT_OFFSET);
int point2y = SDL_clamp((tiley + 8) * 8, -POINT_OFFSET, SCREEN_HEIGHT_PIXELS - POINT_OFFSET);
add_entity(levx, levy, tilex, tiley, 14, point1x, point1y, point2x, point2y, 1, 7);
break;
}
default:
break;
}
@ -2896,7 +3000,7 @@ static void start_at_checkpoint(void)
{
extern editorclass ed;
// Scan the room for a start point or a checkpoint, the start point taking priority
// Scan the room for a start point or a checkpoint/teleporter, the start point taking priority
int testeditor = -1;
bool startpoint = false;
@ -2904,7 +3008,8 @@ static void start_at_checkpoint(void)
{
startpoint = customentities[i].t == 16;
const bool is_startpoint_or_checkpoint = startpoint ||
customentities[i].t == 10;
customentities[i].t == 10 ||
customentities[i].t == 14;
if (!is_startpoint_or_checkpoint)
{
continue;
@ -2943,25 +3048,31 @@ static void start_at_checkpoint(void)
game.edsaverx = 100 + customentities[testeditor].rx;
game.edsavery = 100 + customentities[testeditor].ry;
game.edsavedir = 0;
game.edsavegc = 0;
if (!startpoint)
{
// Checkpoint spawn
if (customentities[testeditor].p1 == 0) // NOT a bool check!
if (customentities[testeditor].t == 14) {
// Actually, teleporter!
game.edsavex += 48;
game.edsavey += 44;
game.edsavedir = 1;
}
else if (customentities[testeditor].p1 == 0) // NOT a bool check!
{
game.edsavegc = 1;
game.edsavey -= 2;
}
else
{
game.edsavegc = 0;
game.edsavey -= 7;
}
game.edsavedir = 0;
}
else
{
// Start point spawn
game.edsavegc = 0;
game.edsavey++;
game.edsavedir = 1 - customentities[testeditor].p1;
}
@ -3171,6 +3282,78 @@ static void handle_draw_input()
}
}
void check_if_dragging(void)
{
extern editorclass ed;
ed.dragging_entity = -1;
// Is the mouse currently over a teleporter point? Loop through entities.
for (size_t i = 0; i < customentities.size(); i++)
{
// If it's not in the current room, continue.
if (customentities[i].x < ed.levx * 40 || customentities[i].x >= (ed.levx + 1) * 40 ||
customentities[i].y < ed.levy * 30 || customentities[i].y >= (ed.levy + 1) * 30)
{
continue;
}
// If it's not a teleporter, continue.
if (customentities[i].t != 14)
{
continue;
}
// Okay, it's a teleporter. First, is our mouse on a point?
SDL_Rect point = {
customentities[i].p3 + POINT_OFFSET - (POINT_SIZE / 2),
customentities[i].p4 + POINT_OFFSET - (POINT_SIZE / 2),
POINT_SIZE,
POINT_SIZE
};
SDL_Point mouse = { key.mousex, key.mousey };
if (SDL_PointInRect(&mouse, &point))
{
// We're on the second point!
ed.dragging_entity = i;
ed.dragging_point = 2;
ed.drag_offset_x = key.mousex - customentities[i].p3;
ed.drag_offset_y = key.mousey - customentities[i].p4;
if (key.leftbutton)
{
ed.dragging = true;
}
else if (key.rightbutton && (ed.rclickdelay == 0))
{
customentities[i].p5 = (customentities[i].p5 + 1) % 4;
ed.rclickdelay = 1;
}
break;
}
// Nope, let's check the other point...
point.x = customentities[i].p1 + POINT_OFFSET - (POINT_SIZE / 2);
point.y = customentities[i].p2 + POINT_OFFSET - (POINT_SIZE / 2);
if (SDL_PointInRect(&mouse, &point))
{
// We're on the first point!
ed.dragging_entity = i;
ed.dragging_point = 1;
ed.drag_offset_x = key.mousex - customentities[i].p1;
ed.drag_offset_y = key.mousey - customentities[i].p2;
if (key.leftbutton)
{
ed.dragging = true;
}
break;
}
}
}
void editorclass::get_input_line(const enum TextMode mode, const std::string& prompt, std::string* ptr)
{
state = EditorState_DRAW;
@ -3370,7 +3553,30 @@ void editorinput(void)
}
// Mouse input
if (key.leftbutton && ed.lclickdelay == 0)
if (ed.dragging)
{
if (key.leftbutton && INBOUNDS_VEC(ed.dragging_entity, customentities))
{
if (ed.dragging_point == 1)
{
customentities[ed.dragging_entity].p1 = key.mousex - ed.drag_offset_x;
customentities[ed.dragging_entity].p2 = key.mousey - ed.drag_offset_y;
}
else
{
customentities[ed.dragging_entity].p3 = key.mousex - ed.drag_offset_x;
customentities[ed.dragging_entity].p4 = key.mousey - ed.drag_offset_y;
}
}
else
{
ed.dragging = false;
}
}
check_if_dragging();
if ( key.leftbutton && ed.lclickdelay == 0 && !ed.dragging)
{
ed.tool_place();
}
@ -3379,11 +3585,16 @@ void editorinput(void)
ed.lclickdelay = 0;
}
if (key.rightbutton)
if (key.rightbutton && !ed.dragging)
{
ed.tool_remove();
}
if (!key.rightbutton)
{
ed.rclickdelay = 0;
}
if (key.middlebutton)
{
ed.direct_mode_tile = cl.gettile(ed.levx, ed.levy, ed.tilex, ed.tiley);

View file

@ -49,6 +49,7 @@ enum EditorTools
EditorTool_WARP_LINES,
EditorTool_CREWMATES,
EditorTool_START_POINT,
EditorTool_TELEPORTERS,
NUM_EditorTools
};
@ -227,7 +228,7 @@ public:
int old_tilex, old_tiley;
int tilex, tiley;
int keydelay, lclickdelay;
int keydelay, lclickdelay, rclickdelay;
bool savekey, loadkey;
int levx, levy;
int entframe, entframedelay;
@ -245,6 +246,13 @@ public:
};
bool x_modifier, z_modifier, c_modifier, v_modifier, b_modifier, h_modifier, f_modifier, toolbox_open;
bool dragging;
int dragging_entity;
int dragging_point;
int drag_offset_x;
int drag_offset_y;
int roomnamehide;
bool saveandquit;
bool help_open, shiftkey;

View file

@ -39,6 +39,8 @@ public:
float newxp, newyp;
bool isplatform;
int x1,y1,x2,y2;
int p1x, p1y, p2x, p2y, p3x, p3y;
int pathtime, pathmaxtime;
//Collision Rules
int onentity;
bool harmful;

View file

@ -17,6 +17,8 @@
#include "Vlogging.h"
#include "Xoshiro.h"
#define LERP(a, b, t) ((a) + (t) * ((b) - (a)))
static int getgridpoint( int t )
{
return t / 8;
@ -1256,7 +1258,7 @@ static void entityclonefix(entclass* entity)
}
}
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1, int p2, int p3, int p4)
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1, int p2, int p3, int p4, int p5, int p6)
{
k = entities.size();
@ -1319,6 +1321,7 @@ void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int
entity.xp = xp;
entity.yp = yp;
entity.type = t;
entity.pathmaxtime = -1;
switch(t)
{
case 0: //Player
@ -1625,6 +1628,12 @@ void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int
entity.onentity = 1;
entity.animate = 100;
entity.para = meta2;
entity.p1x = p1;
entity.p1y = p2;
entity.p2x = p3;
entity.p2y = p4;
entity.p3x = p5;
entity.p3y = p6;
break;
case 15: // Crew Member (warp zone)
entity.rule = 6;
@ -2184,6 +2193,11 @@ void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int
}
}
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1, int p2, int p3, int p4)
{
createentity(xp, yp, t, meta1, meta2, p1, p2, p3, p4, 320, 240);
}
void entityclass::createentity(int xp, int yp, int t, int meta1, int meta2, int p1, int p2)
{
createentity(xp, yp, t, meta1, meta2, p1, p2, 320, 240);
@ -3433,6 +3447,33 @@ bool entityclass::updateentities( int i )
}
}
if ((entities[i].pathmaxtime > -1) && (entities[i].type != 100))
{
float progress = (float) entities[i].pathtime / entities[i].pathmaxtime;
float left_line_x = LERP(entities[i].p1x, entities[i].p2x, progress);
float left_line_y = LERP(entities[i].p1y, entities[i].p2y, progress);
float right_line_x = LERP(entities[i].p2x, entities[i].p3x, progress);
float right_line_y = LERP(entities[i].p2y, entities[i].p3y, progress);
entities[i].xp = LERP(left_line_x, right_line_x, progress);
entities[i].yp = LERP(left_line_y, right_line_y, progress);
entities[i].ax = 0;
entities[i].ay = 0;
entities[i].vx = 0;
entities[i].vy = 0;
if (entities[i].pathtime == entities[i].pathmaxtime)
{
entities[i].pathmaxtime = -1;
}
else
{
entities[i].pathtime++;
}
}
return false;
}

View file

@ -82,6 +82,8 @@ public:
void revertlinecross(std::vector<entclass>& linecrosskludge, int t, int s);
void createentity(int xp, int yp, int t, int meta1, int meta2,
int p1, int p2, int p3, int p4, int p5, int p6);
void createentity(int xp, int yp, int t, int meta1, int meta2,
int p1, int p2, int p3, int p4);
void createentity(int xp, int yp, int t, int meta1, int meta2,

View file

@ -4566,6 +4566,84 @@ void Game::updatestate(void)
}
setstate(0);
break;
case 4100:
//Activating a teleporter (default appear)
state++;
statedelay = 15;
flashlight = 5;
screenshake = 90;
music.playef(9);
break;
case 4101:
//Activating a teleporter 2
state++;
statedelay = 0;
flashlight = 5;
screenshake = 0;
music.playef(10);
break;
case 4102:
{
//Activating a teleporter 2
state++;
statedelay = 5;
int time = 8;
int i = obj.getplayer();
int j = obj.getteleporter();
if (INBOUNDS_VEC(i, obj.entities))
{
obj.entities[i].colour = 0;
obj.entities[i].invis = false;
obj.entities[i].dir = 1;
obj.entities[i].ay = -6;
obj.entities[i].ax = 6;
obj.entities[i].vy = -6;
obj.entities[i].vx = 6;
time = 0;
obj.entities[i].p1x = 320 / 2;
obj.entities[i].p1y = 240 / 2;
obj.entities[i].p2x = 320 / 2;
obj.entities[i].p2y = 240 / 2;
obj.entities[i].p3x = 320 / 2;
obj.entities[i].p3y = 240 / 2;
if (INBOUNDS_VEC(j, obj.entities))
{
// NOTE: Using 37 instead of 44 for better centering!
obj.entities[i].xp = obj.entities[j].xp + 37;
obj.entities[i].yp = obj.entities[j].yp + 37;
obj.entities[i].lerpoldxp = obj.entities[i].xp;
obj.entities[i].lerpoldyp = obj.entities[i].yp;
obj.entities[j].tile = 2;
obj.entities[j].colour = 101;
obj.entities[i].p1x = obj.entities[j].xp + 37;
obj.entities[i].p1y = obj.entities[j].yp + 37;
obj.entities[i].p2x = obj.entities[j].p1x;
obj.entities[i].p2y = obj.entities[j].p1y;
obj.entities[i].p3x = obj.entities[j].p2x;
obj.entities[i].p3y = obj.entities[j].p2y;
obj.entities[i].dir = obj.entities[j].p3x % 2; // 0 or 2 = left, 1 or 3 = right
gravitycontrol = obj.entities[j].p3x > 1; // 0 or 1 = floor, 2 or 3 = ceiling
time = obj.entities[j].p3y;
}
obj.entities[i].pathtime = 0;
obj.entities[i].pathmaxtime = time;
}
statedelay = time + 15;
state = 4109;
break;
}
case 4109:
hascontrol = true;
advancetext = false;
state = 0;
break;
}
}
}

View file

@ -1,6 +1,8 @@
#define MAP_DEFINITION
#include "Map.h"
#include <algorithm>
#include "Alloc.h"
#include "Constants.h"
#include "CustomLevels.h"
@ -420,19 +422,44 @@ void mapclass::roomnamechange(const int x, const int y, const char** lines, cons
specialroomnames.push_back(roomname);
}
static bool compareTeleporterPoints(SDL_Point a, SDL_Point b)
{
if (a.x == b.x)
{
return a.y < b.y;
}
return a.x < b.x;
}
void mapclass::initcustommapdata(void)
{
shinytrinkets.clear();
teleporters.clear();
std::vector<SDL_Point> teleporters;
for (size_t i = 0; i < customentities.size(); i++)
{
const CustomEntity& ent = customentities[i];
if (ent.t != 9)
if (ent.t == 9)
{
continue;
settrinket(ent.rx, ent.ry);
}
else if (ent.t == 14)
{
SDL_Point temp;
temp.x = ent.rx;
temp.y = ent.ry;
teleporters.push_back(temp);
}
}
settrinket(ent.rx, ent.ry);
std::sort(teleporters.begin(), teleporters.end(), compareTeleporterPoints);
for (size_t i = 0; i < teleporters.size(); i++)
{
setteleporter(teleporters[i].x, teleporters[i].y);
}
}
@ -1896,6 +1923,9 @@ void mapclass::loadlevel(int rx, int ry)
case 13: // Warp Tokens
obj.createentity(ex, ey, 13, ent.p1, ent.p2);
break;
case 14: // Teleporter
obj.createentity(ex, ey, 14, 0, ((rx + (ry * 100)) * 20) + tempcheckpoints, ent.p1, ent.p2, ent.p3, ent.p4, ent.p5, ent.p6);
break;
case 15: // Collectable crewmate
obj.createentity(ex - 4, ey + 1, 55, cl.findcrewmate(edi), ent.p1, ent.p2);
break;

View file

@ -3041,7 +3041,7 @@ void scriptclass::teleport(void)
{
obj.entities[i].xp = 150;
obj.entities[i].yp = 110;
if(game.teleport_to_x==17 && game.teleport_to_y==17) obj.entities[i].xp = 88; //prevent falling!
if(game.teleport_to_x==17 && game.teleport_to_y==17 && !map.custommode) obj.entities[i].xp = 88; //prevent falling!
obj.entities[i].lerpoldxp = obj.entities[i].xp;
obj.entities[i].lerpoldyp = obj.entities[i].yp;
}
@ -3082,7 +3082,11 @@ void scriptclass::teleport(void)
game.savedir = obj.entities[player].dir;
}
if(game.teleport_to_x==0 && game.teleport_to_y==0)
if (map.custommode)
{
game.state = 4100;
}
else if(game.teleport_to_x==0 && game.teleport_to_y==0)
{
game.setstate(4020);
}
@ -3128,7 +3132,7 @@ void scriptclass::teleport(void)
else
{
//change music based on location
if (game.teleport_to_x == 2 && game.teleport_to_y == 11)
if (game.teleport_to_x == 2 && game.teleport_to_y == 11 && !map.custommode)
{
/* Special case: Ship music needs to be set here;
* ship teleporter on music map is -1 for jukebox. */
@ -3340,6 +3344,7 @@ void scriptclass::hardreset(void)
int theplayer = obj.getplayer();
if (INBOUNDS_VEC(theplayer, obj.entities)){
obj.entities[theplayer].tile = 0;
obj.entities[theplayer].pathmaxtime = -1;
}
/* Disable duplicate player entities */