dxx-rebirth/common/main/mission.h

265 lines
8.4 KiB
C
Raw Normal View History

2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Header for mission.h
*
*/
#ifndef _MISSION_H
#define _MISSION_H
2014-07-20 22:13:25 +00:00
#include <memory>
2014-08-16 03:59:14 +00:00
#include <string>
2006-03-20 17:12:09 +00:00
#include "pstypes.h"
#include "inferno.h"
#include "dxxsconf.h"
#include "dsx-ns.h"
2006-03-20 17:12:09 +00:00
2014-12-22 04:35:48 +00:00
#include "ntstring.h"
2006-03-20 17:12:09 +00:00
#define MAX_MISSIONS 5000 // ZICO - changed from 300 to get more levels in list
// KREATOR - increased from 30 (limited by Demo and Multiplayer code)
constexpr std::integral_constant<uint8_t, 127> MAX_LEVELS_PER_MISSION{};
constexpr std::integral_constant<uint8_t, 127> MAX_SECRET_LEVELS_PER_MISSION{}; // KREATOR - increased from 6 (limited by Demo and Multiplayer code)
2006-03-20 17:12:09 +00:00
#define MISSION_NAME_LEN 25
#if defined(DXX_BUILD_DESCENT_I)
#define D1_MISSION_FILENAME ""
#elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
#define D1_MISSION_FILENAME "descent"
#endif
2006-03-20 17:12:09 +00:00
#define D1_MISSION_NAME "Descent: First Strike"
#define D1_MISSION_HOGSIZE 6856701 // v1.4 - 1.5
#define D1_MISSION_HOGSIZE2 6856183 // v1.4 - 1.5 - different patch-way
2006-03-20 17:12:09 +00:00
#define D1_10_MISSION_HOGSIZE 7261423 // v1.0
#define D1_MAC_MISSION_HOGSIZE 7456179
#define D1_OEM_MISSION_NAME "Destination Saturn"
#define D1_OEM_MISSION_HOGSIZE 4492107 // v1.4a
#define D1_OEM_10_MISSION_HOGSIZE 4494862 // v1.0
#define D1_SHAREWARE_MISSION_NAME "Descent Demo"
#define D1_SHAREWARE_MISSION_HOGSIZE 2339773 // v1.4
#define D1_SHAREWARE_10_MISSION_HOGSIZE 2365676 // v1.0 - 1.2
#define D1_MAC_SHARE_MISSION_HOGSIZE 3370339
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
#define SHAREWARE_MISSION_FILENAME "d2demo"
#define SHAREWARE_MISSION_NAME "Descent 2 Demo"
#define SHAREWARE_MISSION_HOGSIZE 2292566 // v1.0 (d2demo.hog)
#define MAC_SHARE_MISSION_HOGSIZE 4292746
#define OEM_MISSION_FILENAME "d2"
#define OEM_MISSION_NAME "D2 Destination:Quartzon"
#define OEM_MISSION_HOGSIZE 6132957 // v1.1
#define FULL_MISSION_FILENAME "d2"
#define FULL_MISSION_HOGSIZE 7595079 // v1.1 - 1.2
#define FULL_10_MISSION_HOGSIZE 7107354 // v1.0
#define MAC_FULL_MISSION_HOGSIZE 7110007 // v1.1 - 1.2
2021-06-28 03:37:49 +00:00
#include "movie.h"
#endif
2006-03-20 17:12:09 +00:00
//where the missions go
#define MISSION_DIR "missions/"
constexpr std::integral_constant<std::size_t, 128> DXX_MAX_MISSION_PATH_LENGTH{};
2015-01-15 04:30:03 +00:00
/* Path and filename must be kept in sync. */
class Mission_path
{
public:
Mission_path(const Mission_path &m) :
path(m.path),
filename(std::next(path.cbegin(), std::distance(m.path.cbegin(), m.filename)))
{
}
Mission_path &operator=(const Mission_path &m)
{
path = m.path;
filename = std::next(path.begin(), std::distance(m.path.cbegin(), m.filename));
return *this;
}
Retain directory structure in New Game dialog User jcotton42 suggested copying a D2X-XL feature: preserving the directory structure of the user's missions area when showing a New Game dialog. This was substantially more trouble than it should have been, but the result is good. Previously, the dialog presented all missions at any depth below the starting point, and sorted them as if they were all in the root directory. Now: - Empty directories are hidden entirely. There is nothing for the user to do in them, so there is no point showing them. - A directory with exactly one entry has that entry promoted into the parent, since there is no ambiguity about what the user would want. If the parent in turn has only that one promoted element when the scan of the parent finishes, then the element can be promoted up again. This continues until the root is reached or until a level has more than one entry. For this purpose, both missions and directories count as entries. - Directory entries are decorated to inform the user how many immediate subdirectories are present, how many missions are present immediately in the directory, and how many missions total are present, counting all subdirectories. If there are zero immediate subdirectories, then the directory count is not shown. For this purpose, directories that were hidden due to a lack of missions are not counted. - Sub-dialog boxes for inner directories use a title that reminds the user of the path so far, and recaps the directory/mission statistics. - On entry to the New Game dialog, if the last played mission is in a sub-dialog, appropriate sub-dialogs are opened so that the last played mission can be pre-selected. Currently, there is no in-game override to return to the prior rollup rules. Requested-by: jcotton42 <https://github.com/dxx-rebirth/dxx-rebirth/issues/392>
2018-07-03 05:59:40 +00:00
Mission_path(std::string &&p, const std::size_t offset) :
path(std::move(p)),
2015-01-15 04:30:03 +00:00
filename(std::next(path.cbegin(), offset))
{
}
Retain directory structure in New Game dialog User jcotton42 suggested copying a D2X-XL feature: preserving the directory structure of the user's missions area when showing a New Game dialog. This was substantially more trouble than it should have been, but the result is good. Previously, the dialog presented all missions at any depth below the starting point, and sorted them as if they were all in the root directory. Now: - Empty directories are hidden entirely. There is nothing for the user to do in them, so there is no point showing them. - A directory with exactly one entry has that entry promoted into the parent, since there is no ambiguity about what the user would want. If the parent in turn has only that one promoted element when the scan of the parent finishes, then the element can be promoted up again. This continues until the root is reached or until a level has more than one entry. For this purpose, both missions and directories count as entries. - Directory entries are decorated to inform the user how many immediate subdirectories are present, how many missions are present immediately in the directory, and how many missions total are present, counting all subdirectories. If there are zero immediate subdirectories, then the directory count is not shown. For this purpose, directories that were hidden due to a lack of missions are not counted. - Sub-dialog boxes for inner directories use a title that reminds the user of the path so far, and recaps the directory/mission statistics. - On entry to the New Game dialog, if the last played mission is in a sub-dialog, appropriate sub-dialogs are opened so that the last played mission can be pre-selected. Currently, there is no in-game override to return to the prior rollup rules. Requested-by: jcotton42 <https://github.com/dxx-rebirth/dxx-rebirth/issues/392>
2018-07-03 05:59:40 +00:00
Mission_path(Mission_path &&m) :
Mission_path(std::move(m).path, std::distance(m.path.cbegin(), m.filename))
{
}
2015-01-15 04:30:03 +00:00
Mission_path &operator=(Mission_path &&rhs)
{
std::size_t offset = std::distance(rhs.path.cbegin(), rhs.filename);
path = std::move(rhs.path);
filename = std::next(path.begin(), offset);
return *this;
}
/* Must be in this order for move constructor to work properly */
2014-08-16 03:59:14 +00:00
std::string path; // relative file path
2015-01-15 04:30:03 +00:00
std::string::const_iterator filename; // filename without extension
2015-10-18 21:01:18 +00:00
enum class descent_version_type : uint8_t
{
#if defined(DXX_BUILD_DESCENT_II)
/* These values are written to the binary savegame as part of
* the mission name. If the values are reordered or renumbered,
* old savegames will be unable to find the matching mission
* file.
*/
descent2a, // !name
descent2z, // zname
descent2x, // xname
2015-10-18 21:01:18 +00:00
descent2,
#endif
2015-10-18 21:01:18 +00:00
descent1,
};
2015-01-15 04:30:03 +00:00
};
#if defined(DXX_BUILD_DESCENT_I) || defined(DXX_BUILD_DESCENT_II)
2021-06-28 03:37:49 +00:00
namespace dsx {
2015-01-15 04:30:03 +00:00
struct Mission : Mission_path
{
2014-07-24 02:30:18 +00:00
std::unique_ptr<ubyte[]> secret_level_table; // originating level no for each secret level
2006-03-20 17:12:09 +00:00
// arrays of names of the level files
2014-07-24 02:39:21 +00:00
std::unique_ptr<d_fname[]> level_names;
2014-07-24 02:35:57 +00:00
std::unique_ptr<d_fname[]> secret_level_names;
2014-12-18 04:12:39 +00:00
int builtin_hogsize; // the size of the hogfile for a builtin mission, and 0 for an add-on mission
2014-12-22 04:35:48 +00:00
ntstring<MISSION_NAME_LEN> mission_name;
2014-12-18 04:12:39 +00:00
d_fname briefing_text_filename; // name of briefing file
d_fname ending_text_filename; // name of ending file
ubyte anarchy_only_flag; // if true, mission is only for anarchy
ubyte last_level;
sbyte last_secret_level;
ubyte n_secret_levels;
#if defined(DXX_BUILD_DESCENT_II)
2015-10-18 21:01:18 +00:00
descent_version_type descent_version; // descent 1 or descent 2?
2014-07-23 01:52:10 +00:00
std::unique_ptr<d_fname> alternate_ham_file;
std::unique_ptr<LoadedMovieWithResolution> extra_robot_movie;
2006-03-20 17:12:09 +00:00
#endif
/* Explicitly default move constructor and move operator=
*
* Without this, gcc (tested gcc-4.9, gcc-5) tries to use
* a synthetic operator=(const Mission &) to implement `instance =
* {};`, which fails because Mission contains std::unique_ptr, a
* movable but noncopyable type.
*
* With the explicit default, gcc uses operator=(Mission &&), which
* works.
*
* Explicitly delete copy constructor and copy operator= for
* thoroughness.
*/
Mission(Mission &&) = default;
Mission &operator=(Mission &&) = default;
Mission(const Mission &) = delete;
Mission &operator=(const Mission &) = delete;
Retain directory structure in New Game dialog User jcotton42 suggested copying a D2X-XL feature: preserving the directory structure of the user's missions area when showing a New Game dialog. This was substantially more trouble than it should have been, but the result is good. Previously, the dialog presented all missions at any depth below the starting point, and sorted them as if they were all in the root directory. Now: - Empty directories are hidden entirely. There is nothing for the user to do in them, so there is no point showing them. - A directory with exactly one entry has that entry promoted into the parent, since there is no ambiguity about what the user would want. If the parent in turn has only that one promoted element when the scan of the parent finishes, then the element can be promoted up again. This continues until the root is reached or until a level has more than one entry. For this purpose, both missions and directories count as entries. - Directory entries are decorated to inform the user how many immediate subdirectories are present, how many missions are present immediately in the directory, and how many missions total are present, counting all subdirectories. If there are zero immediate subdirectories, then the directory count is not shown. For this purpose, directories that were hidden due to a lack of missions are not counted. - Sub-dialog boxes for inner directories use a title that reminds the user of the path so far, and recaps the directory/mission statistics. - On entry to the New Game dialog, if the last played mission is in a sub-dialog, appropriate sub-dialogs are opened so that the last played mission can be pre-selected. Currently, there is no in-game override to return to the prior rollup rules. Requested-by: jcotton42 <https://github.com/dxx-rebirth/dxx-rebirth/issues/392>
2018-07-03 05:59:40 +00:00
explicit Mission(const Mission_path &m) :
Mission_path(m)
{
}
explicit Mission(Mission_path &&m) :
Mission_path(std::move(m))
{
}
~Mission();
};
2006-03-20 17:12:09 +00:00
typedef std::unique_ptr<Mission> Mission_ptr;
2014-07-20 22:13:25 +00:00
extern Mission_ptr Current_mission; // current mission
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_II)
/* Wrap in parentheses to avoid precedence problems. Put constant on
* the left to silence clang's overzealous -Wparentheses-equality messages.
*/
#define is_SHAREWARE (SHAREWARE_MISSION_HOGSIZE == Current_mission->builtin_hogsize)
#define is_MAC_SHARE (MAC_SHARE_MISSION_HOGSIZE == Current_mission->builtin_hogsize)
#define is_D2_OEM (OEM_MISSION_HOGSIZE == Current_mission->builtin_hogsize)
2006-03-20 17:12:09 +00:00
2015-10-18 21:01:18 +00:00
#define EMULATING_D1 (Mission::descent_version_type::descent1 == Current_mission->descent_version)
#endif
#define PLAYING_BUILTIN_MISSION (Current_mission->builtin_hogsize != 0)
#define ANARCHY_ONLY_MISSION (1 == Current_mission->anarchy_only_flag)
2021-06-28 03:37:49 +00:00
}
#endif
2006-03-20 17:12:09 +00:00
//values for d1 built-in mission
#define BIMD1_ENDING_FILE_OEM "endsat.txb"
#define BIMD1_ENDING_FILE_SHARE "ending.txb"
#ifdef dsx
namespace dcx {
enum class mission_name_type
{
basename,
pathname,
guess,
};
}
namespace dsx {
#if defined(DXX_BUILD_DESCENT_II)
//values for d2 built-in mission
#define BIMD2_ENDING_FILE_OEM "end2oem.txb"
#define BIMD2_ENDING_FILE_SHARE "ending2.txb"
int load_mission_ham();
void bm_read_extra_robots(const char *fname, Mission::descent_version_type type);
#endif
struct mission_entry_predicate
{
/* May be a basename or may be a path relative to the root of the
* PHYSFS virtual filesystem, depending on what the caller provides.
*
* In both cases, the file extension is omitted.
*/
const char *filesystem_name;
#if defined(DXX_BUILD_DESCENT_II)
bool check_version;
Mission::descent_version_type descent_version;
#endif
mission_entry_predicate with_filesystem_name(const char *fsname) const
{
mission_entry_predicate m = *this;
m.filesystem_name = fsname;
return m;
}
};
2006-03-20 17:12:09 +00:00
//loads the named mission if it exists.
//Returns nullptr if mission loaded ok, else error string.
const char *load_mission_by_name (mission_entry_predicate mission_name, mission_name_type);
2006-03-20 17:12:09 +00:00
#if DXX_USE_EDITOR
void create_new_mission(void);
#endif
}
#endif
2006-03-20 17:12:09 +00:00
#endif