#define MAP_DEFINITION #include "Map.h" #include "Alloc.h" #include "Constants.h" #include "CustomLevels.h" #include "Entity.h" #include "Game.h" #include "GlitchrunnerMode.h" #include "Graphics.h" #include "Localization.h" #include "MakeAndPlay.h" #include "Maths.h" #include "Music.h" #include "Script.h" #include "Unused.h" #include "UtilityClass.h" mapclass::mapclass(void) { //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; revealmap = true; 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(); setroomname(""); hiddenname = ""; roomname_special = false; specialroomnames.clear(); roomnameset = false; tileset = 0; initmapdata(); ypos = 0; oldypos = 0; background = 0; cameramode = 0; cameraseek = 0; minitowermode = false; roomtexton = false; nexttowercolour_set = false; currentregion = 0; SDL_zeroa(region); } static char roomname_static[SCREEN_WIDTH_CHARS]; static char* roomname_heap; void mapclass::destroy(void) { VVV_free(roomname_heap); } //Areamap starts at 100,100 and extends 20x20 const int mapclass::areamap[] = { 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, }; int mapclass::getwidth(void) { if (custommode) { return cl.mapwidth; } return 20; } int mapclass::getheight(void) { if (custommode) { return cl.mapheight; } return 20; } int mapclass::intpol(int a, int b, float c) { return static_cast(a + ((b - a) * c)); } void mapclass::setteleporter(int x, int y) { if (x < 0 || x >= getwidth() || y < 0 || y >= getheight()) { return; } SDL_Point temp; temp.x = x; temp.y = y; teleporters.push_back(temp); } void mapclass::settrinket(int x, int y) { if (x < 0 || x >= getwidth() || y < 0 || y >= getheight()) { return; } SDL_Point temp; temp.x = x; temp.y = y; shinytrinkets.push_back(temp); } 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; } } } void mapclass::resetmap(void) { //clear the explored area of the map SDL_memset(explored, 0, sizeof(explored)); } void mapclass::fullmap(void) { //mark the whole map as explored SDL_memset(explored, 1, sizeof(explored)); } void mapclass::updateroomnames(void) { if (roomnameset) { return; } const int rx = game.roomx; const int ry = game.roomy; for (int i = specialroomnames.size() - 1; i >= 0; i--) { Roomname* roomname = &specialroomnames[i]; if (rx == roomname->x && ry == roomname->y && (roomname->flag == -1 || (INBOUNDS_ARR(roomname->flag, obj.flags) && obj.flags[roomname->flag]))) { roomname_special = true; if (roomname->type == RoomnameType_STATIC) { setroomname(roomname->text[0].c_str()); } if (roomname->type == RoomnameType_GLITCH) { roomname->delay--; if (roomname->delay <= 0) { roomname->progress = (roomname->progress + 1) % 2; roomname->delay = 5; if (roomname->progress == 0) { roomname->delay = 25 + (int) (fRandom() * 10); } } setroomname(roomname->text[roomname->progress].c_str()); } if (roomname->type == RoomnameType_TRANSFORM) { roomname->delay--; if (roomname->delay <= 0) { roomname->progress++; roomname->delay = 2; if ((size_t) roomname->progress >= roomname->text.size()) { roomname->progress = roomname->loop ? 0 : roomname->text.size() - 1; } } setroomname(roomname->text[roomname->progress].c_str()); } break; } } } void mapclass::initmapdata(void) { 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); //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); } void mapclass::initcustommapdata(void) { shinytrinkets.clear(); for (size_t i = 0; i < customentities.size(); i++) { const CustomEntity& ent = customentities[i]; if (ent.t != 9) { continue; } settrinket(ent.rx, ent.ry); } } int mapclass::finalat(int x, int y) { //return the tile index of the final stretch tiles offset by the colour difference const int tile = contents[TILE_IDX(x, y)]; if (tile == 740) { //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; } return tile - (final_mapcol * 3) + offset; } } else if (tile >= 80) { return tile - (final_mapcol * 3); } else { return tile; } } int mapclass::maptiletoenemycol(int t) { //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; } void mapclass::changefinalcol(int t) { //change the map to colour t - for the game's final stretch. //First up, the tiles. This is just a setting: final_mapcol = t; const int temp = 6 - t; //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); } } } 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) { bg_obj.r = intpol(r1, r2, c / 5); bg_obj.g = intpol(g1, g2, c / 5); bg_obj.b = intpol(b1, b2, c / 5); } void mapclass::updatebgobj(TowerBG& bg_obj) { 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; } void mapclass::setbgobjlerp(TowerBG& bg_obj) { bg_obj.bypos = ypos / 2; bg_obj.bscroll = (ypos - oldypos) / 2; } void mapclass::updatetowerglow(TowerBG& bg_obj) { 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--; } } void mapclass::nexttowercolour(void) { /* Prevent cycling title BG more than once per frame. */ if (nexttowercolour_set) { return; } nexttowercolour_set = true; graphics.titlebg.colstate+=5; if (graphics.titlebg.colstate >= 30) graphics.titlebg.colstate = 0; updatebgobj(graphics.titlebg); } void mapclass::settowercolour(int t) { graphics.titlebg.colstate=t*5; if (graphics.titlebg.colstate >= 30) graphics.titlebg.colstate = 0; updatebgobj(graphics.titlebg); } bool mapclass::towerspikecollide(int x, int y) { if (tower.at(x,y,0)>= 6 && tower.at(x,y,0) <= 11) return true; return false; } bool mapclass::collide(int x, int y, const bool invincible) { if (towermode) { if (tower.at(x, y, 0) >= 12 && tower.at(x, y, 0) <= 27) return true; if (invincible) { if (tower.at(x, y, 0) >= 6 && tower.at(x, y, 0) <= 11) return true; } } else if (tileset == 2) { int tile; 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); if (x < 0 || y < 0 || x >= 40 || y >= 29 + extrarow) return false; tile = contents[TILE_IDX(x, y)]; if (tile >= 12 && tile <= 27) return true; if (invincible) { if (tile >= 6 && tile <= 11) return true; } } else { int tile; 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); if (x < 0 || y < 0 || x >= 40 || y >= 29+extrarow) return false; 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; if (invincible) { if (tile>= 6 && tile <= 9) return true; if (tile>= 49 && tile <= 50) return true; if (tileset == 1) { if (tile>= 49 && tile < 80) return true; } } } return false; } void mapclass::settile(int xp, int yp, int t) { if (xp >= 0 && xp < 40 && yp >= 0 && yp < 29+extrarow) { contents[TILE_IDX(xp, yp)] = t; } } int mapclass::area(int _rx, int _ry) { //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; } } } bool mapclass::isexplored(const int rx, const int ry) { const int roomnum = rx + ry*20; if (INBOUNDS_ARR(roomnum, explored)) { return explored[roomnum]; } return false; } void mapclass::setexplored(const int rx, const int ry, const bool status) { const int roomnum = rx + ry*20; if (INBOUNDS_ARR(roomnum, explored)) { explored[roomnum] = status; } } void mapclass::exploretower(void) { for (int i = 0; i < 20; i++) { setexplored(9, i, true); } } void mapclass::hideship(void) { //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); } void mapclass::showship(void) { //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); } void mapclass::resetplayer(void) { resetplayer(false); } void mapclass::resetplayer(const bool player_died) { 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); } } 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); } } 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; } } } void mapclass::warpto(int rx, int ry , int t, int tx, int ty) { 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; } void mapclass::gotoroom(int rx, int ry) { int roomchangedir; std::vector 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 if (game.intimetrial && game.roomx == 46 && game.roomy == 54) { music.niceplay(Music_PREDESTINEDFATEREMIX); } } 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; if (redrawbg) { 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) { game.setstate(0); if (game.roomx == 113 && game.roomy == 104) { game.setstate(50); } } } //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! const int player_idx = obj.getplayer(); if (INBOUNDS_VEC(player_idx, obj.entities)) { 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); } 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); } } } } } } } 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; } } const char* mapclass::currentarea(const int roomx, const int roomy) { /* For translation, the returned value is passed to loc::gettext_roomname_special(). * Returned strings must therefore be found in roomnames_special.xml! */ if (roomx >= 102 && roomx <= 104 && roomy >= 110 && roomy <= 111) { return "The Ship"; } switch (area(roomx, roomy)) { case 0: return "Dimension VVVVVV"; break; case 1: return "Dimension VVVVVV"; break; case 2: return "Laboratory"; break; case 3: return "The Tower"; break; case 4: return "Warp Zone"; break; case 5: return "Space Station"; break; case 6: return "Outside Dimension VVVVVV"; break; case 7: return "Outside Dimension VVVVVV"; break; case 8: return "Outside Dimension VVVVVV"; break; case 9: return "Outside Dimension VVVVVV"; break; case 10: return "Outside Dimension VVVVVV"; break; case 11: return "The Tower"; break; } return "???"; } static void copy_short_to_int(int* dest, const short* src, const size_t size) { size_t i; for (i = 0; i < size; ++i) { dest[i] = src[i]; } } void mapclass::loadlevel(int rx, int ry) { int t; if (revealmap) { if (!finalmode) { setexplored(rx - 100, ry - 100, true); if (rx == 109 && !custommode) { exploretower(); } } } roomtexton = false; roomtext.clear(); roomnameset = false; obj.platformtile = 0; obj.customplatformtile=0; obj.vertplatforms = false; obj.horplatforms = false; setroomname(""); 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; } roomname_special = false; switch(t) { #ifdef MAKEANDPLAY UNUSED(copy_short_to_int); #else 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)); setroomname(otherlevel.roomname); roomname_special = otherlevel.roomname_special; 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)); setroomname(lablevel.roomname); roomname_special = lablevel.roomname_special; 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); setroomname("The Tower"); 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)); setroomname(warplevel.roomname); roomname_special = warplevel.roomname_special; 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)); setroomname(spacestation2.roomname); roomname_special = spacestation2.roomname_special; tileset = 0; break; } case 6: //final level { const short* tmap = finallevel.loadlevel(rx, ry); copy_short_to_int(contents, tmap, SDL_arraysize(contents)); setroomname(finallevel.roomname); roomname_special = finallevel.roomname_special; 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); setroomname("Panic Room"); 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); setroomname("Panic Room"); 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); setroomname("The Final Challenge"); 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); setroomname("The Final Challenge"); 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)); setroomname(finallevel.roomname); roomname_special = finallevel.roomname_special; tileset = 2; if (rx == 108) { background = 7; rcol = 15; } if (rx == 110) { background = 8; rcol = 10; } if (rx == 111) { background = 9; rcol = 0; } break; } #endif 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; } setroomname(room->roomname.c_str()); 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]; if (ent.rx != rx - 100 || ent.ry != ry - 100) { continue; } const int ex = ent.x * 8; const int ey = ent.y * 8; // 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; obj.createentity(ex, ey, 56, ent.p1, 4 + room->enemyv, bx1, by1, bx2, by2); 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; text.text = ent.scriptname.c_str(); text.rtl = ent.p1; 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; } obj.createentity(ex, usethisy + 8, 20 + SDL_clamp(ent.p2, 0, 1), usethistile); for (size_t i = 0; i < script.customscripts.size(); i++) { if (script.customscripts[i].name == obj.customscript) { obj.createblock(ACTIVITY, ex - 8, usethisy + 8, 20, 16, 35); break; } } 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++) { int tile = contents[TILE_IDX(i, j)]; //Damage blocks if(tileset==0) { if (tile == 6 || tile == 8) { //sticking up obj.createblock(2, (i * 8), (j * 8)+4, 8, 4); } if (tile == 7 || tile == 9) { //Sticking down obj.createblock(2, (i * 8), (j * 8), 8, 4); } if (tile == 49 || tile == 50) { //left or right obj.createblock(2, (i * 8), (j * 8)+3, 8, 2); } } else if(tileset==1) { if ((tile >= 63 && tile <= 74) || (tile >= 6 && tile <= 9)) { //sticking up) { if (tile < 10) tile++; //sticking up if(tile%2==0) { obj.createblock(2, (i * 8), (j * 8), 8, 4); } else { //Sticking down obj.createblock(2, (i * 8), (j * 8) + 4, 8, 4); } if (tile < 11) tile--; } if (tile >= 49 && tile <= 62) { //left or right obj.createblock(2, (i * 8), (j * 8)+3, 8, 2); } } else if(tileset==2) { if (tile == 6 || tile == 8) { //sticking up obj.createblock(2, (i * 8), (j * 8)+4, 8, 4); } if (tile == 7 || tile == 9) { //Sticking down obj.createblock(2, (i * 8), (j * 8), 8, 4); } } //Breakable blocks if (tile == 10) { settile(i, j, 0); obj.createentity(i * 8, j * 8, 4); } //Directional blocks if (tile >= 14 && tile <= 17) { obj.createblock(3, i * 8, j * 8, 8, 8, tile-14); } } } 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); } } } } void mapclass::twoframedelayfix(void) { // 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) || !custommode || game.deathseq != -1) return; int block_idx = -1; // obj.checktrigger() sets block_idx int activetrigger = obj.checktrigger(&block_idx); if (activetrigger <= -1 || !INBOUNDS_VEC(block_idx, obj.blocks) || activetrigger < 300) { return; } game.newscript = obj.blocks[block_idx].script; obj.removetrigger(activetrigger); game.setstate(0); game.setstatedelay(0); script.load(game.newscript); } 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) { if (INBOUNDS_ARR(id, region) && id > 0) { 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); if (id == currentregion) { cl.generatecustomminimap(); } } } void mapclass::removeregion(int id) { if (INBOUNDS_ARR(id, region) && id > 0) { SDL_zero(region[id]); if (id == currentregion) { cl.generatecustomminimap(); } } } void mapclass::changeregion(int id) { if (INBOUNDS_ARR(id, region)) { currentregion = id; cl.generatecustomminimap(); } }