|
|
|
@ -649,7 +649,6 @@
|
|
|
|
|
#include "gamefont.h"
|
|
|
|
|
#include "grdefs.h"
|
|
|
|
|
#include "descent.h"
|
|
|
|
|
#include "ddio.h"
|
|
|
|
|
#include "d3movie.h"
|
|
|
|
|
#include "program.h"
|
|
|
|
|
#include "ObjScript.h"
|
|
|
|
@ -672,51 +671,45 @@
|
|
|
|
|
#include "terrain.h"
|
|
|
|
|
#include "multi.h"
|
|
|
|
|
#include "hud.h"
|
|
|
|
|
#include "localization.h"
|
|
|
|
|
#include "levelgoal.h"
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Data
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Info about the current level
|
|
|
|
|
level_info Level_info;
|
|
|
|
|
tMission Current_mission;
|
|
|
|
|
tLevelNode *Current_level = NULL;
|
|
|
|
|
char D3MissionsDir[_MAX_PATH];
|
|
|
|
|
tLevelNode *Current_level = nullptr;
|
|
|
|
|
std::filesystem::path D3MissionsDir;
|
|
|
|
|
extern int Times_game_restored;
|
|
|
|
|
extern msn_urls Net_msn_URLs;
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Function prototypes
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
bool InitMissionScript();
|
|
|
|
|
void DoEndMission();
|
|
|
|
|
void DoMissionMovie(const char *movie);
|
|
|
|
|
|
|
|
|
|
void FreeMission();
|
|
|
|
|
// used in load level callback
|
|
|
|
|
void LoadLevelCB(const char *chunk, int curlen, int filelen);
|
|
|
|
|
// MN3 based mission functions.
|
|
|
|
|
// loads the msn file from the mn3 file specified, specifies the hog and table file.
|
|
|
|
|
bool mn3_Open(const char *mn3file);
|
|
|
|
|
// returns mission information given the mn3 file.
|
|
|
|
|
bool mn3_GetInfo(const char *mn3file, tMissionInfo *msn);
|
|
|
|
|
// closes the current mn3 file
|
|
|
|
|
void mn3_Close();
|
|
|
|
|
|
|
|
|
|
static inline bool IS_MN3_FILE(const char *fname) {
|
|
|
|
|
char name[PSFILENAME_LEN + 1];
|
|
|
|
|
char ext[PSFILENAME_LEN + 1];
|
|
|
|
|
ddio_SplitPath(fname, NULL, name, ext);
|
|
|
|
|
return (stricmp(ext, ".mn3") == 0) ? true : false;
|
|
|
|
|
// MN3 based mission functions.
|
|
|
|
|
|
|
|
|
|
// returns mission information given the mn3 file.
|
|
|
|
|
bool mn3_GetInfo(const std::filesystem::path &mn3file, tMissionInfo *msn);
|
|
|
|
|
|
|
|
|
|
static inline bool IS_MN3_FILE(const std::filesystem::path &fname) {
|
|
|
|
|
std::filesystem::path ext = fname.extension();
|
|
|
|
|
return (stricmp(ext.u8string().c_str(), ".mn3") == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline char *MN3_TO_MSN_NAME(const char *mn3name, char *msnname) {
|
|
|
|
|
char fname[PSFILENAME_LEN + 1];
|
|
|
|
|
ddio_SplitPath(mn3name, NULL, fname, NULL);
|
|
|
|
|
|
|
|
|
|
if (stricmp(fname, "d3_2") == 0) {
|
|
|
|
|
strcpy(fname, "d3");
|
|
|
|
|
static inline std::filesystem::path MN3_TO_MSN_NAME(const std::filesystem::path &mn3name) {
|
|
|
|
|
std::filesystem::path fname = std::filesystem::path(mn3name).stem();
|
|
|
|
|
if (stricmp(fname.u8string().c_str(), "d3_2") == 0) {
|
|
|
|
|
fname = "d3";
|
|
|
|
|
}
|
|
|
|
|
strcat(fname, ".msn");
|
|
|
|
|
strcpy(msnname, fname);
|
|
|
|
|
return msnname;
|
|
|
|
|
fname.replace_extension(".msn");
|
|
|
|
|
return fname;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@ -735,13 +728,13 @@ void InitMission() {
|
|
|
|
|
memset(Current_mission.author, 0, MSN_NAMELEN);
|
|
|
|
|
memset(Current_mission.email, 0, MSN_URLLEN);
|
|
|
|
|
memset(Current_mission.web, 0, MSN_URLLEN);
|
|
|
|
|
Current_mission.hog = NULL;
|
|
|
|
|
Current_mission.levels = NULL;
|
|
|
|
|
Current_mission.filename = NULL;
|
|
|
|
|
Current_mission.hog = nullptr;
|
|
|
|
|
Current_mission.levels = nullptr;
|
|
|
|
|
Current_mission.filename = nullptr;
|
|
|
|
|
Current_mission.game_state_flags = 0;
|
|
|
|
|
Current_mission.mn3_handle = 0;
|
|
|
|
|
// create add on mission directory
|
|
|
|
|
ddio_MakePath(D3MissionsDir, LocalD3Dir, "missions", NULL);
|
|
|
|
|
D3MissionsDir = std::filesystem::path(LocalD3Dir) / "missions";
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
std::filesystem::create_directories(D3MissionsDir, ec);
|
|
|
|
|
if (ec) {
|
|
|
|
@ -760,16 +753,17 @@ void ResetMission() {
|
|
|
|
|
memset(Current_mission.author, 0, MSN_NAMELEN);
|
|
|
|
|
memset(Current_mission.email, 0, MSN_URLLEN);
|
|
|
|
|
memset(Current_mission.web, 0, MSN_URLLEN);
|
|
|
|
|
Current_mission.hog = NULL;
|
|
|
|
|
Current_mission.levels = NULL;
|
|
|
|
|
Current_mission.filename = NULL;
|
|
|
|
|
Current_mission.hog = nullptr;
|
|
|
|
|
Current_mission.levels = nullptr;
|
|
|
|
|
Current_mission.filename = nullptr;
|
|
|
|
|
Current_mission.game_state_flags = 0;
|
|
|
|
|
|
|
|
|
|
// clear out old URLs from memory
|
|
|
|
|
for (int a = 0; a < MAX_MISSION_URL_COUNT; a++) {
|
|
|
|
|
Net_msn_URLs.URL[a][0] = '\0';
|
|
|
|
|
for (auto & a : Net_msn_URLs.URL) {
|
|
|
|
|
a[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if (defined(OEM) || defined(DEMO))
|
|
|
|
|
bool DemoMission(int mode = 0) {
|
|
|
|
|
tMission *msn = &Current_mission;
|
|
|
|
@ -881,7 +875,6 @@ bool DemoMission(int mode = 0) {
|
|
|
|
|
bool LoadMission(const char *mssn) {
|
|
|
|
|
Times_game_restored = 0;
|
|
|
|
|
LOG_INFO << "In LoadMission()";
|
|
|
|
|
// ShowProgressScreen(TXT_LOADINGLEVEL);
|
|
|
|
|
#if (defined(OEM) || defined(DEMO))
|
|
|
|
|
#ifdef OEM
|
|
|
|
|
if (stricmp(mssn, "d3oem.mn3") == 0)
|
|
|
|
@ -902,31 +895,29 @@ bool LoadMission(const char *mssn) {
|
|
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
// #endif
|
|
|
|
|
tLevelNode *lvls = NULL; // Temporary storage for level data.
|
|
|
|
|
tLevelNode *lvls = nullptr; // Temporary storage for level data.
|
|
|
|
|
tMission *msn;
|
|
|
|
|
CFILE *fp = NULL; // Mission file
|
|
|
|
|
CFILE *fp = nullptr; // Mission file
|
|
|
|
|
char errtext[80]; // Stores error if unable to read mission
|
|
|
|
|
char msnfname[_MAX_PATH];
|
|
|
|
|
char mission[_MAX_PATH];
|
|
|
|
|
std::filesystem::path msnfname;
|
|
|
|
|
std::filesystem::path mission;
|
|
|
|
|
int srclinenum = 0; // Current line of source.
|
|
|
|
|
int curlvlnum; // Current level number
|
|
|
|
|
int numlevels; // Number of levels required to read in.
|
|
|
|
|
int cur_objective; // current objective reading.
|
|
|
|
|
bool indesc; // are we in a multi-line block
|
|
|
|
|
bool res = false; // used to specify if no error has occurred.
|
|
|
|
|
char pathname[_MAX_PATH];
|
|
|
|
|
std::filesystem::path pathname;
|
|
|
|
|
ResetMission(); // Reset everything.
|
|
|
|
|
// open MN3 if filename passed was an mn3 file.
|
|
|
|
|
|
|
|
|
|
if (IS_MN3_FILE(mssn)) {
|
|
|
|
|
strcpy(mission, mssn);
|
|
|
|
|
ddio_MakePath(pathname, "missions", mission, NULL);
|
|
|
|
|
mission = mssn;
|
|
|
|
|
pathname = std::filesystem::path("missions") / mission;
|
|
|
|
|
} else {
|
|
|
|
|
strcpy(mission, mssn);
|
|
|
|
|
strcpy(pathname, mssn);
|
|
|
|
|
strcpy(msnfname, mssn);
|
|
|
|
|
// ddio_MakePath(pathname, D3MissionsDir, mission, NULL);
|
|
|
|
|
mission = mssn;
|
|
|
|
|
pathname = mssn;
|
|
|
|
|
msnfname = mssn;
|
|
|
|
|
}
|
|
|
|
|
if (IS_MN3_FILE(mission)) {
|
|
|
|
|
|
|
|
|
@ -934,7 +925,7 @@ bool LoadMission(const char *mssn) {
|
|
|
|
|
strcpy(errtext, TXT_MSN_OPENMN3FAILED);
|
|
|
|
|
goto msnfile_error;
|
|
|
|
|
}
|
|
|
|
|
MN3_TO_MSN_NAME(mission, msnfname);
|
|
|
|
|
msnfname = MN3_TO_MSN_NAME(mission);
|
|
|
|
|
}
|
|
|
|
|
// open mission file
|
|
|
|
|
fp = cfopen(msnfname, "rt");
|
|
|
|
@ -948,7 +939,7 @@ bool LoadMission(const char *mssn) {
|
|
|
|
|
srclinenum = 1;
|
|
|
|
|
numlevels = -1;
|
|
|
|
|
curlvlnum = 0;
|
|
|
|
|
indesc = 0;
|
|
|
|
|
indesc = false;
|
|
|
|
|
cur_objective = -1;
|
|
|
|
|
msn->multiplayable = true;
|
|
|
|
|
msn->singleplayable = true;
|
|
|
|
@ -968,7 +959,7 @@ bool LoadMission(const char *mssn) {
|
|
|
|
|
CleanupStr(command, srcline, sizeof(command));
|
|
|
|
|
CleanupStr(operand, srcline + strlen(command) + 1, sizeof(operand));
|
|
|
|
|
if (strlen(command) && indesc)
|
|
|
|
|
indesc = 0;
|
|
|
|
|
indesc = false;
|
|
|
|
|
if (!stricmp(command, "NAME")) {
|
|
|
|
|
strncpy(msn->name, operand, MSN_NAMELEN - 1);
|
|
|
|
|
} else if (!stricmp(command, "MULTI")) {
|
|
|
|
@ -994,16 +985,16 @@ bool LoadMission(const char *mssn) {
|
|
|
|
|
strcat(msn->desc, operand);
|
|
|
|
|
if (indesc)
|
|
|
|
|
strcat(msn->desc, "\n");
|
|
|
|
|
indesc = 1; // this is a multiline command
|
|
|
|
|
indesc = true; // this is a multiline command
|
|
|
|
|
} else if (!stricmp(command, "URL")) {
|
|
|
|
|
if (curlvlnum != 0) {
|
|
|
|
|
strcpy(errtext, TXT_MSN_LVLCOMMAND);
|
|
|
|
|
goto msnfile_error;
|
|
|
|
|
} else {
|
|
|
|
|
for (int a = 0; a < MAX_MISSION_URL_COUNT; a++) {
|
|
|
|
|
if (Net_msn_URLs.URL[a][0] == '\0') {
|
|
|
|
|
strncpy(Net_msn_URLs.URL[a], operand, MAX_MISSION_URL_LEN - 1);
|
|
|
|
|
Net_msn_URLs.URL[a][MAX_MISSION_URL_LEN - 1] = '\0';
|
|
|
|
|
for (auto & url : Net_msn_URLs.URL) {
|
|
|
|
|
if (url[0] == '\0') {
|
|
|
|
|
strncpy(url, operand, MAX_MISSION_URL_LEN - 1);
|
|
|
|
|
url[MAX_MISSION_URL_LEN - 1] = '\0';
|
|
|
|
|
LOG_INFO.printf("Found a Mission URL: %s", operand);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -1173,11 +1164,12 @@ bool LoadMission(const char *mssn) {
|
|
|
|
|
msn->cur_level = 1;
|
|
|
|
|
msn->num_levels = numlevels;
|
|
|
|
|
msn->levels = lvls;
|
|
|
|
|
msn->filename = mem_strdup(mission);
|
|
|
|
|
msn->filename = mem_strdup(mission.u8string().c_str());
|
|
|
|
|
msn->game_state_flags = 0;
|
|
|
|
|
strcpy(Net_msn_URLs.msnname, mission);
|
|
|
|
|
res = true; // everthing is ok.
|
|
|
|
|
// if error, print it out, else end.
|
|
|
|
|
strcpy(Net_msn_URLs.msnname, mission.u8string().c_str());
|
|
|
|
|
res = true; // everything is ok.
|
|
|
|
|
|
|
|
|
|
// if error, print it out, else end.
|
|
|
|
|
msnfile_error:
|
|
|
|
|
if (!res) {
|
|
|
|
|
char str_text[128];
|
|
|
|
@ -1203,6 +1195,7 @@ fatal_error:
|
|
|
|
|
#endif
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreeMission() {
|
|
|
|
|
// Free up mission script
|
|
|
|
|
int i; //,j;
|
|
|
|
@ -1229,33 +1222,27 @@ void FreeMission() {
|
|
|
|
|
mem_free(Current_mission.levels[i].progress);
|
|
|
|
|
}
|
|
|
|
|
mem_free(Current_mission.levels);
|
|
|
|
|
Current_mission.levels = NULL;
|
|
|
|
|
Current_mission.levels = nullptr;
|
|
|
|
|
}
|
|
|
|
|
//@@ if (Current_mission.d3xmod) {
|
|
|
|
|
//@@ D3XFreeProgram(Current_mission.d3xmod);
|
|
|
|
|
//@@ Current_mission.d3xmod = NULL;
|
|
|
|
|
//@@ }
|
|
|
|
|
|
|
|
|
|
if (Current_mission.hog)
|
|
|
|
|
mem_free(Current_mission.hog);
|
|
|
|
|
if (Current_mission.filename) {
|
|
|
|
|
// these DON't USE mem_free since we use _strdup, which doesn't use our memory routines.
|
|
|
|
|
mem_free(Current_mission.filename);
|
|
|
|
|
Current_mission.filename = NULL;
|
|
|
|
|
Current_mission.filename = nullptr;
|
|
|
|
|
}
|
|
|
|
|
Current_mission.hog = NULL;
|
|
|
|
|
Current_level = NULL;
|
|
|
|
|
Current_mission.hog = nullptr;
|
|
|
|
|
Current_level = nullptr;
|
|
|
|
|
}
|
|
|
|
|
#include "localization.h"
|
|
|
|
|
#include "levelgoal.h"
|
|
|
|
|
|
|
|
|
|
// Load the text (goal strings) for a level
|
|
|
|
|
void LoadLevelText(const char *level_filename) {
|
|
|
|
|
char pathname[_MAX_FNAME], filename[_MAX_FNAME];
|
|
|
|
|
void LoadLevelText(const std::filesystem::path &level_filename) {
|
|
|
|
|
int n_strings;
|
|
|
|
|
ddio_SplitPath(level_filename, pathname, filename, NULL);
|
|
|
|
|
strcat(pathname, filename);
|
|
|
|
|
strcat(pathname, ".str");
|
|
|
|
|
std::filesystem::path pathname = level_filename;
|
|
|
|
|
pathname.replace_extension(".str");
|
|
|
|
|
|
|
|
|
|
char **goal_strings;
|
|
|
|
|
if (CreateStringTable(pathname, &goal_strings, &n_strings)) {
|
|
|
|
|
if (CreateStringTable(pathname.u8string().c_str(), &goal_strings, &n_strings)) {
|
|
|
|
|
int n_goals = Level_goals.GetNumGoals();
|
|
|
|
|
ASSERT(n_strings == (n_goals * 3));
|
|
|
|
|
for (int i = 0; i < n_goals; i++) {
|
|
|
|
@ -1272,7 +1259,7 @@ void LoadLevelText(const char *level_filename) {
|
|
|
|
|
bool LoadMissionLevel(int level) {
|
|
|
|
|
Hud_show_controls = false;
|
|
|
|
|
LoadLevelProgress(LOAD_PROGRESS_START, 0);
|
|
|
|
|
if (!LoadLevel(Current_mission.levels[level - 1].filename, NULL)) {
|
|
|
|
|
if (!LoadLevel(Current_mission.levels[level - 1].filename, nullptr)) {
|
|
|
|
|
char buf[128];
|
|
|
|
|
snprintf(buf, sizeof(buf), TXT_MSNERROR, Current_mission.levels[level - 1].filename);
|
|
|
|
|
SetUICallback(DEFAULT_UICALLBACK);
|
|
|
|
@ -1307,7 +1294,7 @@ bool LoadMissionLevel(int level) {
|
|
|
|
|
#define LOADBAR_W (260 * (float)Max_window_w / (float)FIXED_SCREEN_WIDTH)
|
|
|
|
|
#define LOADBAR_H (22 * (float)Max_window_h / (float)FIXED_SCREEN_HEIGHT)
|
|
|
|
|
#define N_LOAD_MSGS 12
|
|
|
|
|
bool started_page = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
$$TABLE_GAMEFILE "tunnelload.ogf"
|
|
|
|
|
*/
|
|
|
|
@ -1338,7 +1325,7 @@ void LoadLevelProgress(int step, float percent, const char *chunk) {
|
|
|
|
|
lvl_percent_loaded = 0.0f;
|
|
|
|
|
pag_percent_loaded = 0.0f;
|
|
|
|
|
dedicated_last_string_len = -1;
|
|
|
|
|
const char *p = NULL;
|
|
|
|
|
const char *p = nullptr;
|
|
|
|
|
if ((!(Game_mode & GM_MULTI)) && (!Current_mission.levels[Current_mission.cur_level - 1].progress)) {
|
|
|
|
|
p = "tunnelload.ogf";
|
|
|
|
|
} else {
|
|
|
|
@ -1352,12 +1339,6 @@ void LoadLevelProgress(int step, float percent, const char *chunk) {
|
|
|
|
|
n_text_msgs = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ShowProgressScreen (TXT_LOADINGLEVEL);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
Progress_screen_loaded = true;
|
|
|
|
|
return;
|
|
|
|
|
} break;
|
|
|
|
@ -1582,17 +1563,20 @@ void LoadLevelProgress(int step, float percent, const char *chunk) {
|
|
|
|
|
EndFrame();
|
|
|
|
|
rend_Flip();
|
|
|
|
|
}
|
|
|
|
|
/* this functions performs the end mission code
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This function performs the end mission code
|
|
|
|
|
*/
|
|
|
|
|
void DoEndMission() {
|
|
|
|
|
if (Game_mode & GM_MULTI) // If multiplayer, just loop
|
|
|
|
|
{
|
|
|
|
|
// If multiplayer, just loop
|
|
|
|
|
if (Game_mode & GM_MULTI) {
|
|
|
|
|
if (Dedicated_server)
|
|
|
|
|
PrintDedicatedMessage(TXT_DS_MISSIONDONE);
|
|
|
|
|
SetCurrentLevel(1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Shows some text on a background, useful for telling the player what is going on
|
|
|
|
|
// ie "Loading level...", "Receiving data...", etc
|
|
|
|
|
void ShowProgressScreen(const char *str, const char *str2, bool flip) {
|
|
|
|
@ -1615,6 +1599,7 @@ void ShowProgressScreen(const char *str, const char *str2, bool flip) {
|
|
|
|
|
if (flip)
|
|
|
|
|
rend_Flip();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* does a mission briefing, returns if mission was canceled, a false, or 0 value.
|
|
|
|
|
first displays mission goals and some warnings or advice.
|
|
|
|
|
may allow for selection of player ships
|
|
|
|
@ -1678,49 +1663,41 @@ bool InitMissionScript() {
|
|
|
|
|
//@@ }
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern bool IsRestoredGame;
|
|
|
|
|
|
|
|
|
|
void InitLevelScript() {
|
|
|
|
|
if (Current_level->filename) {
|
|
|
|
|
char filename[_MAX_PATH], ext[_MAX_EXT];
|
|
|
|
|
ddio_SplitPath(Current_level->filename, NULL, filename, ext);
|
|
|
|
|
#if defined(WIN32)
|
|
|
|
|
strcat(filename, ".dll");
|
|
|
|
|
#elif defined(MACOSX)
|
|
|
|
|
strcat(filename, ".dylib");
|
|
|
|
|
#elif defined(__LINUX__)
|
|
|
|
|
strcat(filename, ".so");
|
|
|
|
|
#else
|
|
|
|
|
#error Unsupported platform!
|
|
|
|
|
#endif
|
|
|
|
|
std::filesystem::path filename = Current_level->filename;
|
|
|
|
|
filename.replace_extension(MODULE_EXT);
|
|
|
|
|
Osiris_LoadLevelModule(filename);
|
|
|
|
|
}
|
|
|
|
|
//@$-D3XExecScript(Current_level->d3xthread, Current_mission.cur_level, REF_LEVELTYPE, EVT_LEVELSTART, 0, 0);
|
|
|
|
|
tOSIRISEventInfo ei;
|
|
|
|
|
tOSIRISEventInfo ei{};
|
|
|
|
|
// This is a hack... we don't want the level start script to be called when restoring a save game
|
|
|
|
|
if (!IsRestoredGame)
|
|
|
|
|
Osiris_CallLevelEvent(EVT_LEVELSTART, &ei);
|
|
|
|
|
AssignScriptsForLevel(); // initialize all scripts for level.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FreeLevelScript() {
|
|
|
|
|
Osiris_UnloadLevelModule();
|
|
|
|
|
if (Current_level) {
|
|
|
|
|
// free level's script and thread
|
|
|
|
|
//@$-D3XExecScript(Current_level->d3xthread, Current_mission.cur_level, REF_LEVELTYPE, EVT_LEVELEND, 0, 0);
|
|
|
|
|
tOSIRISEventInfo ei;
|
|
|
|
|
tOSIRISEventInfo ei{};
|
|
|
|
|
Osiris_CallLevelEvent(EVT_LEVELEND, &ei);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// return information about a mission
|
|
|
|
|
bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) {
|
|
|
|
|
CFILE *fp;
|
|
|
|
|
|
|
|
|
|
bool GetMissionInfo(const std::filesystem::path &msnfile, tMissionInfo *msn) {
|
|
|
|
|
bool indesc = false; // are we in a multi-line block
|
|
|
|
|
// open mission file
|
|
|
|
|
if (IS_MN3_FILE(msnfile)) {
|
|
|
|
|
return mn3_GetInfo(msnfile, msn);
|
|
|
|
|
}
|
|
|
|
|
fp = cfopen(msnfile, "rt");
|
|
|
|
|
CFILE *fp = cfopen(msnfile, "rt");
|
|
|
|
|
if (!fp) {
|
|
|
|
|
LOG_WARNING.printf("Failed to open mission file %s in GetMissionInfo.", msnfile);
|
|
|
|
|
LOG_WARNING.printf("Failed to open mission file %s in GetMissionInfo.", msnfile.u8string().c_str());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
msn->multi = true;
|
|
|
|
@ -1736,8 +1713,7 @@ bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) {
|
|
|
|
|
int readcount; // read-in count
|
|
|
|
|
readcount = cf_ReadString(srcline, sizeof(srcline), fp);
|
|
|
|
|
if (readcount) {
|
|
|
|
|
// we have a line of source. parse out primary keyword
|
|
|
|
|
// then parse out remainder.
|
|
|
|
|
// We have a line of source. Parse out primary keyword then parse out remainder.
|
|
|
|
|
keyword = strtok(srcline, " \t");
|
|
|
|
|
CleanupStr(command, srcline, sizeof(command));
|
|
|
|
|
CleanupStr(operand, srcline + strlen(command) + 1, sizeof(operand));
|
|
|
|
@ -1756,14 +1732,13 @@ bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) {
|
|
|
|
|
} else if (!stricmp(command, "AUTHOR")) {
|
|
|
|
|
strncpy(msn->author, operand, MSN_NAMELEN - 1);
|
|
|
|
|
} else if (!stricmp(command, "DESCRIPTION") || indesc) {
|
|
|
|
|
// multi-line descriptions require the strcat. the initial
|
|
|
|
|
// strings should be empty for this to work.
|
|
|
|
|
// Multi-line descriptions require the strcat. The initial strings should be empty for this to work.
|
|
|
|
|
strcat(msn->desc, operand);
|
|
|
|
|
if (indesc)
|
|
|
|
|
strcat(msn->desc, "\n");
|
|
|
|
|
indesc = true; // this is a multiline command
|
|
|
|
|
} else if (!stricmp(command, "NUMLEVELS")) {
|
|
|
|
|
// get number of levels
|
|
|
|
|
// Get number of levels
|
|
|
|
|
int value = atoi(operand);
|
|
|
|
|
msn->n_levels = value;
|
|
|
|
|
} else if (!stricmp(command, "LEVEL")) {
|
|
|
|
@ -1777,9 +1752,9 @@ bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) {
|
|
|
|
|
cfclose(fp);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
const char *GetMissionName(const char *mission) {
|
|
|
|
|
tMissionInfo msninfo;
|
|
|
|
|
tMissionInfo msninfo{};
|
|
|
|
|
static char msnname[MSN_NAMELEN];
|
|
|
|
|
msnname[0] = 0;
|
|
|
|
|
if (GetMissionInfo(mission, &msninfo)) {
|
|
|
|
@ -1789,38 +1764,23 @@ const char *GetMissionName(const char *mission) {
|
|
|
|
|
}
|
|
|
|
|
return msnname;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsMissionMultiPlayable(const char *mission) {
|
|
|
|
|
tMissionInfo msninfo;
|
|
|
|
|
tMissionInfo msninfo{};
|
|
|
|
|
if (GetMissionInfo(mission, &msninfo)) {
|
|
|
|
|
return msninfo.multi;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
bool IsMissionSinglePlayable(const char *mission) {
|
|
|
|
|
tMissionInfo msninfo;
|
|
|
|
|
if (GetMissionInfo(mission, &msninfo)) {
|
|
|
|
|
return msninfo.single;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int Mission_voice_hog_handle = 0;
|
|
|
|
|
// MN3 based mission functions.
|
|
|
|
|
// loads the msn file from the mn3 file specified, specifies the hog and table file.
|
|
|
|
|
bool mn3_Open(const char *mn3file) {
|
|
|
|
|
char pathname[_MAX_PATH];
|
|
|
|
|
char filename[PSFILENAME_LEN + 1];
|
|
|
|
|
char ext[PSFILENAME_LEN];
|
|
|
|
|
int mn3_handle;
|
|
|
|
|
// concatanate the mn3 extension if it isn't there.
|
|
|
|
|
char tempMn3File[_MAX_PATH];
|
|
|
|
|
if (!IS_MN3_FILE(mn3file)) {
|
|
|
|
|
strncpy(tempMn3File, mn3file, sizeof(tempMn3File) - 1);
|
|
|
|
|
tempMn3File[sizeof(tempMn3File) - 1] = 0;
|
|
|
|
|
strcat(tempMn3File, ".mn3");
|
|
|
|
|
mn3file = tempMn3File;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ddio_MakePath(pathname, D3MissionsDir, mn3file, NULL);
|
|
|
|
|
int Mission_voice_hog_handle = 0;
|
|
|
|
|
|
|
|
|
|
bool mn3_Open(const std::filesystem::path &mn3file) {
|
|
|
|
|
int mn3_handle;
|
|
|
|
|
// concatenate the mn3 extension if it isn't there.
|
|
|
|
|
std::filesystem::path tempMn3File = mn3file;
|
|
|
|
|
tempMn3File.replace_extension(".mn3");
|
|
|
|
|
|
|
|
|
|
// open MN3 HOG.
|
|
|
|
|
mn3_handle = cf_OpenLibrary(mn3file);
|
|
|
|
|
if (mn3_handle == 0) {
|
|
|
|
@ -1829,42 +1789,43 @@ bool mn3_Open(const char *mn3file) {
|
|
|
|
|
Osiris_ExtractScriptsFromHog(mn3_handle, true);
|
|
|
|
|
}
|
|
|
|
|
// do table file stuff.
|
|
|
|
|
ddio_SplitPath(mn3file, NULL, filename, ext);
|
|
|
|
|
std::filesystem::path filename = mn3file.stem();
|
|
|
|
|
|
|
|
|
|
char voice_hog[_MAX_PATH*2];
|
|
|
|
|
if ((stricmp(filename, "d3") == 0) || (stricmp(filename, "training") == 0)) {
|
|
|
|
|
std::filesystem::path voice_hog;
|
|
|
|
|
if ((stricmp(filename.u8string().c_str(), "d3") == 0) || (stricmp(filename.u8string().c_str(), "training") == 0)) {
|
|
|
|
|
// Open audio hog file
|
|
|
|
|
ddio_MakePath(voice_hog, "missions", "d3voice1.hog", nullptr); // Audio for levels 1-4
|
|
|
|
|
voice_hog = std::filesystem::path("missions") / "d3voice1.hog"; // Audio for levels 1-4
|
|
|
|
|
Mission_voice_hog_handle = cf_OpenLibrary(voice_hog);
|
|
|
|
|
} else if (stricmp(filename, "d3_2") == 0) {
|
|
|
|
|
} else if (stricmp(filename.u8string().c_str(), "d3_2") == 0) {
|
|
|
|
|
// Open audio hog file
|
|
|
|
|
ddio_MakePath(voice_hog, "missions", "d3voice2.hog", nullptr); // Audio for levels 5-17
|
|
|
|
|
voice_hog = std::filesystem::path("missions") / "d3voice2.hog"; // Audio for levels 5-17
|
|
|
|
|
Mission_voice_hog_handle = cf_OpenLibrary(voice_hog);
|
|
|
|
|
}
|
|
|
|
|
strcat(filename, ".gam");
|
|
|
|
|
mng_SetAddonTable(filename);
|
|
|
|
|
filename.replace_extension(".gam");
|
|
|
|
|
mng_SetAddonTable(filename.u8string().c_str());
|
|
|
|
|
Current_mission.mn3_handle = mn3_handle;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns mission information given the mn3 file.
|
|
|
|
|
bool mn3_GetInfo(const char *mn3file, tMissionInfo *msn) {
|
|
|
|
|
bool mn3_GetInfo(const std::filesystem::path &mn3file, tMissionInfo *msn) {
|
|
|
|
|
int handle;
|
|
|
|
|
bool retval;
|
|
|
|
|
char pathname[_MAX_PATH];
|
|
|
|
|
char filename[PSFILENAME_LEN + 1];
|
|
|
|
|
std::filesystem::path pathname;
|
|
|
|
|
std::filesystem::path filename;
|
|
|
|
|
|
|
|
|
|
ddio_MakePath(pathname, "missions", mn3file, nullptr);
|
|
|
|
|
pathname = std::filesystem::path(std::filesystem::path("missions")) / mn3file;
|
|
|
|
|
handle = cf_OpenLibrary(pathname);
|
|
|
|
|
if (handle == 0) {
|
|
|
|
|
LOG_ERROR << "MISSION: MN3 failed to open.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
MN3_TO_MSN_NAME(mn3file, filename);
|
|
|
|
|
filename = MN3_TO_MSN_NAME(mn3file);
|
|
|
|
|
retval = GetMissionInfo(filename, msn);
|
|
|
|
|
cf_CloseLibrary(handle);
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// closes the current mn3 file
|
|
|
|
|
void mn3_Close() {
|
|
|
|
|
if (Current_mission.mn3_handle) {
|
|
|
|
@ -1877,12 +1838,14 @@ void mn3_Close() {
|
|
|
|
|
}
|
|
|
|
|
Current_mission.mn3_handle = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define KEYWORD_LEN 16
|
|
|
|
|
#define NUM_KEYWORDS 16
|
|
|
|
|
#define GOALSTEXT "GOALS"
|
|
|
|
|
#define GOALSTEXTLEN strlen(GOALSTEXT)
|
|
|
|
|
#define MODMINGOALS "MINGOALS"
|
|
|
|
|
#define MODMINGOALSLEN strlen(MODMINGOALS)
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
// Return values:
|
|
|
|
@ -1894,7 +1857,6 @@ int MissionGetKeywords(const char *mission, char *keywords) {
|
|
|
|
|
|
|
|
|
|
char msn_keywords[NUM_KEYWORDS][KEYWORD_LEN];
|
|
|
|
|
char mod_keywords[NUM_KEYWORDS][KEYWORD_LEN];
|
|
|
|
|
int i;
|
|
|
|
|
char *parse_keys = mem_strdup(keywords);
|
|
|
|
|
char seps[] = ",";
|
|
|
|
|
int teams = MAX_NET_PLAYERS;
|
|
|
|
@ -1903,7 +1865,7 @@ int MissionGetKeywords(const char *mission, char *keywords) {
|
|
|
|
|
int mod_key_count = 0;
|
|
|
|
|
int msn_key_count = 0;
|
|
|
|
|
bool goal_per_team = false;
|
|
|
|
|
tMissionInfo msn_info;
|
|
|
|
|
tMissionInfo msn_info{};
|
|
|
|
|
|
|
|
|
|
memset(msn_keywords, 0, sizeof(msn_keywords));
|
|
|
|
|
memset(mod_keywords, 0, sizeof(mod_keywords));
|
|
|
|
@ -1922,7 +1884,7 @@ int MissionGetKeywords(const char *mission, char *keywords) {
|
|
|
|
|
do {
|
|
|
|
|
strcpy(mod_keywords[mod_key_count], tokp);
|
|
|
|
|
mod_key_count++;
|
|
|
|
|
tokp = strtok(NULL, seps);
|
|
|
|
|
tokp = strtok(nullptr, seps);
|
|
|
|
|
} while ((tokp) && (mod_key_count < NUM_KEYWORDS));
|
|
|
|
|
}
|
|
|
|
|
// Break up the msn keywords into an array
|
|
|
|
@ -1931,38 +1893,38 @@ int MissionGetKeywords(const char *mission, char *keywords) {
|
|
|
|
|
do {
|
|
|
|
|
strcpy(msn_keywords[msn_key_count], tokp);
|
|
|
|
|
msn_key_count++;
|
|
|
|
|
tokp = strtok(NULL, seps);
|
|
|
|
|
tokp = strtok(nullptr, seps);
|
|
|
|
|
} while ((tokp) && (msn_key_count < NUM_KEYWORDS));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_KEYWORDS; i++) {
|
|
|
|
|
if (msn_keywords[i][0] == 0) {
|
|
|
|
|
for (auto & msn_keyword : msn_keywords) {
|
|
|
|
|
if (msn_keyword[0] == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (strnicmp(GOALSTEXT, msn_keywords[i], GOALSTEXTLEN) == 0) {
|
|
|
|
|
if (strnicmp(GOALSTEXT, msn_keyword, GOALSTEXTLEN) == 0) {
|
|
|
|
|
// Get the number of goals this game has
|
|
|
|
|
goals = atoi(msn_keywords[i] + GOALSTEXTLEN);
|
|
|
|
|
goals = atoi(msn_keyword + GOALSTEXTLEN);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mem_free(parse_keys);
|
|
|
|
|
|
|
|
|
|
// Loop through looking for matches
|
|
|
|
|
for (i = 0; i < NUM_KEYWORDS; i++) {
|
|
|
|
|
if (mod_keywords[i][0] == 0) {
|
|
|
|
|
for (auto & mod_keyword : mod_keywords) {
|
|
|
|
|
if (mod_keyword[0] == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (strnicmp(mod_keywords[i], MODMINGOALS, MODMINGOALSLEN) == 0) {
|
|
|
|
|
goalsneeded = atoi(mod_keywords[i] + MODMINGOALSLEN);
|
|
|
|
|
} else if (stricmp("GOALPERTEAM", mod_keywords[i]) == 0) {
|
|
|
|
|
if (strnicmp(mod_keyword, MODMINGOALS, MODMINGOALSLEN) == 0) {
|
|
|
|
|
goalsneeded = atoi(mod_keyword + MODMINGOALSLEN);
|
|
|
|
|
} else if (stricmp("GOALPERTEAM", mod_keyword) == 0) {
|
|
|
|
|
goal_per_team = true;
|
|
|
|
|
} else {
|
|
|
|
|
bool found_keyword = false;
|
|
|
|
|
// Loop through looking for matches
|
|
|
|
|
for (int a = 0; a < NUM_KEYWORDS; a++) {
|
|
|
|
|
if (msn_keywords[a][0] == 0) {
|
|
|
|
|
for (auto & msn_keyword : msn_keywords) {
|
|
|
|
|
if (msn_keyword[0] == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (stricmp(msn_keywords[a], mod_keywords[i]) == 0) {
|
|
|
|
|
if (stricmp(msn_keyword, mod_keyword) == 0) {
|
|
|
|
|
// Woohoo! it's found
|
|
|
|
|
found_keyword = true;
|
|
|
|
|
break;
|
|
|
|
@ -1970,7 +1932,7 @@ int MissionGetKeywords(const char *mission, char *keywords) {
|
|
|
|
|
}
|
|
|
|
|
// We never found one we needed, so return -1;
|
|
|
|
|
if (!found_keyword) {
|
|
|
|
|
LOG_WARNING.printf("%s keyword needed in %s not found!", mod_keywords[i], mission);
|
|
|
|
|
LOG_WARNING.printf("%s keyword needed in %s not found!", mod_keyword, mission);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1988,6 +1950,7 @@ int MissionGetKeywords(const char *mission, char *keywords) {
|
|
|
|
|
}
|
|
|
|
|
return teams;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
#ifdef EDITOR
|
|
|
|
|
// Used by editor->game to load all necessary elements for level playing for systems
|
|
|
|
|