scores_maybe_add_player might close Game_wind, if it does not return
early. All two callers unconditionally close Game_wind. Remove the
unnecessary closure in scores_maybe_add_player.
There are two callers for wall_close_door_num. The editor calls it in a
loop for all doors, so copying each active_door is wasteful since none
will survive the end of the loop. Move the copy loop into the caller
that removes elements selectively.
This makes no attempt to fix the bugs associated with changing
Num_open_doors while iterating with Num_open_doors as an upper bound.
Kreator reported an uninitialized value crash when robots fired homing
weapons. No path assigned a value for track_goal, so it had a default
value of 0 (non-poison builds) or 0xfdfd (poison builds). Assign it a
value of object_none when the created weapon is a homing weapon. Leave
it unassigned for non-homing weapons, since those should never examine
track_goal.
Reported-by: kreatordxx <https://github.com/dxx-rebirth/dxx-rebirth/issues/274>
This code has been #ifdef'd out since 2002, relied on implicit-int in
its declaration (deprecated in C, forbidden in C++), and has been broken
for years due to vms_vector changes. Remove it.
This preserves virtually all game information, including active fire. The saved game 'gamesave.sge' can also be loaded with File->Restore Game State. The level can still be saved as usual (but it warns the user if there's an unsaved game state). This functionality may be useful for testing (or cheating! ;) )
zicodxx reported an assertion failure when powerup duplication was
enabled. Local debugging shows that some levels give preplaced powerups
a movement type of MT_NONE rather than MT_PHYSICS. Such powerups have
undefined data in their mtype.phys_info branch, which confuses the
physics simulation code. Powerups only need MT_PHYSICS to bounce away
from their spawn site. Preplaced powerups never move.
Add assertions that preplaced powerups are MT_NONE. Change the
duplication call to create the duplicates with movement MT_NONE.
Reported-by: zicodxx <https://github.com/dxx-rebirth/dxx-rebirth/issues/268>
Some gauges views show the current Omega_charge even when the player
does not have an Omega cannon. Initialize Omega_charge to 0 if the
Omega cannon is not granted.
Various points in the game code call `hide_menus()`, then later use
`show_menus()` to reverse the effect. If the forcibly hidden window is
deleted before `show_menus()` is called, the attempt to show it would
write to freed memory. Add a hook to forget those windows when they are
deleted, so that `show_menus()` does not try to make them visible later.
`obj_get_signature()` examines all objects with a type other than
`OBJ_NONE` to find an unused signature.
`load_game_data()`->`read_object()` set an object's type before calling
`obj_get_signature()`, so `obj_get_signature()` would consider the
uninitialized signature of the newly loaded object for exclusion.
Reorder the initialization to compute the signature before the object is
given a type, then store the signature on the object after the poison
bytes (if any) are written.
This reverts commit 3c20c24ac0.
Rebirth no longer contains the Descent memory manager, but Rebirth still
has many traces of the paging support used in the DOS version. One of
the unfortunate and subtle traces of this support is that it allocates a
contiguous buffer for game data and loads many of the games bitmaps into
that buffer. The buffer is large enough for most workloads, but not
all. When the buffer is too small, it is discarded by
piggy_bitmap_page_out_all and rebuilt piecemeal to contain only what it
needs. Fixing this would be nice, but the memory ownership rules are
messy. The performance consequences of the legacy approach are not
sufficient to justify the effort to fix this properly. Instead, revert
to the legacy design.
This error was reported for gcc. Also set file extension correctly for 'enhanced' missions (though currently unused). Also some tidying up in response to feedback.
This removes an ambiguity when loading a saved game - we need to know the mission descent version **before** calling LoadLevel in state_restore_all_sub (a chicken and egg situation if we go down the fake dummy mission path). This also creates a fully playable mission (albeit simple).
To recognize getaddrinfo as supported, require that the AI_* flags
passed to it be usable:
- AI_NUMERICSERV must be defined as a number or be undefined
- If ipv6=1, then AI_V4MAPPED and AI_ALL must be defined as numbers
When getaddrinfo is used, raise _WIN32_WINNT from 0x501 to 0x600.
Otherwise, lower it to 0x500.
This fixes two problems on systems where sizeof(int) < sizeof(fix64)
(which applies on most systems).
Normally, the omega cannon does not charge for ~1/3 of a
second after firing stops.
The first problem is that, if (Last_omega_fire_time + 1/3 second)
exceeds 0x80000000 (about 9.1 hours), truncation issues confuse this
rule into not applying, thus allowing the omega cannon to charge
whenever energy is available, even while firing.
The second problem is that, if GameTime64 exceeds 0x8000000000000000
(about 4459701.8 years), a sanity check that attempted to compensate for
the incorrect tracking of Last_omega_fire_time would confuse the
recharge rule into deciding that the user had always fired recently,
which would prevent the omega cannon from ever recharging. The sanity
check would reset Last_omega_fire_time when Last_omega_fire_time was in
the future relative to GameTime64. Unfortunately, Last_omega_fire_time
was only an int, so the reset truncates off the high bits of GameTime64.
This truncation is harmless when GameTime64 is less than 0x80000000,
causes the first problem when GameTime64 is not less than 0x80000000,
but is positive, and causes the second problem when GameTime64 is
negative.
These problems were mitigated in prior releases by three factors. First,
a hack resets GameTime64 when restoring from a saved game, so the
affected game needs to run for ~9.1 hours (or ~4459701.8 years) without
reloading. Second, starting a new level resets GameTime64, so the game
needs to stay on a single level for ~9.1 hours (or ~4459701.8 years).
Third, the omega cannon discharges faster than it can recharge, so even
when the first bug allowed it to charge while firing, a player could
still drain Omega_charge to zero by continuous firing. However, it
would take longer to drain due to the bug-induced concurrent recharge,
and would not be subject to the 1/3 second wait normally imposed between
depleting Omega_charge and recharge beginning.
Fix these problems by replacing Last_omega_fire_time with
Omega_recharge_delay, which is 0 when recharging is allowed or a
positive amount of frame time if recharging is disallowed due to recent
firing. Set Omega_recharge_delay to 1/3 second when the user fires.
Decrease it by up to FrameTime when omega_recharge_frame runs.
This does not fix the preexisting problem that reloading a savegame does
not update the recharge delay, which manifests two related problems.
First, firing the omega cannon and then loading a game will bring the
delay through into the save, whether or not the user had been firing
just before saving. Second, not firing the omega cannon and then
loading a game will allow the user to fire immediately on load, even if
the user had been firing when the game was saved. Future work can
address the first problem by clearing the delay, but a savegame file
modification is required to address the second problem.
------------->8-------------
#include <cstdio>
int last_fire;
long t64;
/* The test program only shows the bug on 64-bit systems */
static_assert(sizeof(last_fire) == 4, "sizeof(int) != 4");
static_assert(sizeof(t64) == 8, "sizeof(long) != 8");
void frame(bool expect_recharge, const unsigned line)
/* clang does not support __builtin_LINE, so fake it with a macro
*/
#define frame(E) frame(E, __LINE__)
{
if (last_fire > t64)
{
printf(__FILE__ ":%u:%u: last_fire=%8x t64=%16lx: last_fire > t64, resetting\n", __LINE__, line, last_fire, t64);
last_fire = t64;
}
const int time_bias = 0x5555;
if (last_fire + time_bias > t64)
{
printf(__FILE__ ":%u:%u: %5sexpect_recharge=%i last_fire=%8x t64=%16lx: last_fire recent (%16lx), refusing to recharge\n", __LINE__, line, expect_recharge ? "BUG: " : "", expect_recharge, last_fire, t64, static_cast<long>(last_fire + time_bias));
return;
}
printf(__FILE__ ":%u:%u: %5sexpect_recharge=%i last_fire=%8x t64=%16lx: last_fire old (%16lx), recharging\n", __LINE__, line, expect_recharge ? "" : "BUG: ", expect_recharge, last_fire, t64, static_cast<long>(last_fire + time_bias));
}
int main(int, char **)
{
frame(false);
t64 = 0x7fff;
frame(true);
last_fire = t64 - 4;
frame(false);
t64 = 0x7fffaaab;
frame(true);
last_fire = t64;
++t64;
frame(false);
t64 = 0x7ffffffffd;
frame(true);
last_fire = t64;
++t64;
frame(false);
t64 += 0x10000;
frame(true);
t64 = 0x7fffffffffffff00;
frame(true);
last_fire = t64;
frame(false);
t64 = 0x8000000000000000;
frame(true);
t64 += 0x800000000;
frame(true);
}
Previously, do_laser_firing_player would update Next_laser_firing_time,
then do_omega_stuff would update it again. OMEGA_BASE_TIME is smaller
than Weapon_info[OMEGA_ID].fire_wait, so the first store was overridden
by the second. As a quirk, the override was skipped if the omega cannon
was unable to fire due to object limits or due to insufficient energy,
causing those rare cases to use the longer
Weapon_info[OMEGA_ID].fire_wait delay.
Fold the omega cannon handling of Next_laser_firing_time into
do_laser_firing_player to eliminate that quirk, simplify the code, and
remove the need to recompute fire_frame_overhead from
Last_omega_fire_time.
Constructing valptridx::basic_ptr with a known-invalid magic index does
not require an array. Simplify the static_assert to reject uses of
factory functions with known-invalid magic index values. Fix the two
sites that fail with the stricter static_assert.