#if !defined(NO_CUSTOM_LEVELS) && !defined(NO_EDITOR) #define ED_DEFINITION #include "Editor.h" #include #include "Constants.h" #include "CustomLevels.h" #include "DeferCallbacks.h" #include "Entity.h" #include "Enums.h" #include "Font.h" #include "Game.h" #include "Graphics.h" #include "GraphicsUtil.h" #include "KeyPoll.h" #include "Localization.h" #include "Map.h" #include "Maths.h" #include "Music.h" #include "Screen.h" #include "Script.h" #include "UTF8.h" #include "UtilityClass.h" #include "VFormat.h" #include "Vlogging.h" editorclass::editorclass(void) { reset(); register_tool(EditorTool_WALLS, "Walls", "1", SDLK_1, false); register_tool(EditorTool_BACKING, "Backing", "2", SDLK_2, false); register_tool(EditorTool_SPIKES, "Spikes", "3", SDLK_3, false); register_tool(EditorTool_TRINKETS, "Trinkets", "4", SDLK_4, false); register_tool(EditorTool_CHECKPOINTS, "Checkpoints", "5", SDLK_5, false); register_tool(EditorTool_DISAPPEARING_PLATFORMS, "Disappear", "6", SDLK_6, false); register_tool(EditorTool_CONVEYORS, "Conveyors", "7", SDLK_7, false); register_tool(EditorTool_MOVING_PLATFORMS, "Moving", "8", SDLK_8, false); register_tool(EditorTool_ENEMIES, "Enemies", "9", SDLK_9, false); register_tool(EditorTool_GRAVITY_LINES, "Grav Lines", "0", SDLK_0, false); register_tool(EditorTool_ROOMTEXT, "Roomtext", "R", SDLK_r, false); register_tool(EditorTool_TERMINALS, "Terminals", "T", SDLK_t, false); register_tool(EditorTool_SCRIPTS, "Script Boxes", "Y", SDLK_y, false); register_tool(EditorTool_WARP_TOKENS, "Warp Tokens", "U", SDLK_u, false); 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); } void editorclass::reset(void) { current_tool = EditorTool_WALLS; roomnamehide = 0; zmod = false; xmod = false; cmod = false; vmod = false; hmod = false; bmod = false; spacemod = false; shiftmenu = false; shiftkey = false; saveandquit = false; note = ""; notedelay = 0; oldnotedelay = 0; deletekeyheld = false; textmod = TEXT_NONE; titlemod = false; creatormod = false; desc1mod = false; desc2mod = false; desc3mod = false; websitemod = false; settingsmod = false; warpmod = false; //Two step process warpent = -1; boundarymod = 0; boundarytype = 0; boundx1 = 0; boundx2 = 0; boundy1 = 0; boundy2 = 0; textent = 0; scripttexttype = 0; drawmode = 0; dmtile = 0; dmtileeditor = 0; entcol = 0; tilex = 0; tiley = 0; levx = 0; levy = 0; keydelay = 0; lclickdelay = 0; savekey = false; loadkey = false; updatetiles = true; changeroom = true; entframe = 0; entframedelay = 0; SDL_zeroa(kludgewarpdir); hooklist.clear(); sb.clear(); clearscriptbuffer(); sbx = 0; sby = 0; pagey = 0; lines_visible = 25; scripteditmod = false; sbscript = "null"; scripthelppage = 0; scripthelppagedelay = 0; hookmenupage = 0; hookmenu = 0; returneditoralpha = 0; oldreturneditoralpha = 0; ghosts.clear(); currentghosts = 0; loaded_filepath = ""; state = EditorState_DRAW; substate = EditorSubState_MAIN; } void editorclass::handle_tile_placement(const int tile) { int range = 1; if (bmod) { // Vertical line for (int i = 0; i < 30; i++) { settile(tilex, i, tile); } return; } else if (hmod) { // Horizontal line for (int i = 0; i < 40; i++) { settile(i, tiley, tile); } return; } else if (vmod) { range = 4; } else if (cmod) { range = 3; } else if (xmod) { range = 2; } else if (zmod) { range = 1; } else { settile(tilex, tiley, tile); return; } for (int i = -range; i <= range; i++) { for (int j = -range; j <= range; j++) { settile(tilex + i, tiley + j, tile); } } } void editorclass::tool_remove() { switch (current_tool) { case EditorTool_WALLS: case EditorTool_BACKING: handle_tile_placement(0); break; default: break; } for (size_t i = 0; i < customentities.size(); i++) { if (customentities[i].x == tilex + (levx * 40) && customentities[i].y == tiley + (levy * 30)) { removeedentity(i); } } } void editorclass::entity_clicked(const int index) { CustomEntity* entity = &customentities[index]; lclickdelay = 1; switch (entity->t) { case 1: // Enemies entity->p1 = (entity->p1 + 1) % 4; break; case 2: { // Moving Platforms and Conveyors const bool conveyor = entity->p1 >= 5; entity->p1++; if (conveyor) { entity->p1 = (entity->p1 - 5) % 4 + 5; } else { entity->p1 %= 4; } break; } case 10: // Checkpoints // If it's not textured as a checkpoint, then just leave it be if (entity->p1 == 0 || entity->p1 == 1) { entity->p1 = (entity->p1 + 1) % 2; } break; case 11: case 16: // Gravity Lines, Start Point entity->p1 = (entity->p1 + 1) % 2; break; case 15: // Crewmates entity->p1 = (entity->p1 + 1) % 6; break; case 17: // Roomtext getlin(TEXT_ROOMTEXT, loc::gettext("Enter roomtext:"), &entity->scriptname); textent = index; break; case 18: // Terminals if (entity->p1 == 0 || entity->p1 == 1) { // Flip the terminal, but if it's not textured as a terminal leave it alone entity->p1 = (entity->p1 + 1) % 2; } SDL_FALLTHROUGH; case 19: // Script Boxes (and terminals) getlin(TEXT_SCRIPT, loc::gettext("Enter script name:"), &entity->scriptname); textent = index; break; } } void editorclass::tool_place() { const int entity = edentat(tilex + (levx * 40), tiley + (levy * 30)); if (entity != -1) { entity_clicked(entity); return; } switch (current_tool) { case EditorTool_WALLS: case EditorTool_BACKING: { int tile = 0; if (cl.getroomprop(levx, levy)->directmode >= 1) { tile = dmtile; } else if (current_tool == EditorTool_WALLS) { tile = 1; } else if (current_tool == EditorTool_BACKING) { tile = 2; } handle_tile_placement(tile); break; } case EditorTool_SPIKES: settile(tilex, tiley, 8); break; case EditorTool_TRINKETS: if (cl.numtrinkets() < 100) { addedentity(tilex + (levx * 40), tiley + (levy * 30), 9); lclickdelay = 1; } else { note = loc::gettext("ERROR: Max number of trinkets is 100"); notedelay = 45; } break; case EditorTool_CHECKPOINTS: addedentity(tilex + (levx * 40), tiley + (levy * 30), 10, 1); lclickdelay = 1; break; case EditorTool_DISAPPEARING_PLATFORMS: addedentity(tilex + (levx * 40), tiley + (levy * 30), 3); lclickdelay = 1; break; case EditorTool_CONVEYORS: addedentity(tilex + (levx * 40), tiley + (levy * 30), 2, 5); lclickdelay = 1; break; case EditorTool_MOVING_PLATFORMS: addedentity(tilex + (levx * 40), tiley + (levy * 30), 2, 0); lclickdelay = 1; break; case EditorTool_ENEMIES: addedentity(tilex + (levx * 40), tiley + (levy * 30), 1, 0); lclickdelay = 1; break; case EditorTool_GRAVITY_LINES: addedentity(tilex + (levx * 40), tiley + (levy * 30), 11, 0); lclickdelay = 1; break; case EditorTool_ROOMTEXT: lclickdelay = 1; textent = customentities.size(); addedentity(tilex + (levx * 40), tiley + (levy * 30), 17); getlin(TEXT_ROOMTEXT, loc::gettext("Enter roomtext:"), &(customentities[textent].scriptname)); break; case EditorTool_TERMINALS: lclickdelay = 1; textent = customentities.size(); addedentity(tilex + (levx * 40), tiley + (levy * 30), 18, 0); getlin(TEXT_SCRIPT, loc::gettext("Enter script name:"), &(customentities[textent].scriptname)); break; case EditorTool_SCRIPTS: boundarytype = 0; boundx1 = tilex * 8; boundy1 = tiley * 8; boundarymod = 2; lclickdelay = 1; break; case EditorTool_WARP_TOKENS: warpmod = true; warpent = customentities.size(); addedentity(tilex + (levx * 40), tiley + (levy * 30), 13); lclickdelay = 1; break; case EditorTool_WARP_LINES: //Warp lines if (tilex == 0) { addedentity(tilex + (levx * 40), tiley + (levy * 30), 50, 0); } else if (tilex == 39) { addedentity(tilex + (levx * 40), tiley + (levy * 30), 50, 1); } else if (tiley == 0) { addedentity(tilex + (levx * 40), tiley + (levy * 30), 50, 2); } else if (tiley == 29) { addedentity(tilex + (levx * 40), tiley + (levy * 30), 50, 3); } else { note = loc::gettext("ERROR: Warp lines must be on edges"); notedelay = 45; } lclickdelay = 1; break; case EditorTool_CREWMATES: if (cl.numcrewmates() < 100) { addedentity(tilex + (levx * 40), tiley + (levy * 30), 15, int(fRandom() * 6)); lclickdelay = 1; } else { note = loc::gettext("ERROR: Max number of crewmates is 100"); notedelay = 45; } break; case EditorTool_START_POINT: //If there is another start point, destroy it for (size_t i = 0; i < customentities.size(); i++) { if (customentities[i].t == 16) { removeedentity(i); i--; } } addedentity(tilex + (levx * 40), tiley + (levy * 30), 16, 0); lclickdelay = 1; break; } } void editorclass::draw_tool(EditorTools tool, int x, int y) { extern editorclass ed; switch (tool) { case EditorTool_WALLS: graphics.drawtile(x, y, 83); graphics.drawtile(x + 8, y, 83); graphics.drawtile(x, y + 8, 83); graphics.drawtile(x + 8, y + 8, 83); break; case EditorTool_BACKING: graphics.drawtile(x, y, 680); graphics.drawtile(x + 8, y, 680); graphics.drawtile(x, y + 8, 680); graphics.drawtile(x + 8, y + 8, 680); break; case EditorTool_SPIKES: graphics.drawtile(x + 4, y + 4, 8); break; case EditorTool_TRINKETS: graphics.draw_sprite(x, y, 22, 196, 196, 196); break; case EditorTool_CHECKPOINTS: graphics.draw_sprite(x, y, 21, 196, 196, 196); break; case EditorTool_DISAPPEARING_PLATFORMS: graphics.drawtile(x, y + 4, 3); graphics.drawtile(x + 8, y + 4, 4); break; case EditorTool_CONVEYORS: graphics.drawtile(x, y + 4, 24); graphics.drawtile(x + 8, y + 4, 24); break; case EditorTool_MOVING_PLATFORMS: graphics.drawtile(x, y + 4, 1); graphics.drawtile(x + 8, y + 4, 1); break; case EditorTool_ENEMIES: graphics.draw_sprite(x, y, 78 + ed.entframe, 196, 196, 196); break; case EditorTool_GRAVITY_LINES: graphics.fill_rect(x + 2, y + 8, 12, 1, graphics.getRGB(255, 255, 255)); break; case EditorTool_ROOMTEXT: font::print(PR_FONT_8X8, x + 1, y, "AB", 196, 196, 255 - help.glow); font::print(PR_FONT_8X8, x + 1, y + 9, "CD", 196, 196, 255 - help.glow); break; case EditorTool_TERMINALS: graphics.draw_sprite(x, y, 17, 196, 196, 196); break; case EditorTool_SCRIPTS: graphics.draw_rect(x + 4, y + 4, 8, 8, graphics.getRGB(96, 96, 96)); break; case EditorTool_WARP_TOKENS: graphics.draw_sprite(x, y, 18 + (ed.entframe % 2), 196, 196, 196); break; case EditorTool_WARP_LINES: graphics.fill_rect(x + 6, y + 2, 4, 12, graphics.getRGB(255, 255, 255)); break; case EditorTool_CREWMATES: graphics.draw_sprite(x, y, 186, graphics.col_crewblue); break; case EditorTool_START_POINT: graphics.draw_sprite(x, y, 184, graphics.col_crewcyan); break; } } void editorclass::register_tool(EditorTools tool, const char* name, const char* keychar, const SDL_KeyCode key, const bool shift) { toolnames[tool] = name; toolkeychar[tool] = keychar; toolkey[tool] = key; toolshift[tool] = shift; } void editorclass::gethooks(void) { // Scan through the script and create a hooks list based on it hooklist.clear(); for (size_t i = 0; i < script.customscripts.size(); i++) { Script& script_ = script.customscripts[i]; hooklist.push_back(script_.name); } } void editorclass::loadhookineditor(const std::string& t) { //Find hook t in the scriptclass, then load it into the editor clearscriptbuffer(); for(size_t i = 0; i < script.customscripts.size(); i++) { Script& script_ = script.customscripts[i]; if(script_.name == t) { sb = script_.contents; break; } } if(sb.empty()) { //Always have one line or we'll have problems sb.resize(1); } } void editorclass::addhooktoscript(const std::string& t) { //Adds hook+the scriptbuffer to the end of the scriptclass removehookfromscript(t); Script script_; script_.name = t; script_.contents = sb; script.customscripts.push_back(script_); } void editorclass::removehookfromscript(const std::string& t) { /* Find hook t in the scriptclass, then removes it (and any other code with it) * When this loop reaches the end, it wraps to SIZE_MAX; SIZE_MAX + 1 is 0 */ size_t i; for (i = script.customscripts.size() - 1; i + 1 > 0; --i) { if (script.customscripts[i].name == t) { script.customscripts.erase(script.customscripts.begin() + i); } } } void editorclass::removehook(const std::string& t) { //Check the hooklist for the hook t. If it's there, remove it from here and the script size_t i; removehookfromscript(t); /* When this loop reaches the end, it wraps to SIZE_MAX; SIZE_MAX + 1 is 0 */ for (i = hooklist.size() - 1; i + 1 > 0; --i) { if (hooklist[i] == t) { hooklist.erase(hooklist.begin() + i); } } } void editorclass::addhook(const std::string& t) { //Add an empty function to the list in both editor and script removehook(t); hooklist.push_back(t); addhooktoscript(t); } bool editorclass::checkhook(const std::string& t) { //returns true if hook t already is in the list for(size_t i=0; i 1) { sb.erase(sb.begin() + t); } } void editorclass::insertline(int t) { //insert a blank line into script at line t sb.insert(sb.begin() + t, ""); } void editorclass::getlin(const enum textmode mode, const std::string& prompt, std::string* ptr) { textmod = mode; textptr = ptr; textdesc = prompt; key.enabletextentry(); if (ptr) { key.keybuffer = *ptr; } else { key.keybuffer = ""; textptr = &(key.keybuffer); } oldenttext = key.keybuffer; } void editorclass::addedentity(int xp, int yp, int tp, int p1, int p2, int p3, int p4, int p5, int p6) { CustomEntity entity; entity.x = xp; entity.y = yp; entity.t = tp; entity.p1 = p1; entity.p2 = p2; entity.p3 = p3; entity.p4 = p4; entity.p5 = p5; entity.p6 = p6; entity.scriptname = ""; customentities.push_back(entity); } void editorclass::removeedentity(int t) { customentities.erase(customentities.begin() + t); } int editorclass::edentat(int xp, int yp) { for (size_t i = 0; i < customentities.size(); i++) { if (customentities[i].x == xp && customentities[i].y == yp) return i; } return -1; } static void editormenurender(int tr, int tg, int tb) { extern editorclass ed; switch (game.currentmenuname) { case Menu::ed_settings: font::print(PR_2X | PR_CEN, -1, 75, loc::gettext("Map Settings"), tr, tg, tb); if (game.currentmenuoption == 3) { if (!game.ghostsenabled) font::print(0, 2, 230, loc::gettext("Editor ghost trail is OFF"), tr/2, tg/2, tb/2); else font::print(0, 2, 230, loc::gettext("Editor ghost trail is ON"), tr, tg, tb); } break; case Menu::ed_desc: { if(ed.titlemod) { if(ed.entframe<2) { font::print(PR_2X | PR_CEN | PR_FONT_LEVEL, -1, 35, key.keybuffer+"_", tr, tg, tb); } else { font::print(PR_2X | PR_CEN | PR_FONT_LEVEL, -1, 35, key.keybuffer+" ", tr, tg, tb); } } else { bool title_is_gettext; std::string title = translate_title(cl.title, &title_is_gettext); font::print(PR_2X | PR_CEN | (title_is_gettext ? PR_FONT_INTERFACE : PR_FONT_LEVEL), -1, 35, title, tr, tg, tb); } bool creator_is_gettext = false; std::string creator; if(ed.creatormod) { if(ed.entframe<2) { creator = key.keybuffer + "_"; } else { creator = key.keybuffer + " "; } } else { creator = translate_creator(cl.creator, &creator_is_gettext); } int sp = SDL_max(10, font::height(PR_FONT_LEVEL)); graphics.print_level_creator((creator_is_gettext ? PR_FONT_INTERFACE : PR_FONT_LEVEL), 60, creator, tr, tg, tb); if(ed.websitemod) { if(ed.entframe<2) { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp, key.keybuffer+"_", tr, tg, tb); } else { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp, key.keybuffer+" ", tr, tg, tb); } } else { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp, cl.website, tr, tg, tb); } if(ed.desc1mod) { if(ed.entframe<2) { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*3, key.keybuffer+"_", tr, tg, tb); } else { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*3, key.keybuffer+" ", tr, tg, tb); } } else { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*3, cl.Desc1, tr, tg, tb); } if(ed.desc2mod) { if(ed.entframe<2) { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*4, key.keybuffer+"_", tr, tg, tb); } else { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*4, key.keybuffer+" ", tr, tg, tb); } } else { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*4, cl.Desc2, tr, tg, tb); } if(ed.desc3mod) { if(ed.entframe<2) { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*5, key.keybuffer+"_", tr, tg, tb); } else { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*5, key.keybuffer+" ", tr, tg, tb); } } else if (sp <= 10) { font::print(PR_CEN | PR_FONT_LEVEL, -1, 60+sp*5, cl.Desc3, tr, tg, tb); } const char* label = loc::gettext("Font: "); int len_label = font::len(0, label); const char* name = font::get_level_font_display_name(); font::print(0, 2, 230, label, tr/2, tg/2, tb/2); font::print(PR_FONT_LEVEL, 2+len_label, 230, name, tr/2, tg/2, tb/2); break; } case Menu::ed_music: { font::print(PR_2X | PR_CEN | PR_CJK_HIGH, -1, 65, loc::gettext("Map Music"), tr, tg, tb); font::print_wrap(PR_CEN | PR_CJK_LOW, -1, 85, loc::gettext("Current map music:"), tr, tg, tb); const char* songname; switch(cl.levmusic) { case 0: songname = loc::gettext("No background music"); break; case 1: songname = loc::gettext("1: Pushing Onwards"); break; case 2: songname = loc::gettext("2: Positive Force"); break; case 3: songname = loc::gettext("3: Potential for Anything"); break; case 4: songname = loc::gettext("4: Passion for Exploring"); break; case 5: songname = loc::gettext("N/A: Pause"); break; case 6: songname = loc::gettext("5: Presenting VVVVVV"); break; case 7: songname = loc::gettext("N/A: Plenary"); break; case 8: songname = loc::gettext("6: Predestined Fate"); break; case 9: songname = loc::gettext("N/A: ecroF evitisoP"); break; case 10: songname = loc::gettext("7: Popular Potpourri"); break; case 11: songname = loc::gettext("8: Pipe Dream"); break; case 12: songname = loc::gettext("9: Pressure Cooker"); break; case 13: songname = loc::gettext("10: Paced Energy"); break; case 14: songname = loc::gettext("11: Piercing the Sky"); break; case 15: songname = loc::gettext("N/A: Predestined Fate Remix"); break; default: songname = loc::gettext("?: something else"); break; } font::print_wrap(PR_CEN, -1, 120, songname, tr, tg, tb); break; } case Menu::ed_quit: font::print_wrap(PR_CEN, -1, 90, loc::gettext("Save before quitting?"), tr, tg, tb); break; case Menu::ed_font: { font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Level Font"), tr, tg, tb); font::print_wrap(PR_CEN, -1, 65, loc::gettext("Select the language in which the text in this level is written."), tr, tg, tb); const char* label = loc::gettext("Font: "); int len_label = font::len(0, label); const char* name = font::get_level_font_display_name(); font::print(0, 2, 230, label, tr/2, tg/2, tb/2); font::print(PR_FONT_LEVEL, 2+len_label, 230, name, tr/2, tg/2, tb/2); break; } default: break; } } static void draw_background_grid(void) { for (int j = 0; j < 30; j++) { for (int i = 0; i < 40; i++) { if (i == 19 || i == 20 || j == 14 || j == 29) { // Major guidelines graphics.draw_rect(i * 8, j * 8, 7, 7, graphics.getRGB(32, 32, 32)); } else if (i == 9 || i == 30 || j == 6 || j == 7 || j == 21 || j == 22) { // Minor guidelines graphics.draw_rect(i * 8, j * 8, 7, 7, graphics.getRGB(24, 24, 24)); } else if (i % 4 == 0 || j % 4 == 0) { graphics.draw_rect(i * 8, j * 8, 7, 7, graphics.getRGB(16, 16, 16)); } else { graphics.draw_rect(i * 8, j * 8, 7, 7, graphics.getRGB(8, 8, 8)); } } } } static void draw_background(int warpdir) { extern editorclass ed; switch (warpdir) { case 1: graphics.rcol = cl.getwarpbackground(ed.levx, ed.levy); graphics.drawbackground(3); break; case 2: graphics.rcol = cl.getwarpbackground(ed.levx, ed.levy); graphics.drawbackground(4); break; case 3: graphics.rcol = cl.getwarpbackground(ed.levx, ed.levy); graphics.drawbackground(5); break; default: break; } } static void draw_edgeguide(int type, int x, int y, bool vertical) { static const SDL_Color white = graphics.getRGB(255 - help.glow, 255, 255); static const SDL_Color red = graphics.getRGB(255 - help.glow, 127, 127); if (type != TileType_SOLID && type != TileType_SPIKE) { return; } if (vertical) { graphics.fill_rect(x, y, 8, 2, (type == TileType_SOLID) ? white : red); } else { graphics.fill_rect(x, y, 2, 8, (type == TileType_SOLID) ? white : red); } } static void draw_edgeguides(void) { extern editorclass ed; const int global_x = ed.levx * 40; const int global_y = ed.levy * 30; // Draw edge-guides, so there's no room misalignments! for (int i = 0; i < 40; i++) { if (i < 30) { // Left edge draw_edgeguide(ed.tile_type_wrap(global_x - 1, global_y + i), 0, i * 8, false); // Right edge draw_edgeguide(ed.tile_type_wrap(global_x + 40, global_y + i), 318, i * 8, false); } // Top edge draw_edgeguide(ed.tile_type_wrap(global_x + i, global_y - 1), i * 8, 0, true); // Bottom edge draw_edgeguide(ed.tile_type_wrap(global_x + i, global_y + 30), i * 8, 238, true); } } static void update_entities(void) { extern editorclass ed; for (size_t i = 0; i < customentities.size(); ++i) { CustomEntity* entity = &customentities[i]; if (entity->x / 40 != ed.levx || entity->y / 30 != ed.levy) { // It's not in this room, so just continue continue; } bool grav_line = (entity->t == 11); bool warp_line = (entity->t == 50); if ((grav_line || warp_line) && entity->p4 != 1) { // If it's a grav line or a warp line, and it's not locked if ((grav_line && entity->p1 == 0) || (warp_line && entity->p1 >= 2)) { /* Horizontal */ int tx = entity->x % 40; int tx2 = tx; int ty = entity->y % 30; while (!ed.spikefree(tx, ty)) { --tx; } while (!ed.spikefree(tx2, ty)) { ++tx2; } ++tx; entity->p2 = tx; entity->p3 = (tx2 - tx) * 8; } else { /* Vertical */ int tx = entity->x % 40; int ty = entity->y % 30; int ty2 = ty; while (!ed.spikefree(tx, ty)) { --ty; } while (!ed.spikefree(tx, ty2)) { ++ty2; } ++ty; entity->p2 = ty; entity->p3 = (ty2 - ty) * 8; } } } } static void draw_entities(void) { extern editorclass ed; const RoomProperty* const room = cl.getroomprop(ed.levx, ed.levy); //Draw entities obj.customplatformtile = game.customcol * 12; const int edent_under_cursor = ed.edentat(ed.tilex + ed.levx * 40, ed.tiley + ed.levy * 30); // Special case for drawing gray entities bool custom_gray = room->tileset == 3 && room->tilecol == 6; // Draw entities backward to remain accurate with ingame for (int i = customentities.size() - 1; i >= 0; i--) { CustomEntity* entity = &customentities[i]; // If the entity is in the current room, draw it if (entity->x / 40 == ed.levx && entity->y / 30 == ed.levy) { const int x = entity->x % 40 * 8; const int y = entity->y % 30 * 8; static const char arrows[] = "V^<>"; switch (entity->t) { case 1: // Enemies { const int movement = entity->p1; if (custom_gray) { ed.entcolreal = graphics.getcol(18); } graphics.draw_sprite(x, y, ed.getenemyframe(room->enemytype), ed.entcolreal); if (movement >= 0 && movement < 4) { // If they have a basic movement type, draw an arrow to indicate direction font::print(PR_FONT_8X8, x + 4, y + 4, std::string(1, arrows[movement]), 255, 255, 255 - help.glow); } graphics.draw_rect(x, y, 16, 16, graphics.getRGB(255, 164, 255)); break; } case 2: // Conveyors & Platforms { const int movement = entity->p1; const short length = (movement == 7 || movement == 8) ? 8 : 4; const short glow = 255 - help.glow; for (int j = 0; j < length; j++) { graphics.draw_grid_tile(custom_gray ? graphics.grphx.im_entcolours_tint : graphics.grphx.im_entcolours, obj.customplatformtile, x + (j * 8), y, 8, 8); } switch (movement) { case 0: case 1: case 2: case 3: // If they have a basic movement type, draw an arrow to indicate direction font::print(PR_FONT_8X8, x + 12, y, std::string(1, arrows[movement]), glow, glow, glow); break; case 4: // Always move right, stopping when hitting collision font::print(PR_FONT_8X8, x + 8, y, ">I", glow, glow, glow); break; case 5: font::print(PR_FONT_8X8, x, y, ">>>>", glow, glow, glow); break; case 6: font::print(PR_FONT_8X8, x, y, "<<<<", glow, glow, glow); break; case 7: font::print(PR_FONT_8X8, x + 4, y, "> > > > ", glow, glow, glow); break; case 8: font::print(PR_FONT_8X8, x + 4, y, "< < < < ", glow, glow, glow); break; } if (movement < 0) { // Well, it's a negative type, so it'll just be still. font::print(PR_FONT_8X8, x + 8, y, "[]", glow, glow, glow); } else if (movement > 8) { // Invalid... draw a scary red X font::print(PR_FONT_8X8, x + 12, y, "X", glow, 0, 0); } graphics.draw_rect(x, y, 8 * length, 8, graphics.getRGB(255, 255, 255)); break; } case 3: // Disappearing Platforms for (int j = 0; j < 4; j++) { graphics.draw_grid_tile(custom_gray ? graphics.grphx.im_entcolours_tint : graphics.grphx.im_entcolours, obj.customplatformtile, x + (j * 8), y, 8, 8); } font::print(PR_FONT_8X8, x, y, "////", 255 - help.glow, 255 - help.glow, 255 - help.glow); graphics.draw_rect(x, y, 32, 8, graphics.getRGB(255, 255, 255)); break; case 9: // Shiny Trinkets graphics.draw_sprite(x, y, 22, 196, 196, 196); graphics.draw_rect(x, y, 16, 16, graphics.getRGB(255, 164, 164)); break; case 10: // Checkpoints graphics.draw_sprite(x, y, 20 + entity->p1, 196, 196, 196); graphics.draw_rect(x, y, 16, 16, graphics.getRGB(255, 164, 164)); break; case 11: // Gravity Lines // p2 is in tiles, and p3 is in pixels if (entity->p1 == 0) { // Horizontal gravity line const int left = entity->p2 * 8; const int width = entity->p3; graphics.fill_rect(left, y + 4, width, 1, graphics.getRGB(194, 194, 194)); } else { // Vertical gravity line const int top = entity->p2 * 8; const int height = entity->p3; graphics.fill_rect(x + 3, top, 1, height, graphics.getRGB(194, 194, 194)); } graphics.draw_rect(x, y, 8, 8, graphics.getRGB(164, 255, 164)); break; case 13: // Warp Tokens { std::string text; graphics.draw_sprite(x, y, 18 + (ed.entframe % 2), 196, 196, 196); graphics.draw_rect(x, y, 16, 16, graphics.getRGB(255, 164, 164)); if (i == edent_under_cursor) { text = "(" + help.String(entity->p1 / 40 + 1) + "," + help.String(entity->p2 / 30 + 1) + ")"; } else { text = help.String(cl.findwarptoken(i)); } font::print(PR_BOR | PR_CJK_HIGH, x, y - 8, text, 210, 210, 255); 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)); break; case 16: // Start Point { const short labelcol = ed.entframe < 2 ? 255 : 196; if (entity->p1 == 0) // Facing right { graphics.draw_sprite(x - 4, y, 0, graphics.col_crewcyan); } else // Non-zero is facing left { graphics.draw_sprite(x - 4, y, 3, graphics.col_crewcyan); } graphics.draw_rect(x, y, 16, 24, graphics.getRGB(255, 255, 164)); font::print(PR_BOR | PR_CEN | PR_CJK_HIGH, x + 8, y - 8, loc::gettext("START"), labelcol, labelcol, labelcol); break; } case 17: // Roomtext { int width = 8; int height = 8; if (entity->scriptname.length() > 0) { width = font::len(PR_FONT_LEVEL, entity->scriptname.c_str()); height = font::height(PR_FONT_LEVEL); } graphics.draw_rect(x, y, width, height, graphics.getRGB(96, 96, 96)); font::print(PR_FONT_LEVEL | PR_CJK_LOW, x, y, entity->scriptname, 196, 196, 255 - help.glow); break; } case 18: // Terminals { int sprite = entity->p1; int corrected_y = y; // Not a boolean: just swapping 0 and 1, leaving the rest alone if (sprite == 0) { sprite = 1; // Unflipped } else if (sprite == 1) { sprite = 0; // Flipped; corrected_y -= 8; } graphics.draw_sprite(x, corrected_y + 8, sprite + 16, 96, 96, 96); graphics.draw_rect(x, y, 16, 24, graphics.getRGB(164, 164, 164)); if (i == edent_under_cursor) { font::print(PR_FONT_LEVEL | PR_BOR | PR_CJK_HIGH, x, y - 8, entity->scriptname, 210, 210, 255); } break; } case 19: // Script Triggers graphics.draw_rect(x, y, entity->p1 * 8, entity->p2 * 8, graphics.getRGB(255, 164, 255)); graphics.draw_rect(x, y, 8, 8, graphics.getRGB(255, 255, 255)); if (i == edent_under_cursor) { font::print(PR_FONT_LEVEL | PR_BOR | PR_CJK_HIGH, x, y - 8, entity->scriptname, 210, 210, 255); } break; case 50: // Warp Lines if (entity->p1 >= 2) // Horizontal { int left = entity->p2; int right = left + entity->p3 / 8; graphics.draw_rect((left * 8), y + 1, (right - left) * 8, 6, graphics.getRGB(194, 255, 255)); graphics.draw_rect(x, y, 8, 8, graphics.getRGB(164, 255, 255)); } else // Vertical { int top = entity->p2; int bottom = top + entity->p3 / 8; graphics.draw_rect(x + 1, (top * 8), 6, (bottom - top) * 8, graphics.getRGB(194, 255, 255)); graphics.draw_rect(x, y, 8, 8, graphics.getRGB(164, 255, 255)); } break; } } // Need to also check warp point destinations if (entity->t == 13 && ed.warpent != i) { // Is the destination in this room? if (entity->p1 / 40 == ed.levx && entity->p2 / 30 == ed.levy) { const int x = entity->p1 % 40 * 8; const int y = entity->p2 % 30 * 8; std::string text; graphics.draw_sprite(x, y, 18 + (ed.entframe % 2), 64, 64, 64); graphics.draw_rect((entity->p1 * 8) - (ed.levx * 40 * 8), (entity->p2 * 8) - (ed.levy * 30 * 8), 16, 16, graphics.getRGB(96, 64, 64)); if (ed.tilex == x / 8 && ed.tiley == y / 8) { text = "(" + help.String(entity->x / 40 + 1) + "," + help.String(entity->y / 30 + 1) + ")"; } else { text = help.String(cl.findwarptoken(i)); } font::print(PR_BOR | PR_CJK_HIGH, x, y - 8, text, 190, 190, 225); } } } } static void draw_ghosts(void) { extern editorclass ed; //Draw ghosts (spooky!) if (game.ghostsenabled) { graphics.set_render_target(graphics.ghostTexture); graphics.set_blendmode(graphics.ghostTexture, SDL_BLENDMODE_BLEND); graphics.clear(0, 0, 0, 0); for (int i = 0; i < (int) ed.ghosts.size(); i++) { if (i <= ed.currentghosts) { // We don't want all of them to show up at once :) if (ed.ghosts[i].rx != ed.levx || ed.ghosts[i].ry != ed.levy) continue; SDL_Color ct = ed.ghosts[i].realcol; const int alpha = 3 * ct.a / 4; ct.a = (Uint8)alpha; graphics.draw_sprite(ed.ghosts[i].x, ed.ghosts[i].y, ed.ghosts[i].frame, ct); } } graphics.set_render_target(graphics.gameTexture); graphics.set_texture_alpha_mod(graphics.ghostTexture, 128); graphics.copy_texture(graphics.ghostTexture, NULL, NULL); } } static void draw_bounds(void) { extern editorclass ed; const RoomProperty* const room = cl.getroomprop(ed.levx, ed.levy); if (ed.boundarymod>0) { if(ed.boundarymod==1) { graphics.draw_rect(ed.tilex*8, ed.tiley*8, 8,8,graphics.getRGB(210 + help.glow/2, 191 + help.glow, 255 - help.glow/2)); graphics.draw_rect((ed.tilex*8)+2, (ed.tiley*8)+2, 4,4,graphics.getRGB(105 + help.glow/4, 100 + help.glow/2, 128 - help.glow/4)); } else if(ed.boundarymod==2) { if((ed.tilex*8)+8<=ed.boundx1 || (ed.tiley*8)+8<=ed.boundy1) { graphics.draw_rect(ed.boundx1, ed.boundy1, 8, 8,graphics.getRGB(210 + help.glow/2, 191 + help.glow, 255 - help.glow/2)); graphics.draw_rect(ed.boundx1+2, ed.boundy1+2, 4, 4,graphics.getRGB(105 + help.glow/4, 100 + help.glow/2, 128 - help.glow/4)); } else { graphics.draw_rect(ed.boundx1, ed.boundy1, (ed.tilex*8)+8-ed.boundx1,(ed.tiley*8)+8-ed.boundy1,graphics.getRGB(210 + help.glow/2, 191 + help.glow, 255 - help.glow/2)); graphics.draw_rect(ed.boundx1+2, ed.boundy1+2, (ed.tilex*8)+8-ed.boundx1-4,(ed.tiley*8)+8-ed.boundy1-4,graphics.getRGB(105 + help.glow/4, 100 + help.glow/2, 128 - help.glow/4)); } } } else { //Draw boundaries if(room->enemyx1!=0 || room->enemyy1!=0 || room->enemyx2!=320 || room->enemyy2!=240) { graphics.draw_rect( room->enemyx1, room->enemyy1, room->enemyx2-room->enemyx1, room->enemyy2-room->enemyy1, graphics.getRGB(255-(help.glow/2),64,64)); } if(room->platx1!=0 || room->platy1!=0 || room->platx2!=320 || room->platy2!=240) { graphics.draw_rect( room->platx1, room->platy1, room->platx2-room->platx1, room->platy2-room->platy1, graphics.getRGB(64,64,255-(help.glow/2))); } } } static void draw_cursor(void) { extern editorclass ed; static const SDL_Color blue = graphics.getRGB(32, 32, 200); const int x = ed.tilex * 8; const int y = ed.tiley * 8; switch (ed.current_tool) { case EditorTool_WALLS: case EditorTool_BACKING: // Modifiers! if (ed.bmod) graphics.draw_rect(x, 0, 8, 240, blue); // Vertical else if (ed.hmod) graphics.draw_rect(0, y, 320, 8, blue); // Horizontal else if (ed.vmod) graphics.draw_rect(x - 32, y - 32, 24 + 48, 24 + 48, blue); // 9x9 else if (ed.cmod) graphics.draw_rect(x - 24, y - 24, 24 + 32, 24 + 32, blue); // 7x7 else if (ed.xmod) graphics.draw_rect(x - 16, y - 16, 24 + 16, 24 + 16, blue); // 5x5 else if (ed.zmod) graphics.draw_rect(x - 8, y - 8, 24, 24, blue); // 3x3 SDL_FALLTHROUGH; case EditorTool_SPIKES: case EditorTool_GRAVITY_LINES: case EditorTool_ROOMTEXT: case EditorTool_SCRIPTS: // 1x1 graphics.draw_rect(x, y, 8, 8, blue); break; case EditorTool_TRINKETS: case EditorTool_CHECKPOINTS: case EditorTool_ENEMIES: case EditorTool_WARP_TOKENS: // 2x2 graphics.draw_rect(x, y, 16, 16, blue); break; case EditorTool_DISAPPEARING_PLATFORMS: case EditorTool_CONVEYORS: case EditorTool_MOVING_PLATFORMS: // 1x4 (platforms) graphics.draw_rect(x, y, 32, 8, blue); break; case EditorTool_WARP_LINES: // 1x1, but X if not on an edge (warp lines) if (ed.tilex == 0 || ed.tilex == 39 || ed.tiley == 0 || ed.tiley == 29) { graphics.draw_rect(x, y, 8, 8, blue); } else { font::print(PR_FONT_8X8, x, y, "X", 255, 0, 0); } break; case EditorTool_TERMINALS: case EditorTool_CREWMATES: case EditorTool_START_POINT: // 2x3 graphics.draw_rect(x, y, 16, 24, blue); break; } } static void draw_tile_toolbox(int tileset) { extern editorclass ed; // Tile toolbox for direct mode int t2 = 0; if (ed.dmtileeditor > 0) { if (ed.dmtileeditor <= 4) { t2 = graphics.lerp((4 - ed.dmtileeditor + 1) * 12, (4 - ed.dmtileeditor) * 12); } // Draw five lines of the editor const int temp = ed.dmtile - (ed.dmtile % 40) - 80; graphics.fill_rect(0, -t2, 320, 40, graphics.getRGB(0, 0, 0)); graphics.fill_rect(0, -t2 + 40, 320, 2, graphics.getRGB(255, 255, 255)); int texturewidth; int textureheight; if (graphics.query_texture(graphics.grphx.im_tiles, NULL, NULL, &texturewidth, &textureheight) != 0) { return; } const int numtiles = (int)(texturewidth / 8) * (textureheight / 8); for (int x = 0; x < 40; x++) { for (int y = 0; y < 5; y++) { if (tileset == 0) { graphics.drawtile(x * 8, (y * 8) - t2, (temp + numtiles + (y * 40) + x) % numtiles); } else { graphics.drawtile2(x * 8, (y * 8) - t2, (temp + numtiles + (y * 40) + x) % numtiles); } } } // Highlight our little block graphics.draw_rect(((ed.dmtile % 40) * 8) - 2, 16 - t2 - 2, 12, 12, graphics.getRGB(255 - help.glow, 196, 196)); graphics.draw_rect(((ed.dmtile % 40) * 8) - 1, 16 - t2 - 1, 10, 10, graphics.getRGB(0, 0, 0)); } if (ed.dmtileeditor > 0 && t2 <= 30) { short labellen = 2 + font::len(0, loc::gettext("Tile:")); font::print(PR_BOR, 2, 45 - t2, loc::gettext("Tile:"), 196, 196, 255 - help.glow); font::print(PR_BOR, labellen + 16, 45 - t2, help.String(ed.dmtile), 196, 196, 255 - help.glow); graphics.fill_rect(labellen + 2, 44 - t2, 10, 10, graphics.getRGB(255 - help.glow, 196, 196)); graphics.fill_rect(labellen + 3, 45 - t2, 8, 8, graphics.getRGB(0, 0, 0)); if (tileset == 0) { graphics.drawtile(labellen + 3, 45 - t2, ed.dmtile); } else { graphics.drawtile2(labellen + 3, 45 - t2, ed.dmtile); } } else { short labellen = 2 + font::len(0, loc::gettext("Tile:")); int y = 2 + font::height(0); y = SDL_max(y, 12); font::print(PR_BOR, 2, y, loc::gettext("Tile:"), 196, 196, 255 - help.glow); font::print(PR_BOR, labellen + 16, y, help.String(ed.dmtile), 196, 196, 255 - help.glow); graphics.fill_rect(labellen + 2, y - 1, 10, 10, graphics.getRGB(255 - help.glow, 196, 196)); graphics.fill_rect(labellen + 3, y, 8, 8, graphics.getRGB(0, 0, 0)); if (tileset == 0) { graphics.drawtile(labellen + 3, 12, ed.dmtile); } else { graphics.drawtile2(labellen + 3, 12, ed.dmtile); } } } void editorrender(void) { extern editorclass ed; const RoomProperty* const room = cl.getroomprop(ed.levx, ed.levy); graphics.clear(); switch (ed.state) { case EditorState_DRAW: // Draw the editor guidelines draw_background_grid(); // Draw the background, if any, over the guidelines if (!ed.settingsmod) { draw_background(room->warpdir); } graphics.drawmap(); draw_edgeguides(); draw_entities(); draw_ghosts(); draw_bounds(); if (room->directmode == 1) { draw_tile_toolbox(room->tileset); } draw_cursor(); break; } // Draw GUI if(ed.boundarymod>0) { std::string message; if(ed.boundarymod==1) { switch(ed.boundarytype) { case 0: message = loc::gettext("SCRIPT BOX: Click on top left"); break; case 1: message = loc::gettext("ENEMY BOUNDS: Click on top left"); break; case 2: message = loc::gettext("PLATFORM BOUNDS: Click on top left"); break; default: message = loc::gettext("Click on top left"); break; } } else if(ed.boundarymod==2) { switch(ed.boundarytype) { case 0: message = loc::gettext("SCRIPT BOX: Click on bottom right"); break; case 1: message = loc::gettext("ENEMY BOUNDS: Click on bottom right"); break; case 2: message = loc::gettext("PLATFORM BOUNDS: Click on bottom right"); break; default: message = loc::gettext("Click on bottom right"); break; } } short lines; message = font::string_wordwrap(0, message, 312, &lines); short textheight = font::height(0)*lines; graphics.fill_rect(0,238-textheight,320,240, graphics.getRGB(32,32,32)); graphics.fill_rect(0,239-textheight,320,240, graphics.getRGB(0,0,0)); font::print_wrap(0, 4, 240-textheight, message.c_str(), 255,255,255, 8, 312); } else if(ed.scripteditmod) { //Elaborate C64 BASIC menu goes here! graphics.fill_rect(0,0,320,240, graphics.getRGB(123, 111, 218)); graphics.fill_rect(14,16,292,208, graphics.getRGB(61, 48, 162)); switch(ed.scripthelppage) { case 0: font::print(PR_CEN, -1,28,loc::gettext("**** VVVVVV SCRIPT EDITOR ****"), 123, 111, 218); font::print(PR_CEN, -1,44,loc::gettext("PRESS ESC TO RETURN TO MENU"), 123, 111, 218); if(!ed.hooklist.empty()) { for(int i=0; i<9; i++) { if(ed.hookmenupage+i<(int)ed.hooklist.size()) { if(ed.hookmenupage+i==ed.hookmenu) { std::string text_upper(loc::toupper(ed.hooklist[(ed.hooklist.size()-1)-(ed.hookmenupage+i)])); char buffer[SCREEN_WIDTH_CHARS + 1]; vformat_buf(buffer, sizeof(buffer), loc::get_langmeta()->menu_select.c_str(), "label:str", text_upper.c_str()); font::print(PR_CEN, -1, 68+(i*16), buffer, 123, 111, 218); } else { font::print(PR_CEN, -1, 68+(i*16), ed.hooklist[(ed.hooklist.size()-1)-(ed.hookmenupage+i)], 123, 111, 218); } } } } else { font::print(PR_CEN, -1,110,loc::gettext("NO SCRIPT IDS FOUND"), 123, 111, 218); font::print_wrap(PR_CEN, -1,130,loc::gettext("CREATE A SCRIPT WITH EITHER THE TERMINAL OR SCRIPT BOX TOOLS"), 123, 111, 218, 10, 288); } break; case 1: { //Current scriptname graphics.fill_rect(14,226,292,12, graphics.getRGB(61, 48, 162)); char namebuffer[SCREEN_WIDTH_CHARS + 1]; vformat_buf( namebuffer, sizeof(namebuffer), loc::gettext("CURRENT SCRIPT: {name}"), "name:str", ed.sbscript.c_str() ); font::print(PR_CEN, -1,228, namebuffer, 123, 111, 218); //Draw text int font_height = font::height(PR_FONT_LEVEL); for(int i=0; i 255) tr = 255; if (tg < 0) tg = 0; if (tg > 255) tg = 255; if (tb < 0) tb = 0; if (tb > 255) tb = 255; editormenurender(tr, tg, tb); graphics.drawmenu(tr, tg, tb, game.currentmenuname); } else if (ed.textmod) { short lines; std::string wrapped = font::string_wordwrap(0, ed.textdesc, 312, &lines); short textheight = font::height(0)*lines+font::height(PR_FONT_LEVEL); graphics.fill_rect(0, 238-textheight, 320, 240, graphics.getRGB(32, 32, 32)); graphics.fill_rect(0, 239-textheight, 320, 240, graphics.getRGB(0, 0, 0)); font::print_wrap(0, 4, 240-textheight, wrapped.c_str(), 255, 255, 255, 8, 312); std::string input = key.keybuffer; if (ed.entframe < 2) { input += "_"; } else { input += " "; } font::print(PR_CEN | PR_FONT_LEVEL | PR_CJK_HIGH, -1, 232, input, 196, 196, 255 - help.glow); } else if(ed.warpmod) { //placing warp token int textheight = font::height(0); graphics.fill_rect(0,237-textheight*2,320,240, graphics.getRGB(32,32,32)); graphics.fill_rect(0,238-textheight*2,320,240, graphics.getRGB(0,0,0)); font::print(PR_CJK_LOW, 4, 240-textheight*2, loc::gettext("Left click to place warp destination"), 196, 196, 255 - help.glow); font::print(PR_CJK_LOW, 4, 240-textheight, loc::gettext("Right click to cancel"), 196, 196, 255 - help.glow); } else { char coords[8]; SDL_snprintf(coords, sizeof(coords), "(%d,%d)", ed.levx+1, ed.levy+1); if(ed.spacemod) { graphics.fill_rect(0,207,320,240, graphics.getRGB(32,32,32)); graphics.fill_rect(0,208,320,240, graphics.getRGB(0,0,0)); // Draw tool icons! int tx = 6; const int ty = 210; const int tg = 32; const int page = ed.current_tool / 10; const int max_pages = SDL_ceil(NUM_EditorTools / 10); const int page_tool_count = SDL_min(10, NUM_EditorTools - (page * 10)); for (int i = 0; i < page_tool_count; i++) { const int current_tool_id = i + (page * 10); // First, draw the background graphics.fill_rect(4 + (i * tg), 208, 20, 20, graphics.getRGB(32, 32, 32)); // Draw the actual tool icon ed.draw_tool((EditorTools) current_tool_id, 4 + (i * tg) + 2, 208 + 2); // Draw the tool outline... graphics.draw_rect(4 + (i * tg), 208, 20, 20, (current_tool_id == ed.current_tool) ? graphics.getRGB(200, 200, 200) : graphics.getRGB(96, 96, 96)); // ...and the hotkey const int col = current_tool_id == ed.current_tool ? 255 : 164; font::print(PR_FONT_8X8 | PR_BOR, 22 + i * tg - 4, 224 - 4, ed.toolkeychar[current_tool_id], col, col, col); } // Draw the page number, limit is 1 digit, so the max is 9 pages char buffer[4]; SDL_snprintf(buffer, sizeof(buffer), "%d/%d", page + 1, max_pages + 1); font::print(PR_CJK_HIGH, 4, 232, buffer, 196, 196, 255 - help.glow); // Draw the button hint text char changetooltext[SCREEN_WIDTH_CHARS + 1]; vformat_buf(changetooltext, sizeof(changetooltext), loc::gettext("{button1} and {button2} keys change tool"), "button1:str, button2:str", ",", "." ); font::print(PR_CJK_HIGH | PR_RIGHT, 320, 232, changetooltext, 196, 196, 255 - help.glow); // Draw the current tool name char toolname[SCREEN_WIDTH_CHARS + 1]; SDL_snprintf(toolname, sizeof(toolname), "%s: %s", ed.toolkeychar[ed.current_tool], loc::gettext(ed.toolnames[ed.current_tool])); int bgheight = 2 + font::height(0); int toolnamelen = font::len(0, toolname); graphics.fill_rect(0,206-bgheight,toolnamelen+8,bgheight+1, graphics.getRGB(32,32,32)); graphics.fill_rect(0,207-bgheight,toolnamelen+7,bgheight, graphics.getRGB(0,0,0)); font::print(PR_BOR | PR_CJK_HIGH, 2,198, toolname, 196, 196, 255 - help.glow); int coordslen = font::len(0, coords); graphics.fill_rect(319-coordslen-8,206-bgheight,coordslen+8,bgheight+1, graphics.getRGB(32,32,32)); graphics.fill_rect(320-coordslen-8,207-bgheight,coordslen+8,bgheight, graphics.getRGB(0,0,0)); font::print(PR_BOR | PR_CJK_HIGH | PR_RIGHT, 316, 198, coords, 196, 196, 255 - help.glow); } else { //graphics.fill_rect(0,230,72,240, graphics.RGB(32,32,32)); //graphics.fill_rect(0,231,71,240, graphics.RGB(0,0,0)); if(room->roomname!="") { int font_height = font::height(PR_FONT_LEVEL); if (font_height <= 8) { graphics.footerrect.h = font_height + 2; } else { graphics.footerrect.h = font_height + 1; } graphics.footerrect.y = 240 - graphics.footerrect.h + ed.roomnamehide; graphics.set_blendmode(SDL_BLENDMODE_BLEND); graphics.fill_rect(&graphics.footerrect, graphics.getRGBA(0, 0, 0, graphics.translucentroomname ? 127 : 255)); graphics.set_blendmode(SDL_BLENDMODE_NONE); font::print(PR_CEN | PR_BOR | PR_FONT_LEVEL | PR_CJK_LOW, -1, graphics.footerrect.y+1+ed.roomnamehide, room->roomname, 196, 196, 255 - help.glow); font::print(PR_BOR | PR_CJK_HIGH, 4, 232-graphics.footerrect.h, loc::gettext("SPACE ^ SHIFT ^"), 196, 196, 255 - help.glow); font::print(PR_BOR | PR_CJK_HIGH | PR_RIGHT, 316, 232-graphics.footerrect.h, coords, 196, 196, 255 - help.glow); } else { font::print(PR_BOR | PR_CJK_HIGH, 4, 232, loc::gettext("SPACE ^ SHIFT ^"), 196, 196, 255 - help.glow); font::print(PR_BOR | PR_CJK_HIGH | PR_RIGHT, 316, 232, coords, 196, 196, 255 - help.glow); } } if(ed.shiftmenu) { const char* shiftmenuoptions[] = { loc::gettext("F1: Change Tileset"), loc::gettext("F2: Change Colour"), loc::gettext("F3: Change Enemies"), loc::gettext("F4: Enemy Bounds"), loc::gettext("F5: Platform Bounds"), "", loc::gettext("F9: Reload Resources"), loc::gettext("F10: Direct Mode"), "", loc::gettext("W: Change Warp Dir"), loc::gettext("E: Change Roomname"), }; int menuwidth = 0; for (size_t i = 0; i < SDL_arraysize(shiftmenuoptions); i++) { int len = font::len(0, shiftmenuoptions[i]); if (len > menuwidth) menuwidth = len; } int lineheight = font::height(0); lineheight = SDL_max(10, lineheight); int left_y = 230-SDL_arraysize(shiftmenuoptions)*lineheight; graphics.draw_rect(0, left_y-3, menuwidth+17, 240,graphics.getRGB(64,64,64)); graphics.fill_rect(0,left_y-2,menuwidth+16,240, graphics.getRGB(0,0,0)); for (size_t i = 0; i < SDL_arraysize(shiftmenuoptions); i++) font::print(0, 4, left_y+i*lineheight, shiftmenuoptions[i], 164,164,164); graphics.draw_rect(220, 207,100,60,graphics.getRGB(64,64,64)); graphics.fill_rect(221,208,160,60, graphics.getRGB(0,0,0)); font::print(0, 224, 210, loc::gettext("S: Save Map"),164,164,164); font::print(0, 224, 210+lineheight, loc::gettext("L: Load Map"),164,164,164); } } if(!ed.settingsmod && !ed.scripteditmod) { // Draw the current tool name char toolname[SCREEN_WIDTH_CHARS + 1]; SDL_snprintf(toolname, sizeof(toolname), "%s: %s", ed.toolkeychar[ed.current_tool], loc::gettext(ed.toolnames[ed.current_tool])); font::print(PR_BOR, 2, 2, toolname, 196, 196, 255 - help.glow); } if(ed.notedelay>0 || ed.oldnotedelay>0) { short lines; std::string wrapped = font::string_wordwrap(0, ed.note, 304, &lines); short textheight = 8+(lines-1)*SDL_max(10, font::height(0)); short banner_y = 120 - textheight/2 - 5; float alpha = graphics.lerp(ed.oldnotedelay, ed.notedelay); graphics.fill_rect(0, banner_y, 320, 10+textheight, graphics.getRGB(92,92,92)); graphics.fill_rect(0, banner_y+1, 320, 8+textheight, graphics.getRGB(0,0,0)); font::print_wrap(PR_CEN, -1,banner_y+5, wrapped.c_str(), 196-((45.0f-alpha)*4), 196-((45.0f-alpha)*4), 196-((45.0f-alpha)*4)); } graphics.drawfade(); graphics.render(); } void editorrenderfixed(void) { extern editorclass ed; const RoomProperty* const room = cl.getroomprop(ed.levx, ed.levy); graphics.updatetitlecolours(); game.customcol=cl.getlevelcol(room->tileset, room->tilecol)+1; ed.entcol=cl.getenemycol(game.customcol); ed.entcolreal = graphics.getcol(ed.entcol); if (game.ghostsenabled) { for (size_t i = 0; i < ed.ghosts.size(); i++) { GhostInfo& ghost = ed.ghosts[i]; if ((int) i > ed.currentghosts || ghost.rx != ed.levx || ghost.ry != ed.levy) { continue; } ghost.realcol = graphics.getcol(ghost.col); } ed.currentghosts++; if (ed.zmod) { ed.currentghosts++; } ed.currentghosts = SDL_min(ed.currentghosts, ed.ghosts.size()); } if (!ed.settingsmod) { switch(room->warpdir) { case 1: graphics.rcol=cl.getwarpbackground(ed.levx, ed.levy); graphics.updatebackground(3); break; case 2: graphics.rcol=cl.getwarpbackground(ed.levx, ed.levy); graphics.updatebackground(4); break; case 3: graphics.rcol=cl.getwarpbackground(ed.levx, ed.levy); graphics.updatebackground(5); break; default: break; } } else if (!game.colourblindmode) { graphics.updatetowerbackground(graphics.titlebg); } if (cl.getroomprop(ed.levx, ed.levy)->directmode == 1) { if (ed.dmtileeditor > 0) { ed.dmtileeditor--; } } else { ed.dmtileeditor = 0; } if (cl.getroomprop(ed.levx, ed.levy)->roomname != "") { if (ed.tiley < 28) { if (ed.roomnamehide > 0) { ed.roomnamehide--; } } else { if (ed.roomnamehide < 14) { ed.roomnamehide++; } } } else { if (ed.tiley < 28) { ed.roomnamehide = 0; } else { ed.roomnamehide = 14; } } } void editorlogic(void) { extern editorclass ed; //Misc help.updateglow(); graphics.titlebg.bypos -= 2; graphics.titlebg.bscroll = -2; ed.entframedelay--; if (ed.entframedelay <= 0) { ed.entframe = (ed.entframe + 1) % 4; ed.entframedelay = 8; } ed.oldnotedelay = ed.notedelay; ed.notedelay = SDL_max(ed.notedelay - 1, 0); update_entities(); if (graphics.fademode == FADE_FULLY_BLACK) { //Return to game graphics.titlebg.colstate = 10; map.nexttowercolour(); game.quittomenu(); music.play(6); //should be before game.quittomenu() ed.settingsmod=false; } } static void creategameoptions(void) { game.createmenu(Menu::options); } static void nextbgcolor(void) { map.nexttowercolour(); } static void editormenuactionpress(void) { extern editorclass ed; switch (game.currentmenuname) { case Menu::ed_desc: switch (game.currentmenuoption) { case 0: { bool title_is_gettext; translate_title(cl.title, &title_is_gettext); ed.titlemod=true; key.enabletextentry(); if (title_is_gettext) { key.keybuffer=""; } else { key.keybuffer = cl.title; } break; } case 1: { bool creator_is_gettext; translate_creator(cl.creator, &creator_is_gettext); ed.creatormod=true; key.enabletextentry(); if (creator_is_gettext) { key.keybuffer=""; } else { key.keybuffer = cl.creator; } break; } case 2: ed.desc1mod=true; key.enabletextentry(); key.keybuffer=cl.Desc1; break; case 3: ed.websitemod=true; key.enabletextentry(); key.keybuffer=cl.website; break; case 4: game.createmenu(Menu::ed_font); map.nexttowercolour(); break; case 5: game.returnmenu(); map.nexttowercolour(); break; } music.playef(11); break; case Menu::ed_settings: switch (game.currentmenuoption) { case 0: //Change level description stuff music.playef(11); game.createmenu(Menu::ed_desc); map.nexttowercolour(); break; case 1: //Enter script editormode music.playef(11); ed.scripteditmod=true; ed.clearscriptbuffer(); key.keybuffer=""; ed.hookmenupage=0; ed.hookmenu=0; ed.scripthelppage=0; ed.scripthelppagedelay=0; ed.sby=0; ed.sbx=0, ed.pagey=0; ed.lines_visible = 200/font::height(PR_FONT_LEVEL); break; case 2: music.playef(11); game.createmenu(Menu::ed_music); map.nexttowercolour(); if(cl.levmusic>0) music.play(cl.levmusic); break; case 3: music.playef(11); game.ghostsenabled = !game.ghostsenabled; break; case 4: //Load level ed.settingsmod = false; map.nexttowercolour(); ed.keydelay = 6; ed.getlin(TEXT_LOAD, loc::gettext("Enter map filename to load:"), &(ed.filename)); game.mapheld = true; graphics.backgrounddrawn = false; break; case 5: //Save level ed.settingsmod=false; map.nexttowercolour(); ed.keydelay = 6; ed.getlin(TEXT_SAVE, loc::gettext("Enter map filename to save as:"), &(ed.filename)); game.mapheld = true; graphics.backgrounddrawn = false; break; case 6: /* Game options */ music.playef(11); game.gamestate = TITLEMODE; game.ingame_titlemode = true; game.ingame_editormode = true; DEFER_CALLBACK(creategameoptions); DEFER_CALLBACK(nextbgcolor); break; default: music.playef(11); game.createmenu(Menu::ed_quit); map.nexttowercolour(); break; } break; case Menu::ed_music: switch (game.currentmenuoption) { case 0: case 1: switch (game.currentmenuoption) { case 0: cl.levmusic++; break; case 1: cl.levmusic--; break; } cl.levmusic = POS_MOD(cl.levmusic, 16); if (cl.levmusic > 0) { music.play(cl.levmusic); } else { music.haltdasmusik(); } music.playef(11); break; case 2: music.playef(11); music.fadeout(); game.returnmenu(); map.nexttowercolour(); break; } break; case Menu::ed_quit: switch (game.currentmenuoption) { case 0: //Saving and quit ed.saveandquit = true; ed.settingsmod = false; map.nexttowercolour(); ed.keydelay = 6; ed.getlin(TEXT_SAVE, loc::gettext("Enter map filename to save as:"), &(ed.filename)); game.mapheld = true; graphics.backgrounddrawn = false; break; case 1: //Quit without saving music.playef(11); music.fadeout(); graphics.fademode = FADE_START_FADEOUT; graphics.backgrounddrawn = false; break; case 2: //Go back to editor music.playef(11); game.returnmenu(); map.nexttowercolour(); break; } break; case Menu::ed_font: { uint8_t idx_selected = font::font_idx_options[game.currentmenuoption]; cl.level_font_name = font::get_main_font_name(idx_selected); if (idx_selected == loc::get_langmeta()->font_idx) { loc::new_level_font = ""; } else { loc::new_level_font = cl.level_font_name; } font::set_level_font(cl.level_font_name.c_str()); music.playef(11); game.returnmenu(); map.nexttowercolour(); game.savestatsandsettings_menu(); break; } default: break; } } void editorinput(void) { extern editorclass ed; if (graphics.fademode == FADE_FADING_OUT) { return; } ed.tilex = (key.mx - (key.mx % 8)) / 8; ed.tiley = (key.my - (key.my % 8)) / 8; if (gameScreen.scalingMode == SCALING_STRETCH) { // In this mode specifically, we have to fix the mouse coordinates int screenwidth, screenheight; gameScreen.GetScreenSize(&screenwidth, &screenheight); ed.tilex = ed.tilex * 320 / screenwidth; ed.tiley = ed.tiley * 240 / screenheight; } bool up_pressed = key.isDown(SDLK_UP) || key.isDown(SDL_CONTROLLER_BUTTON_DPAD_UP); bool down_pressed = key.isDown(SDLK_DOWN) || key.isDown(SDL_CONTROLLER_BUTTON_DPAD_DOWN); bool left_pressed = key.isDown(SDLK_LEFT) || key.isDown(SDL_CONTROLLER_BUTTON_DPAD_LEFT); bool right_pressed = key.isDown(SDLK_RIGHT) || key.isDown(SDL_CONTROLLER_BUTTON_DPAD_RIGHT); game.press_left = false; game.press_right = false; game.press_action = false; game.press_map = false; game.press_interact = false; if (key.isDown(KEYBOARD_LEFT) || key.isDown(KEYBOARD_a) || key.controllerWantsLeft(false)) { game.press_left = true; } if (key.isDown(KEYBOARD_RIGHT) || key.isDown(KEYBOARD_d) || key.controllerWantsRight(false)) { game.press_right = true; } if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip)) { game.press_action = true; }; if (key.keymap[SDLK_F9] && (ed.keydelay == 0)) { ed.keydelay = 30; ed.note = loc::gettext("Reloaded resources"); ed.notedelay = 45; graphics.reloadresources(); } if (key.isDown(KEYBOARD_ENTER)) game.press_map = true; if (key.isDown(27) && !ed.settingskey) { ed.settingskey=true; if (ed.textmod) { key.disabletextentry(); if (ed.textmod >= FIRST_ENTTEXT && ed.textmod <= LAST_ENTTEXT) { *ed.textptr = ed.oldenttext; if (ed.oldenttext == "") { ed.removeedentity(ed.textent); } } ed.textmod = TEXT_NONE; ed.shiftmenu = false; ed.shiftkey = false; } else if (key.textentry()) { key.disabletextentry(); ed.titlemod=false; ed.desc1mod=false; ed.desc2mod=false; ed.desc3mod=false; ed.websitemod=false; ed.creatormod=false; music.playef(11); ed.shiftmenu=false; ed.shiftkey=false; } else if(ed.boundarymod>0) { ed.boundarymod=0; } else { bool esc_from_font = false; music.playef(11); if (ed.settingsmod) { if (ed.scripteditmod) { ed.scripteditmod = false; } else if (ed.settingsmod) { if (game.currentmenuname == Menu::ed_settings) { ed.settingsmod = false; } else if (game.currentmenuname == Menu::ed_font) { // Prevent double return esc_from_font = true; game.returnmenu(); map.nexttowercolour(); } else { game.returnmenu(); map.nexttowercolour(); } } } else { ed.settingsmod = true; } if (ed.settingsmod && !esc_from_font) { bool edsettings_in_stack = game.currentmenuname == Menu::ed_settings; if (!edsettings_in_stack) { size_t i; for (i = 0; i < game.menustack.size(); ++i) { if (game.menustack[i].name == Menu::ed_settings) { edsettings_in_stack = true; break; } } } if (edsettings_in_stack) { game.returntomenu(Menu::ed_settings); } else { game.createmenu(Menu::ed_settings); } map.nexttowercolour(); } } } if (!key.isDown(27)) { ed.settingskey=false; } if(ed.scripteditmod) { if(ed.scripthelppage==0) { //hook select menu if(ed.keydelay>0) ed.keydelay--; if(up_pressed && ed.keydelay<=0) { ed.keydelay=6; ed.hookmenu--; } if(down_pressed && ed.keydelay<=0) { ed.keydelay=6; ed.hookmenu++; } if(ed.hookmenu>=(int)ed.hooklist.size()) { ed.hookmenu=ed.hooklist.size()-1; } if(ed.hookmenu<0) ed.hookmenu=0; if(ed.hookmenu=ed.hookmenupage+9) { ed.hookmenupage=ed.hookmenu+8; } if(!key.keymap[SDLK_BACKSPACE]) ed.deletekeyheld=0; if(key.keymap[SDLK_BACKSPACE] && ed.deletekeyheld==0 && !ed.hooklist.empty()) { ed.deletekeyheld=1; music.playef(2); ed.removehook(ed.hooklist[(ed.hooklist.size()-1)-ed.hookmenu]); } if (!game.press_action && !game.press_left && !game.press_right && !up_pressed && !down_pressed && !key.isDown(27)) game.jumpheld = false; if (!game.jumpheld) { if (game.press_action || game.press_left || game.press_right || game.press_map || up_pressed || down_pressed || key.isDown(27)) { game.jumpheld = true; } if ((game.press_action || game.press_map) && !ed.hooklist.empty()) { game.mapheld=true; ed.scripthelppage=1; key.enabletextentry(); key.keybuffer=""; ed.sbscript=ed.hooklist[(ed.hooklist.size()-1)-ed.hookmenu]; ed.loadhookineditor(ed.sbscript); ed.sby=ed.sb.size()-1; ed.pagey=0; while(ed.sby>=ed.lines_visible-5) { ed.pagey++; ed.sby--; } key.keybuffer=ed.sb[ed.pagey+ed.sby]; ed.sbx = UTF8_total_codepoints(ed.sb[ed.pagey+ed.sby].c_str()); music.playef(11); } } } else if(ed.scripthelppage==1) { //Script editor! if (key.isDown(27)) { ed.scripthelppage=0; game.jumpheld = true; //save the script for use again! ed.addhook(ed.sbscript); } if(ed.keydelay>0) ed.keydelay--; if(up_pressed && ed.keydelay<=0) { ed.keydelay=6; ed.sby--; if(ed.sby<=5) { if(ed.pagey>0) { ed.pagey--; ed.sby++; } else { if(ed.sby<0) ed.sby=0; } } key.keybuffer=ed.sb[ed.pagey+ed.sby]; } if(down_pressed && ed.keydelay<=0) { ed.keydelay=6; if(ed.sby+ed.pagey<(int)ed.sb.size()-1) { ed.sby++; if(ed.sby>=ed.lines_visible-5) { ed.pagey++; ed.sby--; } } key.keybuffer=ed.sb[ed.pagey+ed.sby]; } if(key.linealreadyemptykludge) { ed.keydelay=6; key.linealreadyemptykludge=false; } if(key.pressedbackspace && ed.sb[ed.pagey+ed.sby]=="" && ed.keydelay<=0) { //Remove this line completely ed.removeline(ed.pagey+ed.sby); ed.sby--; if(ed.sby<=5) { if(ed.pagey>0) { ed.pagey--; ed.sby++; } else { if(ed.sby<0) ed.sby=0; } } key.keybuffer=ed.sb[ed.pagey+ed.sby]; ed.keydelay=6; } /* Remove all pipes, they are the line separator in the XML * When this loop reaches the end, it wraps to SIZE_MAX; SIZE_MAX + 1 is 0 */ {size_t i; for (i = key.keybuffer.length() - 1; i + 1 > 0; --i) { if (key.keybuffer[i] == '|') { key.keybuffer.erase(key.keybuffer.begin() + i); } }} ed.sb[ed.pagey+ed.sby]=key.keybuffer; ed.sbx = UTF8_total_codepoints(ed.sb[ed.pagey+ed.sby].c_str()); if(!game.press_map && !key.isDown(27)) game.mapheld=false; if (!game.mapheld) { if(game.press_map) { game.mapheld=true; //Continue to next line if(ed.sby+ed.pagey>=(int)ed.sb.size()) //we're on the last line { ed.sby++; if(ed.sby>=ed.lines_visible-5) { ed.pagey++; ed.sby--; } key.keybuffer=ed.sb[ed.pagey+ed.sby]; ed.sbx = UTF8_total_codepoints(ed.sb[ed.pagey+ed.sby].c_str()); } else { //We're not, insert a line instead ed.sby++; if(ed.sby>=ed.lines_visible-5) { ed.pagey++; ed.sby--; } ed.insertline(ed.sby+ed.pagey); key.keybuffer=""; ed.sbx = 0; } } } } } else if (ed.textmod) { *ed.textptr = key.keybuffer; if (!game.press_map && !key.isDown(27)) { game.mapheld = false; } if (!game.mapheld && game.press_map) { game.mapheld = true; key.disabletextentry(); switch (ed.textmod) { case TEXT_GOTOROOM: { char coord_x[16]; char coord_y[16]; const char* comma = SDL_strchr(key.keybuffer.c_str(), ','); bool valid_input = comma != NULL; if (valid_input) { SDL_strlcpy( coord_x, key.keybuffer.c_str(), SDL_min((size_t) (comma - key.keybuffer.c_str() + 1), sizeof(coord_x)) ); SDL_strlcpy(coord_y, &comma[1], sizeof(coord_y)); valid_input = is_number(coord_x) && is_number(coord_y); } if (!valid_input) { ed.note = loc::gettext("ERROR: Invalid format"); ed.notedelay = 45; break; } ed.levx = SDL_clamp(help.Int(coord_x) - 1, 0, cl.mapwidth - 1); ed.levy = SDL_clamp(help.Int(coord_y) - 1, 0, cl.mapheight - 1); graphics.backgrounddrawn = false; break; } case TEXT_LOAD: { std::string loadstring = ed.filename + ".vvvvvv"; if (cl.load(loadstring)) { // don't use filename, it has the full path char buffer[3*SCREEN_WIDTH_CHARS + 1]; vformat_buf(buffer, sizeof(buffer), loc::gettext("Loaded map: {filename}.vvvvvv"), "filename:str", ed.filename.c_str()); ed.note = buffer; } else { ed.note = loc::gettext("ERROR: Could not load level"); } ed.notedelay = 45; break; } case TEXT_SAVE: { std::string savestring = ed.filename + ".vvvvvv"; if (cl.save(savestring)) { char buffer[3*SCREEN_WIDTH_CHARS + 1]; vformat_buf(buffer, sizeof(buffer), loc::gettext("Saved map: {filename}.vvvvvv"), "filename:str", ed.filename.c_str()); ed.note = buffer; } else { ed.note = loc::gettext("ERROR: Could not save level!"); ed.saveandquit = false; } ed.notedelay = 45; if (ed.saveandquit) { graphics.fademode = FADE_START_FADEOUT; /* quit editor */ } break; } case TEXT_SCRIPT: ed.clearscriptbuffer(); if (!ed.checkhook(key.keybuffer)) { ed.addhook(key.keybuffer); } break; default: break; } ed.shiftmenu = false; ed.shiftkey = false; ed.textmod = TEXT_NONE; } } else if (key.textentry()) { if(ed.titlemod) { cl.title=key.keybuffer; if (cl.title == "") { cl.title = "Untitled Level"; } } else if(ed.creatormod) { cl.creator=key.keybuffer; if (cl.creator == "") { cl.creator = "Unknown"; } } else if(ed.websitemod) { cl.website=key.keybuffer; } else if(ed.desc1mod) { cl.Desc1=key.keybuffer; } else if(ed.desc2mod) { cl.Desc2=key.keybuffer; } else if(ed.desc3mod) { cl.Desc3=key.keybuffer; } if(!game.press_map && !key.isDown(27)) game.mapheld=false; if (!game.mapheld) { if(game.press_map) { game.mapheld=true; if(ed.titlemod) { cl.title=key.keybuffer; if (cl.title == "") { cl.title = "Untitled Level"; } ed.titlemod=false; } else if(ed.creatormod) { cl.creator=key.keybuffer; if (cl.creator == "") { cl.creator = "Unknown"; } ed.creatormod=false; } else if(ed.websitemod) { cl.website=key.keybuffer; ed.websitemod=false; } else if(ed.desc1mod) { cl.Desc1=key.keybuffer; } else if(ed.desc2mod) { cl.Desc2=key.keybuffer; } else if(ed.desc3mod) { cl.Desc3=key.keybuffer; ed.desc3mod=false; } key.disabletextentry(); if(ed.desc1mod) { ed.desc1mod=false; ed.desc2mod=true; key.enabletextentry(); key.keybuffer=cl.Desc2; } else if(ed.desc2mod) { ed.desc2mod=false; if (font::height(PR_FONT_LEVEL) <= 10) { ed.desc3mod=true; key.enabletextentry(); key.keybuffer=cl.Desc3; } else { cl.Desc3=""; } } music.playef(11); } } } else { if(ed.settingsmod) { if (!game.press_action && !game.press_left && !game.press_right && !up_pressed && !down_pressed) game.jumpheld = false; if (!game.jumpheld) { if (game.press_action || game.press_left || game.press_right || game.press_map || up_pressed || down_pressed) { game.jumpheld = true; } if(game.menustart) { if (game.press_left || up_pressed) { game.currentmenuoption--; } else if (game.press_right || down_pressed) { game.currentmenuoption++; } } if (game.currentmenuoption < 0) game.currentmenuoption = game.menuoptions.size()-1; if (game.currentmenuoption >= (int) game.menuoptions.size() ) game.currentmenuoption = 0; if (game.press_action) { editormenuactionpress(); } } } else if (ed.keydelay > 0) { ed.keydelay--; } else if (key.keymap[SDLK_LCTRL] || key.keymap[SDLK_RCTRL]) { // Ctrl modifiers int texturewidth; int textureheight; if (cl.getroomprop(ed.levx, ed.levy)->tileset == 0) { if (graphics.query_texture(graphics.grphx.im_tiles, NULL, NULL, &texturewidth, &textureheight) != 0) { return; } } else { if (graphics.query_texture(graphics.grphx.im_tiles2, NULL, NULL, &texturewidth, &textureheight) != 0) { return; } } const int numtiles = (int) (texturewidth / 8) * (textureheight / 8); ed.dmtileeditor=10; if(left_pressed) { ed.dmtile--; ed.keydelay=3; if(ed.dmtile<0) ed.dmtile+=numtiles; } else if(right_pressed) { ed.dmtile++; ed.keydelay=3; if(ed.dmtile>=numtiles) ed.dmtile-=numtiles; } if(up_pressed) { ed.dmtile-=40; ed.keydelay=3; if(ed.dmtile<0) ed.dmtile+=numtiles; } else if(down_pressed) { ed.dmtile+=40; ed.keydelay=3; if(ed.dmtile>=numtiles) ed.dmtile-=numtiles; } } else if (key.keymap[SDLK_LSHIFT] || key.keymap[SDLK_RSHIFT]) { // Shift modifiers if (key.keymap[SDLK_F1]) { ed.switch_tileset(true); ed.keydelay = 6; } if (key.keymap[SDLK_F2]) { ed.switch_tilecol(true); ed.keydelay = 6; } if (key.keymap[SDLK_F3]) { ed.switch_enemy(true); ed.keydelay=6; } if (key.keymap[SDLK_w]) { ed.switch_warpdir(true); ed.keydelay = 6; } if (up_pressed || down_pressed || left_pressed || right_pressed) { ed.keydelay = 6; if (up_pressed) { cl.mapheight--; } else if (down_pressed) { cl.mapheight++; } else if (left_pressed) { cl.mapwidth--; } else if (right_pressed) { cl.mapwidth++; } cl.mapwidth = SDL_clamp(cl.mapwidth, 1, cl.maxwidth); cl.mapheight = SDL_clamp(cl.mapheight, 1, cl.maxheight); char buffer[3*SCREEN_WIDTH_CHARS + 1]; vformat_buf( buffer, sizeof(buffer), loc::gettext("Mapsize is now [{width},{height}]"), "width:int, height:int", cl.mapwidth, cl.mapheight ); ed.note = buffer; ed.notedelay = 45; } if (!ed.shiftkey) { if (ed.shiftmenu) { ed.shiftmenu = false; } else { ed.shiftmenu = true; } } ed.shiftkey = true; } else { // No modifiers ed.shiftkey=false; if(key.keymap[SDLK_F1]) { ed.switch_tileset(false); ed.keydelay = 6; } if(key.keymap[SDLK_F2]) { ed.switch_tilecol(false); ed.keydelay = 6; } if(key.keymap[SDLK_F3]) { ed.switch_enemy(false); ed.keydelay=6; } if(key.keymap[SDLK_F4]) { ed.keydelay=6; ed.boundarytype=1; ed.boundarymod=1; } if(key.keymap[SDLK_F5]) { ed.keydelay=6; ed.boundarytype=2; ed.boundarymod=1; } if(key.keymap[SDLK_F10]) { if(cl.getroomprop(ed.levx, ed.levy)->directmode==1) { cl.setroomdirectmode(ed.levx, ed.levy, 0); ed.note=loc::gettext("Direct Mode Disabled"); /* Kludge fix for rainbow BG here... */ if (cl.getroomprop(ed.levx, ed.levy)->tileset == 2 && cl.getroomprop(ed.levx, ed.levy)->tilecol == 6) { cl.setroomtilecol(ed.levx, ed.levy, 0); } } else { cl.setroomdirectmode(ed.levx, ed.levy, 1); ed.note=loc::gettext("Direct Mode Enabled"); } graphics.backgrounddrawn = false; ed.notedelay = 45; ed.updatetiles = true; ed.keydelay = 6; } for (int i = 0; i < NUM_EditorTools; i++) { if (key.keymap[ed.toolkey[i]]) { ed.current_tool = (EditorTools) i; } } if(key.keymap[SDLK_w]) { ed.switch_warpdir(false); ed.keydelay = 6; } if(key.keymap[SDLK_e]) { ed.keydelay = 6; ed.getlin(TEXT_ROOMNAME, loc::gettext("Enter new room name:"), const_cast(&(cl.getroomprop(ed.levx, ed.levy)->roomname))); game.mapheld=true; } if (key.keymap[SDLK_g]) { ed.keydelay = 6; ed.getlin(TEXT_GOTOROOM, loc::gettext("Enter room coordinates x,y:"), NULL); game.mapheld=true; } //Save and load if(key.keymap[SDLK_s]) { ed.keydelay = 6; ed.getlin(TEXT_SAVE, loc::gettext("Enter map filename to save as:"), &(ed.filename)); game.mapheld=true; } if(key.keymap[SDLK_l]) { ed.keydelay = 6; ed.getlin(TEXT_LOAD, loc::gettext("Enter map filename to load:"), &(ed.filename)); game.mapheld=true; } if(!game.press_map) game.mapheld=false; if (!game.mapheld) { if(game.press_map) { game.mapheld=true; //Ok! Scan the room for the closest checkpoint int testeditor = -1; bool startpoint = false; //First up; is there a start point on this screen? for (size_t i = 0; i < customentities.size(); i++) { //if() on screen if (customentities[i].t == 16 && testeditor == -1) { int tx = customentities[i].x / 40; int ty = customentities[i].y / 30; if (tx == ed.levx && ty == ed.levy) { testeditor = i; startpoint = true; } } } if(testeditor==-1) { //Ok, settle for a check point for (size_t i = 0; i < customentities.size(); i++) { //if() on screen if (customentities[i].t == 10 && testeditor == -1) { int tx = customentities[i].x / 40; int ty = customentities[i].y / 30; if (tx == ed.levx && ty == ed.levy) { testeditor = i; } } } } if(testeditor==-1) { ed.note=loc::gettext("ERROR: No checkpoint to spawn at"); ed.notedelay=45; } else { ed.currentghosts = 0; if(!startpoint) { //Checkpoint spawn int tx=customentities[testeditor].x/40; int ty=customentities[testeditor].y/30; game.edsavex = (customentities[testeditor].x%40)*8 - 4; game.edsavey = (customentities[testeditor].y%30)*8; game.edsaverx = 100+tx; game.edsavery = 100+ty; 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 int tx = customentities[testeditor].x / 40; int ty = customentities[testeditor].y / 30; game.edsavex = (customentities[testeditor].x % 40) * 8 - 4; game.edsavey = (customentities[testeditor].y % 30) * 8; game.edsaverx = 100 + tx; game.edsavery = 100 + ty; game.edsavegc = 0; game.edsavey++; game.edsavedir = 1 - customentities[testeditor].p1; } music.haltdasmusik(); ed.returneditoralpha = 1000; // Let's start it higher than 255 since it gets clamped ed.oldreturneditoralpha = 1000; script.startgamemode(Start_EDITORPLAYTESTING); } } } ed.hmod = key.keymap[SDLK_h]; ed.vmod = key.keymap[SDLK_v]; ed.bmod = key.keymap[SDLK_b]; ed.cmod = key.keymap[SDLK_c]; ed.xmod = key.keymap[SDLK_x]; ed.zmod = key.keymap[SDLK_z]; if(key.keymap[SDLK_COMMA]) { ed.current_tool = (EditorTools) POS_MOD(ed.current_tool - 1, NUM_EditorTools); ed.keydelay = 6; } else if(key.keymap[SDLK_PERIOD]) { ed.current_tool = (EditorTools) POS_MOD(ed.current_tool + 1, NUM_EditorTools); ed.keydelay = 6; } if(up_pressed) { ed.keydelay = 6; ed.levy--; ed.updatetiles = true; ed.changeroom = true; graphics.backgrounddrawn = false; } else if(down_pressed) { ed.keydelay = 6; ed.levy++; ed.updatetiles = true; ed.changeroom = true; graphics.backgrounddrawn = false; } else if(left_pressed) { ed.keydelay = 6; ed.levx--; ed.updatetiles = true; ed.changeroom = true; graphics.backgrounddrawn = false; } else if(right_pressed) { ed.keydelay = 6; ed.levx++; ed.updatetiles = true; ed.changeroom = true; graphics.backgrounddrawn = false; } ed.levx = POS_MOD(ed.levx, cl.mapwidth); ed.levy = POS_MOD(ed.levy, cl.mapheight); if (key.keymap[SDLK_SPACE]) { ed.spacemod = !ed.spacemod; ed.keydelay = 6; } } if(!ed.settingsmod) { if(ed.boundarymod>0) { if(key.leftbutton) { if(ed.lclickdelay==0) { if(ed.boundarymod==1) { ed.lclickdelay=1; ed.boundx1=(ed.tilex*8); ed.boundy1=(ed.tiley*8); ed.boundarymod=2; } else if(ed.boundarymod==2) { if((ed.tilex*8)+8>=ed.boundx1 && (ed.tiley*8)+8>=ed.boundy1) { ed.boundx2=(ed.tilex*8)+8; ed.boundy2=(ed.tiley*8)+8; } else { ed.boundx2=ed.boundx1+8; ed.boundy2=ed.boundy1+8; } if(ed.boundarytype==0) { //Script trigger ed.lclickdelay=1; ed.textent=customentities.size(); ed.addedentity((ed.boundx1/8)+(ed.levx*40),(ed.boundy1/8)+ (ed.levy*30),19, (ed.boundx2-ed.boundx1)/8, (ed.boundy2-ed.boundy1)/8); ed.getlin(TEXT_SCRIPT, loc::gettext("Enter script name:"), &(customentities[ed.textent].scriptname)); ed.lclickdelay=1; } else if(ed.boundarytype==1) { //Enemy bounds cl.setroomenemyx1(ed.levx, ed.levy, ed.boundx1); cl.setroomenemyy1(ed.levx, ed.levy, ed.boundy1); cl.setroomenemyx2(ed.levx, ed.levy, ed.boundx2); cl.setroomenemyy2(ed.levx, ed.levy, ed.boundy2); } else if(ed.boundarytype==2) { //Platform bounds cl.setroomplatx1(ed.levx, ed.levy, ed.boundx1); cl.setroomplaty1(ed.levx, ed.levy, ed.boundy1); cl.setroomplatx2(ed.levx, ed.levy, ed.boundx2); cl.setroomplaty2(ed.levx, ed.levy, ed.boundy2); } else if(ed.boundarytype==3) { //Copy } ed.boundarymod=0; ed.lclickdelay=1; } } } else { ed.lclickdelay=0; } if(key.rightbutton) { ed.boundarymod=0; } } else if(ed.warpmod) { //Placing warp token if(key.leftbutton) { if(ed.lclickdelay==0) { if(ed.free(ed.tilex, ed.tiley)==0) { customentities[ed.warpent].p1=ed.tilex+(ed.levx*40); customentities[ed.warpent].p2=ed.tiley+(ed.levy*30); ed.warpmod=false; ed.warpent=-1; ed.lclickdelay=1; } } } else { ed.lclickdelay=0; } if(key.rightbutton) { ed.removeedentity(ed.warpent); ed.warpmod=false; ed.warpent=-1; } } else { //Mouse input if(key.leftbutton) { if (ed.lclickdelay == 0) { ed.tool_place(); } } else { ed.lclickdelay = 0; } if (key.rightbutton) { // place tiles ed.tool_remove(); } if(key.middlebutton) { ed.dmtile = cl.gettile(ed.levx, ed.levy, ed.tilex, ed.tiley); } } } } if(ed.updatetiles && cl.getroomprop(ed.levx, ed.levy)->directmode==0) { ed.updatetiles=false; //Correctly set the tiles in the current room switch(cl.getroomprop(ed.levx, ed.levy)->tileset) { case 0: //The Space Station for(int j=0; j<30; j++) { for(int i=0; i<40; i++) { int temp=cl.gettile(ed.levx, ed.levy, i, j); if(temp>=3 && temp<80) { //Fix spikes cl.settile(ed.levx, ed.levy, i, j, ed.spikedir(i, j)); } else if(temp==2 || temp>=680) { //Fix background cl.settile( ed.levx, ed.levy, i, j, ed.backedgetile(i, j) + ed.backbase(ed.levx, ed.levy) ); } else if(temp>0) { //Fix tiles cl.settile( ed.levx, ed.levy, i, j, ed.edgetile(i, j) + ed.base(ed.levx, ed.levy) ); } } } break; case 1: //Outside for(int j=0; j<30; j++) { for(int i=0; i<40; i++) { int temp=cl.gettile(ed.levx, ed.levy, i, j); if(temp>=3 && temp<80) { //Fix spikes cl.settile(ed.levx, ed.levy, i, j, ed.spikedir(i, j)); } else if(temp==2 || temp>=680) { //Fix background cl.settile( ed.levx, ed.levy, i, j, ed.outsideedgetile(i, j) + ed.backbase(ed.levx, ed.levy) ); } else if(temp>0) { //Fix tiles cl.settile( ed.levx, ed.levy, i, j, ed.edgetile(i, j) + ed.base(ed.levx, ed.levy) ); } } } break; case 2: //Lab for(int j=0; j<30; j++) { for(int i=0; i<40; i++) { int temp=cl.gettile(ed.levx, ed.levy, i, j); if(temp>=3 && temp<80) { //Fix spikes cl.settile( ed.levx, ed.levy, i, j, ed.labspikedir( i, j, cl.getroomprop(ed.levx, ed.levy)->tilecol ) ); } else if(temp==2 || temp>=680) { //Fix background cl.settile(ed.levx, ed.levy, i, j, 713); } else if(temp>0) { //Fix tiles cl.settile( ed.levx, ed.levy, i, j, ed.edgetile(i, j) + ed.base(ed.levx, ed.levy) ); } } } break; case 3: //Warp Zone/Intermission for(int j=0; j<30; j++) { for(int i=0; i<40; i++) { int temp=cl.gettile(ed.levx, ed.levy, i, j); if(temp>=3 && temp<80) { //Fix spikes cl.settile(ed.levx, ed.levy, i, j, ed.spikedir(i, j)); } else if(temp==2 || temp>=680) { //Fix background cl.settile(ed.levx, ed.levy, i, j, 713); } else if(temp>0) { //Fix tiles cl.settile( ed.levx, ed.levy, i, j, ed.edgetile(i, j) + ed.base(ed.levx, ed.levy) ); } } } break; case 4: //The ship for(int j=0; j<30; j++) { for(int i=0; i<40; i++) { int temp=cl.gettile(ed.levx, ed.levy, i, j); if(temp>=3 && temp<80) { //Fix spikes cl.settile(ed.levx, ed.levy, i, j, ed.spikedir(i, j)); } else if(temp==2 || temp>=680) { //Fix background cl.settile( ed.levx, ed.levy, i, j, ed.backedgetile(i, j) + ed.backbase(ed.levx, ed.levy) ); } else if(temp>0) { //Fix tiles cl.settile( ed.levx, ed.levy, i, j, ed.edgetile(i, j) + ed.base(ed.levx, ed.levy) ); } } } break; case 5: //The Tower break; case 6: //Custom Set 1 break; case 7: //Custom Set 2 break; case 8: //Custom Set 3 break; case 9: //Custom Set 4 break; } graphics.foregrounddrawn = false; } } int editorclass::getenemyframe(int t) { switch(t) { case 0: return 78; break; case 1: return 88; break; case 2: return 36; break; case 3: return 164; break; case 4: return 68; break; case 5: return 48; break; case 6: return 176; break; case 7: return 168; break; case 8: return 112; break; case 9: return 114; break; default: return 78; break; } } void editorclass::settile( int x, int y, int t ) { if (x >= 0 && y >= 0 && x < 40 && y < 30) { cl.settile(levx, levy, x, y, t); } graphics.foregrounddrawn = false; updatetiles = true; } int editorclass::base( int x, int y ) { //Return the base tile for the given tileset and colour const RoomProperty* const room = cl.getroomprop(x, y); if(room->tileset==0) //Space Station { if(room->tilecol>=22) { return 483 + ((room->tilecol-22)*3); } else if(room->tilecol>=11) { return 283 + ((room->tilecol-11)*3); } else { return 83 + (room->tilecol*3); } } else if(room->tileset==1) //Outside { return 480 + (room->tilecol*3); } else if(room->tileset==2) //Lab { return 280 + (room->tilecol*3); } else if(room->tileset==3) //Warp Zone/Intermission { return 80 + (room->tilecol*3); } else if(room->tileset==4) //SHIP { return 101 + (room->tilecol*3); } return 0; } int editorclass::backbase( int x, int y ) { //Return the base tile for the background of the given tileset and colour const RoomProperty* const room = cl.getroomprop(x, y); if(room->tileset==0) //Space Station { //Pick depending on tilecol switch(room->tilecol) { case 0: case 5: case 26: return 680; //Blue break; case 3: case 16: case 23: return 683; //Yellow break; case 9: case 12: case 21: return 686; //Greeny Cyan break; case 4: case 8: case 24: case 28: case 30: return 689; //Green break; case 20: case 29: return 692; //Orange break; case 2: case 6: case 11: case 22: case 27: return 695; //Red break; case 1: case 10: case 15: case 19: case 31: return 698; //Pink break; case 14: case 18: return 701; //Dark Blue break; case 7: case 13: case 17: case 25: return 704; //Cyan break; default: return 680; break; } } else if(room->tileset==1) //outside { return 680 + (room->tilecol*3); } else if(room->tileset==2) //Lab { return 0; } else if(room->tileset==3) //Warp Zone/Intermission { return 120 + (room->tilecol*3); } else if(room->tileset==4) //SHIP { return 741 + (room->tilecol*3); } return 0; } int editorclass::at( int x, int y ) { x = SDL_max(0, SDL_min(x, 39)); y = SDL_max(0, SDL_min(y, 29)); return cl.gettile(levx, levy, x, y); } int editorclass::tile_type_wrap(int x, int y) { x = POS_MOD(x, cl.mapwidth * 40); y = POS_MOD(y, cl.mapheight * 30); const RoomProperty* const room = cl.getroomprop(x / 40, y / 30); int tile = cl.getabstile(x, y); if (tile == 1 || (tile >= 80 && tile <= 679)) { // It's solid. return TileType_SOLID; } if ((tile >= 6 && tile <= 9) || tile == 49 || tile == 50) { // It's a spike! return TileType_SPIKE; } if (room->tileset != 0) { // tiles2.png is slightly different. if (tile >= 51 && tile <= 74) { // It has more spikes! return TileType_SPIKE; } if (tile == 740) { // And a stray solid. return TileType_SOLID; } } return TileType_NONSOLID; } int editorclass::backonlyfree(int x, int y) { // Returns 1 if tile is a background tile, 0 otherwise if (x < 0) return backonlyfree(0, y); if (y < 0) return backonlyfree(x, 0); if (x >= 40) return backonlyfree(39, y); if (y >= 30) return backonlyfree(x, 29); if (x >= 0 && y >= 0 && x < 40 && y < 30) { if (cl.gettile(levx, levy, x, y) >= 680) { return 1; } } return 0; } int editorclass::backfree( int x, int y ) { //Returns 0 if tile is not a block or background tile, 1 otherwise if(x<0) return backfree(0,y); if(y<0) return backfree(x,0); if(x>=40) return backfree(39,y); if(y>=30) return backfree(x,29); if(x>=0 && y>=0 && x<40 && y<30) { if(cl.gettile(levx, levy, x, y)==0) { return 0; } } return 1; } int editorclass::spikefree(int x, int y) { //Returns 0 if tile is not a block or spike, 1 otherwise if (x == -1) return free(0, y); if (y == -1) return free(x, 0); if (x == 40) return free(39, y); if (y == 30) return free(x, 29); if (x >= 0 && y >= 0 && x < 40 && y < 30) { if (cl.gettile(levx, levy, x, y) == 0) { return 0; } else { if (cl.gettile(levx, levy, x, y) >= 680) { return 0; } } } return 1; } int editorclass::free( int x, int y ) { //Returns 0 if tile is not a block, 1 otherwise if(x==-1) return free(0,y); if(y==-1) return free(x,0); if(x==40) return free(39,y); if(y==30) return free(x,29); if(x>=0 && y>=0 && x<40 && y<30) { if(cl.gettile(levx, levy, x, y)==0) { return 0; } else { if(cl.gettile(levx, levy, x, y)>=2 && cl.gettile(levx, levy, x, y)<80) { return 0; } if(cl.gettile(levx, levy, x, y)>=680) { return 0; } } } return 1; } int editorclass::match( int x, int y ) { if(free(x-1,y)==0 && free(x,y-1)==0 && free(x+1,y)==0 && free(x,y+1)==0) return 0; if(free(x-1,y)==0 && free(x,y-1)==0) return 10; if(free(x+1,y)==0 && free(x,y-1)==0) return 11; if(free(x-1,y)==0 && free(x,y+1)==0) return 12; if(free(x+1,y)==0 && free(x,y+1)==0) return 13; if(free(x,y-1)==0) return 1; if(free(x-1,y)==0) return 2; if(free(x,y+1)==0) return 3; if(free(x+1,y)==0) return 4; if(free(x-1,y-1)==0) return 5; if(free(x+1,y-1)==0) return 6; if(free(x-1,y+1)==0) return 7; if(free(x+1,y+1)==0) return 8; return 0; } int editorclass::outsidematch( int x, int y ) { if(backonlyfree(x-1,y)==0 && backonlyfree(x+1,y)==0) return 2; if(backonlyfree(x,y-1)==0 && backonlyfree(x,y+1)==0) return 1; return 0; } int editorclass::backmatch( int x, int y ) { //Returns the first position match for a border // 5 1 6 // 2 X 4 // 7 3 8 if(backfree(x-1,y)==0 && backfree(x,y-1)==0 && backfree(x+1,y)==0 && backfree(x,y+1)==0) return 0; if(backfree(x-1,y)==0 && backfree(x,y-1)==0) return 10; if(backfree(x+1,y)==0 && backfree(x,y-1)==0) return 11; if(backfree(x-1,y)==0 && backfree(x,y+1)==0) return 12; if(backfree(x+1,y)==0 && backfree(x,y+1)==0) return 13; if(backfree(x,y-1)==0) return 1; if(backfree(x-1,y)==0) return 2; if(backfree(x,y+1)==0) return 3; if(backfree(x+1,y)==0) return 4; if(backfree(x-1,y-1)==0) return 5; if(backfree(x+1,y-1)==0) return 6; if(backfree(x-1,y+1)==0) return 7; if(backfree(x+1,y+1)==0) return 8; return 0; } int editorclass::edgetile( int x, int y ) { switch(match(x,y)) { case 14: return 0; break; case 10: return 80; break; case 11: return 82; break; case 12: return 160; break; case 13: return 162; break; case 1: return 81; break; case 2: return 120; break; case 3: return 161; break; case 4: return 122; break; case 5: return 42; break; case 6: return 41; break; case 7: return 2; break; case 8: return 1; break; case 0: default: return 0; break; } } int editorclass::outsideedgetile( int x, int y ) { switch(outsidematch(x,y)) { case 2: return 0; break; case 1: return 1; break; case 0: default: return 2; break; } } int editorclass::backedgetile( int x, int y ) { switch(backmatch(x,y)) { case 14: return 0; break; case 10: return 80; break; case 11: return 82; break; case 12: return 160; break; case 13: return 162; break; case 1: return 81; break; case 2: return 120; break; case 3: return 161; break; case 4: return 122; break; case 5: return 42; break; case 6: return 41; break; case 7: return 2; break; case 8: return 1; break; case 0: default: return 0; break; } } int editorclass::labspikedir( int x, int y, int t ) { // a slightly more tricky case if(free(x,y+1)==1) return 63 + (t*2); if(free(x,y-1)==1) return 64 + (t*2); if(free(x-1,y)==1) return 51 + (t*2); if(free(x+1,y)==1) return 52 + (t*2); return 63 + (t*2); } int editorclass::spikedir( int x, int y ) { if(free(x,y+1)==1) return 8; if(free(x,y-1)==1) return 9; if(free(x-1,y)==1) return 49; if(free(x+1,y)==1) return 50; return 8; } void editorclass::switch_tileset(const bool reversed) { const char* tilesets[] = {"Space Station", "Outside", "Lab", "Warp Zone", "Ship"}; int tiles = cl.getroomprop(levx, levy)->tileset; if (reversed) { tiles--; } else { tiles++; } const int modulus = SDL_arraysize(tilesets); tiles = POS_MOD(tiles, modulus); cl.setroomtileset(levx, levy, tiles); clamp_tilecol(levx, levy, false); char buffer[3*SCREEN_WIDTH_CHARS + 1]; vformat_buf( buffer, sizeof(buffer), loc::gettext("Now using {area} Tileset"), "area:str", loc::gettext(tilesets[tiles]) ); note = buffer; notedelay = 45; updatetiles = true; graphics.backgrounddrawn = false; } void editorclass::switch_tilecol(const bool reversed) { int tilecol = cl.getroomprop(levx, levy)->tilecol; if (reversed) { tilecol--; } else { tilecol++; } cl.setroomtilecol(levx, levy, tilecol); clamp_tilecol(levx, levy, true); notedelay = 45; note = loc::gettext("Tileset Colour Changed"); updatetiles = true; graphics.backgrounddrawn = false; } void editorclass::clamp_tilecol(const int rx, const int ry, const bool wrap) { const RoomProperty* const room = cl.getroomprop(rx, ry); const int tileset = room->tileset; int tilecol = room->tilecol; int mincol = -1; int maxcol = 5; // Only Space Station allows tileset -1 if (tileset != 0) { mincol = 0; } switch (tileset) { case 0: maxcol = 31; break; case 1: maxcol = 7; break; case 2: if (room->directmode) { maxcol = 6; } break; case 3: maxcol = 6; break; case 5: maxcol = 29; break; } // If wrap is true, wrap-around, otherwise just cap if (tilecol > maxcol) { tilecol = (wrap ? mincol : maxcol); } if (tilecol < mincol) { tilecol = (wrap ? maxcol : mincol); } cl.setroomtilecol(rx, ry, tilecol); } void editorclass::switch_enemy(const bool reversed) { const RoomProperty* const room = cl.getroomprop(levx, levy); int enemy = room->enemytype; if (reversed) { enemy--; } else { enemy++; } const int modulus = 10; enemy = POS_MOD(enemy, modulus); cl.setroomenemytype(levx, levy, enemy); note = loc::gettext("Enemy Type Changed"); notedelay = 45; } void editorclass::switch_warpdir(const bool reversed) { static const int modulus = 4; const RoomProperty* const room = cl.getroomprop(levx, levy); int warpdir = room->warpdir; if (reversed) { --warpdir; } else { ++warpdir; } warpdir = POS_MOD(warpdir, modulus); cl.setroomwarpdir(levx, levy, warpdir); switch (warpdir) { default: note = loc::gettext("Room warping disabled"); break; case 1: note = loc::gettext("Room warps horizontally"); break; case 2: note = loc::gettext("Room warps vertically"); break; case 3: note = loc::gettext("Room warps in all directions"); break; } notedelay = 45; graphics.backgrounddrawn = false; } #endif /* NO_CUSTOM_LEVELS and NO_EDITOR */