diff --git a/desktop_version/lang/ar/strings.xml b/desktop_version/lang/ar/strings.xml index 7c192c43..67f2ccee 100644 --- a/desktop_version/lang/ar/strings.xml +++ b/desktop_version/lang/ar/strings.xml @@ -254,6 +254,14 @@ + + + + + + + + diff --git a/desktop_version/lang/ca/strings.xml b/desktop_version/lang/ca/strings.xml index fa956d2f..71ae192e 100644 --- a/desktop_version/lang/ca/strings.xml +++ b/desktop_version/lang/ca/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/cy/strings.xml b/desktop_version/lang/cy/strings.xml index 90b23913..3980ffd8 100644 --- a/desktop_version/lang/cy/strings.xml +++ b/desktop_version/lang/cy/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/de/strings.xml b/desktop_version/lang/de/strings.xml index e869c9db..b90f35c7 100644 --- a/desktop_version/lang/de/strings.xml +++ b/desktop_version/lang/de/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/en/strings.xml b/desktop_version/lang/en/strings.xml index ee3db7b6..4a4dfa47 100644 --- a/desktop_version/lang/en/strings.xml +++ b/desktop_version/lang/en/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/eo/strings.xml b/desktop_version/lang/eo/strings.xml index cc34e896..f3b14c5b 100644 --- a/desktop_version/lang/eo/strings.xml +++ b/desktop_version/lang/eo/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/es/strings.xml b/desktop_version/lang/es/strings.xml index c2cfae35..9776ff5c 100644 --- a/desktop_version/lang/es/strings.xml +++ b/desktop_version/lang/es/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/es_419/strings.xml b/desktop_version/lang/es_419/strings.xml index ea6a1cf9..d4c13fc9 100644 --- a/desktop_version/lang/es_419/strings.xml +++ b/desktop_version/lang/es_419/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/es_AR/strings.xml b/desktop_version/lang/es_AR/strings.xml index e9bf98b1..88909cf1 100644 --- a/desktop_version/lang/es_AR/strings.xml +++ b/desktop_version/lang/es_AR/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/fr/strings.xml b/desktop_version/lang/fr/strings.xml index b7f69dc4..448984c8 100644 --- a/desktop_version/lang/fr/strings.xml +++ b/desktop_version/lang/fr/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/ga/strings.xml b/desktop_version/lang/ga/strings.xml index bb19ca2c..80822248 100644 --- a/desktop_version/lang/ga/strings.xml +++ b/desktop_version/lang/ga/strings.xml @@ -250,6 +250,14 @@ Déan cóip chúltaca, ar eagla na heagla." explanation="translation maintenance + + + + + + + + diff --git a/desktop_version/lang/it/strings.xml b/desktop_version/lang/it/strings.xml index 4a3c32f3..ea96b996 100644 --- a/desktop_version/lang/it/strings.xml +++ b/desktop_version/lang/it/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/ja/strings.xml b/desktop_version/lang/ja/strings.xml index 0586db9e..5110c1b8 100644 --- a/desktop_version/lang/ja/strings.xml +++ b/desktop_version/lang/ja/strings.xml @@ -263,6 +263,14 @@ Escキーを押すと表示を終了する。" explanation="" max="38*6" max_loc + + + + + + + + diff --git a/desktop_version/lang/ko/strings.xml b/desktop_version/lang/ko/strings.xml index 10ff2981..41215e35 100755 --- a/desktop_version/lang/ko/strings.xml +++ b/desktop_version/lang/ko/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/nl/strings.xml b/desktop_version/lang/nl/strings.xml index 09a551ae..c467bd62 100644 --- a/desktop_version/lang/nl/strings.xml +++ b/desktop_version/lang/nl/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/pl/strings.xml b/desktop_version/lang/pl/strings.xml index c88a28fa..11bdc8f9 100644 --- a/desktop_version/lang/pl/strings.xml +++ b/desktop_version/lang/pl/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/pt_BR/strings.xml b/desktop_version/lang/pt_BR/strings.xml index dd5d677b..343cd958 100644 --- a/desktop_version/lang/pt_BR/strings.xml +++ b/desktop_version/lang/pt_BR/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/pt_PT/strings.xml b/desktop_version/lang/pt_PT/strings.xml index 51ca859b..4a1490f1 100644 --- a/desktop_version/lang/pt_PT/strings.xml +++ b/desktop_version/lang/pt_PT/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/ru/strings.xml b/desktop_version/lang/ru/strings.xml index 3b310c88..3b3bbc17 100644 --- a/desktop_version/lang/ru/strings.xml +++ b/desktop_version/lang/ru/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/szl/strings.xml b/desktop_version/lang/szl/strings.xml index 38640252..f11447aa 100644 --- a/desktop_version/lang/szl/strings.xml +++ b/desktop_version/lang/szl/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/tr/strings.xml b/desktop_version/lang/tr/strings.xml index cd98037d..87fa08e0 100644 --- a/desktop_version/lang/tr/strings.xml +++ b/desktop_version/lang/tr/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/uk/strings.xml b/desktop_version/lang/uk/strings.xml index 33c6fa97..6c560b19 100644 --- a/desktop_version/lang/uk/strings.xml +++ b/desktop_version/lang/uk/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/zh/strings.xml b/desktop_version/lang/zh/strings.xml index a9b83ef0..56760fe1 100644 --- a/desktop_version/lang/zh/strings.xml +++ b/desktop_version/lang/zh/strings.xml @@ -255,6 +255,14 @@ + + + + + + + + diff --git a/desktop_version/lang/zh_TW/strings.xml b/desktop_version/lang/zh_TW/strings.xml index 9d58c94e..0072b485 100644 --- a/desktop_version/lang/zh_TW/strings.xml +++ b/desktop_version/lang/zh_TW/strings.xml @@ -255,6 +255,14 @@ + + + + + + + + diff --git a/desktop_version/src/Entity.cpp b/desktop_version/src/Entity.cpp index 7fe5efec..0d5a9ed5 100644 --- a/desktop_version/src/Entity.cpp +++ b/desktop_version/src/Entity.cpp @@ -2810,6 +2810,14 @@ bool entityclass::updateentities( int i ) { if (INBOUNDS_VEC(temp, entities) && entities[temp].vy > -3) entities[temp].vy = -3; } + + if (game.glitchlessmode && INBOUNDS_VEC(temp, entities)) + { + /* Fix line clipping: Invalidate flipping eligibility so + * a second flip is impossible. */ + entities[temp].onground = 0; + entities[temp].onroof = 0; + } } else if (entities[i].state == 2) { @@ -3974,6 +3982,10 @@ int entityclass::getscm(void) } } + if (game.glitchlessmode) + { + return -1; + } return 0; } @@ -3991,6 +4003,10 @@ int entityclass::getlineat( int t ) } } + if (game.glitchlessmode) + { + return -1; + } return 0; } @@ -4010,6 +4026,10 @@ int entityclass::getcrewman( int t, int fallback /*= 0*/ ) } } + if (game.glitchlessmode) + { + return -1; + } return fallback; } @@ -4034,6 +4054,10 @@ int entityclass::getcustomcrewman( int t ) } } + if (game.glitchlessmode) + { + return -1; + } return 0; } @@ -4271,31 +4295,35 @@ static int yline( int a, int b ) return 1; } -bool entityclass::entityhlinecollide( int t, int l ) +bool entityclass::entityhlinecollide(const int person_idx, const int line_idx) { - if (!INBOUNDS_VEC(t, entities) || !INBOUNDS_VEC(l, entities)) + if (!INBOUNDS_VEC(person_idx, entities) || !INBOUNDS_VEC(line_idx, entities)) { vlog_error("entityhlinecollide() out-of-bounds!"); return false; } - //Returns true is entity t collided with the horizontal line l. - if(entities[t].xp + entities[t].cx+entities[t].w>=entities[l].xp) + const entclass* person = &entities[person_idx]; + const entclass* line = &entities[line_idx]; + + const bool in_vicinity = + person->xp + person->cx + person->w >= line->xp && + person->xp + person->cx <= line->xp + line->w; + if (!in_vicinity) { - if(entities[t].xp + entities[t].cx<=entities[l].xp+entities[l].w) - { - int linetemp = 0; - - linetemp += yline(entities[t].yp, entities[l].yp); - linetemp += yline(entities[t].yp + entities[t].h, entities[l].yp); - linetemp += yline(entities[t].oldyp, entities[l].yp); - linetemp += yline(entities[t].oldyp + entities[t].h, entities[l].yp); - - if (linetemp > -4 && linetemp < 4) return true; - return false; - } + return false; } - return false; + + /* Here we compare the person's old position versus their new one. + * All points are either above or below the line. Else, it's a collision. */ + int linetemp = 0; + + linetemp += yline(person->yp, line->yp); + linetemp += yline(person->yp + person->h, line->yp); + linetemp += yline(person->oldyp, line->yp); + linetemp += yline(person->oldyp + person->h, line->yp); + + return linetemp > -4 && linetemp < 4; } bool entityclass::entityvlinecollide( int t, int l ) @@ -4877,32 +4905,40 @@ void entityclass::collisioncheck(int i, int j, bool scm /*= false*/) } break; case 4: //Person vs horizontal line! - if(game.deathseq==-1) + { + const bool collision = + game.deathseq == -1 + && entities[j].onentity > 0 + && entityhlinecollide(i, j); + if (!collision) { - //Here we compare the person's old position versus his new one versus the line. - //All points either be above or below it. Otherwise, there was a collision this frame. - if (entities[j].onentity > 0) - { - if (entityhlinecollide(i, j)) - { - music.playef(Sound_GRAVITYLINE); - game.gravitycontrol = (game.gravitycontrol + 1) % 2; - game.totalflips++; - if (game.gravitycontrol == 0) - { - if (entities[i].vy < 1) entities[i].vy = 1; - } - else - { - if (entities[i].vy > -1) entities[i].vy = -1; - } + break; + } - entities[j].state = entities[j].onentity; - entities[j].life = 6; - } - } + music.playef(Sound_GRAVITYLINE); + game.gravitycontrol = (game.gravitycontrol + 1) % 2; + game.totalflips++; + if (game.gravitycontrol == 0) + { + if (entities[i].vy < 1) entities[i].vy = 1; + } + else + { + if (entities[i].vy > -1) entities[i].vy = -1; + } + + entities[j].state = entities[j].onentity; + entities[j].life = 6; + + if (game.glitchlessmode) + { + /* Fix line clipping: Invalidate flipping eligibility so + * a second flip is impossible. */ + entities[i].onground = 0; + entities[i].onroof = 0; } break; + } case 5: //Person vs vertical gravity/warp line! if(game.deathseq==-1) { diff --git a/desktop_version/src/Game.cpp b/desktop_version/src/Game.cpp index 201007c2..d06e26d1 100644 --- a/desktop_version/src/Game.cpp +++ b/desktop_version/src/Game.cpp @@ -154,6 +154,7 @@ void Game::init(void) musicmutebutton = 0; glitchrunkludge = false; + glitchlessmode = false; gamestate = TITLEMODE; prevgamestate = TITLEMODE; hascontrol = true; @@ -4857,6 +4858,11 @@ void Game::deserializesettings(tinyxml2::XMLElement* dataNode, struct ScreenSett GlitchrunnerMode_set(GlitchrunnerMode_string_to_enum(pText)); } + if (SDL_strcmp(pKey, "glitchlessmode") == 0) + { + glitchlessmode = help.Int(pText); + } + if (SDL_strcmp(pKey, "showingametimer") == 0) { showingametimer = help.Int(pText); @@ -4974,6 +4980,14 @@ void Game::deserializesettings(tinyxml2::XMLElement* dataNode, struct ScreenSett } setdefaultcontrollerbuttons(); + + if (GlitchrunnerMode_get() != GlitchrunnerNone && glitchlessmode) + { + /* Glitchrunner and glitchless mode are incompatible. + * If the file was manually edited to enable both, disable both. */ + GlitchrunnerMode_set(GlitchrunnerNone); + glitchlessmode = false; + } } bool Game::savestats(bool sync /*= true*/) @@ -5150,6 +5164,8 @@ void Game::serializesettings(tinyxml2::XMLElement* dataNode, const struct Screen GlitchrunnerMode_enum_to_string(GlitchrunnerMode_get()) ); + xml::update_tag(dataNode, "glitchlessmode", (int) glitchlessmode); + xml::update_tag(dataNode, "showingametimer", (int) showingametimer); xml::update_tag(dataNode, "vsync", (int) screen_settings->useVsync); @@ -6946,7 +6962,8 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) maxspacing = 15; break; case Menu::speedrunneroptions: - option(loc::gettext("glitchrunner mode")); + option(loc::gettext("glitchrunner mode"), !glitchlessmode); + option(loc::gettext("glitchless mode"), GlitchrunnerMode_get() == GlitchrunnerNone); option(loc::gettext("input delay")); option(loc::gettext("interact button")); option(loc::gettext("fake load screen")); diff --git a/desktop_version/src/Game.h b/desktop_version/src/Game.h index 05817b05..d4581714 100644 --- a/desktop_version/src/Game.h +++ b/desktop_version/src/Game.h @@ -332,6 +332,7 @@ public: int state, statedelay; bool glitchrunkludge; + bool glitchlessmode; enum GameGamestate gamestate; enum GameGamestate prevgamestate; //only used sometimes diff --git a/desktop_version/src/Input.cpp b/desktop_version/src/Input.cpp index 1599dc4a..234331db 100644 --- a/desktop_version/src/Input.cpp +++ b/desktop_version/src/Input.cpp @@ -787,36 +787,56 @@ static void menuactionpress(void) { case 0: // Glitchrunner mode + if (game.glitchlessmode) + { + music.playef(Sound_CRY); + break; + } + music.playef(Sound_VIRIDIAN); game.createmenu(Menu::setglitchrunner); game.currentmenuoption = GlitchrunnerMode_get(); map.nexttowercolour(); break; case 1: + /* Glitchless mode */ + if (GlitchrunnerMode_get() != GlitchrunnerNone) + { + music.playef(2); + break; + } + + music.playef(11); + game.glitchlessmode = !game.glitchlessmode; + + /* Recreate menu to update glitchrunner mode */ + game.createmenu(game.currentmenuname, true); + break; + case 2: /* Input delay */ music.playef(Sound_VIRIDIAN); game.inputdelay = !game.inputdelay; game.savestatsandsettings_menu(); break; - case 2: + case 3: /* Interact button toggle */ music.playef(Sound_VIRIDIAN); game.separate_interact = !game.separate_interact; game.savestatsandsettings_menu(); break; - case 3: + case 4: // toggle fake load screen game.skipfakeload = !game.skipfakeload; game.savestatsandsettings_menu(); music.playef(Sound_VIRIDIAN); break; - case 4: + case 5: // toggle in game timer game.showingametimer = !game.showingametimer; game.savestatsandsettings_menu(); music.playef(Sound_VIRIDIAN); break; - case 5: + case 6: // english sprites loc::english_sprites = !loc::english_sprites; if (!loc::english_sprites) @@ -3004,7 +3024,10 @@ void gameinput(void) game.menupage = 30; // Pause screen } - if (game.deathseq == -1 && (key.isDown(SDLK_r) || key.isDown(game.controllerButton_restart)) && !game.nodeathmode)// && map.custommode) //Have fun glitchrunners! + if (game.deathseq == -1 && + (key.isDown(SDLK_r) || key.isDown(game.controllerButton_restart)) + && !game.nodeathmode + && (map.custommode || !game.glitchlessmode)) /* Have fun glitchrunners! */ { game.deathseq = 30; } diff --git a/desktop_version/src/Map.cpp b/desktop_version/src/Map.cpp index c06d21a6..f2c10c28 100644 --- a/desktop_version/src/Map.cpp +++ b/desktop_version/src/Map.cpp @@ -862,6 +862,20 @@ void mapclass::resetplayer(const bool player_died) game.scmprogress = game.roomx - 40; } } + + if (game.glitchlessmode) + { + /* Fix warp token death warps: + * Reset the state of all warp tokens to 0. */ + for (size_t i = 0; i < obj.entities.size(); i++) + { + entclass* entity = &obj.entities[i]; + if (entity->type == 11) + { + entity->state = 0; + } + } + } } void mapclass::warpto(int rx, int ry , int t, int tx, int ty) diff --git a/desktop_version/src/Render.cpp b/desktop_version/src/Render.cpp index c3c1d7c0..6a442733 100644 --- a/desktop_version/src/Render.cpp +++ b/desktop_version/src/Render.cpp @@ -1013,10 +1013,38 @@ static void menurender(void) { font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Glitchrunner Mode"), tr, tg, tb); int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Re-enable glitches that existed in previous versions of the game."), tr, tg, tb); + + if (game.glitchlessmode) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Glitchrunner mode is incompatible with glitchless mode."), tr, tg, tb); + break; + } + drawglitchrunnertext(next_y); break; } case 1: + { + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Glitchless Mode"), tr, tg, tb); + const int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disable glitches that might otherwise be useful for speedruns."), tr, tg, tb); + + if (GlitchrunnerMode_get() != GlitchrunnerNone) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Glitchless mode is incompatible with glitchrunner mode."), tr, tg, tb); + break; + } + + if (game.glitchlessmode) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Glitchless mode is ON"), tr, tg, tb); + } + else + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Glitchless mode is OFF"), tr / 2, tg / 2, tb / 2); + } + break; + } + case 2: { font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Input Delay"), tr, tg, tb); int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Re-enable the 1-frame input delay from previous versions of the game."), tr, tg, tb); @@ -1030,7 +1058,7 @@ static void menurender(void) } break; } - case 2: + case 3: { char buffer[SCREEN_WIDTH_CHARS + 1]; const char* button; @@ -1051,7 +1079,7 @@ static void menurender(void) font::print_wrap(PR_CEN, -1, next_y, buffer, tr, tg, tb); break; } - case 3: + case 4: { font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Fake Load Screen"), tr, tg, tb); int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disable the fake loading screen which appears on game launch."), tr, tg, tb); @@ -1061,7 +1089,7 @@ static void menurender(void) font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Fake loading screen is ON"), tr, tg, tb); break; } - case 4: + case 5: { font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("In-Game Timer"), tr, tg, tb); int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Toggle the in-game timer outside of time trials."), tr, tg, tb); @@ -1075,7 +1103,7 @@ static void menurender(void) } break; } - case 5: + case 6: { font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("English Sprites"), tr, tg, tb); int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Show the original English word enemies regardless of your language setting."), tr, tg, tb); @@ -2272,6 +2300,12 @@ static void mode_indicator_text(const int alpha) y += spacing; } + if (game.glitchlessmode) + { + font::print(flags, x, y, loc::gettext("Glitchless mode enabled"), r, g, b); + y += spacing; + } + if (graphics.flipmode) { const char* english = "Flip Mode enabled"; @@ -2363,6 +2397,7 @@ void gamerender(void) ); bool any_mode_active = map.invincibility || GlitchrunnerMode_get() != GlitchrunnerNone + || game.glitchlessmode || graphics.flipmode || game.slowdown < 30; bool draw_mode_indicator_text = mode_indicator_alpha > 100 && any_mode_active; diff --git a/desktop_version/src/Script.cpp b/desktop_version/src/Script.cpp index b384f68c..d5fb96bc 100644 --- a/desktop_version/src/Script.cpp +++ b/desktop_version/src/Script.cpp @@ -132,7 +132,7 @@ void scriptclass::tokenize( const std::string& t ) } } -static int getcolorfromname(std::string name) +static int getcolorfromname(const std::string& name) { if (name == "player") return CYAN; else if (name == "cyan") return CYAN; @@ -150,7 +150,7 @@ static int getcolorfromname(std::string name) return color; // Last effort to give a valid color, maybe they just input the color? } -static int getcrewmanfromname(std::string name) +static int getcrewmanfromname(const std::string& name) { if (name == "player") return obj.getplayer(); // Return the player int color = getcolorfromname(name); // Maybe they passed in a crewmate name, or an id? @@ -158,6 +158,28 @@ static int getcrewmanfromname(std::string name) return obj.getcrewman(color); } +static bool color_valid(const int index, const std::string& name) +{ + if (!INBOUNDS_VEC(index, obj.entities)) + { + return false; + } + + if (!game.glitchlessmode) + { + return true; + } + + const int color = getcolorfromname(name); + const bool using_aem = color == -1; + if (using_aem) + { + return true; + } + + return obj.entities[index].colour == color; +} + /* Also used in gamestate 1001. */ void foundtrinket_textbox1(textboxclass* THIS); @@ -1080,7 +1102,7 @@ void scriptclass::run(void) int crewmate = getcrewmanfromname(words[1]); if (crewmate != -1) i = crewmate; // Ensure AEM is kept - if (INBOUNDS_VEC(i, obj.entities)) + if (color_valid(i, words[1])) { obj.entities[i].tile = ss_toi(words[2]); }