Commit graph

262 commits

Author SHA1 Message Date
Kp 12c06e59aa Move N_robot_types into LevelSharedRobotInfoState 2018-12-30 00:43:59 +00:00
Kp e84a65edc6 Move Robot_info into LevelSharedRobotInfoState 2018-12-30 00:43:59 +00:00
Kp 9be3c8e2e8 Move Walls into d_level_unique_wall_state 2018-12-30 00:43:58 +00:00
Kp b3cd7588ef Move Delta_lights into d_level_shared_destructible_light_state 2018-12-30 00:43:58 +00:00
Kp 46644b4e91 Move Dl_indices into d_level_shared_destructible_light_state 2018-12-30 00:43:58 +00:00
Kp 040de5b10e Rename ObjectState to LevelUniqueObjectState for consistency 2018-12-30 00:43:57 +00:00
Kp 1a5844e218 Move Vertices out of global scope 2018-12-30 00:43:57 +00:00
Kp 5873ccb71e Pass vcvertptr as context 2018-12-30 00:43:57 +00:00
Kp aa9e6da297 Pass Dl_indices as context 2018-12-30 00:43:57 +00:00
Kp 706a704c6d Move unique_side to unique_segment 2018-12-13 02:31:38 +00:00
Kp d83972dfbb Hide game window when advancing a dead player to the next level
Commit 88b5e616a9 ("Replace calls to
window_set_visible in DoPlayerDead() with stop/start_time()") switched
from hiding the game window to only stopping time, and that only if
hiding the window would have stopped time.  In multiplayer, this allows
time to continue running.  This introduced a crash if the player dies
during the mine countdown.  When the player dies, the game checks if the
reactor has been destroyed.  If so, the game immediately advances to the
next level.  That advance will try to draw the game window if it is
visible.  When the window is drawn, if time is not stopped, then game
logic runs.  Some of that logic is not prepared to deal with the
inconsistent state present when a new level is only partially loaded.
That inconsistent state then causes a crash.  Call stack:

    #11 slew_frame () at similar/main/slew.cpp:152
    #12 in d2x::object_move_one () at similar/main/object.cpp:1758
    #13 in d2x::object_move_all () at similar/main/object.cpp:1956
    #14 in d2x::GameProcessFrame () at similar/main/game.cpp:1848
    #15 d2x::game_handler () at similar/main/game.cpp:1615
    #16 in dcx::window_send_event () at common/include/window.h:116
    #17 dcx::event_process () at common/arch/sdl/event.cpp:176
    #18 in newmenu_do2 () at similar/main/newmenu.cpp:498
    #19 in newmenu_do2<dcx::unused_newmenu_userdata_t> () at common/main/newmenu.h:184
    #20 newmenu_do<dcx::unused_newmenu_userdata_t const> () at common/main/newmenu.h:190
    #21 newmenu_do<1ul, dcx::unused_newmenu_userdata_t const> () at common/main/newmenu.h:196
    #22 net_udp_wait_for_requests () at similar/main/net_udp.cpp:4563
    #23 net_udp_level_sync () at similar/main/net_udp.cpp:4607
    #24 in multi_level_sync () at similar/main/multi.cpp:3458
    #25 in d2x::StartNewLevelSub () at similar/main/gameseq.cpp:1803
    #26 in d2x::StartNewLevel () at similar/main/gameseq.cpp:2018
    #27 in d2x::AdvanceLevel () at similar/main/gameseq.cpp:1648
    #28 in d2x::DoPlayerDead () at similar/main/gameseq.cpp:1721

The root cause of this is the layering violation:
- Killing the player can have the side effect of advancing the level
- Advancing the level can have the side effect of calling multiplayer code while the level data is in an inconsistent state
- Calling multiplayer code can cause the event system to redraw the game
- Redrawing the game can cause game logic to run

Hack around this by restoring the logic that hides the game window, so
that the window is not redrawn and the game logic is not run.  This does
not fix the layering problem, but prevents crashing affected users.  To
avoid undoing the feature from the breaking commit, hide the window only
when advancing to a new level, rather than unconditionally.  A player
advancing to a new level already lacks the move-at-cold-start capability
even on successfully escaping the mine, so no functionality is lost with
this change.  Players who are dead and do not advance to a new level
retain that capability.

Fixes: 88b5e616a9 ("Replace calls to window_set_visible in DoPlayerDead() with stop/start_time()")
Reported-by: Ninjared <https://forum.dxx-rebirth.com/showthread.php?tid=1097>
2018-12-08 19:16:01 +00:00
Kp 5a64ee5132 Add experimental support for larger cooperative games
- Raise the player limit to 8.
- Remove the logic that forces player counts up/down when switching
  between cooperative and deathmatch game modes.
- Add heuristics to add start positions for the extra players, since
  standard maps will not have the required number of starts.
2018-12-03 04:25:11 +00:00
Kp a0ed5cb283 Pass Vclip as context 2018-10-21 00:24:07 +00:00
Kp e1d1cbb0ed Pass context to set_powerup_id 2018-10-08 03:58:48 +00:00
Kp c4cb930f8e Pass ObjectState to obj_delete 2018-09-09 01:00:39 +00:00
Kp ec1cf005b6 Enable -Wformat-truncation
Add macro cf_assert ("control flow" assert) to hint to gcc that certain
conditions are impossible.  Use it to avoid generating range checks for
situations that never happen.  If the event did happen, the only
consequence would be truncated UI text, rather than a correctness
problem.
2018-08-26 18:10:36 +00:00
Kp 8bbdeedeb2 Consolidate stuck object state
Move it to a structure.  Make all the modifiers methods.  Change all
callers of those methods to pass the structure.  This makes the stuck
object handling free of direct access to global game data.
2018-08-04 17:52:57 +00:00
Kp 57334255ac Simplify stuck object cleanup 2018-08-04 17:52:57 +00:00
Kp 5d74fd48b5 Fix player appearance sound truncation
The player appearance sound was tied to the player appearance vclip,
but this is wrong because the vclip has a shorter lifetime than the
sound.  This mistake was previously hidden by the hack that caused
non-permanent sounds to be routed off to a separate queue that lost the
association between object and sound.  Commit 4a98e79 eliminated that
hack because it complicated the work of that commit.  However, without
the hack, object sound effects cannot outlive their host object.

Fix this by binding the sound to the position of the appearance effect,
not to the appearance object.  This works since the appearance effect
cannot move, so there was no value to binding the sound to the object.
This solution could not be used for objects that move and terminate
before their associated sound effect.

Reported-by: Ryusei117 <https://github.com/dxx-rebirth/dxx-rebirth/issues/88#issuecomment-390054173>
Fixes: 4a98e796ab ("Prevent stacking weapon rotation sounds")
2018-07-14 17:23:15 +00:00
Kp 30a83eec41 Eliminate some uses of valptridx::operator-> 2018-06-24 05:06:15 +00:00
Kp 0fd7e0e4ce Pass context arrays to WALL_IS_DOORWAY 2018-06-24 05:06:15 +00:00
Kp f2f41cd60e Fix capitalization of MVE entries
Reported-by: ryusei117 <https://github.com/dxx-rebirth/dxx-rebirth/issues/379#issuecomment-390058146>
Fixes: 6e439e17fd ("Match case for MVE files in MVL archives")
2018-05-18 02:47:32 +00:00
Kp 6e439e17fd Match case for MVE files in MVL archives
PhysFS 3 requires a case match.  PhysFS 2 did not.  Match the case in
the archive, which should work in both PhysFS 2 and PhysFS 3.

Reported-by: ryusei117 <https://github.com/dxx-rebirth/dxx-rebirth/issues/379>
Useful-hint-from: Manuel-K <https://github.com/dxx-rebirth/dxx-rebirth/issues/379#issuecomment-388849440>
2018-05-15 03:12:45 +00:00
Kp 4a98e796ab Prevent stacking weapon rotation sounds
digi_play_sample_once is supposed to cancel prior instances of the
sound, but that functionality was lost in
21082c6db2.  As a result, the sound can be
stacked up to the limit of the engine.  Even when the functionality
existed, its implementation was wrong.

- Change these sounds to be attached to the player generating them.
- Pass the cancellation flag to other players when sending a sound.
- Send the full value of `volume`, rather than truncating it.
- Implement cancellation by killing and restarting the earlier version of a cancelled sound.
- Delete an ugly hack that prevented the patched logic from ever running.
- Fix an ancient quirk in digi_mixer that caused it to report all sounds as not playing, which then caused digi_sync_sounds to instantly cancel the new sound.

Reported-by: Mako88 <https://github.com/dxx-rebirth/dxx-rebirth/issues/88>
Reported-by: ryusei117 <https://github.com/dxx-rebirth/dxx-rebirth/issues/88#issuecomment-269597361>
Fixes: 21082c6db2 ("Added own channel management to SDL_mixer sound interface since the builtin channel management of this lib cannot handle our needs; Little code cleanup")
2018-05-13 03:14:34 +00:00
Kp 14dac5042a Factor out count_number_of_objects_of_type predicate
count_number_of_objects_of_type is dsx due to object differences, but
the predicate can be dcx.
2018-04-30 05:31:16 +00:00
Kp 9b028f26f2 Make eclip_num unsigned 2018-03-31 21:53:01 +00:00
Kp e8645b2062 Pass vertex factory to compute_center_point_on_side 2018-03-12 03:43:47 +00:00
Kp ad8fd0f871 Pass valptridx factories to obj_relink 2018-03-12 03:43:46 +00:00
Kp c179b2f783 Load robot customizations before use
AlexanderBorisov reported[1] a broken demo that ultimately traced to
incorrect reuse of stale data.  The campaign[2] includes per-level robot
definitions for level 3.  Descent incorrectly accessed Robot_info before
reloading it for the new level, so it used the level 3 data on level 4
robots during a warm start of level 4 for regular play, but used only
the level 4 data for a cold start of level 4 for regular play and for
playback of a demo recorded solely on level 4.  The particular
customizations applied on level 3 caused a level 4 demo recorded with
level 3 robot customizations to use different record lengths than a
level 4 demo recorded with level 4 robot customizations.  Since demos do
not record their record lengths, mismatched record lengths lead to
effectively corrupt demos.  In this case, it manifested as writing 8
anim_angles (per model_num 108, the model incorrectly inherited from
level 3) instead of 1 anim_angles (per model_num 37, used only on cold
starts).  The extra anim_angles were then misinterpreted as a run of 42
ND_EVENT_EOF, because the extra angles were unused and the newdemo code
defines the null byte as EOF (separate from receiving an actual EOF
indicator from the file I/O library).  That run of spurious EOF caused
the demo playback code to refuse to play past the spurious EOF,
resulting in a seemingly broken demo.

As an unfortunate, but unavoidable, consequence, this change modifies
the common path (warm playthrough of every level in the campaign) to
work like the uncommon path (cold start of each level).  Further, the
affected robot triggers a trap in d2x::do_silly_animation[3] when using
the correct model_num, because that polygon model has too few models for
the joints used by the robot.  When using the incorrect level 3 data,
the robot animates without trapping.

[1] https://forum.dxx-rebirth.com/showthread.php?tid=1023
[2] http://www.enspiar.com/dmdb/viewMission.php?id=212
```
sha1sum ENTROPY.HOG ENTROPY.MN2
7bc7a12d00a1ddd3ae92ce90eb67d581ecab004a  ENTROPY.HOG
f2688a634f22b30a02c43d8fa1a049deb5a03f70  ENTROPY.MN2

stat -c '%s %Y %n' ENTROPY.HOG ENTROPY.MN2
2402638 875757712 ENTROPY.HOG
511 875757164 ENTROPY.MN2
```
[3]
```
   799				if (jointnum >= Polygon_models[objp.rtype.pobj_info.model_num].n_models) {
   800					Int3();		// Contact Mike: incompatible data, illegal jointnum, problem in pof file?
   801					continue;
   802				}
```
2017-12-31 21:11:25 +00:00
Kp 49c0cdae2e Simplify calls to gr_set_default_canvas
Rather than use an inline wrapper and rely on the compiler optimizer to
redirect gr_set_current_canvas(nullptr) to gr_set_default_canvas,
rewrite all relevant calls directly in the source.

git grep -l 'gr_set_current_canvas' | xargs sed -i -e 's:gr_set_current_canvas(\s*NULL\|nullptr\s*);:gr_set_default_canvas();:'
2017-11-05 20:49:08 +00:00
Kp a24490033f Allow players to remove thief at level start
Commit f4b21088a0 ("Track vulcan ammo explicitly") fixed an original
retail bug that prevented the thief from stealing energy weapons,
because the thief could only steal weapons for which the player had ammo
and energy weapons never have ammo.  This went unremarked for several
years, until a recent report of the new semantics as a game-breaking
regression because the thief is now "ridiculously potent".

Address this report, as well as an intermittently raised issue from
various users over time, by adding two new knobs to both the single
player "Gameplay" menu and the multiplayer setup screen: "Remove Thief
at level start" and "Prevent Thief Stealing Energy Weapons".

"Remove Thief" deletes the thief object during level load.  It has no
impact on save games, and changing it after entering a level has no
effect on any thief already in the level.

"Prevent Thief Stealing" is checked at the moment of theft and, when
enabled, prevents stealing primary weapons other than Vulcan/Gauss.
This can be changed at will in single player and is immediately
effective.  In multiplayer, this option can only be changed by the game
host in the pre-game setup.

For both knobs, there is one pair of checkboxes to control this as a
player preference, which applies in single player games.  There is a
second pair of checkboxes in the multiplayer setup, which applies only
to multiplayer games.  Therefore, in multiplayer, the host chooses thief
settings and all clients use the host's choice.  The host may configure
the thief differently in multiplayer from how the host plays in single
player.

For users who wanted to remove the thief, no specific tally has been
kept for who requested it or when.  Now that the code is being updated,
this is thrown in as an easy addition.

Reported-by: MegaDescent <http://forum.dxx-rebirth.com/showthread.php?tid=980> (for the thief stealing energy weapons as a game-breaking regression)
2017-08-26 19:47:52 +00:00
Kp 4ea9ef5b0c Use reference for robot_info 2017-08-26 19:47:51 +00:00
Kp e859833f62 Use valptridx for Players
Currently, N_players is still a free variable and Players.count is
unused.  Future work will replace N_players with Players.count.
2017-08-13 20:38:32 +00:00
Kp 4cf4ce38b0 Cache player references 2017-08-13 20:38:31 +00:00
Kp e8d82d7d85 Pass valptridx factories to static functions 2017-07-26 03:15:59 +00:00
Kp f16c4def1d Unify AI cloak setup
- Initialize AI cloak data in D1, too.
- Initialize reactor's idea of player position from init_ai_for_ship
2017-07-08 18:17:49 +00:00
Kp 596ecbb38d Rename segment::value to segment::station_idx
`value` is generic and unclear.  It is always meant to be used as an
index into the Station array, so rename it `station_idx` to show this.

Define and consistently use `station_none` to represent that no station
is assigned.
2017-07-08 18:17:49 +00:00
Kp adcf02e454 Expand tt:: indirection to std::
All supported compilers have an acceptable <type_traits>.  Commit
4cb3d46148 ("Move <type_traits> test to Cxx11RequiredFeature") made
<type_traits> support mandatory in August and no one has objected.
Remove the indirection and use namespace std directly for type_traits
members.
2017-06-25 20:46:03 +00:00
Kp 599ac9dee0 Always qualify valptridx type/factory
Previously, valptridx used PREFIX for allow-invalid+mutable, c#PREFIX
for allow-invalid+const, v#PREFIX for require-valid+mutable, vc#PREFIX
for require-valid+const.  Convert the types, factories, and all usage
sites to specify a qualifier for all four combinations:

	im#PREFIX -> allow-invalid+mutable
	ic#PREFIX -> allow-invalid+const
	vm#PREFIX -> require-valid+mutable
	vc#PREFIX -> require-valid+const

Changes to common/include/valptridx.h and common/include/fwd-valptridx.h
are manual.  All other changes are generated by:

	git grep -lz -e '\(obj\|seg\|clwall\|wall\|actdoor\|trg\)\(ptridx\|ptr\|idx\)\(_t\)\?\>' | xargs -0 sed -i -e 's/\<\(v\?\)\(\(obj\|seg\|clwall\|wall\|actdoor\|trg\)\(ptridx\|ptr\|idx\)\(_t\)\?\)\>/\1m\2/g'

for the 'm' prefix and:

	git grep -lz -e '\(obj\|seg\|clwall\|wall\|actdoor\|trg\)\(ptridx\|ptr\|idx\)\(_t\)\?\>' | xargs -0 sed -i -e 's/\<\([cm]\(obj\|seg\|clwall\|wall\|actdoor\|trg\)\(ptridx\|ptr\|idx\)\(_t\)\?\)\>/i&/g'

for the 'i' prefix.
2017-06-10 03:31:02 +00:00
Kp 79a4a19711 Pass player to init_player_stats_level 2017-04-30 16:25:16 +00:00
Kp 08e4a6e620 Use stdint constants for some INT*_MAX
clang becomes confused trying to determine which vm_distance_squared
constructor to use for a literal input of 0x7fffffffffffffff, even
though the size of the input requires it to be `long` and only one
constructor can take a `long`.  Switch from an explicit
0x7fffffffffffffff to the symbolic constant INT64_MAX, which has the
same value, but a platform-appropriate suffix to force the compiler to
pick the right type.

For general clarity, switch some other instances of integer maximum
literals to symbolic constants of the same value.

This commit has no effect on the generated code (except for changes to
line numbers).

Reported-by: kreatordxx <https://github.com/dxx-rebirth/dxx-rebirth/pull/324>
Fixes: 17208cca79 ("Disallow int for vm_distance_squared")
2017-02-22 03:05:43 +00:00
Chris Taylor b17a9f8550 start_time() time when game over
After getting game over, the game would be stopped when playing a demo or loading a saved game. This is now fixed.
2017-02-20 08:04:55 +08:00
Kp 7c658fd8cd Use mask for Primary_last_was_super 2017-02-19 19:33:44 +00:00
Kp 7df99350dd Pass player to do_laser_firing_player 2017-02-08 23:34:41 +00:00
Chris Taylor 88b5e616a9 Replace calls to window_set_visible in DoPlayerDead() with stop/start_time()
This is so game_flush_inputs() isn't called - part of change allowing player to respawn and begin moving with the same keypress. With this commit, you can use this feature with the 'when dead, respawn by pressing the Fire key' option - i.e. hold down movement key, then press fire key to respawn.
2017-02-08 12:17:51 +08:00
Kp 59bad96d5f Add back homing weapons cheat for D2
Kreator proposed restoring the Descent 2 cheat that grants homing
capability to all weapons.  This commit implements that proposition,
with some changes to the implementation details.

Based-on-patch-by: Chris Taylor <chris@icculus.org>
Requested-by: Chris Taylor <https://github.com/dxx-rebirth/dxx-rebirth/pull/318>
2017-01-29 21:02:48 +00:00
Kp 8c706a88bd Initialize grs_main_bitmap at construction 2017-01-28 18:12:20 +00:00
Chris Taylor a418f8caec Remove call to window_close(Game_wind) when multiplayer level syncing fails
Replace call to window_close(Game_wind) with returning window_event_result::close to game_handler. Applies to when there is a failure in net_udp_level_sync(). Closing a window within its handler is problematic - it can result in an unstable state.
2017-01-10 13:02:59 +08:00
Chris Taylor 4cc801f42f Remove calls to window_close(Game_wind) when game finished or over
Replace calls to window_close(Game_wind) with returning window_event_result::close to game handler. Applies to when DoEndGame() is called, DoGameOver() is called, aborting in the kmatrix screen (multiplayer game) during AdvanceLevel() and playing one demo frame causes playback to stop in GameProcessFrame(). Closing a window within its handler is problematic - it can result in an unstable state.
2017-01-08 21:08:50 +08:00
Kp 22e364e030 Pass canvas to gr_clear_canvas 2017-01-01 00:45:45 +00:00