diff --git a/desktop_version/src/Editor.cpp b/desktop_version/src/Editor.cpp index 7ef87371b16..f6735455025 100644 --- a/desktop_version/src/Editor.cpp +++ b/desktop_version/src/Editor.cpp @@ -27,6 +27,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) { @@ -49,6 +53,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); } void editorclass::reset(void) @@ -89,11 +94,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; @@ -716,6 +728,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)); @@ -988,6 +1067,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; } @@ -1362,6 +1445,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; } @@ -2192,6 +2284,20 @@ void editorclass::tool_place() add_entity(tilex + (levx * 40), tiley + (levy * 30), 16, 0); lclickdelay = 1; break; + case EditorTool_TELEPORTERS: + { + lclickdelay = 1; + int x = tilex + (levx * 40); + int y = tiley + (levy * 30); + + int point1x = SDL_clamp((x + 9) * 8, -POINT_OFFSET, SCREEN_WIDTH_PIXELS - POINT_OFFSET); + int point1y = SDL_clamp((y - 4) * 8 + 37, -POINT_OFFSET, SCREEN_HEIGHT_PIXELS - POINT_OFFSET); + int point2x = SDL_clamp((x + 16) * 8 + 38, -POINT_OFFSET, SCREEN_WIDTH_PIXELS - POINT_OFFSET); + int point2y = SDL_clamp((y + 8) * 8, -POINT_OFFSET, SCREEN_HEIGHT_PIXELS - POINT_OFFSET); + + add_entity(x, y, 14, point1x, point1y, point2x, point2y, 1, 7); + break; + } default: break; } @@ -2444,7 +2550,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; @@ -2452,7 +2558,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; @@ -2493,26 +2600,31 @@ static void start_at_checkpoint(void) game.edsavey = (customentities[testeditor].y % 30) * 8; game.edsaverx = 100 + tx; game.edsavery = 100 + ty; + 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; } @@ -2668,6 +2780,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.mx, key.my }; + + if (SDL_PointInRect(&mouse, &point)) + { + // We're on the second point! + ed.dragging_entity = i; + ed.dragging_point = 2; + ed.drag_offset_x = key.mx - customentities[i].p3; + ed.drag_offset_y = key.my - 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.mx - customentities[i].p1; + ed.drag_offset_y = key.my - 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; @@ -2867,7 +3051,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.mx - ed.drag_offset_x; + customentities[ed.dragging_entity].p2 = key.my - ed.drag_offset_y; + } + else + { + customentities[ed.dragging_entity].p3 = key.mx - ed.drag_offset_x; + customentities[ed.dragging_entity].p4 = key.my - ed.drag_offset_y; + } + } + else + { + ed.dragging = false; + } + } + + check_if_dragging(); + + if ( key.leftbutton && ed.lclickdelay == 0 && !ed.dragging) { ed.tool_place(); } @@ -2876,11 +3083,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); diff --git a/desktop_version/src/Editor.h b/desktop_version/src/Editor.h index ae911eb4832..1033477359a 100644 --- a/desktop_version/src/Editor.h +++ b/desktop_version/src/Editor.h @@ -28,6 +28,7 @@ enum EditorTools EditorTool_WARP_LINES, EditorTool_CREWMATES, EditorTool_START_POINT, + EditorTool_TELEPORTERS, NUM_EditorTools }; @@ -197,7 +198,7 @@ class editorclass int old_tilex, old_tiley; int tilex, tiley; - int keydelay, lclickdelay; + int keydelay, lclickdelay, rclickdelay; bool savekey, loadkey; int levx, levy; int entframe, entframedelay; @@ -215,6 +216,13 @@ class editorclass }; bool x_modifier, z_modifier, c_modifier, v_modifier, b_modifier, h_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; diff --git a/desktop_version/src/Ent.h b/desktop_version/src/Ent.h index f4ddd43c348..ce6930f7fe2 100644 --- a/desktop_version/src/Ent.h +++ b/desktop_version/src/Ent.h @@ -39,6 +39,8 @@ class entclass 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; diff --git a/desktop_version/src/Entity.cpp b/desktop_version/src/Entity.cpp index a50d9722ade..6528b9333fb 100644 --- a/desktop_version/src/Entity.cpp +++ b/desktop_version/src/Entity.cpp @@ -1,6 +1,7 @@ #define OBJ_DEFINITION #include "Entity.h" +#include #include #include "CustomLevels.h" @@ -17,6 +18,8 @@ #include "Vlogging.h" #include "Xoshiro.h" +#define LERP(a, b, t) ((a) + (t) * ((b) - (a))) + static int getgridpoint( int t ) { return t / 8; @@ -1239,7 +1242,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(); @@ -1298,6 +1301,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 @@ -1604,6 +1608,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; @@ -2163,6 +2173,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); @@ -3410,6 +3425,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; } diff --git a/desktop_version/src/Entity.h b/desktop_version/src/Entity.h index 796918b304f..556c1791acd 100644 --- a/desktop_version/src/Entity.h +++ b/desktop_version/src/Entity.h @@ -82,6 +82,8 @@ class entityclass void revertlinecross(std::vector& 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, diff --git a/desktop_version/src/Game.cpp b/desktop_version/src/Game.cpp index 199936a10a0..29874d44ef1 100644 --- a/desktop_version/src/Game.cpp +++ b/desktop_version/src/Game.cpp @@ -4207,6 +4207,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; } } } diff --git a/desktop_version/src/Map.cpp b/desktop_version/src/Map.cpp index 99bffbf517d..4b185673232 100644 --- a/desktop_version/src/Map.cpp +++ b/desktop_version/src/Map.cpp @@ -1,6 +1,8 @@ #define MAP_DEFINITION #include "Map.h" +#include + #include "Alloc.h" #include "Constants.h" #include "CustomLevels.h" @@ -416,23 +418,46 @@ 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(); #if !defined(NO_CUSTOM_LEVELS) + + std::vector teleporters; + for (size_t i = 0; i < customentities.size(); i++) { const CustomEntity& ent = customentities[i]; - if (ent.t != 9) + + SDL_Point temp; + temp.x = ent.x / 40; + temp.y = ent.y / 30; + if (ent.t == 9) { - continue; + settrinket(temp.x, temp.y); } + else if (ent.t == 14) + { + teleporters.push_back(temp); + } + } - const int rx = ent.x / 40; - const int ry = ent.y / 30; + std::sort(teleporters.begin(), teleporters.end(), compareTeleporterPoints); - settrinket(rx, ry); + for (size_t i = 0; i < teleporters.size(); i++) + { + setteleporter(teleporters[i].x, teleporters[i].y); } #endif } @@ -1892,6 +1917,11 @@ 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; diff --git a/desktop_version/src/Script.cpp b/desktop_version/src/Script.cpp index 341a58656fc..5435d793f78 100644 --- a/desktop_version/src/Script.cpp +++ b/desktop_version/src/Script.cpp @@ -2980,7 +2980,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; } @@ -3021,7 +3021,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); } @@ -3067,7 +3071,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. */ @@ -3272,6 +3276,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 */