//----------------------------------------------------------------------------- // Filename: njamedit.cpp // Created: 22. Sep 2003. by Milan Babuskov // // Purpose: Handle everything about level editor // // This file implements methods of NjamEngine class // which are defined in njam.h // // Tabs should be at 4 spaces. // Each section is separated with line: //-------... /*----------------------------------------------------------------------------- Copyright 2003 Milan Babuskov This file is part of Njam (http://njam.sourceforge.net). Njam is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Njam is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Njam in file COPYING; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -----------------------------------------------------------------------------*/ #include #include #include "SDL.h" #include "SDL_image.h" #include "njamutils.h" #include "njammap.h" #include "njamfont.h" #include "njam.h" //----------------------------------------------------------------------------- void NjamEngine::LevelEditor() { int level_type = 0; // cooperative int level_type_was = 0; char filename[250]; filename[0] = '\0'; int x=2, y=1; m_Maps.Clear(); if( m_GameOptions.UsedSkin > 0 ) m_Maps.SetMapImages(m_Skins[m_GameOptions.UsedSkin-1], 25, 25); else m_Maps.SetMapImages(m_Skins[0], 25, 25); m_CurrentMap = 0; m_Maps.SetCurrentMap(0); m_SwapLevel = -1; bool changed = false; while (true) { RenderEditor(changed, x, y, level_type); SDL_Flip(m_Screen); RenderEditor(changed, x, y, level_type); // Twice for double buffer. Only needed when hwsurfaces SDL_Flip(m_Screen); // are used // wait for a keypress SDLKey key = NjamGetch(true); // true = wait for keypress if (key == SDLK_ESCAPE && (!changed || Query("ARE YOU SURE YOU WISH TO EXIT? (Y/N)"))) return; if (key == SDLK_k) { level_type = 1 - level_type; changed = true; } if (key == SDLK_p) { if (m_Maps.IsOk(MAPS)) m_Maps.SetTile(1, 1, ttWall); else m_Maps.SetTile(1, 1, ttEmpty); changed = true; } if (key == SDLK_c) { m_Maps.ClearCurrent(); changed = true; } if (key == SDLK_l) // select file from directory { if (SelectMap('A', filename) > -1) { int w=0; while (filename[w]) w++; if (filename[w-1] == 'P') // COOP level_type_was = 0; else level_type_was = 1; level_type = level_type_was; m_CurrentMap = 0; m_Maps.SetCurrentMap(0); changed = false; } } if (key == SDLK_s) { if (level_type != level_type_was || filename[0] == '\0') key = SDLK_a; else if (CheckForSave()) { LogFile::LogFile("Saving maps"); m_Maps.Save(filename); level_type_was = level_type; changed = false; } } if (key == SDLK_a) { if (CheckForSave()) { if (EnterFileName(filename)) { char buf[250]; char types[2][5] = { "COOP", "DUEL" }; sprintf(buf, "levels/%s.%s\0", filename, types[level_type]); sprintf(filename, "%s\0", buf); LogFile::LogFile("Saving maps"); m_Maps.Save(filename); level_type_was = level_type; changed = false; } } } if (key == SDLK_t) // test the level { if (!m_Maps.IsOk(MAPS)) Message("LEVEL IS NOT MARKED AS PLAYABLE. PRESS P TO CHANGE."); else if (m_Maps.CountSpecificTiles(ttDoor) != 1) Message("THERE MUST BE EXACTLY ONE DOOR ON LEVEL!"); else if (m_Maps.CountSpecificTiles(ttGHouse) != 1) Message("THERE MUST BE EXACTLY ONE PENTAGRAM ON LEVEL!"); else { int used_skin_orig = m_GameOptions.UsedSkin; m_Ghosts = 8; bool AllowPowerups = true; if (level_type == 0) { m_Ghosts = 5; AllowPowerups = false; } // if random skin, then use first skin always if (m_GameOptions.UsedSkin == 0) m_GameOptions.UsedSkin = 1; m_Maps.RevertToMap(m_CurrentMap); // make copy because playing the game will ruin it SetupGame(); // because of this undo isn't available anymore... m_Player[0].Playing = true; m_Player[1].Playing = true; m_Player[2].Playing = false; m_Player[3].Playing = false; m_GameType = gtDuel; PlayMap(AllowPowerups); m_Maps.SetCurrentMap(m_CurrentMap); // get the data back m_GameOptions.UsedSkin = used_skin_orig; } } if (key == SDLK_u) m_Maps.SetCurrentMap(m_CurrentMap); if (key == SDLK_w) // swap { if (m_SwapLevel == -1) // turn swap on m_SwapLevel = m_CurrentMap; else if (m_SwapLevel == m_CurrentMap) // turn swap off m_SwapLevel = -1; else // do the swap and turn off { m_Maps.RevertToMap(m_CurrentMap); // copy data back Message("LEVELS SWAPPED"); m_Maps.SwapMaps(m_CurrentMap, m_SwapLevel); m_Maps.SetCurrentMap(m_CurrentMap); // set the new buffer m_SwapLevel = -1; changed = true; } } if (key == SDLK_z && m_CurrentMap > 0) { m_Maps.RevertToMap(m_CurrentMap); // copy data back m_CurrentMap--; m_Maps.SetCurrentMap(m_CurrentMap); } if (key == SDLK_x && m_CurrentMap < MAPS-1) { m_Maps.RevertToMap(m_CurrentMap); // copy data back m_CurrentMap++; m_Maps.SetCurrentMap(m_CurrentMap); } if (key >= '0' && key <= '9') { changed = true; m_Maps.SetTile(x, y, (TileType)(key-'0')); } int oldx=x, oldy=y; if (key == SDLK_UP && y > 1) y--; if (key == SDLK_DOWN && y < MAPH-2) y++; if (key == SDLK_LEFT && x > 1) x--; if (key == SDLK_RIGHT && x < MAPW-2) x++; if (x == 1 || x == MAPW - 2) { if (y == 1 || y == MAPH - 2) { x = oldx; y = oldy; } } } } //----------------------------------------------------------------------------- void NjamEngine::RenderEditor(bool changed, int x, int y, int level_type) { SDL_Rect r, src; NjamSetRect(r, 700, 0, 100, 600); SDL_FillRect(m_Screen, &r, 0); m_Maps.RenderMap(m_Screen); // draw selector NjamSetRect(r, x*25-1, y*25-1, 27, 27); Uint32 FillColor = SDL_MapRGB(m_Screen->format, 255, 255, 127); SDL_FillRect(m_Screen, &r, FillColor); NjamSetRect(r, x*25+1, y*25+1); NjamSetRect(src, m_Maps.GetTile(x, y) * 25 + 1, 1, 23, 23); int current_skin = m_GameOptions.UsedSkin - 1; if( current_skin < 0 ) current_skin = 0; SDL_BlitSurface(m_Skins[current_skin], &src, m_Screen, &r); // draw help options m_FontYellow->WriteTextColRow(m_Screen, 70, 0, "LEVELSET"); m_FontYellow->WriteTextColRow(m_Screen, 70, 1, "L LOAD"); m_FontYellow->WriteTextColRow(m_Screen, 70, 2, "S SAVE"); m_FontYellow->WriteTextColRow(m_Screen, 70, 3, "A SAVE AS"); m_FontYellow->WriteTextColRow(m_Screen, 70, 4, "K KIND"); char buff[20]; sprintf(buff, "LEVEL: %02d\0", m_CurrentMap + 1); m_FontYellow->WriteTextColRow(m_Screen, 70, 6, buff); m_FontYellow->WriteTextColRow(m_Screen, 70, 7, "P PLAYABLE"); m_FontYellow->WriteTextColRow(m_Screen, 70, 8, "U UNDO"); m_FontYellow->WriteTextColRow(m_Screen, 70, 9, "C CLEAR"); m_FontYellow->WriteTextColRow(m_Screen, 70, 10, "T TEST"); m_FontYellow->WriteTextColRow(m_Screen, 70, 11, "W SWAP"); if (m_SwapLevel != -1) { char buff[20]; sprintf(buff, ":%02d", m_SwapLevel + 1); m_FontYellow->WriteTextColRow(m_Screen, 76, 11, buff); } m_FontYellow->WriteTextColRow(m_Screen, 70, 12, "Z PREVIOUS"); m_FontYellow->WriteTextColRow(m_Screen, 70, 13, "X NEXT"); if (level_type == 0) m_FontBlue->WriteTextXY(m_Screen, 710, 250, "COOPERATIVE"); else m_FontBlue->WriteTextXY(m_Screen, 725, 250, "DUEL"); if (changed) m_FontBlue->WriteTextXY(m_Screen, 705, 270, "UNSAVED CHANGES"); if (!m_Maps.IsOk(MAPS)) { m_FontBlue->WriteTextXY(m_Screen, 705, 222, "LEVEL MARKED AS"); m_FontBlue->WriteTextXY(m_Screen, 714, 232, "NOT PLAYABLE"); } for (int i=0; i<10; i++) { sprintf(buff, "%d\0", i); m_FontYellow->WriteTextXY(m_Screen, 720, 297+i*30, buff); NjamSetRect(src, i*25, 0, 25, 25); NjamSetRect(r, 750, 292 + i*30); SDL_BlitSurface(m_Skins[current_skin], &src, m_Screen, &r); } NjamSetRect(r, 227, 583, 244, 13); FillColor = SDL_MapRGB(m_Screen->format, 170, 170, 210); SDL_FillRect(m_Screen, &r, FillColor); NjamSetRect(r, 228, 584, 242, 11); SDL_FillRect(m_Screen, &r, 0); m_FontBlue->WriteTextXY(m_Screen, 231, 585, "SEND YOUR LEVELS TO MBABUSKOV@YAHOO.COM"); } //----------------------------------------------------------------------------- void NjamEngine::Message(char *text) { int l=0; while (text[l]) l++; // render background Uint32 FillColor; SDL_Rect dest; FillColor = SDL_MapRGB(m_Screen->format, 0, 0, 0); NjamSetRect(dest, (800-l*10-50)/2, 275, l*10+50, 55); SDL_FillRect(m_Screen, &dest, FillColor); FillColor = SDL_MapRGB(m_Screen->format, 0, 120, 0); NjamSetRect(dest, (800-l*10-30)/2, 285, l*10+30, 35); SDL_FillRect(m_Screen, &dest, FillColor); m_FontYellow->WriteTextXY(m_Screen, (800-l*10)/2, 295, text); SDL_Flip(m_Screen); NjamGetch(true); } //----------------------------------------------------------------------------- bool NjamEngine::Query(char *text) { int l=0; while (text[l]) l++; // render background Uint32 FillColor; SDL_Rect dest; FillColor = SDL_MapRGB(m_Screen->format, 0, 0, 0); NjamSetRect(dest, (800-l*10-50)/2, 275, l*10+50, 55); SDL_FillRect(m_Screen, &dest, FillColor); FillColor = SDL_MapRGB(m_Screen->format, 0, 120, 0); NjamSetRect(dest, (800-l*10-30)/2, 285, l*10+30, 35); SDL_FillRect(m_Screen, &dest, FillColor); m_FontYellow->WriteTextXY(m_Screen, (800-l*10)/2, 295, text); SDL_Flip(m_Screen); while (true) { SDLKey k = NjamGetch(true); if (k == SDLK_y) return true; if (k == SDLK_n) return false; } } //----------------------------------------------------------------------------- // returns true if eveything is ok bool NjamEngine::CheckForSave() { m_Maps.RevertToMap(m_CurrentMap); // copy CURRENT data back char message[200]; for (int i=0; iformat, 255, 255, 255); Uint32 t = SDL_GetTicks(); bool CursorOn = true; filename[0] = '\0'; while (true) { SDL_Rect dest; NjamSetRect(dest, 249, 224); SDL_BlitSurface(m_LevelsetImage, NULL, m_Screen, &dest); m_FontYellow->WriteTextXY(m_Screen, 274, 303, filename); // get len int l = 0; while (filename[l]) l++; if (SDL_GetTicks() > t + 400) // make the cursor blink { t = SDL_GetTicks(); CursorOn = !CursorOn; } if (CursorOn) { NjamSetRect(dest, 276 + 10 * l, 303, 10, 15); // draw cursor SDL_FillRect(m_Screen, &dest, white); } SDL_Flip(m_Screen); SDLKey key = NjamGetch(false); char allowed[] = "0123456789abcdefghijklmnopqrstuvwxyz "; bool ok = false; for (int k=0; allowed[k]; k++) if (allowed[k] == key) ok = true; if (ok && l < 23) { char c = key; if (c >= 'a' && c <= 'z') c -= ('a' - 'A'); filename[l] = c; filename[l+1] = '\0'; } if (key == SDLK_ESCAPE) return false; if (key == SDLK_BACKSPACE && l > 0) // backspace filename[l-1] = '\0'; if ((key == SDLK_KP_ENTER || key == SDLK_RETURN) && filename[0]) // entered { sprintf(file_name, "%s\0", filename); return true; } } } //--------------------------------------------------------------------------- int NjamEngine::SelectMap(char type, char *filename) { struct list_item *first = NULL; // open folder, load all entries DIR *dir; struct dirent *ent; if ((dir = opendir("levels")) == NULL) { printf("Unable to open directory\n"); return -1; } while ((ent = readdir(dir)) != NULL) { // check if last letter of name maches: int p = 0; while (ent->d_name[p]) p++; char c = ent->d_name[p-1]; if (c != 'P' && c != 'L') continue; if (type != 'A' && c != type) continue; struct list_item *new_file = new struct list_item; new_file->next = first; new_file->prev = NULL; if (first) first->prev = new_file; first = new_file; // copy file name for (int i=0; ent->d_name[i] && i<29; i++) { first->item_text[i] = ent->d_name[i]; first->item_text[i+1] = '\0'; } } if (closedir(dir) != 0) printf("Unable to close directory\n"); // show and let player select if more than one levelset struct list_item *selected; if (first->next) // there is more than 1 levelset selected = SelectFromList(first); else selected = first; int result = 0; // no levels loaded (so far) if (selected) { char file_name[200]; sprintf(file_name, "levels/%s\0", selected->item_text); result = m_Maps.Load(file_name); if (filename) sprintf(filename, "%s\0", file_name); } while (first) // free memory { struct list_item *temp = first; first = first->next; delete temp; } return result; } //----------------------------------------------------------------------------- struct list_item *NjamEngine::SelectFromList(struct list_item *first) { if (!first) return NULL; struct list_item *visible = first; struct list_item *selected = first; while (true) { // render background, FIXME: make nice bitmap for this Uint32 FillColor; SDL_Rect dest; // thick black border (1px) FillColor = SDL_MapRGB(m_Screen->format, 0, 0, 0); NjamSetRect(dest, 240, 175, 320, 250); SDL_FillRect(m_Screen, &dest, FillColor); FillColor = SDL_MapRGB(m_Screen->format, 230, 200, 100); // whole NjamSetRect(dest, 241, 176, 318, 248); SDL_FillRect(m_Screen, &dest, FillColor); // title FillColor = SDL_MapRGB(m_Screen->format, 100, 0, 0); NjamSetRect(dest, 249, 184, 302, 22); SDL_FillRect(m_Screen, &dest, FillColor); m_FontYellow->WriteTextXY(m_Screen, 325, 187, "SELECT LEVELSET"); // filelist FillColor = SDL_MapRGB(m_Screen->format, 80, 50, 0); NjamSetRect(dest, 249, 214, 302, 202); SDL_FillRect(m_Screen, &dest, FillColor); // render items struct list_item *temp = visible; int current = 0; while (temp && current < 10) // ten items on screen allowed { if (temp == selected) { FillColor = SDL_MapRGB(m_Screen->format, 120, 80, 0); NjamSetRect(dest, 250, 215+20*current, 300, 20); SDL_FillRect(m_Screen, &dest, FillColor); } m_FontYellow->WriteTextXY(m_Screen, 252, 216+20*current, temp->item_text); temp = temp->next; current++; } SDL_Flip(m_Screen); // wait for a keypress SDLKey key = NjamGetch(true); // true = wait for keypress if (key == SDLK_ESCAPE) return NULL; if ((key == SDLK_KP_ENTER || key == SDLK_RETURN)) break; if (key == SDLK_DOWN) { if (selected->next) { selected = selected->next; if (current == 10 && selected == temp) // out of screen - move the top visible = visible->next; } } if (key == SDLK_UP) { if (selected->prev) { if (selected == visible) // over the top visible = selected->prev; selected = selected->prev; } } } return selected; } //-----------------------------------------------------------------------------