Merge pull request #613 from winterheart/mission-list

Enhance mission levels select window
This commit is contained in:
Louis Gombert 2024-10-20 21:00:03 +02:00 committed by GitHub
commit 496b2ed7c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 524 additions and 488 deletions

View File

@ -1459,7 +1459,7 @@ tConvertObject object_convert[] = {
int object_convert_size = sizeof(object_convert) / sizeof(tConvertObject);
int chunk_start, chunk_size, filelen;
uint32_t chunk_start, chunk_size, filelen;
int CountDataToPageIn();
@ -2648,7 +2648,44 @@ void BuildXlateTable(CFILE *ifile, int (*lookup_func)(const char *), int16_t *xl
xlate_table[i] = -1;
}
#define ISCHUNK(name) (!strncmp(chunk_name, name, 4))
#define ISCHUNK(name) (strncmp(chunk_name, name, 4) == 0)
/**
* Read and check level header
* @param fp file handle of level
* @return version of level, -1 on error
*/
int ReadHeader(CFILE *fp) {
// Read & check tag
char chunk_name[4];
cf_ReadBytes((uint8_t *)chunk_name, 4, fp);
if (!ISCHUNK(LEVEL_FILE_TAG)) {
return -1;
}
// Read version number
int version = cf_ReadInt(fp);
return version;
}
void ReadInfoChunk(CFILE *fp, level_info &info) {
// Initialize info
strcpy(info.name, "Unnamed");
strcpy(info.designer, "Anonymous");
strcpy(info.copyright, "");
strcpy(info.notes, "");
cf_ReadString(info.name, sizeof(info.name), fp);
// Localize level name here...
strcpy(info.name, LocalizeLevelName(info.name));
cf_ReadString(info.designer, sizeof(info.designer), fp);
cf_ReadString(info.copyright, sizeof(info.copyright), fp);
cf_ReadString(info.notes, sizeof(info.notes), fp);
}
// Reads info about our lightmaps
void ReadNewLightmapChunk(CFILE *fp, int version) {
@ -3588,6 +3625,60 @@ static inline char* GetCurrentSumString() {
return output_buf;
}
bool LoadLevelInfo(const std::filesystem::path &filename, level_info &info) {
CFILE *ifile = cfopen(filename, "rb");
bool found = false;
if (!ifile) {
LOG_ERROR.printf("Failed to open mission file %s", filename.u8string().c_str());
return false;
}
try {
// catch cfile errors
// Read and check header
int version = ReadHeader(ifile);
if (version > LEVEL_FILE_VERSION) {
LOG_ERROR.printf("Mission file too new (version %d)", version);
cfclose(ifile);
return false;
}
if (version < LEVEL_FILE_OLDEST_COMPATIBLE_VERSION) {
LOG_ERROR.printf("Mission file too old (version %d)", version);
cfclose(ifile);
return false;
}
while (!cfeof(ifile)) {
char chunk_name[4];
cf_ReadBytes((uint8_t *)chunk_name, 4, ifile);
chunk_start = cftell(ifile);
chunk_size = cf_ReadInt(ifile);
if (ISCHUNK(CHUNK_LEVEL_INFO)) {
ReadInfoChunk(ifile, info);
found = true;
break;
} else {
cfseek(ifile, chunk_start + chunk_size, SEEK_SET);
}
}
} catch (cfile_error *cfe) {
LOG_FATAL.printf("Error reading: file = <%s>, error = \"%s\"", cfe->file->name, cfe->msg);
ASSERT(cfe->read_write == CFE_READING);
cfclose(ifile);
return false;
}
cfclose(ifile);
return found;
}
extern bool Disable_editor_rendering;
#define LEVEL_LOADED_PCT_CALC (filelen) ? (float)(chunk_size + chunk_start) / (float)filelen : 0.0f
@ -3620,19 +3711,11 @@ int LoadLevel(char *filename, void (*cb_fn)(const char *, int, int)) {
filelen = cfilelength(ifile);
// Read & check tag
cf_ReadBytes((uint8_t *)tag, 4, ifile);
if (strncmp(tag, LEVEL_FILE_TAG, 4)) {
cfclose(ifile);
retval = 0;
goto end_loadlevel;
}
version = ReadHeader(ifile);
// Read & check version number
version = cf_ReadInt(ifile);
// Check for too-new version
// Check for too new version
if (version > LEVEL_FILE_VERSION) {
LOG_ERROR.printf("Mission file too new (version %d)", version);
cfclose(ifile);
#if (defined(EDITOR) || defined(NEWEDITOR))
if (GetFunctionMode() == EDITOR_MODE)
@ -3645,6 +3728,7 @@ int LoadLevel(char *filename, void (*cb_fn)(const char *, int, int)) {
// Check for too-old version
if (version < LEVEL_FILE_OLDEST_COMPATIBLE_VERSION) {
LOG_ERROR.printf("Mission file too old (version %d)", version);
cfclose(ifile);
#if (defined(EDITOR) || defined(NEWEDITOR))
if (GetFunctionMode() == EDITOR_MODE)
@ -3690,12 +3774,6 @@ int LoadLevel(char *filename, void (*cb_fn)(const char *, int, int)) {
// Clear terrain sounds
ClearTerrainSound();
// Init level info
strcpy(Level_info.name, "Unnamed");
strcpy(Level_info.designer, "Anonymous");
strcpy(Level_info.copyright, "");
strcpy(Level_info.notes, "");
BOA_AABB_checksum = BOA_mine_checksum = 0;
for (i = 0; i < MAX_ROOMS; i++) {
BOA_AABB_ROOM_checksum[i] = 0;
@ -3929,14 +4007,7 @@ int LoadLevel(char *filename, void (*cb_fn)(const char *, int, int)) {
} else if (ISCHUNK(CHUNK_TERRAIN)) {
ReadTerrainChunks(ifile, version);
} else if (ISCHUNK(CHUNK_LEVEL_INFO)) {
cf_ReadString(Level_info.name, sizeof(Level_info.name), ifile);
// Localize level name here...
strcpy(Level_info.name, LocalizeLevelName(Level_info.name));
cf_ReadString(Level_info.designer, sizeof(Level_info.designer), ifile);
cf_ReadString(Level_info.copyright, sizeof(Level_info.copyright), ifile);
cf_ReadString(Level_info.notes, sizeof(Level_info.notes), ifile);
ReadInfoChunk(ifile, Level_info);
if (version >= 83) {
Gravity_strength = cf_ReadFloat(ifile);

View File

@ -444,6 +444,7 @@
*/
#include "cfile.h"
#include "Mission.h"
#include "room.h"
// Chunk types
@ -634,6 +635,15 @@
// 130->131 Added the powerups ignore gravity checkbox
// 131->132 Added another ff bounce texture for Dan (his last day wish)
/**
* Get information from level such as level name, author copyrights and notes.
* This function much lighter that LoadLevel() and gets only INFO chunk from mission.
* @param filename name of mission file
* @param info resulted info
* @return true on success
*/
bool LoadLevelInfo(const std::filesystem::path &filename, level_info &info);
// Load a level file
// Returns 1 if file read ok, else 0
// cb_fn returns current chunk, parm1 = # bytes in chunk, parm2 = number of bytes in file

File diff suppressed because it is too large Load Diff

View File

@ -167,6 +167,8 @@
#ifndef MISSION_H
#define MISSION_H
#include <vector>
#include "pstypes.h"
#include "descent.h"
@ -178,10 +180,10 @@
#define LOAD_PROGRESS_DONE 200
// Load level progress worker
void LoadLevelProgress(int step, float percent, const char *chunk = NULL);
void LoadLevelProgress(int step, float percent, const char *chunk = nullptr);
// array constants
const int MSN_FILENAMELEN = _MAX_PATH, MSN_URLLEN = 256;
// Array constants
const int MSN_URLLEN = 256;
#define MAX_KEYWORDLEN 300
@ -209,22 +211,22 @@ extern level_info Level_info;
// level information
struct tLevelNode {
// level flags
unsigned flags; // level flags
unsigned objective_flags; // level objective flags
// Level flags
uint32_t flags; // level flags
uint32_t objective_flags; // level objective flags
// movies
// Movies
char *moviename;
char *endmovie;
// level filename
// Level filename
char *filename; // mine filename.
char *briefname; // briefing filename
char *hog; // hog file name for this level
char *score; // music score of level
char *progress; // File name containing the progress background screen
// level branching
// Level branching
uint8_t lvlbranch0, lvlbranch1; // FORK or BRANCH command
uint8_t secretlvl; // SECRET command
uint8_t pad;
@ -251,10 +253,10 @@ struct tMission {
// listing of levels.
int num_levels; // number of levels
int cur_level; // current level playing.
tLevelNode *levels; // array of levels
std::vector<tLevelNode> levels; // array of levels
};
// structyre used to get information about a mission
// structure used to get information about a mission
struct tMissionInfo {
char name[MSN_NAMELEN];
char author[MSN_NAMELEN];
@ -272,7 +274,19 @@ struct tMissionInfo {
extern tMission Current_mission;
extern tLevelNode *Current_level;
extern char D3MissionsDir[];
extern std::filesystem::path D3MissionsDir;
/**
* Loads the mn3 file, specifies the hog and table file.
* @param mn3file file for loading
* @return true on success
*/
bool mn3_Open(const std::filesystem::path &mn3file);
/**
* Closes the current mn3 file
*/
void mn3_Close();
// Get the name field out of the mission file
const char *GetMissionName(const char *mission);
@ -308,13 +322,18 @@ bool LoadMissionLevel(int level);
bool InitMissionScript();
// Shows text on a background
void ShowProgressScreen(const char *str, const char *str2 = NULL, bool flip = true);
void ShowProgressScreen(const char *str, const char *str2 = nullptr, bool flip = true);
// See if a mission file is multiplayer playable.
bool IsMissionMultiPlayable(const char *mission);
// return information about a mission
bool GetMissionInfo(const char *msnfile, tMissionInfo *msn);
/**
* Fill information about a mission.
* @param msnfile mission file
* @param msn mission info to be filled
* @return true on success
*/
bool GetMissionInfo(const std::filesystem::path &msnfile, tMissionInfo *msn);
// Returns the max number of teams this mission can support for this mod, or
//-1 if this level shouldn't be played with this mission

View File

@ -1138,9 +1138,6 @@ extern matrix editor_player_orient;
extern int editor_player_roomnum;
#endif
extern bool mn3_Open(const char *mn3file);
extern void mn3_Close();
extern bool Game_gauge_do_time_test;
extern char Game_gauge_usefile[_MAX_PATH];
@ -1331,8 +1328,6 @@ bool SimpleStartLevel(const std::filesystem::path& level_name) {
Current_mission.cur_level = 1;
Current_mission.num_levels = 1;
Current_mission.levels = mem_rmalloc<tLevelNode>();
memset(Current_mission.levels, 0, sizeof(tLevelNode));
Current_level = nullptr;
Current_mission.levels[0].filename = mem_strdup(level_name.u8string().c_str());
@ -1455,7 +1450,7 @@ void DeleteAmbientObjects() {
void Localization_SetLanguage(int type);
int Localization_GetLanguage(void);
void LoadLevelText(const char *level_filename);
void LoadLevelText(const std::filesystem::path &level_filename);
// Starts the level, which has already been loaded
void StartLevel() {

View File

@ -654,6 +654,9 @@
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <sstream>
#include <vector>
#include "log.h"
#include "menu.h"
@ -661,7 +664,6 @@
#include "game.h"
#include "Mission.h"
#include "multi_ui.h"
#include "ctlconfig.h"
#include "config.h"
#include "gamesave.h"
#include "gamesequence.h"
@ -698,7 +700,8 @@ bool Directplay_lobby_launched_game = false;
#define IDV_LOADLEVEL 20
#define IDV_OK 1
#define IDV_CANCEL 2
bool MenuLoadLevel(void);
bool MenuLoadLevel();
#endif
// for command line joining of games
bool Auto_connected = false;
@ -742,7 +745,7 @@ int MainMenu() {
main_menu.AddItem(IDV_CREDITS, KEY_C, TXT_MENUCREDITS);
main_menu.AddItem(IDV_QUIT, KEY_Q, TXT_MENUQUIT, MM_ENDMENU_TYPE);
#ifdef _DEBUG
main_menu.AddItem(0, 0, NULL);
main_menu.AddItem(0, 0, nullptr);
main_menu.AddItem(IDV_LOADLEVEL, 0, "Load Level");
#endif
// page in ui data.
@ -781,7 +784,7 @@ int MainMenu() {
Demo_restart = false;
SetGameMode(GM_NORMAL);
SetFunctionMode(LOADDEMO_MODE);
exit_menu = 1;
exit_menu = true;
continue;
} else if (MultiDLLGameStarting) {
// Go back into the multiplayer DLL @ the game list
@ -789,32 +792,12 @@ int MainMenu() {
LOG_DEBUG << "Returning to Multiplayer!";
if (ReturnMultiplayerGameMenu()) {
exit_menu = 1;
exit_menu = true;
SetFunctionMode(GAME_MODE);
continue;
}
}
/*
else
{
if(FirstGame)
{
//MenuScene();
//ui_ShowCursor();
//Descent->defer();
//DoUIFrame();
//rend_Flip();
//GetUIFrameResult();
res = IDV_NEWGAME;
}
else {
main_menu.SetMusicRegion(MM_MUSIC_REGION);
res = main_menu.DoUI();
}
}
if (FirstGame)
res = IDV_NEWGAME;
*/
res = FirstGame ? IDV_NEWGAME : -1;
if (res == -1) {
main_menu.SetMusicRegion(MM_MUSIC_REGION);
@ -836,9 +819,9 @@ int MainMenu() {
break;
case IDV_QUIT:
if (DoMessageBox(TXT_MENUQUIT, TXT_QUITMESSAGE, MSGBOX_YESNO)) {
exit_game = 1;
exit_menu = 1;
Mem_quick_exit = 1; // tell the mem library to not free up each chunk individually
exit_game = true;
exit_menu = true;
Mem_quick_exit = true; // tell the mem library to not free up each chunk individually
}
break;
case IDV_LOADGAME:
@ -864,12 +847,12 @@ int MainMenu() {
LOG_DEBUG << "Multiplayer!";
// make all ships available
LOG_DEBUG << "Making all ships available";
for (int i = 0; i < MAX_SHIPS; i++) {
if (Ships[i].used)
PlayerSetShipPermission(-1, Ships[i].name, true);
for (auto & Ship : Ships) {
if (Ship.used)
PlayerSetShipPermission(-1, Ship.name, true);
}
if (MainMultiplayerMenu()) {
exit_menu = 1;
exit_menu = true;
SetFunctionMode(GAME_MODE);
}
} break;
@ -877,12 +860,12 @@ int MainMenu() {
if (LoadDemoDialog()) {
SetGameMode(GM_NORMAL);
SetFunctionMode(LOADDEMO_MODE);
exit_menu = 1;
exit_menu = true;
}
break;
case IDV_CREDITS:
SetFunctionMode(CREDITS_MODE);
exit_menu = 1;
exit_menu = true;
/*
#if defined(DEMO)
//extern void ShowStaticScreen(char *bitmap_filename);
@ -901,7 +884,7 @@ int MainMenu() {
case IDV_LOADLEVEL: {
if (MenuLoadLevel()) {
ShowProgressScreen(TXT_LOADINGLEVEL);
exit_menu = 1;
exit_menu = true;
}
} break;
#endif
@ -940,11 +923,10 @@ bool ProcessCommandLine() {
#endif
tokp = strtok(p, "/");
if (stricmp(tokp, "ip") == 0) {
tokp = strtok(NULL, "/");
tokp = strtok(nullptr, "/");
Auto_login_port[0] = '\0';
strcpy(Auto_login_addr, tokp);
// char seldll[_MAX_PATH*2];
// ddio_MakePath(seldll,Base_directory,"online","Direct TCP~IP Game.d3c",NULL);
if (LoadMultiDLL("Direct TCP~IP")) {
CallMultiDLL(MT_AUTO_LOGIN);
if (MultiDLLGameStarting) {
@ -957,11 +939,10 @@ bool ProcessCommandLine() {
LOG_WARNING << "Couldn't load DLL.";
}
} else if (stricmp(tokp, "pxo") == 0) {
tokp = strtok(NULL, "/");
tokp = strtok(nullptr, "/");
Auto_login_port[0] = '\0';
strcpy(Auto_login_addr, tokp);
// char seldll[_MAX_PATH*2];
// ddio_MakePath(seldll,Base_directory,"online","parallax online.d3c",NULL);
if (LoadMultiDLL("parallax online")) {
CallMultiDLL(MT_AUTO_LOGIN);
if (MultiDLLGameStarting) {
@ -1042,6 +1023,7 @@ bool ProcessCommandLine() {
SetUICallback(DEFAULT_UICALLBACK);
return exit_menu ? true : false;
}
//////////////////////////////////////////////////////////////////////////////
// Start New Game
#define MSNDLG_WIDTH 512
@ -1057,6 +1039,7 @@ bool ProcessCommandLine() {
#define MSNBTN_X2 ((3 * MSNDLG_WIDTH / 4) - (MSNBTN_W / 2))
#define MSNBTN_Y (MSNDLG_HEIGHT - 64)
#define UID_MSNLB 100
#define UID_LVLB 101
#define UID_MSNINFO 0x1000
#define TRAINING_MISSION_NAME "Pilot Training"
@ -1090,25 +1073,23 @@ static inline int count_missions(const std::vector<std::filesystem::path> &missi
return c;
}
static inline int generate_mission_listbox(newuiListBox *lb, int n_maxfiles, char **filelist,
static inline int generate_mission_listbox(newuiListBox *lb, std::vector<std::filesystem::path> &filelist,
const std::vector<std::filesystem::path> &missions_directories) {
int c = 0;
for (const auto &missions_directory : missions_directories) {
ddio_DoForeachFile(
missions_directory, std::regex(".*\\.mn3"), [&c, &lb, &n_maxfiles, &filelist](const std::filesystem::path &path) {
missions_directory, std::regex(".*\\.mn3"), [&c, &lb, &filelist](const std::filesystem::path &path) {
tMissionInfo msninfo{};
if (c < n_maxfiles) {
if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0)
return;
if (GetMissionInfo(path.filename().u8string().c_str(), &msninfo) && msninfo.name[0] && msninfo.single) {
filelist[c] = mem_strdup(path.filename().u8string().c_str());
lb->AddItem(msninfo.name);
c++;
if (!(c % 2))
DoWaitMessage(true);
}
}
});
if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0)
return;
if (GetMissionInfo(path.filename(), &msninfo) && msninfo.name[0] && msninfo.single) {
filelist.push_back(path.filename());
lb->AddItem(msninfo.name);
c++;
if (!(c % 2))
DoWaitMessage(true);
}
});
}
return c;
@ -1121,8 +1102,8 @@ bool MenuNewGame() {
newuiTiledWindow menu;
newuiSheet *select_sheet;
newuiListBox *msn_lb;
char **filelist = nullptr;
int n_missions, i, res;
std::vector<std::filesystem::path> filelist;
int n_missions, res;
bool found = false;
bool do_menu = true, load_mission = false, retval = true;
#ifdef DEMO
@ -1174,59 +1155,41 @@ bool MenuNewGame() {
menu.Create(TXT_MENUNEWGAME, 0, 0, 448, 384);
select_sheet = menu.GetSheet();
select_sheet->NewGroup(NULL, 10, 0);
select_sheet->NewGroup(nullptr, 10, 0);
msn_lb = select_sheet->AddListBox(352, 256, UID_MSNLB);
select_sheet->NewGroup(NULL, 160, 280, NEWUI_ALIGN_HORIZ);
select_sheet->NewGroup(nullptr, 160, 280, NEWUI_ALIGN_HORIZ);
select_sheet->AddButton(TXT_OK, UID_OK);
select_sheet->AddButton(TXT_CANCEL, UID_CANCEL);
#ifndef OEM
select_sheet->AddButton(TXT_MSNINFO, UID_MSNINFO);
#endif
#ifndef OEM
// add mission names to listbox
// count valid mission files.
// add a please wait dialog here.
auto missions_directories = cf_LocateMultiplePaths("missions");
n_missions = 0;
n_missions += count_missions(missions_directories);
if (n_missions) {
// allocate extra mission slot because of check below which adds a name to the filelist.
filelist = (char **)mem_malloc(sizeof(char *) * (n_missions + 1));
for (i = 0; i < (n_missions + 1); i++)
filelist[i] = NULL;
} else {
DoMessageBox(TXT_ERROR, TXT_NOMISSIONS, MSGBOX_OK);
retval = false;
DoWaitMessage(false);
goto missions_fail;
}
// generate real listbox now.
generate_mission_listbox(msn_lb, n_missions, filelist, missions_directories);
// #ifdef RELEASE
int k;
for (k = 0; k < n_missions; k++) {
if (!filelist[k])
continue;
if (stricmp(filelist[k], "d3.mn3") == 0) {
n_missions = generate_mission_listbox(msn_lb, filelist, cf_LocateMultiplePaths("missions"));
if (n_missions == 0) {
DoMessageBox(TXT_ERROR, TXT_NOMISSIONS, MSGBOX_OK);
DoWaitMessage(false);
return false;
}
for (auto const &mission : filelist) {
if (stricmp(mission.u8string().c_str(), "d3.mn3") == 0) {
found = true;
break;
}
}
if (!found) {
filelist[n_missions] = mem_strdup("d3.mn3");
filelist.emplace_back("d3.mn3");
msn_lb->AddItem(TXT_MAINMISSION);
n_missions++;
}
// #endif
#else
#define OEM_MISSION_NAME "Descent 3: Sol Ascent"
#define OEM_TRAINING_NAME "Pilot Training "
#define OEM_TRAINING_NAME "Pilot Training"
n_missions = 2;
filelist = mem_rmalloc<char *>(2);
filelist[0] = mem_strdup(OEM_MISSION_FILE);
;
filelist.emplace_back(OEM_MISSION_NAME);
msn_lb->AddItem(OEM_MISSION_NAME);
filelist[1] = mem_strdup(OEM_TRAINING_FILE);
filelist.emplace_back(OEM_TRAINING_NAME);
msn_lb->AddItem(OEM_TRAINING_NAME);
#endif
DoWaitMessage(false);
@ -1238,14 +1201,14 @@ redo_newgame_menu:
res = menu.DoUI();
#ifndef OEM
if (res == UID_MSNINFO) {
tMissionInfo msninfo;
tMissionInfo msninfo{};
int index = msn_lb->GetCurrentIndex();
if (index >= 0 && index < n_missions) {
if (GetMissionInfo(filelist[index], &msninfo)) {
if (msninfo.name[0]) {
newuiTiledWindow infownd;
newuiSheet *sheet;
infownd.Create(NULL, 0, 0, 384, 192);
infownd.Create(nullptr, 0, 0, 384, 192);
infownd.Open();
sheet = infownd.GetSheet();
sheet->NewGroup(TXT_MSNNAME, 0, 0);
@ -1260,7 +1223,7 @@ redo_newgame_menu:
sheet->AddText(msninfo.desc);
else
sheet->AddText(TXT_NONE);
sheet->NewGroup(NULL, 240, 118);
sheet->NewGroup(nullptr, 240, 118);
sheet->AddButton(TXT_OK, UID_OK);
infownd.DoUI();
infownd.Close();
@ -1277,15 +1240,11 @@ redo_newgame_menu:
retval = false;
} else if (res == UID_OK || res == UID_MSNLB) {
int index = msn_lb->GetCurrentIndex();
char *nameptr = NULL;
if (index >= 0 && index < n_missions) {
std::filesystem::path nameptr;
if (index >= 0 && index < filelist.size()) {
nameptr = filelist[index];
}
#ifndef OEM
if (!nameptr || !LoadMission(nameptr)) {
#else
if (!LoadMission(nameptr)) {
#endif
if (nameptr.empty() || !LoadMission(nameptr.u8string().c_str())) {
DoMessageBox(TXT_ERROR, TXT_ERRLOADMSN, MSGBOX_OK);
retval = false;
} else {
@ -1300,8 +1259,7 @@ redo_newgame_menu:
highest = std::min(highest + 1, Current_mission.num_levels);
#endif
if (highest > 1) {
int start_level;
start_level = DisplayLevelWarpDlg(highest);
int start_level = DisplayLevelWarpDlg(highest);
if (start_level == -1) {
goto redo_newgame_menu;
} else {
@ -1317,63 +1275,87 @@ redo_newgame_menu:
}
}
menu.Destroy();
missions_fail:
// free all memory
for (i = 0; i < n_missions; i++) {
if (filelist[i]) {
mem_free(filelist[i]);
}
}
if (filelist) {
mem_free(filelist);
}
return retval;
#endif
}
// DisplayLevelWarpDlg
// pass in the max level allowed to be chosen, if -1, than all levels are allowed (a.k.a level warp cheat)
int DisplayLevelWarpDlg(int max_level) {
newuiMessageBox hwnd;
newuiSheet *sheet;
newuiTiledWindow menu;
newuiSheet *select_sheet;
newuiListBox *level_listbox;
int chosen_level = 1, res;
int highest_allowed;
char buffer[128];
char *input_text;
// creates a sheet
sheet = hwnd.GetSheet();
// Create UI
menu.Create(Current_mission.name, 0, 0, 448, 384);
select_sheet = menu.GetSheet();
select_sheet->NewGroup(nullptr, 10, 0);
level_listbox = select_sheet->AddListBox(352, 256, UID_LVLB, UILB_NOSORT);
select_sheet->NewGroup(nullptr, 160, 280, NEWUI_ALIGN_HORIZ);
select_sheet->AddButton(TXT_OK, UID_OK);
select_sheet->AddButton(TXT_CANCEL, UID_CANCEL);
if (max_level != -1) {
hwnd.Create(TXT_LEVELSELECT, MSGBOX_OKCANCEL);
highest_allowed = max_level;
snprintf(buffer, sizeof(buffer), TXT_LEVELSELECTB, highest_allowed);
} else {
// level warp
hwnd.Create(TXT_LEVELWARP, MSGBOX_OKCANCEL);
highest_allowed = Current_mission.num_levels;
snprintf(buffer, sizeof(buffer), TXT_LEVELWARPB, Current_mission.num_levels);
}
sheet->NewGroup(buffer, 0, 0);
input_text = sheet->AddEditBox(NULL, 4, 64, IDV_QUIT, UIED_NUMBERS);
sprintf(input_text, "%d", chosen_level);
redo_level_choose:
hwnd.Open();
res = hwnd.DoUI();
hwnd.Close();
if (res == UID_OK || res == IDV_QUIT) {
chosen_level = atoi(input_text);
if (chosen_level < 1 || chosen_level > highest_allowed) {
snprintf(buffer, sizeof(buffer), TXT_CHOOSELEVEL, highest_allowed);
DoMessageBox(TXT_ERROR, buffer, MSGBOX_OK);
goto redo_level_choose;
int mn3_handle = -1;
// Because of D3: Retribution split mission nature we have to load library early
if (stricmp(Current_mission.filename, "d3.mn3") == 0) {
mn3_handle = cf_OpenLibrary(std::filesystem::path("missions") / "d3_2.mn3");
}
if (stricmp(Current_mission.filename, "d3_2.mn3") == 0) {
mn3_handle = cf_OpenLibrary(std::filesystem::path("missions") / "d3.mn3");
}
for (int i = 0; i < highest_allowed; i++) {
std::stringstream ss;
level_info info{};
LoadLevelInfo(Current_mission.levels[i].filename, info);
ss << std::setw(2) << std::setfill('0') << i + 1 << ". " << info.name;
level_listbox->AddItem(ss.str().c_str());
}
if (mn3_handle != -1) {
cf_CloseLibrary(mn3_handle);
}
// Main UI loop
menu.Open();
do {
res = menu.DoUI();
if (res == UID_OK || res == UID_LVLB) {
int index = level_listbox->GetCurrentIndex();
chosen_level = index + 1; // Levels are 1-based
if (chosen_level < 1 || chosen_level > highest_allowed) {
DoMessageBox(TXT_ERROR, TXT_CHOOSELEVEL, MSGBOX_OK);
continue; // Allow another selection
}
if (res == UID_LVLB) {
res = UID_OK;
}
} else if (res == UID_CANCEL) {
chosen_level = -1;
}
} else {
chosen_level = -1;
}
hwnd.Destroy();
} while (res != UID_OK && res != UID_CANCEL);
menu.Close();
menu.Destroy();
return chosen_level;
}
#ifdef _DEBUG
// Loads a level and starts the game
bool MenuLoadLevel(void) {
bool MenuLoadLevel() {
std::filesystem::path buffer;
if (DoPathFileDialog(false, buffer, "Load Level", {"*.d3l"}, PFDF_FILEMUSTEXIST)) {
SimpleStartLevel(buffer);

View File

@ -55,7 +55,8 @@
#ifndef MENU_H
#define MENU_H
#include "pstypes.h"
#include <cstdint>
#include "grdefs.h"
#define MAX_MENUS 5

View File

@ -602,8 +602,8 @@ int msn_CheckGetMission(network_address *net_addr, char *filename) {
return 1;
#else
// Don't download local missions
char pathname[_MAX_PATH];
ddio_MakePath(pathname, D3MissionsDir, filename, NULL);
std::filesystem::path pathname;
pathname = D3MissionsDir / filename;
if (cfexist(filename) || cfexist(pathname)) {
return 1;
}

View File

@ -826,7 +826,7 @@ void MultiStartServer(int playing, char *scriptname, int dedicated_server_num_te
//-1 Not compatible!
//>=0 Number of teams supported for this mod & level
#define SCRIPTBADFORMISSION -1
#define SCRIPTBADFORMISSION (-1)
int CheckMissionForScript(char *mission, char *script, int dedicated_server_num_teams) {
char mod_keys[MAX_KEYWORDLEN];

View File

@ -89,7 +89,7 @@ void mng_ClearAddonTables();
// Push the given table file as an addon table file
// returns true on success
bool mng_SetAddonTable(char *name);
bool mng_SetAddonTable(const char *name);
// Pushes an addon pack onto the stack so we can keep track of it
void mng_PushAddonPage(int pagetype, char *name, int overlay);

View File

@ -2729,7 +2729,7 @@ void mng_ClearAddonTables() {
}
// Push the given table file as an addon table file
// returns true on success
bool mng_SetAddonTable(char *name) {
bool mng_SetAddonTable(const char *name) {
ASSERT(Num_addon_tables < MAX_ADDON_TABLES);
if (Num_addon_tables >= MAX_ADDON_TABLES)
return false;

View File

@ -325,7 +325,7 @@ void (*DLLCheckBoxSetCheck)(void *cb, bool state);
bool (*DLLCheckBoxIsChecked)(void *cb);
uint32_t (*DLLnw_GetHostAddressFromNumbers)(char *str);
void (*TableFilesClear)(void);
bool (*TableFileAdd)(char *filename);
bool (*TableFileAdd)(const char *filename);
void (*DLLDebugBreak_callback_stop)(void);
void (*DLLDebugBreak_callback_resume)(void);
void (*DLLInt3MessageBox)(const char *file, const char *line);

View File

@ -928,7 +928,7 @@ typedef void (*TableFilesClear_fp)(void);
DMFCDLLOUT(TableFilesClear_fp TableFilesClear;)
// Adds a table file into the manage system for add-on data
typedef bool (*TableFileAdd_fp)(char *filename);
typedef bool (*TableFileAdd_fp)(const char *filename);
DMFCDLLOUT(TableFileAdd_fp TableFileAdd;)
// Debugger interaction