The historical savegame format cannot support finding a mission in a
subdirectory. Add a backwards-incompatible modification to store the
full path in the savegame, and store it in a way that old versions will
fail gracefully.[1] When loading demos, or legacy savegames, search for
the mission in all available directories. Demos are still written with
an unqualified path because the demo loading code would crash if given
an oversized path. Mission names sent over the network as part of
multiplayer use the guess logic now, so that guests do not need to have
the mission in the same path as the host.
[1] Versions affected by issue #486 may fail ungracefully.
Reported-by: AlumiuN <https://github.com/dxx-rebirth/dxx-rebirth/issues/491>
- In D2X, do not accept Descent2-specific directives from Descent 1
`.msn` files.
- Set the descent_version field correctly in the `mle`. Previously,
`.msn` was set to descent1 and all `.mn2` were set to descent2,
regardless of whether the `.mn2` used `name`, `xname`, `zname`, or
`!name`.
- Avoid rewinding the file and rereading the same line while checking
the possible name types.
- Avoid recomputing end-of-string when it is already known.
- Avoid re-reading the mission file's version when the mission is
chosen. Instead, use the version that was recorded when the mission
was loaded into the mission list. This also fixes a bug where Descent
1 `.msn` files would be classified as descent_version_type::descent2
since both use `name =`, but that string has a different meaning
depending on whether the file is `.msn` or `.mn2`.
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>
Make the listbox handlers and all sub-handlers (when_selected callbacks) return a window_event_result. Makes code more readable and removes one use of window_close, making it easier to inform event system if a window closes in future.
Rename symbol EDITOR to DXX_USE_EDITOR to show that it is a DXX
symbol, not one inherited from a library. Move it to dxxsconf.h to
shorten the command line.
This is a mostly automated transform, but the changes to SConstruct were
manual.
git grep -wl EDITOR -- '*.h' '*.cpp' | xargs sed -i -e 's/^\s*#ifdef \(EDITOR\)\>/#if DXX_USE_\1/' -e 's/\s*#\(el\)\?if \(.*\)defined(\(EDITOR\))/#\1if \2DXX_USE_\3/' -e 's/^\s*#ifndef \(EDITOR\)\>/#if !DXX_USE_\1/'
C++ does not require this pattern.
import re, fileinput
to = re.compile(r'^typedef struct ([a-z_A-Z]+)\s*{')
tc = re.compile(r'^}(.*?)\s*([a-z_A-Z]+);$')
osn = None
for line in fileinput.input(inplace=True):
m = to.match(line)
if m:
osn = m.group(1)
print 'struct %s\n{' % osn
continue
if osn:
m = tc.match(line)
if m:
csn = m.group(2)
if osn == csn:
print '}%s;' % m.group(1)
osn = None
continue
else:
osn = None
print line,