diff --git a/desktop_version/lang/ar/strings.xml b/desktop_version/lang/ar/strings.xml index dc4ab172a4..d415c59d65 100644 --- a/desktop_version/lang/ar/strings.xml +++ b/desktop_version/lang/ar/strings.xml @@ -249,6 +249,14 @@ + + + + + + + + diff --git a/desktop_version/lang/ca/strings.xml b/desktop_version/lang/ca/strings.xml index 9067a50d8f..a81210338b 100644 --- a/desktop_version/lang/ca/strings.xml +++ b/desktop_version/lang/ca/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/cy/strings.xml b/desktop_version/lang/cy/strings.xml index 75233e0a01..b293efe0e1 100644 --- a/desktop_version/lang/cy/strings.xml +++ b/desktop_version/lang/cy/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/de/strings.xml b/desktop_version/lang/de/strings.xml index 8bc440f81a..057f9178eb 100644 --- a/desktop_version/lang/de/strings.xml +++ b/desktop_version/lang/de/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/en/strings.xml b/desktop_version/lang/en/strings.xml index 86a4217f37..b15abd005a 100644 --- a/desktop_version/lang/en/strings.xml +++ b/desktop_version/lang/en/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/eo/strings.xml b/desktop_version/lang/eo/strings.xml index 6c08504366..77e2884863 100644 --- a/desktop_version/lang/eo/strings.xml +++ b/desktop_version/lang/eo/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/es/strings.xml b/desktop_version/lang/es/strings.xml index 68c8a2468d..812efa152c 100644 --- a/desktop_version/lang/es/strings.xml +++ b/desktop_version/lang/es/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/es_419/strings.xml b/desktop_version/lang/es_419/strings.xml index 827a3b97f9..3d2e6585f0 100644 --- a/desktop_version/lang/es_419/strings.xml +++ b/desktop_version/lang/es_419/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/es_AR/strings.xml b/desktop_version/lang/es_AR/strings.xml index e2cdc45706..90ecaad166 100644 --- a/desktop_version/lang/es_AR/strings.xml +++ b/desktop_version/lang/es_AR/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/fr/strings.xml b/desktop_version/lang/fr/strings.xml index 411cc564be..908d2a01fd 100644 --- a/desktop_version/lang/fr/strings.xml +++ b/desktop_version/lang/fr/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/ga/strings.xml b/desktop_version/lang/ga/strings.xml index cbdf99877c..4f278b3465 100644 --- a/desktop_version/lang/ga/strings.xml +++ b/desktop_version/lang/ga/strings.xml @@ -245,6 +245,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 f4798b2b6a..42226339d7 100644 --- a/desktop_version/lang/it/strings.xml +++ b/desktop_version/lang/it/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/ja/strings.xml b/desktop_version/lang/ja/strings.xml index f7fe0042f1..43f206d30e 100644 --- a/desktop_version/lang/ja/strings.xml +++ b/desktop_version/lang/ja/strings.xml @@ -258,6 +258,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 e7b69f1f94..f33b999efa 100755 --- a/desktop_version/lang/ko/strings.xml +++ b/desktop_version/lang/ko/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/nl/strings.xml b/desktop_version/lang/nl/strings.xml index 6d1aeebc24..af335ea782 100644 --- a/desktop_version/lang/nl/strings.xml +++ b/desktop_version/lang/nl/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/pl/strings.xml b/desktop_version/lang/pl/strings.xml index 059bd1edd8..ed1fcf8d82 100644 --- a/desktop_version/lang/pl/strings.xml +++ b/desktop_version/lang/pl/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/pt_BR/strings.xml b/desktop_version/lang/pt_BR/strings.xml index 0d084ab8a1..346fa40e5a 100644 --- a/desktop_version/lang/pt_BR/strings.xml +++ b/desktop_version/lang/pt_BR/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/pt_PT/strings.xml b/desktop_version/lang/pt_PT/strings.xml index 3d24a84f57..5cc4fac82e 100644 --- a/desktop_version/lang/pt_PT/strings.xml +++ b/desktop_version/lang/pt_PT/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/ru/strings.xml b/desktop_version/lang/ru/strings.xml index 04c6476b5e..b2cebd4af8 100644 --- a/desktop_version/lang/ru/strings.xml +++ b/desktop_version/lang/ru/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/szl/strings.xml b/desktop_version/lang/szl/strings.xml index 3eadda2581..eb41bb9e94 100644 --- a/desktop_version/lang/szl/strings.xml +++ b/desktop_version/lang/szl/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/tr/strings.xml b/desktop_version/lang/tr/strings.xml index c7cfdf82b7..14c269b605 100644 --- a/desktop_version/lang/tr/strings.xml +++ b/desktop_version/lang/tr/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/uk/strings.xml b/desktop_version/lang/uk/strings.xml index dbfc81a9c6..2b4f8424ca 100644 --- a/desktop_version/lang/uk/strings.xml +++ b/desktop_version/lang/uk/strings.xml @@ -244,6 +244,14 @@ + + + + + + + + diff --git a/desktop_version/lang/zh/strings.xml b/desktop_version/lang/zh/strings.xml index d437c5ba4c..73e1c34788 100644 --- a/desktop_version/lang/zh/strings.xml +++ b/desktop_version/lang/zh/strings.xml @@ -250,6 +250,14 @@ + + + + + + + + diff --git a/desktop_version/lang/zh_TW/strings.xml b/desktop_version/lang/zh_TW/strings.xml index 157c0cbaaf..2ab10df716 100644 --- a/desktop_version/lang/zh_TW/strings.xml +++ b/desktop_version/lang/zh_TW/strings.xml @@ -250,6 +250,14 @@ + + + + + + + + diff --git a/desktop_version/src/Entity.cpp b/desktop_version/src/Entity.cpp index f70246d725..c7ab52fd1b 100644 --- a/desktop_version/src/Entity.cpp +++ b/desktop_version/src/Entity.cpp @@ -2783,6 +2783,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) { @@ -3945,6 +3953,10 @@ int entityclass::getscm(void) } } + if (game.glitchlessmode) + { + return -1; + } return 0; } @@ -3962,6 +3974,10 @@ int entityclass::getlineat( int t ) } } + if (game.glitchlessmode) + { + return -1; + } return 0; } @@ -3981,6 +3997,10 @@ int entityclass::getcrewman( int t, int fallback /*= 0*/ ) } } + if (game.glitchlessmode) + { + return -1; + } return fallback; } @@ -4005,6 +4025,10 @@ int entityclass::getcustomcrewman( int t ) } } + if (game.glitchlessmode) + { + return -1; + } return 0; } @@ -4242,31 +4266,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; + return false; + } - 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); + /* 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; - if (linetemp > -4 && linetemp < 4) return true; - return false; - } - } - return false; + 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 ) @@ -4848,32 +4876,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 1d7bad140c..fad16998d2 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; @@ -4826,6 +4827,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); @@ -4939,6 +4945,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*/) @@ -5115,6 +5129,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); @@ -6826,7 +6842,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 362a956ced..fdac0c0d34 100644 --- a/desktop_version/src/Game.h +++ b/desktop_version/src/Game.h @@ -330,6 +330,7 @@ class Game 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 468c265aac..fce36a0df6 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) @@ -2989,7 +3009,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 747ebc383d..8670f6032b 100644 --- a/desktop_version/src/Map.cpp +++ b/desktop_version/src/Map.cpp @@ -854,6 +854,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 20efc8ffd0..a42352d647 100644 --- a/desktop_version/src/Render.cpp +++ b/desktop_version/src/Render.cpp @@ -1022,10 +1022,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); @@ -1039,7 +1067,7 @@ static void menurender(void) } break; } - case 2: + case 3: { char buffer[SCREEN_WIDTH_CHARS + 1]; const char* button; @@ -1060,7 +1088,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); @@ -1070,7 +1098,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); @@ -1084,7 +1112,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); @@ -2265,6 +2293,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"; @@ -2356,6 +2390,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 a58b1735a1..8c93566bc1 100644 --- a/desktop_version/src/Script.cpp +++ b/desktop_version/src/Script.cpp @@ -131,7 +131,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; @@ -149,7 +149,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? @@ -157,6 +157,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); @@ -1022,7 +1044,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]); }