Compare commits

...
Author SHA1 Message Date
JohnAFernandez 3043e1ee2d Update release make to point to 24.0.1 2024-05-11 23:04:13 -04:00
mjn-mixael 7a7841c30a Do not unload debrief bitmaps in API mode 2024-05-11 23:02:38 -04:00
JohnAFernandez d5653674f1 Merge branch 'pr_24_0_1' of https://github.com/scp-fs2open/fs2open.github.com into pr_24_0_1 2024-05-11 22:58:24 -04:00
JohnAFernandez d74627ba61 Remove version_override.cmake
This will hopefully allow the point release to succeed.
2024-04-17 16:36:13 -04:00
JohnAFernandez ed20982b32 Update Version Override 2024-03-25 23:59:39 -04:00
JohnAFernandez e21cc10c5e Update Release Mako with Previous Thread 2024-03-25 22:54:52 -04:00
Asteroth b5fe16081d fix an issue with animation directions 2024-03-25 22:54:52 -04:00
mjn-mixael 41bff809e3 Don't init the debrief ui in API mode 2024-03-25 22:54:52 -04:00
mjn-mixael f119f3d370 Default post processing to on because so many things require it 2024-03-25 22:54:52 -04:00
Joshua Glatt a90a0a55bd Handle container data correctly with eval_num() (#6031) 2024-03-25 22:54:52 -04:00
Joshua Glatt d08d8b4b02 fix crash when right-clicking on container modifier (#6030) 2024-03-25 22:54:52 -04:00
Mike Nelson f1292ad15a Push back instead of direct assignment (#6024)
* push back instead of direct assignment

* remove i
2024-03-25 22:54:51 -04:00
wookieejedi eb911c8ca2 Fix CTD with HUD Preset Switching (#6022)
* Fix CTD with HUD Preset Switching

After #5302 it was discoved that pressing the `Next` button in the HUD presets would cause a CTD.

Thanks for Lafiel for figuring this out
`If HC_current_file is HC_preset_filenames.size() - 1, then it'll oob on a vector access.`

Testing this PR fixes that crash.

* use static cast
2024-03-25 22:54:51 -04:00
MageKing17 125de7f131 Fix SSM strikes with a variable count.
In #2464, a somewhat hard-to-understand equation involving taking the modulo of a random number was replaced with a function that would do those calculations instead. The problem is that the function performed the *entire* formula, while the code it replaced was only operating on the *difference* between the two numbers. Fortunately, this is as easy to fix as changing a `+=` into just an `=`.
2024-03-25 22:54:51 -04:00
Birk Magnussen 78d0bddbe1 Add comment on MAX_OBJECTS 2024-03-25 22:54:51 -04:00
Birk Magnussen b009991fae Increase collision pair cache bitshift 2024-03-25 22:54:51 -04:00
Goober5000 f2806dc77a fix "this ship name is already in use" bug
This rather insidious FRED bug was all down to an improper check for the current ship.  It is possible for multiple objects of different types to be selected at the same time.  If, for example, a waypoint and ship are selected, and the waypoint was selected first, the waypoint will be what `cur_object_index` refers to.  Since the ship is the one and only ship selected, it will be what `single_ship` refers to.  Therefore, to avoid flagging its own ship name as a duplicate, the loop should exclude the single ship, not the current object.

The bug can be reliably triggered by selecting a waypoint, then adding a ship to the selection, then switching the focus to another application and back to FRED.  This finally squashes the bug.
2024-03-14 17:03:17 -04:00
SirKnightly 6cbf0fbbf8 Automated build commit 2024-02-26 02:47:29 +00:00
MageKing17 3a48613a2d Add some security to avoid invalid Random::next() calls. (#5988)
There were a few instances of `Random::next()` being called without ensuring its arguments couldn't trip an assertion. This should hopefully catch them all.
2024-02-14 15:03:51 -05:00
Goober5000 99875810c6 Merge pull request #5986 from Goober5000/24_0_RC2_edits
edit the release template
2024-02-11 01:53:27 -05:00
Goober5000 13420121dd edit the release template
A bit of wordsmithing and link updating for the release thread.
2024-02-10 00:34:28 -05:00
Goober5000 26c5152d4a Merge pull request #5985 from Goober5000/pof_file_valid_fname_checks
make model file checks consistent
2024-02-09 17:24:08 -05:00
BMagnu 5066e498a3 Virtual POF fixes (#5984)
* Fix wrong filename being used

* Fix replacement map not used for collision check

* Fix poly count and incorrect submodel numbers
2024-02-09 23:10:39 +01:00
Goober5000 4bf806c485 make model file checks consistent
The general expectation is that model filenames can be specified as 'none' or '<none>' for no model, but as discovered by no tea, this was not consistent across the code base.  For example, `$Model File: none` was handled as expected but `$External Model File: none` was not.  This PR makes the handling consistent for all ship, weapon, and shockwave POF files, and also adds some extra checks when models are loaded.
2024-02-09 17:00:45 -05:00
Goober5000 2403f13483 Merge pull request #5983 from Goober5000/fred_popup_enhancement
fix FRED's "new object type" popup
2024-02-09 00:29:52 -05:00
Goober5000 8e98cddd6e Merge pull request #5982 from Goober5000/issue_5980
address issue 5980
2024-02-08 12:56:54 -05:00
Goober5000 30cabc4174 fix FRED's "new object type" popup
1. Fix the item ID indexing on the popup submenus and make sure they correspond with the ship dropdown menu
2. Remove the "player start" popup menu option as it has very limited utility and was already removed in qtFRED
3. Reduce a bunch of casts by using more appropriate popup menu types
4. Actually place an object in the mission when one is chosen from the popup menu

Also:
1. Remove the `Default_player_model` variable as its use is redundant
2. Remove unused arguments of `create_player()`
2024-02-08 03:26:25 -05:00
Goober5000 8a3b36d921 uncorrect angles if we're saving without the corrected angles flag (which will happen if the save format isn't STANDARD) 2024-02-07 22:51:37 -05:00
Goober5000 b74824f793 use a different method for removing blank backgrounds 2024-02-07 18:27:52 -05:00
Goober5000 6a26ca5061 only display these warnings once, and only on explicit save 2024-02-07 18:27:52 -05:00
John Fernandez d34570ae90 Merge pull request #5979 from MjnMixael/fix_select_zoom
Unhardcode renderSelectModel zoom value
2024-02-06 23:40:41 -05:00
mjn-mixael a0f4c4d2b3 documentation error 2024-02-05 16:20:33 -06:00
mjn-mixael 63990c54a9 unhardcode zoom value 2024-02-05 16:14:07 -06:00
Goober5000 6593565165 Merge pull request #5976 from notimaginative/fix_cmdline_mod_on_unix
refactor unix modlist handling to fix serveral issues
2024-02-05 17:00:59 -05:00
Goober5000 af93922ba5 Merge pull request #5975 from notimaginative/fix_chdir_bug
fix error handling in cfile_push_chdir()
2024-02-05 17:00:47 -05:00
Fabian Woltermann daeb1a7940 Merge pull request #5977 from BMagnu/shadow_map_ifdef
Fix faulty / missing optimization for AMD shader compilation
2024-02-05 18:34:08 +01:00
Birk Magnussen f56cbc59f1 improve shader for shadow map generation 2024-02-05 14:43:54 +01:00
Taylor Richards 79418a073d use proper SCP types 2024-02-05 00:04:34 -05:00
Goober5000 ce2e165510 Merge pull request #5962 from Goober5000/warp_duration_fixes
fix warp time durations
2024-02-04 23:49:15 -05:00
Goober5000 e9ee311709 fix warp time durations
Properly set the warp durations when ships arrive and depart, so that the warp animation can open and close smoothly without any discontinuities.
2024-02-04 23:30:22 -05:00
Taylor Richards 7576d4f2fe refactor unix modlist handling to fix serveral issues
The result of this special parsing should produce a mod string identical
to the string passed on the command line with the following exceptions:
  directory name case, directory separators

Should address the following:
 - no fundamental difference in modlist compared to Windows
 - no errors or warnings that are not produced on Windows
 - do not remove mods from list if missing
 - verify mod directory is accessible and actually a directory
 - do minimum amount of work to see if mod directory is valid
 - fix bug with trainling slashes
2024-02-04 22:08:28 -05:00
Taylor Richards 866b06ca35 fix error handling in cfile_push_chdir()
Old Volition bug where the original directory is pushed onto a stack
before the chdir() call. If that call fails then the stack is left with
a used entry that won't be removed until restart and can cause the stack
to fill up. Now it's added only if the chdir() call succeeds.

Also take the opportunity to make Cfile_stack dynamic.
2024-02-04 22:03:56 -05:00
Goober5000 f3d15f8ddc Merge pull request #5972 from Goober5000/send_message_clarification
update sexp help for send-message
2024-02-04 15:19:10 -05:00
Goober5000 c9fe191d31 Merge pull request #5973 from Goober5000/modular_medals_fix
ensure medal information is correctly assigned
2024-02-04 15:18:53 -05:00
John Fernandez bad255fffb Merge pull request #5970 from Goober5000/sexp_exited_refinement
be mindful of death rolls when checking for ship stats
2024-02-03 23:31:01 -05:00
Goober5000 be260ef74c ensure medal information is correctly assigned
Make sure all medal fields are properly assigned.  In particular, if a modular -mdl.tbm is parsed, don't overwrite information from the base medals.tbl.  Fixes a bug reported in Discord.
2024-02-03 21:25:05 -05:00
Goober5000 0ac3d52261 update sexp help for send-message
Clarify message priorities, especially the conditions that allow High messages to be sent by Command.  Motivated by #5968.
2024-02-03 19:14:40 -05:00
John Fernandez f7df03c654 Merge pull request #5932 from Dark-Visor/sexp_ship_helps_final
Clarify which SEXPS work on ships not arrived
2024-02-03 17:14:34 -05:00
John Fernandez a7e1a27e36 Merge pull request #5963 from MjnMixael/fix_custom_string_dlg
Fix assert when adding custom strings
2024-02-03 16:03:56 -05:00
Goober5000 ab319a631c be mindful of death rolls when checking for ship stats
In #5649 / b6c0e8b, the ship registry was updated to take ship death rolls into account.  There was one small wrinkle that it did not account for, however: SEXP operators that return numeric values had previously (i.e. before the introduction of the ship registry) checked the mission log for ship destruction, but after #5649 the EXITED status was not set until after the ship had completely exploded, resulting in certain SEXPs having their conditions slightly modified.  The equivalent of checking the mission log for ship destruction is now checking the ship status for DEATH_ROLL, so it is necessary to make that change for the numeric SEXPs to behave as they did before.

The motivating result for all this is to fix SEXPs like `hits-left` firing after a ship has been destroyed but before it has finished exploding.  This can be seen by loading the first non-training mission of FSPort and comparing the messages that are sent if you use cheats to kill Alpha 2.
2024-02-02 16:51:38 -05:00
John Fernandez 7c2950480e Merge pull request #5969 from Goober5000/fix_blink_locked_anim
fix Blink Locked Animation: option
2024-02-01 07:48:00 -05:00
Goober5000 a80bc26bfc fix Blink Locked Animation: option
Due to a copy-paste error in #4581, this option was not set properly.
2024-02-01 02:08:56 -05:00
John Fernandez ead3c62d23 Merge pull request #5966 from MjnMixael/unfocused_pause_again
Prevent compiler optimization
2024-01-30 22:22:19 -05:00
mjn-mixael 5cd502fb79 prevent compiler optimization 2024-01-30 15:26:25 -06:00
Dark-Visor 905d777e18 Update to wording of sexp helps PR
Clarify wording on SEXP helps so that it is clear whether an Event will
be evaluated or have delayed evaluation.
2024-01-30 21:12:14 +00:00
Dark-Visor 66deb83980 Clarify which SEXPS work on ships not arrived
SEXP helps should let the FREDder know whether a given ship argument
needs to be already in-mission for the SEXPression to operate. Update
the helps to include (ship needs to be in-mission) or (ship does not
need to be in-mission) or other relevant helpful information.
2024-01-30 21:12:14 +00:00
mjn-mixael de3e382180 better defense 2024-01-30 08:11:58 -06:00
mjn-mixael 0190fda7ee fix assert when adding custom strings 2024-01-30 08:11:58 -06:00
Goober5000 9651dc1598 Merge pull request #5957 from Goober5000/fix_subsystem_message
fix ship subsystem parse warning
2024-01-29 22:53:18 -05:00
Goober5000 743cf779bf Merge pull request #5961 from Goober5000/another_gravity_fix
Third gravity fix
2024-01-29 22:38:49 -05:00
Goober5000 89a6e2aa7d fix ship subsystem parse warning
As reported by Trivial Psychic, if the number of subsystems exceeds the max as the result of a modular table, the error message was very misleading.  This fixes the error message and also makes it report the correct number of parsed subsystems.
2024-01-29 22:04:25 -05:00
Goober5000 84033d901c Third gravity fix
`ai_get_weapon` is not guaranteed to return a valid `wip`, so check for that here.  This should now cover all uses of `ai_get_weapon` in the codebase.

Fixes an Event Horizon crash reported in Discord.  Follow-up to #5842, commit 8dbe0e7.
2024-01-29 22:04:02 -05:00
John Fernandez 4b49123070 Merge pull request #5965 from notimaginative/fix_linux_build_image
switch to new linux build image
2024-01-29 16:36:40 -05:00
Taylor Richards 213bcbc80b find&replace ALL the things 2024-01-29 15:04:47 -05:00
Taylor Richards 0da34f0bc8 switch to new linux build image
Upgrades to Ubuntu 20.04 to fix Node v20 problem.
Bumps compiler to gcc-9.

NOTE: This breaks support for Linux distros older than Ubuntu 20.04
2024-01-29 14:57:38 -05:00
John Fernandez 8fcf385e7e Merge pull request #5955 from MjnMixael/no_ui_pause
Pause the UI properly based on user settings
2024-01-28 00:53:37 -05:00
mjn-mixael 4f4722ccad also pause the UI properly based on user settings 2024-01-25 13:14:24 -06:00
John Fernandez 7d94561ea5 Merge pull request #5920 from MjnMixael/properly_check_windowed
Properly check if we're running in a windowed mode
2024-01-25 00:21:43 -05:00
John Fernandez 248cec7cf9 Merge pull request #5921 from MjnMixael/save_multi_settings
Save these to the ini file
2024-01-25 00:20:50 -05:00
John Fernandez e987d1cd7b Merge pull request #5929 from MjnMixael/clean_cutscene_info
Cleanup ui cutscene api
2024-01-25 00:20:18 -05:00
John Fernandez f1aa29551b Merge pull request #5954 from MjnMixael/fix_discord_option
Fix discord option needing restart
2024-01-24 17:21:34 -05:00
Goober5000 76734fb5ed Merge pull request #5946 from Goober5000/splash_fix
fix splash crash and add supplemental script hook
2024-01-24 14:42:29 -05:00
mjn-mixael fb0a62c400 fix discord option needing restart 2024-01-24 10:07:37 -06:00
Goober5000 1403301b65 add variable to prevent any hooks from running until OnGameInit has run 2024-01-23 14:51:06 -05:00
John Fernandez 1d1eb5ec4d Merge pull request #5952 from MjnMixael/fix_custom_string_save
Fix custom string saving
2024-01-23 14:29:45 -05:00
Goober5000 7283412880 Merge pull request #5953 from Goober5000/fix_fire_at_coords
fix crash in fire-beam-at-coordinates
2024-01-23 00:40:12 -05:00
Mike Nelson 1d2ca8eb5a print option settings to the log (#5914) 2024-01-23 00:40:04 -05:00
Goober5000 130ddb41f1 Merge pull request #5947 from JohnAFernandez/keep-observers-from-crashing
Fix: Keep Chase view from crashing in multi
2024-01-23 00:13:07 -05:00
Goober5000 9462241c0b Merge pull request #5948 from BMagnu/fix_msaa_compile
Fix MSAA for some graphics drivers
2024-01-23 00:12:58 -05:00
Goober5000 14bba9aea1 fix crash in fire-beam-at-coordinates
A small oversight in #5837 - beam jitter is not done when targeting coordinates.
2024-01-23 00:07:33 -05:00
mjn-mixael c7b43b0e0e fix custom string saving 2024-01-22 12:18:25 -06:00
Birk Magnussen 1355c484fe Remove language-defined declaration 2024-01-19 22:15:29 +01:00
JohnAFernandez 1866c6adb5 Whoops, forgot to push typo fix 2024-01-19 01:49:43 -05:00
JohnAFernandez a11517984e Keep Chase view from crashing
For Observers in multi
2024-01-19 01:29:08 -05:00
Goober5000 f207d2d12c add new On Splash End hook for scripts that have been doing post-splash stuff in On Game Init 2024-01-18 17:00:02 -05:00
Goober5000 4970252e9f tweak initialization sequence to head off potential future problems 2024-01-18 16:51:36 -05:00
Goober5000 d789e9b33a fix crash caused by incomplete script initialization 2024-01-18 00:43:14 -05:00
Fabian Woltermann 292f7d3b03 Merge pull request #5938 from MjnMixael/unload_pilot_scpui
Fix SCPUI overwriting campaign data
2024-01-15 19:09:47 +01:00
mjn-mixael 3bfc37172a Expose unloading a player and campaign file 2024-01-14 22:32:25 -06:00
Goober5000 2bb1c10557 Merge pull request #5916 from Goober5000/number_hack_upgrade
upgrade the OPF_POSITIVE number hack
2024-01-14 19:19:16 -05:00
Goober5000 40fac5dd5f Merge pull request #5838 from Goober5000/turret_constify
constify some turret code
2024-01-12 20:46:03 -05:00
John Fernandez 2a33e374c2 Merge pull request #5926 from MjnMixael/is_movie_playing
LUA method to check if a movie is currently playing
2024-01-10 02:52:31 -05:00
mjn-mixael d48d00d946 static cast 2024-01-09 08:11:03 -06:00
mjn-mixael 562875130c Cleanup ui cutscene handling 2024-01-08 13:57:51 -06:00
mjn-mixael 4401101fea lua method to check if a movie is currently playing 2024-01-07 14:41:01 -06:00
mjn-mixael 509f887f6c Save these to the ini file 2024-01-05 17:04:58 -06:00
mjn-mixael 266079997a remove unused code 2024-01-05 13:31:31 -06:00
mjn-mixael f4a3903478 properly check if we're running in a windowed mode 2024-01-05 13:13:24 -06:00
Taylor Richards f4202df450 codesign Mac builds to prevent broken app message (#5919)
Apple Silicon Macs require that all code is signed in order to run. Our builds
are partially signed due to the SDL framework already being signed, so it is
seen as broken. To fix that we need to codesign all the rest of the necessary
files with an ad-hoc signature in order to allow the builds to work properly.
2024-01-04 22:25:59 -05:00
Goober5000 e619c0c3d3 Merge pull request #5910 from Goober5000/campaign_load_refactor
campaign error message improvement
2024-01-04 21:51:24 -05:00
Goober5000 ff0b79b6be move this and make it static 2024-01-04 13:49:27 -05:00
Goober5000 701d20c112 upgrade the OPF_POSITIVE number hack
This is a new method of ensuring that OPF_POSITIVE numbers are positive.  It works for all numeric arguments, including variables and literals, and caches the status of the node to avoid lookups for every negative number on every frame.  Tested on both operator and non-operator cases.

Follow-up to #5915.
2024-01-04 13:43:19 -05:00
Goober5000 9a45c9fd05 Merge pull request #5915 from Goober5000/number_hack_update
restore number hack sign checking in all builds
2024-01-04 13:41:47 -05:00
JohnAFernandez 146102e82c Merge pull request #5909 from Goober5000/remove_mht
remove mainhalltemp
2024-01-04 11:33:41 -05:00
Goober5000 be2c18d608 add back exceptions 2024-01-03 18:46:10 -05:00
Goober5000 c9644cdcae Merge pull request #5913 from Goober5000/5911_followup
5911 followup
2024-01-03 18:03:05 -05:00
Goober5000 d088e895f8 restore number hack sign checking in all builds
Operators that specify OPF_POSITIVE should be guaranteed to get positive numbers.  This code was previously disabled for performance reasons, but following the contract of the SEXP system is more important.  A future PR will improve the performance of the functions used here.

Also, the `num-players` operator takes no arguments, so fix that.
2024-01-03 14:24:43 -05:00
Goober5000 f1a9b2639b 5911 followup
In consultation with @BMagnu, code that validates data in the POF file itself should happen in `read_model_file_no_subsys`, as virtual POFs rely on their source files having correct data.  I've added comments and slightly rearranged things to make this clearer, and also moved the new code from `read_and_process_model_file`.  Additionally, I fixed two bugs by changing `parent > pm->n_models` to `parent >= pm->n_models` and adding a newline to the `nprintf`.
2024-01-03 11:34:18 -05:00
Fabian Woltermann a1690dcb90 Merge pull request #5911 from The-E/fix/invalid_eye_point_data
Fixes an issue with faulty eyepoint data causing a crash.
2024-01-03 14:45:26 +01:00
Fabian Woltermann 2c4088efdd Fixes an issue with faulty eyepoint data causing a crash. 2024-01-03 13:50:23 +01:00
Goober5000 da3725a3ac Merge pull request #5902 from Goober5000/refine_number_checking
refine numeric error checking in FRED
2024-01-03 00:00:22 -05:00
JohnAFernandez 81b7460e57 Merge pull request #5908 from MjnMixael/lua_enum_validation
Add some better error and validation checking to lua enums scripting API
2024-01-02 23:41:58 -05:00
Goober5000 c9a1127764 fall back to default campaign when starting the game or choosing a new pilot 2024-01-02 02:27:37 -05:00
Goober5000 6eb9ae2aec remove mainhalltemp
These files are not referenced anywhere and haven't been used since before FreeSpace 2 was released.
2024-01-01 23:51:45 -05:00
Goober5000 d3d9ee3092 refine campaign failure error messages 2024-01-01 17:41:42 -05:00
Goober5000 f66fc92859 int to boolean 2024-01-01 17:41:42 -05:00
Goober5000 94cbc350a4 handle main hall if campaign could not be loaded 2024-01-01 17:41:42 -05:00
Goober5000 550e9ec5b2 distinguish between corrupt and missing campaign files 2024-01-01 17:41:30 -05:00
mjn-mixael de3463958d Add some better error and validation checking to lua enums 2024-01-01 13:27:11 -06:00
Asteroth 03d7845124 improve comments (#5903) 2023-12-31 15:56:41 -05:00
Goober5000 661dceb0d8 Merge pull request #5906 from Goober5000/fix_vr_aspect_lock
additional HUD fixes for VR
2023-12-31 12:53:59 -05:00
m!m 202686dab6 Merge pull request #5907 from asarium/fix/balanceLuaStack
Fix unbalanced stack when retrieving Lua main thread
2023-12-31 18:18:41 +01:00
asarium b8da1106eb Fix unbalanced stack when retrieving Lua main thread
After getting the main state from the Lua registry, this function had
two Lua thread values on its Lua stack but we only removed one which
lead to a value being left on the stack.

Why did this not blow up before? Because undefined behavior.

The only place where this was called was when creating a Lua reference
value where luaL_ref is called in the same expression (see
UniqueLuaReference::create). The evaluation order of the parameters for
the make_shared call is not defined by the standard and for some reason
all major compilers (except clang) choose to execute the luaL_ref call
before the getMainThread call.

For those compilers, everything is fine. We have some extra values on
the Lua stack but that gets cleaned up after the API function finishes.

However, clang first evaluates the getMainThread which leaves an extra
Lua thread on the Lua stack. The luaL_ref call then references this
thread value instead of the intended value. In the concrete case, this
meant that LuaFunction saw that the reference points to a thread and
rightfully threw an exception.

This fixes #5904.
2023-12-31 17:44:58 +01:00
Goober5000 43f7921121 additional HUD fixes for VR
Use a single canonical `frametime` value for all HUD rendering, in both `hud_render_preprocess` and `hud_render_all`.  Previously, certain functions called through `hud_render_preprocess` were using the full frametime, rather than the half frametime needed for VR when the HUD is rendered twice.

Fixes #5882.
2023-12-30 20:35:15 -05:00
Goober5000 f4669db41c constify some turret code
Apply `const` to a whole bunch of functions.  Also change a bunch of ints to bools.
2023-12-30 00:00:31 -05:00
Tyler Blount b3df3364e4 Merge pull request #5837 from Goober5000/turret_refactor
untangle turret code a bit
2023-12-29 21:31:51 -07:00
Goober5000 b6a76b788d reuse beam points for collision test 2023-12-29 16:51:36 -05:00
Goober5000 e21b07662e refactor 'share fire direction'
Use a simpler, more robust, and more correct method of sharing the fire direction among beams: replicate the actual beam vector for all firing points.  This further cleans up and simplifies the beam firing code and also clarifies some vector math.
2023-12-29 16:35:34 -05:00
Goober5000 9b4b0b552b fix use of target position, and also don't get gvec if we don't need it 2023-12-29 16:34:22 -05:00
Goober5000 bdab7a0c55 untangle turret code a bit
1. make `check_ok_to_fire` return a boolean (like `check_los`)
2. make `check_ok_to_fire` and `check_los` both use a position argument since we don't need the whole turret
3. remove the redundant position prediction code from `ship_get_global_turret_gun_info` (since that needs to be a "pure" function) while making sure "share fire direction" still works
4. skip calculation of the vector in `ship_get_global_turret_gun_info` if we don't need it
5. move the position prediction code out of `aifft_rotate_turret` and into its own function
6. prune some gun position calculation calls since in most cases one will suffice
7. fix position "jitter" with weapon subsystem damage in missions that use gravity
8. `model_rotate_gun` now uses a nullptr position for a reset instead of an extra boolean argument
9. use a more efficient method to calculate gun barrel length
10. a couple variable name changes for clarity
2023-12-29 16:28:24 -05:00
Goober5000 e269cdab6e Merge pull request #5863 from Baezon/beams-respect-hull-check-self-damage
Make beams respect 'check hull' and 'can damage shooter' flags
2023-12-29 16:27:50 -05:00
wookieejedi edc76bcb40 Fix typo in lua error prints (#5901)
2 line typo fix `refere` -> `refer`
2023-12-29 13:42:35 -05:00
Goober5000 cbde5a8e85 refine numeric error checking in FRED
1. Allow a `+` to appear in front of numbers, just as with `-`
2. Check for `.` and `,` within numbers to give a less misleading error message than "not a number"
3. The old number hack inadvertently skipped numeric literals, so specifically check for those
2023-12-28 14:46:47 -05:00
Goober5000 7fcfc35820 Merge pull request #5898 from Goober5000/invalid_point_message_fix
make error messages more accurate
2023-12-24 14:31:06 -05:00
Goober5000 f852421d76 update messages per feedback 2023-12-24 13:27:51 -05:00
Goober5000 2321311725 make error messages more accurate
There are several SEXP argument types that themselves encompass multiple argument types: for example, ships and wings.  This could be misleading when there is an error message displayed.  So, this updates the error codes to explicitly cover all of the possibilities.
2023-12-24 13:27:51 -05:00
JohnAFernandez 733e1adcba Merge pull request #5892 from JohnAFernandez/Fix-Coverity-Workflow
Fix Coverity WorkFlow
2023-12-20 16:17:29 -05:00
Goober5000 485eb16fe3 Merge pull request #5827 from Goober5000/delay_status_tweak
tweak event status evaluation when the game is not in-mission
2023-12-19 19:34:35 -05:00
John Fernandez a493f07333 Fix Coverity WorkFlow
By using app image and switching to Checkout V1.

Also removes Vulkan SDK install, since it seems to be taken care of by the app image or is disabled.  I can't figure it out from the app images.
2023-12-19 12:12:56 -05:00
Asteroth e543c1aea2 make beams respect check hull and self damage 2023-12-05 16:46:32 -05:00
Goober5000 0d1d6fdf44 tweak event status evaluation when the game is not in-mission
The requirement for delayed event status checks to be strictly greater than the event fired timestamp (see commit eed50619ca / PR #5267) means that `is-event-true-delay 0` will not work in debriefings if the event becomes true and the debriefing SEXPs are evaluated on the exact same timestamp.  Normally this is not a problem, but one important exception is the `end-mission` operator which instantly sends the player to debriefing.

This timestamp behavior is working as designed and should be consistent as long as the `end-mission` operator has been around (I've verified it as far back as 3.7.4).  But it does lead to inconvenient situations where the event that called `end-mission` cannot be evaluated by the debriefing.  This makes it slightly more complicated to plan debriefing stages based on these events.

Therefore, it makes sense to add an exception to the event status function to ignore the delay if the evaluation is not in-mission.  This is consistent with evaluating events that *never* fired and also satisfies the principle of least astonishment for mission designers.

This also adds relevant documentation to the event and goal status operators.
2023-11-27 18:08:59 -05:00
129 changed files with 1760 additions and 1582 deletions

No files matched your search

+14 -2
View File
@@ -15,7 +15,7 @@ jobs:
configuration: [FastDebug, Release]
name: Linux
runs-on: ubuntu-latest
container: ghcr.io/scp-fs2open/linux_build:sha-5ac6f70
container: ghcr.io/scp-fs2open/linux_build:sha-1ff82e8
steps:
- uses: actions/checkout@v1
name: Checkout
@@ -24,7 +24,7 @@ jobs:
- name: Configure CMake
env:
CONFIGURATION: ${{ matrix.configuration }}
COMPILER: gcc-5
COMPILER: gcc-9
ENABLE_QTFRED: OFF
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
- name: Compile
@@ -269,6 +269,18 @@ jobs:
ARCH: ${{ matrix.arch }}
XDG_RUNTIME_DIR: /root
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
- name: Sign builds
working-directory: ./build/bin
run: |
# make sure OpenAL framework is signed
find . -name OpenAL -type f | while read xx; do codesign --force -s - $xx; done
find . -name OpenAL.framework -type d | while read xx; do codesign --force -s - $xx; done
# make sure dylibs are signed
find . -name \*.dylib -type f | while read xx; do codesign --force -s - $xx; done
# sign binaries
find . -perm +111 -type f | grep MacOS | while read xx; do codesign --force -s - $xx; done
# sign apps
find . -name \*.app -maxdepth 1 | while read xx; do codesign --force -s - $xx; done
- name: Package build result
working-directory: ./build/bin
# Use GNU tar here (part of runner image) to avoid weird corruption bug with bsdtar
+14 -2
View File
@@ -44,7 +44,7 @@ jobs:
name: Linux
needs: create_release # Don't run this job until create_release is done and successful
runs-on: ubuntu-latest
container: ghcr.io/scp-fs2open/linux_build:sha-5ac6f70
container: ghcr.io/scp-fs2open/linux_build:sha-1ff82e8
steps:
- uses: actions/checkout@v1
# checks-out your repository under $GITHUB_WORKSPACE, so your workflow can access it.
@@ -55,7 +55,7 @@ jobs:
- name: Configure CMake
env:
CONFIGURATION: ${{ matrix.configuration }}
COMPILER: gcc-5
COMPILER: gcc-9
ENABLE_QTFRED: OFF
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
- name: Compile
@@ -339,6 +339,18 @@ jobs:
ARCH: ${{ matrix.arch }}
XDG_RUNTIME_DIR: /root
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
- name: Sign builds
working-directory: ./build/bin
run: |
# make sure OpenAL framework is signed
find . -name OpenAL -type f | while read xx; do codesign --force -s - $xx; done
find . -name OpenAL.framework -type d | while read xx; do codesign --force -s - $xx; done
# make sure dylibs are signed
find . -name \*.dylib -type f | while read xx; do codesign --force -s - $xx; done
# sign binaries
find . -perm +111 -type f | grep MacOS | while read xx; do codesign --force -s - $xx; done
# sign apps
find . -name \*.app -maxdepth 1 | while read xx; do codesign --force -s - $xx; done
- name: Package build result
working-directory: ./build/bin
# Use GNU tar here (part of runner image) to avoid weird corruption bug with bsdtar
+14 -2
View File
@@ -15,7 +15,7 @@ jobs:
configuration: [FastDebug, Release]
name: Linux
runs-on: ubuntu-latest
container: ghcr.io/scp-fs2open/linux_build:sha-5ac6f70
container: ghcr.io/scp-fs2open/linux_build:sha-1ff82e8
steps:
- uses: actions/checkout@v1
name: Checkout
@@ -30,7 +30,7 @@ jobs:
- name: Configure CMake
env:
CONFIGURATION: ${{ matrix.configuration }}
COMPILER: gcc-5
COMPILER: gcc-9
ENABLE_QTFRED: OFF
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
- name: Compile
@@ -282,6 +282,18 @@ jobs:
ARCH: ${{ matrix.arch }}
XDG_RUNTIME_DIR: /root
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
- name: Sign builds
working-directory: ./build/bin
run: |
# make sure OpenAL framework is signed
find . -name OpenAL -type f | while read xx; do codesign --force -s - $xx; done
find . -name OpenAL.framework -type d | while read xx; do codesign --force -s - $xx; done
# make sure dylibs are signed
find . -name \*.dylib -type f | while read xx; do codesign --force -s - $xx; done
# sign binaries
find . -perm +111 -type f | grep MacOS | while read xx; do codesign --force -s - $xx; done
# sign apps
find . -name \*.app -maxdepth 1 | while read xx; do codesign --force -s - $xx; done
- name: Package build result
working-directory: ./build/bin
# Use GNU tar here (part of runner image) to avoid weird corruption bug with bsdtar
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
configuration: [Debug, Release]
compiler: [gcc-5, gcc-13, clang-16]
compiler: [gcc-9, gcc-13, clang-16]
cmake_options: [""]
include:
# Also include configurations that check if the code compiles without the graphics backends
@@ -25,7 +25,7 @@ jobs:
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
name: Linux
runs-on: ubuntu-latest
container: ghcr.io/scp-fs2open/linux_build:sha-5ac6f70
container: ghcr.io/scp-fs2open/linux_build:sha-1ff82e8
steps:
- uses: actions/checkout@v1
name: Checkout
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
configuration: [Debug, Release]
compiler: [gcc-5, gcc-13, clang-16]
compiler: [gcc-9, gcc-13, clang-16]
cmake_options: [""]
include:
# Also include configurations that check if the code compiles without the graphics backends
@@ -22,7 +22,7 @@ jobs:
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
name: Linux
runs-on: ubuntu-latest
container: ghcr.io/scp-fs2open/linux_build:sha-5ac6f70
container: ghcr.io/scp-fs2open/linux_build:sha-1ff82e8
steps:
- uses: actions/checkout@v1
name: Checkout
+6 -20
View File
@@ -2,13 +2,13 @@ name: Weekly Coverity Scan for Freespace Open
# adapted from code by "albert-github" on the doxygen repository
# many thanks for figuring this out first so it could be less painful for us!
# This mode is for testing
#This mode is for testing
#on: [push, pull_request]
# In production, push on a specific schedule
on:
schedule:
- cron: "1 0 * * 5" # Run once per a week on Friday morning (midnight UTC, 4 AM EST), to avoid Coverity's submission limits
- cron: "5 0 * * 5" # Run once per a week on Friday morning (midnight UTC, 4 AM EST), to avoid Coverity's submission limits
env:
QT_VERSION: 5.12.12
@@ -19,23 +19,16 @@ jobs:
build:
name: Build FSO With Coverity Wrapper
runs-on: ${{ matrix.config.os }}
container: ghcr.io/scp-fs2open/linux_build:sha-1ff82e8
strategy:
fail-fast: false
matrix:
config:
- { name: "GCC on ubuntu", os: ubuntu-latest, build_type: "Release" }
steps:
- name: Install Dependencies
run: |
sudo apt-get install ninja-build
sudo apt install cmake libsdl2-dev g++
sudo apt-get install libopenal-dev
sudo apt-get install -y libudev-dev
sudo apt-get install doxygen
sudo apt install graphviz
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v1
with:
submodules: recursive # `true` to checkout submodules, `recursive` to recursively checkout submodules
@@ -51,17 +44,10 @@ jobs:
echo "$(pwd)/cov-scan/bin" >> $GITHUB_PATH
echo "NPROC=$(getconf _NPROCESSORS_ONLN)" >> $GITHUB_ENV
- name: Prepare Vulkan SDK
uses: humbletim/[email protected]
with:
vulkan-query-version: 1.3.204.0
vulkan-components: Vulkan-Headers, Vulkan-Loader
vulkan-use-cache: true
- name: Configure CMake for FSO
env:
CONFIGURATION: Release
compiler: gcc-5
compiler: gcc-9
ENABLE_QTFRED: OFF
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
+3 -3
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
if [ "$COMPILER" = "gcc-5" ]; then
export CC=gcc-5
export CXX=g++-5
if [ "$COMPILER" = "gcc-9" ]; then
export CC=gcc-9
export CXX=g++-9
fi
if [ "$COMPILER" = "gcc-13" ]; then
export CC=gcc-13
+5 -5
View File
@@ -7,15 +7,15 @@ PLACEHOLDER
[b]Deprecations:[/b]
PLACEHOLDER
Deprecations are a mechanism in FSO where a certain feature or aspect of the engine has changed or is no longer supported. Since this would normally break existing mods we have the mod table feature "[tt]$Target Version:[/tt]" with which a mod can specify what version of FSO it was developed with. The features listed above will be removed or changed when the target version of a mod is at least the version released in this post.
Deprecations are a mechanism in FSO where a certain feature or aspect of the engine has changed or is no longer supported. Since this would normally break existing mods, we have the mod table feature "[tt]$Target Version:[/tt]" with which a mod can specify what version of FSO it was developed with. Any features listed under Deprecations will be removed or changed when the target version of a mod is at least the version released in this post.
[size=5pt]Previous [url=https://www.hard-light.net/forums/index.php?topic=98682.0]23.0.0 Release Thread[/url][/size]
[size=5pt]Previous [url=https://www.hard-light.net/forums/index.php?topic=99238.0]24.0.1 Release Thread[/url][/size]
Launchers, if you don't have one already:
[b]All platforms: [/b] For every day use, we recommend [url=https://www.hard-light.net/forums/index.php?topic=94068.0]Knossos[/url], an integrated solution for downloading and launching mods.
[b]All platforms: [/b] For every day use, we recommend [url=https://knossosnet.github.io/Knossos-Release-Page/]Knossos.NET[/url], an integrated solution for downloading and launching mods.
[hidden=Alternative Launchers]
Cross-platform: [url=https://www.hard-light.net/forums/index.php?topic=89162]wxLauncher 0.12.x Test Build[/url] (ongoing project for a unified launcher, you should upgrade to the latest RC/test build if you have not yet)
Cross-platform: [url=https://www.hard-light.net/forums/index.php?topic=89162]wxLauncher 0.12.x Test Build[/url] (legacy project for a unified launcher)
[b]Important:[/b] For best compatibility with FSO 3.8 and later you should use at least wxLauncher 0.12.
Windows: [url=http://scp.fsmods.net/builds/Launcher55g.zip]Launcher 5.5g[/url] ([url=https://scp.indiegames.us/builds/Launcher55g.zip]Mirror[/url]) ([url=https://www.mediafire.com/?wdvzn7hhhzh418m]Mirror[/url]) Not compatible with Windows 8+, use wxLauncher above
@@ -41,7 +41,7 @@ Linux: [url=https://www.hard-light.net/forums/index.php/topic,53206.0.html]YAL[
[b]What are those SSE, SSE2 and AVX builds I keep seeing everywhere?[/b]
[url=https://www.hard-light.net/forums/index.php?topic=65628.0]Your answer is in this topic.[/url]
Don't want to deal with that? Use [url=https://www.hard-light.net/forums/index.php?topic=94068.0]Knossos[/url] and it will download the best build specifically for your PC!
Don't want to deal with that? Use [url=https://knossosnet.github.io/Knossos-Release-Page/]Knossos.NET[/url] and it will download the best build specifically for your PC!
[/hidden]
[img]https://scp.indiegames.us/img/linux-icon.png[/img] [color=green][size=12pt]Linux 64-bit[/size][/color]
+2 -2
View File
@@ -597,8 +597,8 @@ extern void set_predicted_enemy_pos(vec3d *predicted_enemy_pos, object *pobjp, v
extern int is_instructor(object *objp);
extern int find_enemy(int objnum, float range, int max_attackers, int ship_info_index = -1);
float ai_get_weapon_speed(ship_weapon *swp);
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, vec3d *gun_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range);
float ai_get_weapon_speed(const ship_weapon *swp);
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, const vec3d *gun_pos, const object *pobjp, const vec3d *enemy_pos, const vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range);
// function to change rearm status for ai ships (called from sexpression code)
extern void ai_set_rearm_status( int team, int new_status );
+6 -6
View File
@@ -76,7 +76,7 @@ extern void big_ship_collide_recover_start(object *objp, object *big_objp, vec3d
// If truly_random flag set (haha), then generate a pretty random number. Otherwise, generate a static rand which
// tends to not change from frame to frame.
// Try four times and choose nearest point to increase chance of getting a good point.
void ai_bpap(object *objp, vec3d *attacker_objp_pos, vec3d *attacker_objp_fvec, vec3d *attack_point, vec3d *local_attack_point, float fov, float weapon_travel_dist, vec3d *surface_normal, ship_subsys *ss)
void ai_bpap(const object *objp, const vec3d *attacker_objp_pos, const vec3d *attacker_objp_fvec, vec3d *attack_point, vec3d *local_attack_point, float fov, float weapon_travel_dist, vec3d *surface_normal, const ship_subsys *ss)
{
float nearest_dist;
vec3d result_point, best_point;
@@ -193,7 +193,7 @@ done_1:
//
// Note: Default value for fov is 1.0f 1.0f means don't use fov parameter.
// If fov != 1.0f, try up to four times to find a point that's in the field of view.
void ai_big_pick_attack_point_turret(object *objp, ship_subsys *ssp, vec3d *gpos, vec3d *gvec, vec3d *attack_point, float fov, float weapon_travel_dist)
void ai_big_pick_attack_point_turret(const object *objp, ship_subsys *ssp, const vec3d *gpos, const vec3d *gvec, vec3d *attack_point, float fov, float weapon_travel_dist)
{
if (!timestamp_elapsed(ssp->turret_pick_big_attack_point_timestamp)) {
vec3d result_point;
@@ -213,7 +213,7 @@ void ai_big_pick_attack_point_turret(object *objp, ship_subsys *ssp, vec3d *gpos
// Note: Default value for fov is 1.0f 1.0f means don't use fov parameter.
// If fov != 1.0f, try up to four times to find a point that's in the field of view.
// Note, attacker_objp can be a ship or a weapon.
void ai_big_pick_attack_point(object *objp, object *attacker_objp, vec3d *attack_point, float fov)
void ai_big_pick_attack_point(const object *objp, const object *attacker_objp, vec3d *attack_point, float fov)
{
Assert(objp->instance > -1);
Assert(objp->type == OBJ_SHIP);
@@ -411,7 +411,7 @@ int ai_big_maybe_follow_subsys_path(int do_dot_check)
}
}
if ( ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, 1) ) {
if ( ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, true) ) {
subsys_in_sight = 1;
}
}
@@ -541,7 +541,7 @@ bool ai_new_maybe_reposition_attack_subsys() {
get_subsystem_pos(&gsubpos, target_objp, aip->targeted_subsys);
// you're in sight! shoot it!
if (ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, 0))
if (ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, false))
return false;
// not in sight, gotta get there
@@ -1034,7 +1034,7 @@ void ai_big_chase()
ai_big_pick_attack_point(En_objp, Pl_objp, &enemy_pos, 0.8f);
weapon_info* wip = ai_get_weapon(&shipp->weapons);
bool ballistic_aiming = !IS_VEC_NULL(&The_mission.gravity) && wip->gravity_const != 0.0f;
bool ballistic_aiming = !IS_VEC_NULL(&The_mission.gravity) && wip && wip->gravity_const != 0.0f;
bool valid_ballistic_trajectory = true;
if (!ballistic_aiming) {
+2 -2
View File
@@ -24,8 +24,8 @@ void ai_big_subsys_path_cleanup(ai_info *aip);
void ai_big_strafe();
int ai_big_maybe_enter_strafe_mode(const object *objp, int weapon_objnum);
void ai_big_strafe_maybe_attack_turret(const object *ship_objp, const object *weapon_objp);
void ai_big_pick_attack_point(object *objp, object *attacker_objp, vec3d *attack_point, float fov=1.0f);
void ai_big_pick_attack_point_turret(object *objp, ship_subsys *ssp, vec3d *gpos, vec3d *gvec, vec3d *attack_point, float weapon_travel_dist, float fov=1.0f);
void ai_big_pick_attack_point(const object *objp, const object *attacker_objp, vec3d *attack_point, float fov=1.0f);
void ai_big_pick_attack_point_turret(const object *objp, ship_subsys *ssp, const vec3d *gpos, const vec3d *gvec, vec3d *attack_point, float weapon_travel_dist, float fov=1.0f);
// default distance for following subsystem path points --wookieejedi
// the value is 5 because that was the original value specified in ai_big_maybe_follow_subsys_path()
+22 -24
View File
@@ -1021,7 +1021,7 @@ void ai_level_init()
/**
* Check if object is a stealth ship
*/
bool is_object_stealth_ship(object* objp)
bool is_object_stealth_ship(const object *objp)
{
if (objp->type == OBJ_SHIP) {
if (Ships[objp->instance].flags[Ship::Ship_Flags::Stealth]) {
@@ -1109,7 +1109,7 @@ float get_skill_stealth_dot_scaler()
return 1.0f;
}
int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
int ai_is_stealth_visible(const object *viewer_objp, const object *stealth_objp)
{
ship *shipp;
vec3d vec_to_stealth;
@@ -1905,7 +1905,7 @@ int Player_attacking_enabled = 1;
/**
* Determine whether an object is targetable within a nebula
*/
int object_is_targetable(object *target, ship *viewer)
bool object_is_targetable(const object *target, const ship *viewer)
{
// if target is ship, check if visible by team
if (target->type == OBJ_SHIP)
@@ -2413,7 +2413,7 @@ int find_nearby_threat(int objnum, int enemy_team_mask, float range, int *count)
/**
* Number of live turrets with target_objnum
*/
int num_turrets_attacking(object *turret_parent, int target_objnum)
int num_turrets_attacking(const object *turret_parent, int target_objnum)
{
ship_subsys *ss;
ship *shipp;
@@ -3636,7 +3636,7 @@ int Debug_k = 0;
*
* Return this value. Return value of 0.0f means no collision is possible.
*/
float compute_collision_time(vec3d *targpos, vec3d *targvel, vec3d *attackpos, float weapon_speed)
float compute_collision_time(const vec3d *targpos, const vec3d *targvel, const vec3d *attackpos, float weapon_speed)
{
vec3d vec_to_target;
float pos_dot_vel;
@@ -6305,7 +6305,7 @@ int ai_fire_primary_weapon(object *objp)
// threshold => max distance from pos to be considered "near"
//
// exit: number of ships within threshold units of pos
int num_nearby_fighters(int enemy_team_mask, vec3d *pos, float threshold)
int num_nearby_fighters(int enemy_team_mask, const vec3d *pos, float threshold)
{
ship_obj *so;
object *ship_objp;
@@ -6476,7 +6476,7 @@ bool ai_select_secondary_weapon(object *objp, ship_weapon *swp, flagset<Weapon::
/**
* @return number of objects homing on object *target_objp
*/
int compute_num_homing_objects(object *target_objp)
int compute_num_homing_objects(const object *target_objp)
{
int count = 0;
@@ -6558,7 +6558,7 @@ float compute_incoming_payload(object *target_objp)
// OR: secondary los flag is set but no line of sight is availabe
// Note: If player is attacking a ship, that ship is allowed to fire at player. Otherwise, we get in a situation in which
// player is attacking a large ship, but that large ship is not defending itself with missiles.
int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int secondary_bank, ship_subsys* turret)
bool check_ok_to_fire(int objnum, int target_objnum, const weapon_info *wip, int secondary_bank, const vec3d *firing_pos_global)
{
int num_homers = 0;
@@ -6574,8 +6574,8 @@ int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int second
if (The_mission.ai_profile->flags[AI::Profile_Flags::Require_exact_los] || wip->wi_flags[Weapon::Info_Flags::Require_exact_los]) {
//Check if th AI has Line of Sight and is allowed to fire
if (!check_los(objnum, target_objnum, 10.0f, -1, secondary_bank, turret)) {
return 0;
if (!check_los(objnum, target_objnum, 10.0f, -1, secondary_bank, firing_pos_global)) {
return false;
}
}
@@ -6589,14 +6589,14 @@ int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int second
// At Easy, 2/7...at Expert, 5/7
int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
if (t > Ai_info[Ships[tobjp->instance].ai_index].ai_chance_to_use_missiles_on_plr) {
return 0;
return false;
}
}
int swarmers = 0;
if (wip->wi_flags[Weapon::Info_Flags::Swarm])
swarmers = 2; // Note, always want to be able to fire swarmers if no currently incident homers.
if (The_mission.ai_profile->max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
return 0;
return false;
}
} else if (num_homers > 3) {
float incoming_payload;
@@ -6604,22 +6604,22 @@ int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int second
incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
if (incoming_payload > tobjp->hull_strength) {
return 0;
return false;
}
}
}
}
return 1;
return true;
}
// --------------------------------------------------------------------------
// Returns true if *aip has a line of sight to its current target.
// threshold defines the minimum radius for an object to be considered relevant for LoS
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, ship_subsys* turret) {
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, const vec3d *firing_pos_global) {
//Do both checks over an XOR-Check for more detailed messages
int sources = (primary_bank != -1 ? 1 : 0) + (secondary_bank != -1 ? 1 : 0) + (turret != nullptr ? 1 : 0);
int sources = (primary_bank != -1 ? 1 : 0) + (secondary_bank != -1 ? 1 : 0) + (firing_pos_global != nullptr ? 1 : 0);
Assertion(sources >= 1, "Cannot check line of sight from a model with neither a primar bank, nor secondary bank nor a turret set as a source.");
Assertion(sources <= 1, "Cannot check line of sight from a model with more than one source set.");
@@ -6627,10 +6627,8 @@ bool check_los(int objnum, int target_objnum, float threshold, int primary_bank,
object* firing_ship = &Objects[objnum];
if (turret != nullptr) {
vec3d turret_fvec;
//It doesn't like not having an fvec output vector, so give it a dummy vector
ship_get_global_turret_gun_info(firing_ship, turret, &start, &turret_fvec, 1, nullptr);
if (firing_pos_global != nullptr) {
start = *firing_pos_global;
} else {
bool is_primary = secondary_bank == -1;
ship *shipp = &Ships[firing_ship->instance];
@@ -6942,7 +6940,7 @@ float ai_get_weapon_dist(ship_weapon *swp)
return MIN((Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime), Weapon_info[weapon_num].weapon_range);
}
float ai_get_weapon_speed(ship_weapon *swp)
float ai_get_weapon_speed(const ship_weapon *swp)
{
int bank_num, weapon_num;
@@ -6986,7 +6984,7 @@ weapon_info* ai_get_weapon(ship_weapon *swp)
// Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
// *pobjp object firing the weapon
// *eobjp object being fired upon
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, vec3d *gun_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range)
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, const vec3d *gun_pos, const object *pobjp, const vec3d *enemy_pos, const vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range)
{
ship *shipp = &Ships[pobjp->instance];
float range_time;
@@ -10243,7 +10241,7 @@ void guard_object_was_hit(object *guard_objp, object *hitter_objp)
}
// don't attack if you can't see him
if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], true) < 1.0f ) {
// if he's a stealth and visible, but not targetable, ok to attack.
if ( is_object_stealth_ship(hitter_objp) ) {
if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_IN_FRUSTUM ) {
@@ -15812,7 +15810,7 @@ void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
}
// only set as target if can be targeted.
if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], true) < 1.0f) {
return;
}
+7 -7
View File
@@ -14,24 +14,24 @@
#include "weapon/weapon.h"
//Returns true if the specified object is a stealth ship, false if not
bool is_object_stealth_ship(object* objp);
bool is_object_stealth_ship(const object* objp);
//Number of live turrets on the object attacking
int num_turrets_attacking(object *turret_parent, int target_objnum);
int num_turrets_attacking(const object *turret_parent, int target_objnum);
//Determine whether an object is targetable within a nebula (checks for stealth)
int object_is_targetable(object *target, ship *viewer);
bool object_is_targetable(const object *target, const ship *viewer);
//Returns the number of enemy fighters within threshold of pos.
int num_nearby_fighters(int enemy_team_mask, vec3d *pos, float threshold);
int num_nearby_fighters(int enemy_team_mask, const vec3d *pos, float threshold);
//Returns true if OK for *aip to fire its current weapon at its current target.
int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int secondary_bank, ship_subsys* turret);
bool check_ok_to_fire(int objnum, int target_objnum, const weapon_info *wip, int secondary_bank, const vec3d *firing_pos_global);
//Returns true if *aip has a line of sight to its current target.
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, ship_subsys* turret);
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, const vec3d *firing_pos_global);
//Does all the stuff needed to aim and fire a turret.
void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss);
void ai_turret_execute_behavior(const ship *shipp, ship_subsys *ss);
#endif
+142 -192
View File
@@ -118,7 +118,7 @@ void turret_instance_find_world_orient(matrix* out_mat, int model_instance_num,
* @param tpos Turret initial position
* @param dist Distance from turret to center point of object
*
* @return 1 if objp is in fov of the specified turret. Otherwise return 0.
* @return true if objp is in fov of the specified turret. Otherwise return false.
*/
bool object_in_turret_fov(const object *objp, const ship_subsys *ss, const vec3d *tvec, const vec3d *tpos, float dist)
{
@@ -175,9 +175,9 @@ bool object_in_turret_fov(const object *objp, const ship_subsys *ss, const vec3d
* @param bomb_objp Bomb specified
* @param ship_objp Ship specified
*
* @return 1 if bomb_objp is headed towards ship_objp
* @return true if bomb_objp is headed towards ship_objp
*/
int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
bool bomb_headed_towards_ship(const object *bomb_objp, const object *ship_objp)
{
float dot;
vec3d bomb_to_ship_vector;
@@ -185,11 +185,7 @@ int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
dot = vm_vec_dot(&bomb_objp->orient.vec.fvec, &bomb_to_ship_vector);
if ( dot > 0 ) {
return 1;
}
return 0;
return ( dot > 0 );
}
// Set active weapon for turret
@@ -201,7 +197,7 @@ int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
* @note All non-negative return values are expressed in what I like to call "widx"s.
* @return -1 if unable to find a weapon for the target at all.
*/
int turret_select_best_weapon(ship_subsys *turret, object * /*target*/)
int turret_select_best_weapon(const ship_subsys *turret, const object * /*target*/)
{
//TODO: Fill this out with extraodinary gun-picking algorithms
if(turret->weapons.num_primary_banks > 0)
@@ -335,7 +331,7 @@ bool turret_weapon_has_subtype(const ship_weapon *swp, int subtype)
*
* @return a pointer to the Weapon_info for weapon_num
*/
weapon_info *get_turret_weapon_wip(ship_weapon *swp, int weapon_num)
const weapon_info *get_turret_weapon_wip(const ship_weapon *swp, int weapon_num)
{
Assert(weapon_num < MAX_SHIP_WEAPONS);
Assert(weapon_num >= 0);
@@ -352,7 +348,7 @@ weapon_info *get_turret_weapon_wip(ship_weapon *swp, int weapon_num)
return &Weapon_info[wi_index];
}
int get_turret_weapon_next_fire_stamp(ship_weapon *swp, int weapon_num)
int get_turret_weapon_next_fire_stamp(const ship_weapon *swp, int weapon_num)
{
Assert(weapon_num < MAX_SHIP_WEAPONS);
Assert(weapon_num >= 0);
@@ -379,7 +375,7 @@ void set_turret_weapon_next_fire_stamp(ship_weapon *swp, int weapon_num, int del
*
* @todo This function is kinda slow
*/
float longest_turret_weapon_range(ship_weapon *swp)
float longest_turret_weapon_range(const ship_weapon *swp)
{
float longest_range_so_far = 0.0f;
float weapon_range;
@@ -770,10 +766,10 @@ void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
* @param laser_flag
* @param missile_flag
*/
int get_nearest_turret_objnum(int turret_parent_objnum, ship_subsys *turret_subsys, int enemy_team_mask, vec3d *tpos, vec3d *tvec, int current_enemy, bool big_only_flag, bool small_only_flag, bool tagged_only_flag, bool beam_flag, bool flak_flag, bool laser_flag, bool missile_flag)
int get_nearest_turret_objnum(int turret_parent_objnum, const ship_subsys *turret_subsys, int enemy_team_mask, const vec3d *tpos, const vec3d *tvec, int current_enemy, bool big_only_flag, bool small_only_flag, bool tagged_only_flag, bool beam_flag, bool flak_flag, bool laser_flag, bool missile_flag)
{
eval_enemy_obj_struct eeo;
ship_weapon *swp = &turret_subsys->weapons;
auto swp = &turret_subsys->weapons;
// list of stuff to go thru
ship_obj *so;
@@ -1067,7 +1063,7 @@ DCF_BOOL(use_parent_target, Use_parent_target)
* @param tvec Forward vector of turret (world coords)
* @param current_enemy Objnum of current turret target
*/
int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d *tvec, int current_enemy)
int find_turret_enemy(const ship_subsys *turret_subsys, int objnum, const vec3d *tpos, const vec3d *tvec, int current_enemy)
{
int enemy_team_mask, enemy_objnum;
ship_info *sip;
@@ -1188,8 +1184,6 @@ void ship_get_global_turret_info(const object *objp, const model_subsystem *tp,
model_instance_local_to_global_dir(gvec, &tp->turret_norm, model_instance_num, tp->subobj_num, &objp->orient, true);
}
void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss);
/**
* Given an object and a turret on that object, return the actual firing point of the gun and its normal.
*
@@ -1200,86 +1194,44 @@ void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss);
* @param objp Pointer to object
* @param ssp Pointer to turret subsystem
* @param gpos Absolute position of gun firing point
* @param avg_origin Use virtual gun pos corresponding to the average of the firing points
* @param gvec Vector from *gpos to *targetp
* @param use_angles Use current angles
* @param targetp Pointer to target object
* @param targetp Absolute position of target object
*/
void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vec3d *gpos, vec3d *gvec, int use_angles, vec3d *targetp)
void ship_get_global_turret_gun_info(const object *objp, const ship_subsys *ssp, vec3d *gpos, bool avg_origin, vec3d *gvec, bool use_angles, const vec3d *targetp)
{
vec3d * gun_pos;
vec3d *gun_pos;
model_subsystem *tp = ssp->system_info;
polymodel_instance *pmi = model_get_instance(Ships[objp->instance].model_instance_num);
polymodel *pm = model_get(pmi->model_num);
gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
vec3d avg_gun_pos;
if (avg_origin) {
vm_vec_avg_n(&avg_gun_pos, tp->turret_num_firing_points, tp->turret_firing_point);
gun_pos = &avg_gun_pos;
} else {
gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
}
model_instance_local_to_global_point(gpos, gun_pos, pm, pmi, tp->turret_gun_sobj, &objp->orient, &objp->pos);
// we might not need to calculate this
if (!gvec)
return;
if (use_angles) {
model_instance_local_to_global_dir(gvec, &tp->turret_norm, pm, pmi, tp->turret_gun_sobj, &objp->orient);
} else if (tp->flags[Model::Subsystem_Flags::Share_fire_direction]) {
vec3d avg, tmp_pos, tmp_target, enemy_point, turret_norm;
vm_vec_avg_n(&avg, tp->turret_num_firing_points, tp->turret_firing_point);
model_instance_local_to_global_point(&tmp_pos, &avg, pm, pmi, tp->turret_gun_sobj, &objp->orient, &objp->pos);
if (targetp == nullptr) {
ship* shipp = &Ships[Objects[ssp->parent_objnum].instance];
Assertion(ssp->turret_enemy_objnum >= 0, "The turret enemy object number %d for %s on ship name %s is invalid.", ssp->turret_enemy_objnum, ssp->sub_name, shipp->ship_name);
object *lep = &Objects[ssp->turret_enemy_objnum];
int best_weapon_tidx = turret_select_best_weapon(ssp, lep);
//This turret doesn't have any good weapons
if (best_weapon_tidx < 0)
return;
weapon_info *wip = get_turret_weapon_wip(&ssp->weapons, best_weapon_tidx);
float weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
turret_ai_update_aim(&Ai_info[shipp->ai_index], &Objects[ssp->turret_enemy_objnum], ssp);
if ((ssp->targeted_subsys != nullptr) && !(ssp->flags[Ship::Subsystem_Flags::No_SS_targeting])) {
vm_vec_unrotate(&enemy_point, &ssp->targeted_subsys->system_info->pnt, &Objects[ssp->turret_enemy_objnum].orient);
vm_vec_add2(&enemy_point, &ssp->last_aim_enemy_pos);
} else {
if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].is_big_or_huge())) {
// the turret norm here is from the perspective of the base submodel, not the gun submodel
model_instance_local_to_global_dir(&turret_norm, &tp->turret_norm, pm, pmi, pm->submodel[tp->subobj_num].parent, &objp->orient);
ai_big_pick_attack_point_turret(lep, ssp, &tmp_pos, &turret_norm, &enemy_point, MIN(wip->max_speed * wip->lifetime, wip->weapon_range), tp->turret_fov);
}
else {
enemy_point = ssp->last_aim_enemy_pos;
}
}
vec3d target_moving_direction = ssp->last_aim_enemy_vel;
//Try to guess where the enemy will be, and store that spot in predicted_enemy_pos
if (The_mission.ai_profile->flags[AI::Profile_Flags::Use_additive_weapon_velocity]) {
vm_vec_scale_sub2(&target_moving_direction, &objp->phys_info.vel, wip->vel_inherit_amount);
}
set_predicted_enemy_pos_turret(&tmp_target, &tmp_pos, objp, &enemy_point, &target_moving_direction, wip->max_speed, ssp->turret_time_enemy_in_range * (weapon_system_strength + 1.0f) / 2.0f);
} else {
tmp_target = *targetp;
}
vm_vec_normalized_dir(gvec, &tmp_target, &tmp_pos);
} else {
//vector gun_pos2;
//vm_vec_add(&gun_pos2, gpos, gun_pos);
Assertion(targetp != nullptr, "The targetp parameter must not be null here!");
vm_vec_normalized_dir(gvec, targetp, gpos);
}
// per firingpoint based changes go here for turrets
}
/**
* Update turret aiming data based on max turret aim update delay
*/
void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss)
void turret_ai_update_aim(const ai_info *aip, const object *En_Objp, ship_subsys *ss)
{
if (Missiontime >= ss->next_aim_pos_time)
{
@@ -1294,26 +1246,15 @@ void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss)
}
}
/**
* Rotate a turret towards an enemy.
*
* Some obscure model thing only John Slagel knows about.
* Sets predicted enemy position.
* Sets predicted enemy position. Previously done at the beginning of aifft_rotate_turret.
* If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
*
* @return TRUE if caller should use angles in subsequent rotations.
*/
int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep, vec3d *predicted_enemy_pos, vec3d *gvec)
void aifft_update_predicted_enemy_pos(const object *objp, const ship *shipp, ship_subsys *ss, const vec3d *global_gun_pos, const vec3d *global_gun_vec, const object *lep, vec3d *predicted_enemy_pos)
{
int ret_val __UNUSED = 0; // to be used in future, see comment @ end of function
auto pmi = model_get_instance(shipp->model_instance_num);
auto pm = model_get(pmi->model_num);
if (ss->turret_enemy_objnum != -1) {
model_subsystem *tp = ss->system_info;
vec3d gun_pos, gun_vec, target_vel;
//float weapon_speed;
vec3d target_vel;
float weapon_system_strength;
//HACK HACK HACK -WMC
//This should use the best weapon variable
@@ -1322,15 +1263,13 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
//This turret doesn't have any good weapons
if (best_weapon_tidx < 0)
return 0;
return;
weapon_info *wip = get_turret_weapon_wip(&ss->weapons, best_weapon_tidx);
auto wip = get_turret_weapon_wip(&ss->weapons, best_weapon_tidx);
// weapon_system_strength scales time enemy in range in 0..1. So, the lower this is, the worse the aiming will be.
weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
ship_get_global_turret_info(objp, tp, &gun_pos, &gun_vec);
//Update "known" position and velocity of target. Only matters if max_aim_update_delay is set.
turret_ai_update_aim(&Ai_info[shipp->ai_index], &Objects[ss->turret_enemy_objnum], ss);
@@ -1344,7 +1283,7 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
}
} else {
if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].is_big_or_huge())) {
ai_big_pick_attack_point_turret(lep, ss, &gun_pos, &gun_vec, &enemy_point, tp->turret_fov, MIN(wip->max_speed * wip->lifetime, wip->weapon_range));
ai_big_pick_attack_point_turret(lep, ss, global_gun_pos, global_gun_vec, &enemy_point, tp->turret_fov, MIN(wip->max_speed * wip->lifetime, wip->weapon_range));
} else {
enemy_point = ss->last_aim_enemy_pos;
}
@@ -1357,25 +1296,13 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
vm_vec_scale_sub2(&target_vel, &objp->phys_info.vel, wip->vel_inherit_amount);
}
if (tp->flags[Model::Subsystem_Flags::Turret_distant_firepoint] || Always_use_distant_firepoints) {
//The firing point of this turret is so far away from the its center that we should consider this for firing calculations
//This will do the enemy position prediction based on their relative position and speed to the firing point, not the turret center.
vec3d temp;
ship_get_global_turret_gun_info(objp, ss, &gun_pos, &temp, 1, nullptr);
}
if (IS_VEC_NULL(&The_mission.gravity) || wip->gravity_const == 0.0f)
set_predicted_enemy_pos_turret(predicted_enemy_pos, &gun_pos, objp, &enemy_point, &target_vel, wip->max_speed, ss->turret_time_enemy_in_range * (weapon_system_strength + 1.0f)/2.0f);
set_predicted_enemy_pos_turret(predicted_enemy_pos, global_gun_pos, objp, &enemy_point, &target_vel, wip->max_speed, ss->turret_time_enemy_in_range * (weapon_system_strength + 1.0f)/2.0f);
else {
vec3d shoot_vec;
vec3d gravity_vec = The_mission.gravity * wip->gravity_const;
if (physics_lead_ballistic_trajectory(&gun_pos, &enemy_point, &target_vel, wip->max_speed, &gravity_vec, &shoot_vec)) {
*predicted_enemy_pos = gun_pos + shoot_vec * 1000.0f;
} else {
if ((ss->system_info->flags[Model::Subsystem_Flags::Turret_reset_idle]) && (timestamp_elapsed(ss->rotation_timestamp))) {
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
}
return ret_val;
if (physics_lead_ballistic_trajectory(global_gun_pos, &enemy_point, &target_vel, wip->max_speed, &gravity_vec, &shoot_vec)) {
*predicted_enemy_pos = *global_gun_pos + shoot_vec * 1000.0f;
}
}
@@ -1387,36 +1314,54 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
// Add to predicted_enemy_pos value in .45 to 1.5x radius of enemy ship, so will often miss, but not by a huge amount.
vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
}
}
}
/**
* Rotate a turret towards an enemy.
*
* Some obscure model thing only John Slagel knows about.
*
* @return TRUE if caller should use angles in subsequent rotations.
*/
bool aifft_rotate_turret(const object *objp, const ship *shipp, ship_subsys *ss, const vec3d *global_gun_pos, const vec3d *global_gun_vec, const vec3d *predicted_enemy_pos)
{
bool ret_val __UNUSED = false; // to be used in future, see comment @ end of function
auto pmi = model_get_instance(shipp->model_instance_num);
auto pm = model_get(pmi->model_num);
if (ss->turret_enemy_objnum != -1) {
model_subsystem *tp = ss->system_info;
//Get the normalized dir between the turret and the predicted enemy position.
//If the dot product is smaller than or equal to the turret's FOV, try and point the gun at it.
vec3d v2e;
vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
vm_vec_normalized_dir(&v2e, predicted_enemy_pos, global_gun_pos);
bool in_fov;
in_fov = turret_fov_test(ss, gvec, &v2e);
in_fov = turret_fov_test(ss, global_gun_vec, &v2e);
if (in_fov) {
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos);
} else if ((tp->flags[Model::Subsystem_Flags::Turret_reset_idle]) &&(timestamp_elapsed(ss->rotation_timestamp))) {
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
ret_val = model_rotate_gun(objp, pm, pmi, ss, nullptr);
}
} else if ((ss->system_info->flags[Model::Subsystem_Flags::Turret_reset_idle]) && (timestamp_elapsed(ss->rotation_timestamp))) {
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
ret_val = model_rotate_gun(objp, pm, pmi, ss, nullptr);
}
// by default "ret_val" should be set to 1 for multi-part turrets, and 0 for single-part turrets
// but we need to keep retail behavior by default, which means always returning 0 unless a special
// by default "ret_val" should be set to true for multi-part turrets, and false for single-part turrets
// but we need to keep retail behavior by default, which means always returning false unless a special
// flag is used. the "ret_val" stuff is here is needed/wanted at a later date however.
//return ret_val;
// return 0 by default (to preserve retail behavior) but allow for a per-subsystem option
// return false by default (to preserve retail behavior) but allow for a per-subsystem option
// for using the turret normals for firing
if (ss->system_info->flags[Model::Subsystem_Flags::Fire_on_normal])
return 1;
return true;
return 0;
return false;
}
/**
@@ -1424,7 +1369,7 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
*
* @return Dot product of vector from point abs_gunposp to *enemy_subsysp, if hittable
*/
float aifft_compute_turret_dot(object *objp, object *enemy_objp, vec3d *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
float aifft_compute_turret_dot(const object *objp, const object *enemy_objp, const vec3d *abs_gunposp, const ship_subsys *turret_subsysp, const ship_subsys *enemy_subsysp)
{
float dot_out;
vec3d subobj_pos, vector_out;
@@ -1432,7 +1377,7 @@ float aifft_compute_turret_dot(object *objp, object *enemy_objp, vec3d *abs_gunp
vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
vm_vec_add2(&subobj_pos, &enemy_objp->pos);
if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, true, &dot_out, &vector_out)) {
vec3d turret_norm;
model_instance_local_to_global_dir(&turret_norm, &turret_subsysp->system_info->turret_norm, Ships[objp->instance].model_instance_num, turret_subsysp->system_info->subobj_num, &objp->orient, true);
@@ -1486,7 +1431,7 @@ DCF(mf, "Adjusts the maximum number of tries an AI may do when trying to pick a
*
* @return Dot product from turret to subsystem in *dot_out
*/
ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
ship_subsys *aifft_find_turret_subsys(const object *objp, const ship_subsys *ssp, const vec3d *global_gun_pos, const object *enemy_objp, float *dot_out)
{
ship *eshipp, *shipp;
ship_info *esip;
@@ -1503,10 +1448,6 @@ ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *en
float best_dot = 0.0f;
*dot_out = best_dot;
// Compute absolute gun position.
vec3d abs_gun_pos;
ship_get_global_turret_info(objp, ssp->system_info, &abs_gun_pos, nullptr);
// Only pick a turret to attack on large ships.
if (!esip->is_big_or_huge())
return best_subsysp;
@@ -1572,7 +1513,7 @@ ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *en
}
for(idx=offset; idx<aifft_list_size; idx+=stride){
dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);
dot = aifft_compute_turret_dot(objp, enemy_objp, global_gun_pos, ssp, aifft_list[idx]);
if ((dot - dot_fov_modifier)* aifft_rank[idx] > best_dot) {
best_dot = (dot - dot_fov_modifier)*aifft_rank[idx];
@@ -1587,34 +1528,17 @@ ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *en
}
/**
* @return !0 if the specified target should scan for a new target, otherwise return 0
* @return true if the specified target should scan for a new target, otherwise return false
*/
int turret_should_pick_new_target(ship_subsys *turret)
bool turret_should_pick_new_target(ship_subsys *turret)
{
// int target_type;
if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
return 1;
}
/*
if ( turret->turret_enemy_objnum == -1 ) {
return 1;
}
target_type = Objects[turret->turret_enemy_objnum].type;
if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
return 1;
}
*/
return 0;
return timestamp_elapsed(turret->turret_next_enemy_check_stamp);
}
/**
* Set the next fire timestamp for a turret, based on weapon type and ai class
*/
void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsys *turret, ai_info *aip)
void turret_set_next_fire_timestamp(int weapon_num, const weapon_info *wip, ship_subsys *turret, const ai_info *aip)
{
Assert(weapon_num < MAX_SHIP_WEAPONS);
float wait = 1000.0f;
@@ -1727,13 +1651,13 @@ void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsy
/**
* Decide if a turret should launch an aspect seeking missile
*/
int turret_should_fire_aspect(ship_subsys *turret, weapon_info *wip, bool in_sight)
bool turret_should_fire_aspect(const ship_subsys *turret, const weapon_info *wip, bool in_sight)
{
if ( in_sight && (turret->turret_time_enemy_in_range >= MIN(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
return 1;
return true;
}
return 0;
return false;
}
/**
@@ -1755,9 +1679,9 @@ void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
/**
* Fire a weapon from a turret
*/
bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, vec3d *turret_pos, vec3d *firing_vec, vec3d *predicted_pos = nullptr, float flak_range_override = 100.0f, bool play_sound = true)
bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, const vec3d *orig_firing_pos, const vec3d *orig_firing_vec, const vec3d *predicted_pos = nullptr, float flak_range_override = 100.0f, bool play_sound = true)
{
matrix turret_orient;
matrix firing_orient;
int weapon_objnum;
ai_info *parent_aip;
ship *parent_ship;
@@ -1819,16 +1743,21 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
}
#endif
if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, wip, -1, turret)) {
if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, wip, -1, orig_firing_pos)) {
auto turret_enemy_objp = (turret->turret_enemy_objnum >= 0) ? &Objects[turret->turret_enemy_objnum] : nullptr;
ship_weapon* swp = &turret->weapons;
turret->turret_last_fire_direction = *firing_vec;
turret->turret_last_fire_direction = *orig_firing_vec;
if (turret->turret_inaccuracy > 0.0f)
vm_vec_random_cone(firing_vec, firing_vec, turret->turret_inaccuracy);
const vec3d *firing_vec = orig_firing_vec;
vec3d firing_vec_buf;
vm_vector_2_matrix(&turret_orient, firing_vec, nullptr, nullptr);
if (turret->turret_inaccuracy > 0.0f) {
vm_vec_random_cone(&firing_vec_buf, firing_vec, turret->turret_inaccuracy);
firing_vec = &firing_vec_buf;
}
vm_vector_2_matrix(&firing_orient, firing_vec, nullptr, nullptr);
// grab and set some burst data before turret_set_next_fire_timestamp wipes it
int old_burst_seed = swp->burst_seed[weapon_num];
@@ -2007,11 +1936,15 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
}
}
}
vec3d firing_pos_buf;
const vec3d *firing_pos = &firing_pos_buf;
// zookeeper - Firepoints should cycle normally between shots,
// so we need to get the position info separately for each shot
ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, turret_pos, firing_vec, 1, nullptr);
ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &firing_pos_buf, false, nullptr, true, nullptr);
weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, -1, 1, 0,0.0f, turret);
weapon_objnum = weapon_create(firing_pos, &firing_orient, turret_weapon_class, parent_objnum, -1, 1, 0, 0.0f, turret);
weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);
//nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));
@@ -2043,7 +1976,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
if(predicted_pos != NULL)
{
// pick a firing range so that it detonates properly
flak_pick_range(objp, turret_pos, predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
flak_pick_range(objp, firing_pos, predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
// determine what that range was
flak_range = flak_get_range(objp);
@@ -2056,7 +1989,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
// do mflash if the weapon has it
if (wip->muzzle_flash >= 0) {
mflash_create(turret_pos, firing_vec, &Objects[parent_ship->objnum].phys_info, wip->muzzle_flash);
mflash_create(firing_pos, firing_vec, &Objects[parent_ship->objnum].phys_info, wip->muzzle_flash);
}
// in multiplayer (and the master), then send a turret fired packet.
@@ -2075,7 +2008,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
if ( play_sound && wip->launch_snd.isValid() ) {
// Don't play turret firing sound if turret sits on player ship... it gets annoying.
if ( parent_objnum != OBJ_INDEX(Player_obj) || (turret->flags[Ship::Subsystem_Flags::Play_sound_for_player]) ) {
snd_play_3d( gamesnd_get_game_sound(wip->launch_snd), turret_pos, &View_position );
snd_play_3d( gamesnd_get_game_sound(wip->launch_snd), firing_pos, &View_position );
}
}
}
@@ -2107,7 +2040,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
{
int weapon_objnum;
matrix turret_orient;
matrix firing_orient;
vec3d turret_pos, turret_fvec;
// parent not alive, quick out.
@@ -2120,7 +2053,7 @@ void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
tsi->turret->turret_next_fire_pos = tsi->weapon_num;
// change firing point
ship_get_global_turret_gun_info(&Objects[tsi->parent_objnum], tsi->turret, &turret_pos, &turret_fvec, 1, NULL);
ship_get_global_turret_gun_info(&Objects[tsi->parent_objnum], tsi->turret, &turret_pos, false, &turret_fvec, true, nullptr);
tsi->turret->turret_next_fire_pos++;
//check if this really is a swarm. If not, how the hell did it get here?
@@ -2135,11 +2068,11 @@ void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
turret_fvec = tsi->turret->turret_last_fire_direction;
}
// make turret_orient from turret_fvec -- turret->turret_last_fire_direction
vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
// make firing_orient from turret_fvec -- turret->turret_last_fire_direction
vm_vector_2_matrix(&firing_orient, &turret_fvec, NULL, NULL);
// create weapon and homing info
weapon_objnum = weapon_create(&turret_pos, &turret_orient, tsi->weapon_class, tsi->parent_objnum, -1, 1, 0, 0.0f, tsi->turret);
weapon_objnum = weapon_create(&turret_pos, &firing_orient, tsi->weapon_class, tsi->parent_objnum, -1, 1, 0, 0.0f, tsi->turret);
weapon_set_tracking_info(weapon_objnum, tsi->parent_objnum, tsi->target_objnum, 1, tsi->target_subsys);
// do other cool stuff if weapon is created.
@@ -2198,7 +2131,7 @@ int Num_turrets_fired = 0;
* all turret movement and resetting when idle
*/
extern int Nebula_sec_range;
void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
void ai_turret_execute_behavior(const ship *shipp, ship_subsys *ss)
{
float weapon_firing_range;
float weapon_min_range; // *Weapon minimum firing range -Et1
@@ -2282,12 +2215,21 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
objp = &Objects[shipp->objnum];
Assert(objp->type == OBJ_SHIP);
// Use the turret info for all guns, not one gun in particular.
vec3d gvec, gpos;
ship_get_global_turret_info(objp, tp, &gpos, &gvec);
vec3d global_gun_pos, global_gun_vec;
if (tp->flags[Model::Subsystem_Flags::Turret_distant_firepoint] || Always_use_distant_firepoints) {
//The firing point of this turret is so far away from the its center that we should consider this for firing calculations
//This will do the enemy position prediction based on their relative position and speed to the firing point, not the turret center.
ship_get_global_turret_gun_info(objp, ss, &global_gun_pos, false, &global_gun_vec, true, nullptr);
} else {
// Use the turret info for all guns, not one gun in particular.
ship_get_global_turret_info(objp, tp, &global_gun_pos, &global_gun_vec);
}
// Update predicted enemy position. This used to be done in aifft_rotate_turret
aifft_update_predicted_enemy_pos(objp, shipp, ss, &global_gun_pos, &global_gun_vec, lep, &predicted_enemy_pos);
// Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
int use_angles = aifft_rotate_turret(objp, shipp, ss, lep, &predicted_enemy_pos, &gvec);
bool use_angles = aifft_rotate_turret(objp, shipp, ss, &global_gun_pos, &global_gun_vec, &predicted_enemy_pos);
// Multiplayer clients are now able to try to track their targets and also reset to their idle positions.
// But everything after this point is turret firing, so we need to bail here.
@@ -2311,18 +2253,20 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
float dist_to_enemy = 0.0f;
if (lep) {
if (The_mission.ai_profile->flags[AI::Profile_Flags::Turrets_ignore_target_radius])
dist_to_enemy = MAX(0, vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos));
dist_to_enemy = MAX(0, vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &global_gun_pos));
else
dist_to_enemy = MAX(0, vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius);
dist_to_enemy = MAX(0, vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &global_gun_pos) - lep->radius);
}
// count the number of enemies, in case we have a spawning weapon
int num_ships_nearby = num_nearby_fighters(iff_get_attackee_mask(obj_team(objp)), &gpos, 1500.0f);
int num_ships_nearby = num_nearby_fighters(iff_get_attackee_mask(obj_team(objp)), &global_gun_pos, 1500.0f);
// some flags considering there may be different weapon types on this turret
bool we_did_non_spawning_logic = false;
bool tentative_return = false;
float turret_barrel_length = -1.0f;
//WMC - go through all valid weapons. Fire spawns if there are any.
int valid_weapons[MAX_SHIP_WEAPONS];
int num_valid = 0;
@@ -2354,7 +2298,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
if ( (wip->wi_flags[Weapon::Info_Flags::Spawn]) && !(wip->wi_flags[Weapon::Info_Flags::Smart_spawn]) )
{
if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
turret_fire_weapon(i, ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
turret_fire_weapon(i, ss, parent_objnum, &global_gun_pos, &ss->turret_last_fire_direction);
} else {
// Regardless of firing rate, don't check whether should fire for awhile.
if (tp->flags[Model::Subsystem_Flags::Use_multiple_guns])
@@ -2402,10 +2346,16 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
// If the barrel of this turret is long, we shouldn't try to fire if the enemy is under the gun.
// Note that this isn't the same thing as min_range, which is unrelated to barrel length.
if (tp->flags[Model::Subsystem_Flags::Turret_distant_firepoint] || Always_use_distant_firepoints) {
vec3d fpoint, fvec;
ship_get_global_turret_gun_info(objp, ss, &fpoint, &fvec, use_angles, &predicted_enemy_pos);
auto barrel_length = vm_vec_dist(&fpoint, &gpos);
if (dist_to_enemy < barrel_length) continue;
if (turret_barrel_length < 0.0f) {
if (tp->turret_gun_sobj >= 0) {
auto pm = model_get(tp->model_num);
auto submodel = &pm->submodel[tp->turret_gun_sobj];
turret_barrel_length = 2 * submodel->rad;
} else {
turret_barrel_length = 0.0f;
}
}
if (dist_to_enemy < turret_barrel_length) continue;
}
// Don't try to fire beyond weapon_limit_range (or within min range)
@@ -2523,7 +2473,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
// Maybe pick a new enemy, unless targeting has been taken over by scripting
if ( turret_should_pick_new_target(ss) && !ss->scripting_target_override && !(ss->flags[Ship::Subsystem_Flags::Forced_target])) {
Num_find_turret_enemy++;
int objnum = find_turret_enemy(ss, parent_objnum, &gpos, &gvec, ss->turret_enemy_objnum);
int objnum = find_turret_enemy(ss, parent_objnum, &global_gun_pos, &global_gun_vec, ss->turret_enemy_objnum);
if (objnum >= 0) {
if (ss->turret_enemy_objnum == -1) {
@@ -2545,7 +2495,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
float dot = 1.0f;
lep = &Objects[ss->turret_enemy_objnum];
if (( lep->type == OBJ_SHIP ) && !(ss->flags[Ship::Subsystem_Flags::No_SS_targeting])) {
ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);
ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, &global_gun_pos, lep, &dot);
}
// recheck in 2-3 seconds
ss->turret_next_enemy_check_stamp = timestamp((int)((MAX(dot, 0.5f) * The_mission.ai_profile->turret_target_recheck_time) + (The_mission.ai_profile->turret_target_recheck_time / 2.0f)));
@@ -2593,7 +2543,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
//This can't happen. See above code
//Assert(ss->turret_enemy_objnum != -1);
bool in_fov = turret_fov_test(ss, &gvec, &v2e);
bool in_fov = turret_fov_test(ss, &global_gun_vec, &v2e);
bool something_was_ok_to_fire = false;
// the only reason predicted_enemy_pos might be zero, while everything else is valid is if
@@ -2648,13 +2598,13 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
// We're ready to fire... now get down to specifics, like where is the
// actual gun point and normal, not just the one for whole turret.
ship_get_global_turret_gun_info(objp, ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
ship_get_global_turret_gun_info(objp, ss, &global_gun_pos, false, &global_gun_vec, use_angles, &predicted_enemy_pos);
// Fire in the direction the turret is facing, not right at the target regardless of turret dir.
// [Yet this retail comment precedes the calculation of vector-to-enemy...]
vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
vm_vec_sub(&v2e, &predicted_enemy_pos, &global_gun_pos);
dist_to_enemy = vm_vec_normalize(&v2e);
float dot = vm_vec_dot(&v2e, &gvec);
float dot = vm_vec_dot(&v2e, &global_gun_vec);
// (flak jitter moved to after we obtain shoot_vector below)
@@ -2719,7 +2669,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
// Check if turret should fire and enemy's engines are
// in line of sight
if (turret_should_fire_aspect(ss, wip, in_sight) &&
ship_get_closest_subsys_in_sight(&Ships[lep->signature], SUBSYSTEM_ENGINE, &gpos))
ship_get_closest_subsys_in_sight(&Ships[lep->signature], SUBSYSTEM_ENGINE, &global_gun_pos))
{
ok_to_fire = true;
}
@@ -2729,14 +2679,14 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
if ( ok_to_fire && (tp->flags[Model::Subsystem_Flags::Turret_hull_check]) ) {
int model_num = Ship_info[shipp->ship_info_index].model_num;
vec3d end;
vm_vec_scale_add(&end, &gpos, &gvec, model_get_radius(model_num));
vm_vec_scale_add(&end, &global_gun_pos, &global_gun_vec, model_get_radius(model_num));
mc_info hull_check;
hull_check.model_instance_num = shipp->model_instance_num;
hull_check.model_num = model_num;
hull_check.orient = &objp->orient;
hull_check.pos = &objp->pos;
hull_check.p0 = &gpos;
hull_check.p0 = &global_gun_pos;
hull_check.p1 = &end;
hull_check.flags = MC_CHECK_MODEL | MC_CHECK_RAY;
@@ -2776,7 +2726,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
// (Despite the retail comment above, retail actually fired toward the enemy; v2e = vector-to-enemy)
vec3d shoot_vector = v2e;
if (tp->flags[Model::Subsystem_Flags::Fire_on_normal])
shoot_vector = gvec;
shoot_vector = global_gun_vec;
// if the weapon is a flak gun, add some jitter to its aim so it fires in a "cone"
// to make a cool visual effect and make them less lethal
@@ -2784,7 +2734,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
flak_jitter_aim(&shoot_vector, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS), wip);
}
turret_fire_weapon(valid_weapons[valid_index], ss, parent_objnum, &gpos, &shoot_vector, &predicted_enemy_pos, 100.0f, play_sound);
turret_fire_weapon(valid_weapons[valid_index], ss, parent_objnum, &global_gun_pos, &shoot_vector, &predicted_enemy_pos, 100.0f, play_sound);
}
else
{
+6
View File
@@ -1049,6 +1049,7 @@ bool asteroid_is_within_view(vec3d *pos, float range, bool range_override)
*/
static void maybe_throw_asteroid()
{
Assertion(Asteroid_field.num_used_field_debris_types > 0, "maybe_throw_asteroid() called while num_used_field_debris_types was 0; this should never happen, get a coder!");
for (asteroid_target& target : Asteroid_targets) {
if (!timestamp_elapsed(target.throw_stamp))
@@ -2584,6 +2585,11 @@ void asteroid_frame()
return;
}
// If no asteroid types are defined for the field, abort.
if (Asteroid_field.num_used_field_debris_types <= 0) {
return;
}
// If there are no explicit targets, fall back to default retail targeting behavior
if (Asteroid_field.target_names.empty()) {
int objnum = set_asteroid_throw_objnum();
+1 -1
View File
@@ -1211,7 +1211,7 @@ void get_turret_cam_pos(camera *cam, vec3d *pos)
{
if(ssp->system_info->subobj_num == cam->get_object_host_submodel())
{
ship_get_global_turret_gun_info(cam->get_object_host(), ssp, pos, &normal_cache, 1, NULL);
ship_get_global_turret_gun_info(cam->get_object_host(), ssp, pos, true, &normal_cache, true, nullptr);
vec3d offset = vmd_zero_vector;
offset.xyz.x = 0.0001f;
vm_vec_add2(pos, &offset); // prevent beam turrets from crashing with a nullvec
+17 -17
View File
@@ -91,12 +91,10 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
};
// clang-format on
#define CFILE_STACK_MAX 8
int cfile_inited = 0;
static int Cfile_stack_pos = 0;
static char Cfile_stack[CFILE_STACK_MAX][CFILE_ROOT_DIRECTORY_LEN];
static SCP_vector<SCP_string> Cfile_stack;
std::array<CFILE, MAX_CFILE_BLOCKS> Cfile_block_list;
@@ -330,17 +328,16 @@ int cfile_push_chdir(int type)
_getcwd(OriginalDirectory, CFILE_ROOT_DIRECTORY_LEN - 1);
Assert(Cfile_stack_pos < CFILE_STACK_MAX);
if (Cfile_stack_pos >= CFILE_STACK_MAX) {
return -1;
}
strcpy_s(Cfile_stack[Cfile_stack_pos++], OriginalDirectory);
cf_create_default_path_string(dir, type, NULL);
return _cfile_chdir(dir.c_str(), OriginalDirectory);
int rc = _cfile_chdir(dir.c_str(), OriginalDirectory);
// if success then push the original directory on to the stack
if (rc == 0) {
Cfile_stack.push_back(OriginalDirectory);
}
return rc;
}
/**
@@ -363,13 +360,16 @@ int cfile_chdir(const char *dir)
int cfile_pop_dir()
{
Assert(Cfile_stack_pos);
if ( !Cfile_stack_pos )
if (Cfile_stack.empty())
return -1;
Cfile_stack_pos--;
return cfile_chdir(Cfile_stack[Cfile_stack_pos]);
int rc = cfile_chdir(Cfile_stack.back().c_str());
Assertion(rc == 0, "Failed to chdir() to previous directory (%s)!", Cfile_stack.back().c_str());
Cfile_stack.pop_back();
return rc;
}
// flush (delete all files in) the passed directory (by type), return the # of files deleted
+102 -61
View File
@@ -40,6 +40,7 @@
#ifdef SCP_UNIX
#include "osapi/osapi.h"
#include <dirent.h>
#include <sys/stat.h>
#endif
#include <cstring>
@@ -1208,93 +1209,133 @@ bool cmdline_parm::has_param() {
}
#ifdef SCP_UNIX
// Return a vector with all filesystem names of "parent/dir" relative to parent.
// dir must not contain a slash.
static SCP_vector<SCP_string> unix_get_single_dir_names(const SCP_string& parent, const SCP_string& dir)
extern void normalize_directory_separators(SCP_string &str);
static SCP_string get_real_mod_path(const char *mod)
{
SCP_vector<SCP_string> ret;
DIR *dirp;
SCP_string fullpath;
SCP_string mod_path = mod;
DIR *dp;
if ((dp = opendir(parent.c_str())) == NULL) {
Warning(LOCATION, "Can't open directory '%s' when searching mod paths. Ignoring. errno=%d", parent.c_str(), errno);
return ret;
normalize_directory_separators(mod_path);
fullpath = SCP_string(".") + DIR_SEPARATOR_STR;
fullpath += mod_path;
// check if path already exists
dirp = opendir(fullpath.c_str());
if (dirp) {
// found it, return unchanged
closedir(dirp);
return mod_path;
}
dirent *dirp;
while ((dirp = readdir(dp)) != NULL) {
if (!stricmp(dirp->d_name, dir.c_str())) {
ret.push_back(dirp->d_name);
// not found, check for a difference in case
SCP_string new_mod_path;
SCP_string part;
struct dirent *dir;
SCP_string::size_type separator;
SCP_string::size_type offset = 0;
fullpath = SCP_string(".") + DIR_SEPARATOR_STR;
do {
separator = mod_path.find(DIR_SEPARATOR_CHAR, offset);
if (separator != SCP_string::npos) {
part = mod_path.substr(offset, separator-offset);
offset += separator-offset + 1;
} else {
part = mod_path.substr(offset);
offset += part.length();
}
}
(void)closedir(dp);
return ret;
}
dirp = opendir(fullpath.c_str());
// Return a vector with all filesystem names of "parent/dir" relative to parent.
// Recurses to deal with slashes in dir.
static SCP_vector<SCP_string> unix_get_dir_names(const SCP_string& parent, const SCP_string& dir)
{
size_t slash = dir.find_first_of("/\\");
// no subdirectories, no need to recurse
if (slash == std::string::npos) {
return unix_get_single_dir_names(parent, dir);
}
// get the names of the first component of dir
SCP_vector<SCP_string> this_dir_names = unix_get_single_dir_names(parent, dir.substr(0, slash));
SCP_string rest = dir.substr(slash + 1);
SCP_vector<SCP_string> ret;
// search for the rest of dir in each of these
SCP_vector<SCP_string>::iterator ii, end = this_dir_names.end();
for (ii = this_dir_names.begin(); ii != end; ++ii) {
SCP_string this_dir_path = parent + "/" + *ii;
SCP_vector<SCP_string> mod_path = unix_get_dir_names(this_dir_path, rest);
// add all found paths relative to parent
SCP_vector<SCP_string>::iterator ii2, end2 = mod_path.end();
for (ii2 = mod_path.begin(); ii2 != end2; ++ii2) {
ret.push_back(*ii + "/" + *ii2);
if ( !dirp ) {
break;
}
while ((dir = readdir(dirp)) != nullptr) {
if (stricmp(dir->d_name, part.c_str())) {
continue;
}
SCP_string fn = fullpath + dir->d_name;
// make sure it accessible and a directory
struct stat buf;
if (stat(fn.c_str(), &buf) == -1) {
continue;
}
if ( !S_ISDIR(buf.st_mode) ) {
continue;
}
if ( !new_mod_path.empty() ) {
new_mod_path += DIR_SEPARATOR_STR;
}
new_mod_path += dir->d_name;
fullpath += dir->d_name;
fullpath += DIR_SEPARATOR_STR;
break;
}
if (dirp) {
closedir(dirp);
}
} while (offset < mod_path.length());
// Append anything left from mod_path onto the new path. This will catch trailing separators
// as well as mod folders which don't exist so that the mod string remains unchanged except
// for the directory name case.
if (new_mod_path.length() != mod_path.length()) {
new_mod_path += mod_path.substr(new_mod_path.length());
}
return ret;
return new_mod_path;
}
// For case sensitive filesystems (e.g. Linux/BSD) perform case-insensitive dir matches.
//
// NOTE: Aside from change in case no alterations should be made to the modlist. Missing
// mod folders should be left in place and no errors or warnings should be generated.
// There should be no difference between Windows and non-Windows modlist handling.
static void handle_unix_modlist(char **modlist, size_t *len)
{
// search filesystem for given paths
SCP_vector<SCP_string> mod_paths;
for (char *cur_mod = strtok(*modlist, ","); cur_mod != NULL; cur_mod = strtok(NULL, ","))
{
SCP_vector<SCP_string> this_mod_paths = unix_get_dir_names(".", cur_mod);
// Ignore non-existing mods for unit tests
if (!running_unittests && this_mod_paths.empty()) {
ReleaseWarning(LOCATION, "Can't find mod '%s'. Ignoring.", cur_mod);
}
mod_paths.insert(mod_paths.end(), this_mod_paths.begin(), this_mod_paths.end());
for (char *cur_mod = strtok(*modlist, ","); cur_mod != NULL; cur_mod = strtok(NULL, ",")) {
SCP_string path = get_real_mod_path(cur_mod);
mod_paths.push_back(path);
}
// create new char[] to replace modlist
size_t total_len = 0;
SCP_vector<SCP_string>::iterator ii, end = mod_paths.end();
for (ii = mod_paths.begin(); ii != end; ++ii) {
total_len += ii->length() + 1;
for (auto &path : mod_paths) {
total_len += path.length() + 1;
}
char *new_modlist = new char[total_len + 1];
memset(new_modlist, 0, total_len + 1);
end = mod_paths.end();
for (ii = mod_paths.begin(); ii != end; ++ii) {
strcat_s(new_modlist, total_len + 1, ii->c_str());
strcat_s(new_modlist, total_len + 1, ","); // replace later with NUL
for (auto &path : mod_paths) {
strcat_s(new_modlist, total_len + 1, path.c_str());
strcat_s(new_modlist, total_len + 1, ",");
}
// remove trailing comma
new_modlist[total_len-1] = '\0';
// make the rest of the modlist manipulation unaware that anything happened here
delete [] *modlist;
*modlist = new_modlist;
@@ -1799,7 +1840,7 @@ bool SetCmdlineParams()
if(mod_arg.found() ) {
Cmdline_mod = mod_arg.str();
// strip off blank space it it's there
// strip off blank space if it's there
if ( Cmdline_mod[strlen(Cmdline_mod)-1] == ' ' ) {
Cmdline_mod[strlen(Cmdline_mod)-1] = '\0';
}
+1 -1
View File
@@ -947,7 +947,7 @@ int check_control(int id, int key = -1);
void control_get_axes_readings(int *axis_v, float frame_time);
/**
* @brief Markes the given control (by IoActionId) as used
* @brief Marks the given control (by IoActionId) as used
*
* @details Updates the ::used timestamp, triggers a script hook, and marks ::continous_ongoing as true
*/
+14
View File
@@ -25,6 +25,8 @@
extern int Cmdline_nomovies;
bool Movie_active = false;
const char* Cutscene_bitmap_name[GR_NUM_RESOLUTIONS] = {
"ViewFootage",
@@ -60,6 +62,18 @@ static cutscene_info *get_cutscene_pointer(char *cutscene_filename)
return NULL;
}
int get_cutscene_index_by_name(const char* name)
{
for (int i = 0; i < static_cast<int>(Cutscenes.size()); i++) {
if (!stricmp(name, Cutscenes[i].name)) {
return i;
}
}
// Didn't find anything.
return -1;
}
static void cutscene_info_init(cutscene_info *csni)
{
csni->filename[0] = '\0';
+4
View File
@@ -37,6 +37,8 @@ typedef struct cutscene_info
extern SCP_vector<cutscene_info> Cutscenes;
extern bool Movie_active;
// initializa table data
void cutscene_init();
@@ -48,4 +50,6 @@ void cutscenes_screen_do_frame();
void cutscene_mark_viewable(const char* filename);
int get_cutscene_index_by_name(const char* name);
#endif
+3
View File
@@ -278,6 +278,7 @@ bool play(const char* name) {
// clear the screen and hide the mouse cursor
io::mouse::CursorManager::get()->pushStatus();
io::mouse::CursorManager::get()->showCursor(false);
Movie_active = true;
gr_reset_clip();
gr_set_color(255, 255, 255);
gr_set_clear_color(0, 0, 0);
@@ -304,6 +305,8 @@ bool play(const char* name) {
mprintf(("MOVIE ERROR: Found invalid movie! (%s)\n", name));
}
Movie_active = false;
// show the mouse cursor again
io::mouse::CursorManager::get()->popStatus();
+3 -3
View File
@@ -501,11 +501,11 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
}
else if (vaporize) {
db->model_num = Debris_vaporize_model;
db->submodel_num = Random::next(Debris_num_submodels);
db->submodel_num = (Debris_num_submodels <= 0 ? 0 : Random::next(Debris_num_submodels));
}
else {
db->model_num = Debris_model;
db->submodel_num = Random::next(Debris_num_submodels);
db->submodel_num = (Debris_num_submodels <= 0 ? 0 : Random::next(Debris_num_submodels));
}
}
else {
@@ -644,7 +644,7 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
if (spark_timeout >= 0) {
db->fire_timeout = _timestamp(spark_timeout);
} else if (parent_objnum >= 0) {
float t = 1000*Objects[parent_objnum].radius/3 + Random::next(fl2i(1000*3*Objects[parent_objnum].radius));
float t = 1000*Objects[parent_objnum].radius/3 + (fl2i(1000*3*Objects[parent_objnum].radius) == 0 ? 0 : Random::next(fl2i(1000*3*Objects[parent_objnum].radius)));
db->fire_timeout = _timestamp(fl2i(t)); // fireballs last from 5 - 30 seconds
} else {
db->fire_timeout = TIMESTAMP::immediate();
+7 -4
View File
@@ -118,10 +118,13 @@ uniform sampler2DArray sMiscmap;
uniform sampler2DArray shadow_map;
out vec4 fragOut0;
#ifndef FLAG_SHADOW_MAP
out vec4 fragOut1;
out vec4 fragOut2;
out vec4 fragOut3;
out vec4 fragOut4;
#endif
vec3 FresnelLazarovEnv(vec3 specColor, vec3 view, vec3 normal, float gloss)
{
@@ -185,10 +188,9 @@ void main()
#ifdef FLAG_SHADOW_MAP
// need depth and depth squared for variance shadow maps
fragOut0 = vec4(vertIn.position.z, vertIn.position.z * vertIn.position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0);
if (true) {
return;
}
#endif
return;
#else
vec3 eyeDir = vec3(normalize(-vertIn.position).xyz);
vec2 texCoord = vertIn.texCoord.xy;
@@ -461,4 +463,5 @@ void main()
fragOut3 = vec4(specColor.rgb, fresnelFactor);
fragOut4 = emissiveColor;
}
#endif
}
-1
View File
@@ -5,7 +5,6 @@ out vec4 fragOut2;
out vec4 fragOut3;
out vec4 fragOut4;
out vec4 fragOut5;
out float gl_FragDepth;
uniform sampler2DMS texColor;
uniform sampler2DMS texPos;
+1 -1
View File
@@ -1052,7 +1052,7 @@ float fireball_wormhole_intensity(fireball *fb)
rad = (float)pow((fb->total_time - t) / fb->warp_close_duration, 0.4f);
}
return rad;
}
}
extern void warpin_queue_render(model_draw_list *scene, object *obj, matrix *orient, vec3d *pos, int texture_bitmap_num, float radius, float life_percent, float max_radius, bool warp_3d, int warp_glow_bitmap, int warp_ball_bitmap, int warp_model_id);
+1
View File
@@ -72,6 +72,7 @@
#define MAX_POLYGON_MODELS 300
// object.h
//If this value exceeds 2^16-1, this will break collision pair caching as is. Proceed with caution.
#define MAX_OBJECTS 5000 //Increased from 3500 to 5000 in 2022
// from weapon.h (and beam.h)
+1 -1
View File
@@ -453,7 +453,7 @@ public:
// - is not "none"
// - is not "<none>"
inline bool VALID_FNAME(const char* x) {
return strlen((x)) && stricmp((x), "none") != 0 && stricmp((x), "<none>") != 0;
return (x[0] != '\0') && stricmp(x, "none") != 0 && stricmp(x, "<none>") != 0;
}
/**
* @brief Checks if the specified string may be a valid file name
+28 -14
View File
@@ -460,7 +460,7 @@ static auto PostProcessOption __UNUSED = options::OptionBuilder<bool>("Graphics.
std::pair<const char*, int>{"Controls whether post processing is enabled in the engine.", 1727})
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.default_val(false)
.default_val(true)
.bind_to_once(&Gr_post_processing_enabled)
.importance(69)
.finish();
@@ -1754,17 +1754,6 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
return true;
}
void gr_force_windowed()
{
if ( !Gr_inited ) {
return;
}
if ( Os_debugger_running ) {
os_sleep(1000);
}
}
int gr_activated = 0;
void gr_activate(int active)
{
@@ -1779,7 +1768,7 @@ void gr_activate(int active)
}
if (active) {
if (Cmdline_fullscreen_window || Cmdline_window) {
if (gr_is_viewport_window()) {
os::getMainViewport()->restore();
} else {
os::getMainViewport()->setState(os::ViewportState::Fullscreen);
@@ -1789,7 +1778,7 @@ void gr_activate(int active)
}
if (active) {
if (!Cmdline_fullscreen_window && !Cmdline_window) {
if (!gr_is_viewport_window()) {
gr_set_gamma(Gr_gamma);
}
}
@@ -2968,3 +2957,28 @@ void gr_get_post_process_effect_names(SCP_vector<SCP_string>& names)
names.push_back(eff.name);
}
}
bool gr_is_viewport_window()
{
if (Fred_running) {
return true;
}
if (Using_in_game_options) {
switch (Gr_configured_window_state)
{
case os::ViewportState::Windowed:
case os::ViewportState::Borderless:
return true;
break;
default:
break;
}
} else {
if (Cmdline_window || Cmdline_fullscreen_window) {
return true;
}
}
return false;
}
+2
View File
@@ -1428,6 +1428,8 @@ void gr_set_gamma(float gamma);
void gr_get_post_process_effect_names(SCP_vector<SCP_string> &names);
bool gr_is_viewport_window();
// Include this last to make the 2D rendering function available everywhere
#include "graphics/render.h"
+5 -8
View File
@@ -1697,7 +1697,7 @@ void hud_render_preprocess(float frametime)
if ( hud_disabled() ) {
// if the hud is disabled, we still need to make sure that the indicators are properly handled
hud_do_lock_indicators(flFrametime);
hud_do_lock_indicators(frametime);
return;
}
@@ -1823,15 +1823,15 @@ void hud_maybe_display_supernova()
/**
* @brief Undertakes main HUD render.
*/
void hud_render_all()
void hud_render_all(float frametime)
{
int i;
hud_render_gauges();
hud_render_gauges(-1, frametime);
// start rendering cockpit dependent gauges if possible
for ( i = 0; i < (int)Player_displays.size(); ++i ) {
hud_render_gauges(i);
hud_render_gauges(i, frametime);
}
hud_clear_msg_buffer();
@@ -1840,7 +1840,7 @@ void hud_render_all()
font::set_font(font::FONT1);
}
void hud_render_gauges(int cockpit_display_num)
void hud_render_gauges(int cockpit_display_num, float frametime)
{
size_t j, num_gauges;
ship_info* sip = &Ship_info[Player_ship->ship_info_index];
@@ -1866,9 +1866,6 @@ void hud_render_gauges(int cockpit_display_num)
}
}
//Since we render things twice in VR mode, frametime needs to be halved for HUD, as the HUD uses the frametime to advance ANI's and crucially, the missile lock...
float frametime = openxr_enabled() ? flFrametime * 0.5f : flFrametime;
// Check if this ship has its own HUD gauges.
if ( sip->hud_enabled ) {
num_gauges = sip->hud_gauges.size();
+2 -2
View File
@@ -119,8 +119,8 @@ void hud_close();
void hud_level_close();
void hud_update_frame(float frametime); // updates hud systems not dependant on rendering
void hud_render_preprocess(float frametime); // renders 3d dependant gauges
void hud_render_all();
void hud_render_gauges(int cockpit_display_num = -1);
void hud_render_all(float frametime);
void hud_render_gauges(int cockpit_display_num, float frametime);
void hud_stop_looped_engine_sounds();
// set the offset values for this render frame
+2 -2
View File
@@ -327,8 +327,8 @@ void ssm_create(object *target, vec3d *start, size_t ssm_index, ssm_firing_info
// Init the ssm data
count = Ssm_info[ssm_index].count;
if (Ssm_info[ssm_index].max_count != -1) {
count += Random::next(count, Ssm_info[ssm_index].max_count);
if (Ssm_info[ssm_index].max_count != -1 && (Ssm_info[ssm_index].max_count - count) > 0) {
count = Random::next(count, Ssm_info[ssm_index].max_count);
}
// override in multiplayer
+2 -2
View File
@@ -288,7 +288,7 @@ int draw_subsys_brackets(graphics::line_draw_list* draw_list, ship_subsys* subsy
}
// determine if subsystem is on far or near side of the ship
int in_sight = ship_subsystem_in_sight(targetp, subsys, &View_position, &subobj_pos, 0);
bool in_sight = ship_subsystem_in_sight(targetp, subsys, &View_position, &subobj_pos, false);
if (draw)
{
@@ -761,7 +761,7 @@ void HudGaugeBrackets::renderBoundingBracketsSubobject()
}
// determine if subsystem is on far or near side of the ship
Player->subsys_in_view = ship_subsystem_in_sight(targetp, subsys, &View_position, &subobj_pos, 0);
Player->subsys_in_view = ship_subsystem_in_sight(targetp, subsys, &View_position, &subobj_pos, false) ? 1 : 0;
// AL 29-3-98: If subsystem is destroyed, draw gray brackets
// Goober5000: this will now execute for fighterbays if the bay has been given a
+1 -3
View File
@@ -1323,10 +1323,8 @@ void hud_config_button_do(int n)
break;
}
if (HC_current_file >= (int)HC_preset_filenames.size()) {
if ((++HC_current_file) >= static_cast<int> (HC_preset_filenames.size())) {
HC_current_file = 0;
} else {
HC_current_file++;
}
// load em up
+4 -4
View File
@@ -506,7 +506,7 @@ int hud_lock_on_subsys_ok()
vm_vec_add2(&subobj_pos, &target_objp->pos);
if ( Player->subsys_in_view < 0 ) {
in_sight = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos);
in_sight = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos) ? 1 : 0;
} else {
in_sight = Player->subsys_in_view;
}
@@ -640,7 +640,7 @@ void hud_do_lock_indicator(float frametime)
vec3d subobj_pos;
vm_vec_unrotate(&subobj_pos, &Player->locking_subsys->system_info->pnt, &tobjp->orient);
vm_vec_add2(&subobj_pos, &tobjp->pos);
int target_subsys_in_sight = ship_subsystem_in_sight(tobjp, Player->locking_subsys, &Player_obj->pos, &subobj_pos);
bool target_subsys_in_sight = ship_subsystem_in_sight(tobjp, Player->locking_subsys, &Player_obj->pos, &subobj_pos);
if (!target_subsys_in_sight || Player->locking_subsys->system_info->type != SUBSYSTEM_ENGINE) {
Player->locking_subsys =
@@ -1059,7 +1059,7 @@ void hud_lock_determine_lock_target(lock_info *lock_slot, weapon_info *wip)
vm_vec_unrotate(&subobj_pos, &Player->locking_subsys->system_info->pnt, &lock_slot->obj->orient);
vm_vec_add2(&subobj_pos, &lock_slot->obj->pos);
int target_subsys_in_sight = ship_subsystem_in_sight(lock_slot->obj, Player_ai->targeted_subsys, &Player_obj->pos, &subobj_pos);
bool target_subsys_in_sight = ship_subsystem_in_sight(lock_slot->obj, Player_ai->targeted_subsys, &Player_obj->pos, &subobj_pos);
if ( !target_subsys_in_sight || Player->locking_subsys->system_info->type != SUBSYSTEM_ENGINE ) {
lock_slot->subsys = ship_get_closest_subsys_in_sight(&Ships[lock_slot->obj->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
@@ -1110,7 +1110,7 @@ void hud_lock_determine_lock_target(lock_info *lock_slot, weapon_info *wip)
vm_vec_unrotate(&subobj_pos, &Player_ai->targeted_subsys->system_info->pnt, &lock_slot->obj->orient);
vm_vec_add2(&subobj_pos, &lock_slot->obj->pos);
int target_subsys_in_sight = ship_subsystem_in_sight(lock_slot->obj, Player_ai->targeted_subsys, &Player_obj->pos, &subobj_pos);
bool target_subsys_in_sight = ship_subsystem_in_sight(lock_slot->obj, Player_ai->targeted_subsys, &Player_obj->pos, &subobj_pos);
if ( !target_subsys_in_sight || Player_ai->targeted_subsys->system_info->type != SUBSYSTEM_ENGINE ) {
lock_slot->subsys = ship_get_closest_subsys_in_sight(&Ships[lock_slot->obj->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
+1 -1
View File
@@ -4688,7 +4688,7 @@ void load_gauge_lock(gauge_settings* settings)
stuff_boolean(&loop_locked_anim);
}
if(optional_string("Blink Locked Animation:")) {
stuff_boolean(&loop_locked_anim);
stuff_boolean(&blink_locked_anim);
}
hud_gauge->initBitmaps(fname_lock, fname_spin);
+8 -8
View File
@@ -470,7 +470,7 @@ int hud_target_invalid_awacs(object *objp)
}
// check for invalid status
if((Player_ship != NULL) && (awacs_get_level(objp, Player_ship) < 1.0f)){
if((Player_ship != nullptr) && (awacs_get_level(objp, Player_ship) < 1.0f)){
return 1;
}
@@ -872,7 +872,7 @@ void hud_target_hotkey_select( int k )
// a simple walk of the list to get the count
for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ){
if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
if (awacs_get_level(hitem->objp, Player_ship, true) > 1.0f) {
visible_count++;
}
}
@@ -894,7 +894,7 @@ void hud_target_hotkey_select( int k )
first_target = NULL;
for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
if (awacs_get_level(hitem->objp, Player_ship, true) > 1.0f) {
// get the first valid target
if (first_target == NULL) {
first_target = hitem;
@@ -939,7 +939,7 @@ void hud_target_hotkey_select( int k )
// next is before current target, so search from start of list
for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
if (awacs_get_level(hitem->objp, Player_ship, true) > 1.0f) {
target = hitem;
break;
}
@@ -1830,7 +1830,7 @@ void hud_target_live_turret(int next_flag, int auto_advance, int only_player_tar
distance = vm_vec_normalized_dir(&vec_to_subsys, &gsubpos, &View_position);
// check if facing and in view
int facing = ship_subsystem_in_sight(objp, A, &View_position, &gsubpos, 0);
bool facing = ship_subsystem_in_sight(objp, A, &View_position, &gsubpos, false);
if (!auto_advance && get_closest_turret && !only_player_target) {
// if within 3 degrees and not previous subsys, use subsys in front
@@ -2334,7 +2334,7 @@ void hud_update_closest_turret()
}
// check if facing and in view
int facing = ship_subsystem_in_sight(objp, ss, &View_position, &gsubpos, 0);
bool facing = ship_subsystem_in_sight(objp, ss, &View_position, &gsubpos, false);
if (facing) {
new_distance *= 0.5f;
@@ -3426,7 +3426,7 @@ void hud_show_selection_set()
// Goober5000 - don't draw indicators for non-visible ships, per Mantis #1972
// (the only way we could get here is if the hotkey set contained a mix of visible
// and invisible ships)
if (awacs_get_level(targetp, Player_ship, 1) < 1) {
if (awacs_get_level(targetp, Player_ship, true) < 1.0f) {
continue;
}
@@ -3590,7 +3590,7 @@ void hud_show_hostile_triangle()
}
// check if ship is stealthy
if (awacs_get_level(&Objects[sp->objnum], Player_ship, 1) < 1) {
if (awacs_get_level(&Objects[sp->objnum], Player_ship, true) < 1.0f) {
continue;
}
+1 -1
View File
@@ -1906,7 +1906,7 @@ int hud_targetbox_subsystem_in_view(object *target_objp, int *sx, int *sy)
// is it subsystem in view
if ( Player->subsys_in_view == -1 ) {
rval = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos, 0);
rval = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos, false) ? 1 : 0;
} else {
rval = Player->subsys_in_view;
}
+1 -1
View File
@@ -72,7 +72,7 @@ bool Use_mouse_to_fly = false;
static SCP_string mouse_mode_display(bool mode) { return mode ? XSTR("Joy-0", 1699) : XSTR("Mouse", 1774); }
static auto UseMouseOption __UNUSED = options::OptionBuilder<bool>("Input.UseMouse",
std::pair<const char*, int>{"Mouse", 1376},
std::pair<const char*, int>{"Mouse", 1373},
std::pair<const char*, int>{"Whether or not to use the mouse for flying", 1765})
.category("Input")
.display(mouse_mode_display)
+1 -1
View File
@@ -64,7 +64,7 @@ bool *Lcl_unexpected_tstring_check = nullptr;
// NOTE: with map storage of XSTR strings, the indexes no longer need to be contiguous,
// but internal strings should still increment XSTR_SIZE to avoid collisions.
// retail XSTR_SIZE = 1570
// #define XSTR_SIZE 1816 // This is the next available ID
// #define XSTR_SIZE 1817 // This is the next available ID
// struct to allow for strings.tbl-determined x offset
+6
View File
@@ -1070,6 +1070,9 @@ void barracks_button_pressed(int n)
} else {
gamesnd_play_iface(InterfaceSounds::SCROLL);
// we now have a new pilot, so try to load its current campaign, falling back if necessary
mission_load_up_campaign(true);
if (Campaign_file_missing) {
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "The currently active campaign cannot be found. Please select another...", 1600));
gameseq_post_event(GS_EVENT_CAMPAIGN_ROOM);
@@ -1081,6 +1084,9 @@ void barracks_button_pressed(int n)
if (Num_pilots && !barracks_pilot_accepted()) {
gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
// we now have a new pilot, so try to load its current campaign, falling back if necessary
mission_load_up_campaign(true);
if (Campaign_file_missing) {
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "The currently active campaign cannot be found. Please select another...", 1600));
gameseq_post_event(GS_EVENT_CAMPAIGN_ROOM);
+1 -3
View File
@@ -906,8 +906,7 @@ void main_hall_do(float frametime)
Game_mode = GM_NORMAL;
// see if we have a missing campaign and force the player to select a new campaign if so
if (!(Player->flags & PLAYER_FLAGS_IS_MULTI) && Campaign_file_missing &&
!Campaign_room_no_campaigns) {
if (!(Player->flags & PLAYER_FLAGS_IS_MULTI) && Campaign_file_missing && !Campaign_room_no_campaigns) {
int rc = popup(0,
3,
XSTR("Go to Campaign Room", 1607),
@@ -933,7 +932,6 @@ void main_hall_do(float frametime)
break;
}
} else {
gameseq_post_event(GS_EVENT_NEW_CAMPAIGN);
gamesnd_play_iface(InterfaceSounds::IFACE_MOUSE_CLICK);
}
-258
View File
@@ -1,258 +0,0 @@
/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include "freespace.h"
#include "gamesequence/gamesequence.h"
#include "gamesnd/gamesnd.h"
#include "io/key.h"
#include "menuui/mainhallmenu.h"
#include "menuui/mainhalltemp.h"
#include "mission/missioncampaign.h"
#include "mission/missionload.h"
#include "playerman/player.h"
#include "ui/ui.h"
// ------------------------------------------------------------------------------------------------------------------------
// TEMP MAIN HALL DEFINES/VARS
//
#define MHT_NUM_BUTTONS 6
const char *Mht_bitmap_fname[GR_NUM_RESOLUTIONS] = {
"mht_background", // GR_640
"2_mht_background" // GR_1024
};
const char *Mht_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
"mht_mask", // GR_640
"2_mht_mask" // GR_1024
};
// button defs
#define MHT_READY_ROOM 0
#define MHT_CAMPAIGN_ROOM 1
#define MHT_OPTIONS 2
#define MHT_TECH_ROOM 3
#define MHT_BARRACKS 4
#define MHT_EXIT 5
UI_WINDOW Mht_window; // the window object for the join screen
int Mht_bitmap; // the background bitmap
ui_button_info Mht_buttons[GR_NUM_RESOLUTIONS][MHT_NUM_BUTTONS] = {
{ // GR_640
ui_button_info( "MHT_00", 15, 194, -1, -1, 2 ), // ready room
ui_button_info( "MHT_01", 15, 222, -1, -1, 5 ), // campaigns
ui_button_info( "MHT_02", 14, 251, -1, -1, 4 ), // options
ui_button_info( "MHT_03", 14, 280, -1, -1, 3 ), // tech room
ui_button_info( "MHT_04", 14, 309, -1, -1, 1 ), // barracks
ui_button_info( "MHT_05", 16, 339, -1, -1, 0 ), // exit
},
{ // GR_1024
ui_button_info( "2_MHT_00", 25, 312, -1, -1, 2 ), // ready room
ui_button_info( "2_MHT_01", 25, 357, -1, -1, 5 ), // campaigns
ui_button_info( "2_MHT_02", 24, 403, -1, -1, 4 ), // options
ui_button_info( "2_MHT_03", 24, 450, -1, -1, 3 ), // tech room
ui_button_info( "2_MHT_04", 25, 497, -1, -1, 1 ), // barracks
ui_button_info( "2_MHT_05", 27, 544, -1, -1, 0 ), // exit
}
};
// ------------------------------------------------------------------------------------------------------------------------
// TEMP MAIN HALL FUNCTIONS
//
void mht_check_buttons();
void mht_button_pressed(int n);
void mht_exit_game();
void mht_init()
{
int idx;
// create the interface window
Mht_window.create(0, 0, gr_screen.max_w_unscaled,gr_screen.max_h_unscaled, 0);
Mht_window.set_mask_bmap(Mht_bitmap_mask_fname[gr_screen.res]);
// load the background bitmap
Mht_bitmap = bm_load(Mht_bitmap_fname[gr_screen.res]);
if(Mht_bitmap < 0){
// we failed to load the bitmap - this is very bad
Int3();
}
// create the interface buttons
for(idx=0; idx<MHT_NUM_BUTTONS; idx++){
// create the object
Mht_buttons[gr_screen.res][idx].button.create(&Mht_window, "", Mht_buttons[gr_screen.res][idx].x, Mht_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
// set the sound to play when highlighted
Mht_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
// set the ani for the button
Mht_buttons[gr_screen.res][idx].button.set_bmaps(Mht_buttons[gr_screen.res][idx].filename);
// set the hotspot
Mht_buttons[gr_screen.res][idx].button.link_hotspot(Mht_buttons[gr_screen.res][idx].hotspot);
}
// remove any multiplayer flags from the game mode
Game_mode &= ~(GM_MULTIPLAYER);
// initialize the music
main_hall_start_music();
// set the game_mode based on the type of player
Assert( Player != NULL );
if ( Player->flags & PLAYER_FLAGS_IS_MULTI ){
Game_mode = GM_MULTIPLAYER;
} else {
Game_mode = GM_NORMAL;
}
}
void mht_do()
{
int k = Mht_window.process();
// need to ensure ambient is playing, since it may be stopped by a playing movie
main_hall_start_ambient();
// process any keypresses
switch(k){
case KEY_ESC :
mht_exit_game();
break;
case KEY_B:
gameseq_post_event( GS_EVENT_BARRACKS_MENU );
break;
case KEY_G:
if(Player->flags & PLAYER_FLAGS_IS_MULTI){
break;
}
if (Num_recent_missions > 0) {
strcpy_s( Game_current_mission_filename, Recent_missions[0] );
} else {
mission_load_up_campaign();
strcpy_s( Game_current_mission_filename, Campaign.missions[0].name );
}
Campaign.current_mission = -1;
gameseq_post_event(GS_EVENT_START_GAME_QUICK);
break;
case KEY_L:
gameseq_post_event( GS_EVENT_LOAD_MISSION_MENU );
break;
case KEY_F2:
gameseq_post_event(GS_EVENT_OPTIONS_MENU);
break;
case KEY_M:
if (Player->flags & PLAYER_FLAGS_IS_MULTI){
main_hall_do_multi_ready();
}
break;
}
// process button presses
mht_check_buttons();
// draw the background, etc
gr_reset_clip();
GR_MAYBE_CLEAR_RES(Mht_bitmap);
if(Mht_bitmap != -1){
gr_set_bitmap(Mht_bitmap);
gr_bitmap(0,0,GR_RESIZE_MENU);
}
Mht_window.draw();
// flip the buffer
gr_flip();
}
void mht_close()
{
// unload any bitmaps
if(!bm_unload(Mht_bitmap)){
nprintf(("General","WARNING : could not unload background bitmap %s\n", Mht_bitmap_fname[gr_screen.res]));
}
// destroy the UI_WINDOW
Mht_window.destroy();
}
void mht_check_buttons()
{
int idx;
for(idx=0; idx<MHT_NUM_BUTTONS; idx++){
// we only really need to check for one button pressed at a time, so we can break after
// finding one.
if(Mht_buttons[gr_screen.res][idx].button.pressed()){
mht_button_pressed(idx);
break;
}
}
}
void mht_button_pressed(int n)
{
switch(n){
case MHT_READY_ROOM:
if (Player->flags & PLAYER_FLAGS_IS_MULTI){
main_hall_do_multi_ready();
} else {
gameseq_post_event(GS_EVENT_NEW_CAMPAIGN);
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
}
break;
case MHT_CAMPAIGN_ROOM:
gameseq_post_event(GS_EVENT_CAMPAIGN_ROOM);
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
break;
case MHT_OPTIONS:
gameseq_post_event(GS_EVENT_OPTIONS_MENU);
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
break;
case MHT_TECH_ROOM:
gameseq_post_event( GS_EVENT_TECH_MENU );
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
break;
case MHT_BARRACKS:
gameseq_post_event( GS_EVENT_BARRACKS_MENU );
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
break;
case MHT_EXIT:
mht_exit_game();
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
break;
}
}
void mht_exit_game()
{
// stop music first
main_hall_stop_music(true);
main_hall_stop_ambient();
gameseq_post_event(GS_EVENT_QUIT_GAME);
}
-33
View File
@@ -1,33 +0,0 @@
/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#ifndef __TEMP_MAIN_HALL_MENU_HEADER_FILE
#define __TEMP_MAIN_HALL_MENU_HEADER_FILE
// ------------------------------------------------------------------------------------------------------------------------
// TEMP MAIN HALL DEFINES/VARS
//
// ------------------------------------------------------------------------------------------------------------------------
// TEMP MAIN HALL FUNCTIONS
//
// initialize the temporary main hall
void mht_init();
// do a frame for the main hall
void mht_do();
// close the temporary main hall
void mht_close();
#endif
+8 -2
View File
@@ -917,7 +917,11 @@ int sim_room_button_pressed(int n)
case CAMPAIGN_TAB:
if ( !strlen(Campaign.filename) ) {
popup( PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "The currently active campaign cannot be found, unable to switch to campaign mode!", 1612));
if (Campaign_load_failure == CAMPAIGN_ERROR_MISSING) {
popup(PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR("The currently active campaign cannot be found. Unable to switch to campaign mode.", 1612));
} else {
popup(PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR("The currently active campaign cannot be loaded. Unable to switch to campaign mode.", 1816));
}
break;
}
@@ -1050,7 +1054,9 @@ void sim_room_init()
Campaign.filename[0] = 0;
Campaign.num_missions = 0;
mission_campaign_load_failure_popup();
// don't display the popup in the sim room - first because there is already logic to prevent listing campaign missions,
// second because there's not much the player can do about it in the sim room, and third because displaying the popup
// clears the error code
}
Num_campaign_missions = 0;
+32 -21
View File
@@ -60,7 +60,7 @@ char *Campaign_names[MAX_CAMPAIGNS] = { NULL };
char *Campaign_file_names[MAX_CAMPAIGNS] = { NULL };
char *Campaign_descs[MAX_CAMPAIGNS] = { NULL };
int Num_campaigns;
int Campaign_file_missing;
bool Campaign_file_missing = false;
int Campaign_load_failure = 0;
int Campaign_names_inited = 0;
SCP_vector<SCP_string> Ignored_campaigns;
@@ -428,7 +428,7 @@ int mission_campaign_load(const char* filename, const char* full_path, player* p
char name[NAME_LENGTH], type[NAME_LENGTH], temp[NAME_LENGTH];
if (campaign_is_ignored(filename)) {
Campaign_file_missing = 1;
Campaign_file_missing = true;
Campaign_load_failure = CAMPAIGN_ERROR_IGNORED;
return CAMPAIGN_ERROR_IGNORED;
}
@@ -634,19 +634,25 @@ int mission_campaign_load(const char* filename, const char* full_path, player* p
if ((Game_mode & GM_MULTIPLAYER) && Campaign.num_missions > UINT8_MAX)
throw parse::ParseException("Number of campaign missions is too high and breaks multi!");
}
catch (const parse::ParseException& e)
catch (const parse::FileOpenException& foe)
{
mprintf(("Error parsing '%s'\r\nError message = %s.\r\n", filename, e.what()));
mprintf(("Error opening '%s'\r\nError message = %s.\r\n", filename, foe.what()));
Campaign.filename[0] = 0;
Campaign.num_missions = 0;
if (!Fred_running && !(Game_mode & GM_MULTIPLAYER)) {
Campaign_file_missing = 1;
Campaign_load_failure = CAMPAIGN_ERROR_MISSING;
return CAMPAIGN_ERROR_MISSING;
}
Campaign_file_missing = true;
Campaign_load_failure = CAMPAIGN_ERROR_MISSING;
return CAMPAIGN_ERROR_MISSING;
}
catch (const parse::ParseException& pe)
{
mprintf(("Error parsing '%s'\r\nError message = %s.\r\n", filename, pe.what()));
Campaign.filename[0] = 0;
Campaign.num_missions = 0;
Campaign_file_missing = true;
Campaign_load_failure = CAMPAIGN_ERROR_CORRUPT;
return CAMPAIGN_ERROR_CORRUPT;
}
@@ -681,7 +687,7 @@ int mission_campaign_load(const char* filename, const char* full_path, player* p
}
// all is good here, move along
Campaign_file_missing = 0;
Campaign_file_missing = false;
return 0;
}
@@ -721,7 +727,7 @@ void mission_campaign_init()
{
mission_campaign_clear();
Campaign_file_missing = 0;
Campaign_file_missing = false;
player_loadout_init();
}
@@ -1539,26 +1545,31 @@ bool campaign_is_ignored(const char *filename)
return false;
}
static bool campaign_reportable_error(int val)
{
return val != 0 && val != CAMPAIGN_ERROR_MISSING && val != CAMPAIGN_ERROR_IGNORED;
}
// returns 0: loaded, !0: error
int mission_load_up_campaign( player *pl )
int mission_load_up_campaign(bool fall_back_from_current)
{
int rc = -1, idx;
if ( pl == NULL )
pl = Player;
auto pl = Player;
// find best campaign to use:
// 1) last used
// 2) builtin
// 3) anything else
// Note that in each step we only fall back when the error is benign, e.g. ignored or missing;
// if there's some other real error with the campaign file, we report it.
// Also note that if we *have* a current campaign, we shouldn't fall back *at all*, lest we repeatedly
// reset what the current campaign is, *unless* we are starting a brand new session or loading a new pilot.
// last used...
if ( strlen(pl->current_campaign) ) {
if (!campaign_is_ignored(pl->current_campaign)) {
return mission_campaign_load(pl->current_campaign, nullptr, pl);
}
else {
Campaign_file_missing = 1;
rc = mission_campaign_load(pl->current_campaign, nullptr, pl);
if (rc == 0 || !fall_back_from_current || campaign_reportable_error(rc)) {
return rc;
}
}
@@ -1566,7 +1577,7 @@ int mission_load_up_campaign( player *pl )
rc = mission_campaign_load(Default_campaign_file_name, nullptr, pl);
// everything else...
if (rc < 0) {
if (rc < 0 && !campaign_reportable_error(rc)) {
// no descriptions, no sorting
mission_campaign_build_list(false, false);
+3 -3
View File
@@ -161,8 +161,8 @@ extern SCP_vector<SCP_string> Ignored_campaigns;
extern char Default_campaign_file_name[MAX_FILENAME_LEN - 4];
// if the campaign file is missing this will get set for us to check against
extern int Campaign_file_missing;
extern bool Campaign_file_missing; // if the campaign file is missing this will get set for us to check against
extern int Campaign_load_failure;
/*
* initialise Player_loadout with default values
@@ -218,7 +218,7 @@ int mission_campaign_get_info(const char *filename, char *name, int *type, int *
int mission_campaign_get_mission_list(const char *filename, char **list, int max);
// load up a campaign for the current player.
int mission_load_up_campaign( player *p = NULL );
int mission_load_up_campaign(bool fall_back_from_current = false);
// stores mission goals and events in Campaign struct
void mission_campaign_store_goals_and_events();
+7 -3
View File
@@ -1963,7 +1963,9 @@ void debrief_init(bool API_Access)
// be backed out if used chooses to replace them.
scoring_level_close();
debrief_ui_init(); // init UI items
if (!API_Access) {
debrief_ui_init(); // init UI items
}
debrief_award_init();
show_stats_init();
debrief_voice_init();
@@ -2113,11 +2115,13 @@ void debrief_close(bool API_Access)
}
// unload bitmaps
if (Background_bitmap >= 0){
// Not used by the API
if (!API_Access && Background_bitmap >= 0) {
bm_release(Background_bitmap);
}
if (Award_bg_bitmap >= 0){
// Not used by the API
if (!API_Access && Award_bg_bitmap >= 0) {
bm_release(Award_bg_bitmap);
}
+4 -4
View File
@@ -1332,7 +1332,7 @@ void wl_load_icons(int weapon_class)
icon = &Wl_icons[weapon_class];
if (!Use_3d_weapon_icons || (wip->render_type == WRT_LASER && !strlen(wip->tech_model)))
if (!Use_3d_weapon_icons || (wip->render_type == WRT_LASER && !VALID_FNAME(wip->tech_model)))
{
first_frame = bm_load_animation(Weapon_info[weapon_class].icon_filename, &num_frames, nullptr, nullptr, nullptr, false, CF_TYPE_INTERFACE);
@@ -1343,9 +1343,9 @@ void wl_load_icons(int weapon_class)
multi_send_anti_timeout_ping();
if ( icon->model_index == -1 && ( ( strlen(wip->tech_model) && !strlen(wip->anim_filename) ) || (first_frame == -1) ) )
if ( icon->model_index == -1 && ( ( VALID_FNAME(wip->tech_model) && !VALID_FNAME(wip->anim_filename) ) || (first_frame == -1) ) )
{
if(strlen(wip->tech_model))
if(VALID_FNAME(wip->tech_model))
{
icon->model_index = model_load(wip->tech_model, 0, NULL, 0);
}
@@ -2742,7 +2742,7 @@ void weapon_select_do(float frametime)
weapon_info *wip = &Weapon_info[Selected_wl_class];
//Get the model
if (strlen(wip->tech_model)) {
if (VALID_FNAME(wip->tech_model)) {
modelIdx = model_load(wip->tech_model, 0, NULL, 0);
}
if (wip->render_type != WRT_LASER && modelIdx == -1) {
+1 -3
View File
@@ -162,16 +162,14 @@ static auto DiscordOption __UNUSED = options::OptionBuilder<bool>("Game.Discord"
if (!val) {
Discord_presence = false;
libs::discord::shutdown();
return true;
}
} else {
if (val) {
Discord_presence = true;
libs::discord::init();
return true;
}
}
return false;
return true;
})
.finish();
+3 -1
View File
@@ -299,13 +299,15 @@ namespace animation {
if (direction == ModelAnimationDirection::RWD) {
instanceData.state = ModelAnimationState::RUNNING;
instanceData.time -= timeOffset;
instanceData.time -= timeOffset;
instanceData.canonicalDirection = ModelAnimationDirection::RWD;
if (force)
instanceData.time = instant ? 0 : instanceData.duration - timeOffset;
}
else {
instanceData.state = ModelAnimationState::RUNNING;
instanceData.time += timeOffset;
instanceData.canonicalDirection = ModelAnimationDirection::FWD;
if (force)
instanceData.time = instant ? instanceData.duration : 0 + timeOffset;
}
+1 -1
View File
@@ -1091,7 +1091,7 @@ extern int modelstats_num_sortnorms;
#endif
// Tries to move joints so that the turret points to the point dst.
extern int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, ship_subsys *ss, vec3d *dst, bool reset = false);
extern bool model_rotate_gun(const object *objp, const polymodel *pm, const polymodel_instance *pmi, ship_subsys *ss, const vec3d *dst);
// Rotates the angle of a submodel. Use this so the right unlocked axis
// gets stuffed.
+1 -1
View File
@@ -66,7 +66,7 @@ namespace Model {
Turret_use_ammo, // enables ammo consumption for turrets (DahBlount)
Autorepair_if_disabled, // Allows the subsystem to repair itself even if disabled (MageKing17)
No_autorepair_if_disabled, // Inversion of the previous; disallows this particular subsystem if the ship-wide flag is set (MageKing17)
Share_fire_direction, // (DahBlount) Whenever the turret fires, make all firing points fire in the same direction.
Share_fire_direction, // (DahBlount) Whenever the turret fires a beam, make all beams fire in the same direction.
No_sparks, // Subsystem does not generate sparks if hit - m!m
No_impact_debris, // Don't spawn the small debris on impact - m!m
Hide_turret_from_loadout_stats, // Turret is not accounted for in auto-generated "Turrets" line in the ship loadout window --wookieejedi
+1 -1
View File
@@ -3294,7 +3294,7 @@ void bsp_polygon_data::replace_textures_used(const SCP_map<int, int>& replacemen
Num_verts[it->first] -= poly.Num_verts;
Num_verts[it->second] += poly.Num_verts;
--Num_polies[it->first];
++Num_polies[it->first];
++Num_polies[it->second];
}
}
}
+35 -15
View File
@@ -2886,6 +2886,8 @@ modelread_status read_model_file_no_subsys(polymodel * pm, const char* filename,
create_family_tree(pm);
// Now do submodel movement post-processing. This should come before all other error checking.
// ---------- submodel movement sanity checks ----------
for (i = 0; i < pm->n_models; i++) {
@@ -2931,6 +2933,16 @@ modelread_status read_model_file_no_subsys(polymodel * pm, const char* filename,
}
}
// And now look through all the submodels and set the model flag if any are intrinsic-moving
for (i = 0; i < pm->n_models; i++) {
if (pm->submodel[i].rotation_type == MOVEMENT_TYPE_INTRINSIC || pm->submodel[i].translation_type == MOVEMENT_TYPE_INTRINSIC) {
pm->flags |= PM_FLAG_HAS_INTRINSIC_MOTION;
break;
}
}
// -------------------- Now do any other error checking that validates data in the POF --------------------
// some dockpoint checks
for (i = 0; i < pm->n_docks; i++) {
auto dock = &pm->docking_bays[i];
@@ -2966,11 +2978,14 @@ modelread_status read_model_file_no_subsys(polymodel * pm, const char* filename,
}
}
// And now look through all the submodels and set the model flag if any are intrinsic-moving
for (i = 0; i < pm->n_models; i++) {
if (pm->submodel[i].rotation_type == MOVEMENT_TYPE_INTRINSIC || pm->submodel[i].translation_type == MOVEMENT_TYPE_INTRINSIC) {
pm->flags |= PM_FLAG_HAS_INTRINSIC_MOTION;
break;
// For several revisions, Pof Tools was writing invalid eyepoint data. We cannot guarantee that this will catch all instances,
// but should at least head off potential crashes before they happen
for (i = 0; i < pm->n_view_positions; ++i) {
if (pm->view_positions[i].parent < 0 || pm->view_positions[i].parent >= pm->n_models) {
nprintf(("Warning", "Model %s has an invalid eye position %i. Use a recent version of Pof Tools to fix the model.\n", pm->filename, i));
pm->view_positions[i].parent = 0;
pm->view_positions[i].norm = {{{1.0f, 0.0f, 0.0f}}};
pm->view_positions[i].pnt = ZERO_VECTOR;
}
}
@@ -3244,7 +3259,7 @@ void model_load_texture(polymodel *pm, int i, char *file)
//returns the number of the pof tech model if specified, otherwise number of pof model
int model_load(ship_info* sip, bool prefer_tech_model)
{
if (prefer_tech_model && sip->pof_file_tech[0] != '\0') {
if (prefer_tech_model && VALID_FNAME(sip->pof_file_tech)) {
// This cannot load into sip->subsystems, as this will overwrite the subsystems model_num to the
// techroom model, which is decidedly wrong for the mission itself.
return model_load(sip->pof_file_tech, 0, nullptr);
@@ -3283,6 +3298,11 @@ int model_load(const char* filename, int n_subsystems, model_subsystem* subsyst
return -1;
}
// Valid file
if (!VALID_FNAME(filename)) {
return -1;
}
TRACE_SCOPE(tracing::LoadModelFile);
mprintf(( "Loading model '%s' into slot '%i'\n", filename, num ));
@@ -4375,16 +4395,16 @@ void submodel_translate(bsp_info *sm, submodel_instance *smi)
submodel_canonicalize_translation(sm, smi);
}
// Tries to move joints so that the turret points to the point dst.
// Tries to move joints so that the turret points to the point dst. If dst is nullptr, the turret's joints are reset.
// turret1 is the angles of the turret, turret2 is the angles of the gun from turret
// Returns 1 if rotated gun, 0 if no gun to rotate (rotation handled by AI)
int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, ship_subsys *ss, vec3d *dst, bool reset)
// Returns true if rotated gun, false if no gun to rotate (rotation handled by AI)
bool model_rotate_gun(const object *objp, const polymodel *pm, const polymodel_instance *pmi, ship_subsys *ss, const vec3d *dst)
{
model_subsystem *turret = ss->system_info;
// This should not happen
if ( turret->turret_gun_sobj < 0 || turret->subobj_num == turret->turret_gun_sobj ) {
return 0;
return false;
}
auto base_sm = &pm->submodel[turret->subobj_num];
@@ -4401,7 +4421,7 @@ int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, ship_
// Find the heading and pitch that the gun needs to turn to
float desired_base_angle, desired_gun_angle;
if (!reset) {
if (dst) {
vec3d world_axis, world_pos, planar_dst, dir, rotated_vec;
matrix save_base_orient;
@@ -4476,10 +4496,10 @@ int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, ship_
float step_size = turret->turret_turning_rate * calc_time;
float base_delta, gun_delta;
if (reset)
step_size /= 3.0f;
else
if (dst)
ss->rotation_timestamp = timestamp(turret->turret_reset_delay);
else
step_size /= 3.0f;
base_delta = vm_interp_angle(&base_smi->cur_angle, desired_base_angle, step_size, turret->turret_base_fov > -1.0f);
gun_delta = vm_interp_angle(&gun_smi->cur_angle, desired_gun_angle, step_size);
@@ -4525,7 +4545,7 @@ int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, ship_
ss->points_to_target = sqrt((base_delta*base_delta) + (gun_delta*gun_delta));
}
return 1;
return true;
}
+14 -23
View File
@@ -127,7 +127,7 @@ bool read_virtual_model_file(polymodel* pm, const SCP_string& filename, model_pa
const auto& virtual_pof = virtual_pof_it->second[depthLocal];
depthLocal++;
read_model_file(pm, filename.c_str(), ferror, deferredTasks, depth);
read_model_file(pm, virtual_pof.basePOF.c_str(), ferror, deferredTasks, depth);
for (const auto& operation : virtual_pof.operationList)
operation->process(pm, deferredTasks, depth, virtual_pof);
@@ -262,18 +262,14 @@ int reallocate_and_copy_array_vmalloc(T*& array, int& size, size_t to_add) {
return size_before;
}
#define REPLACE_IF_EQ(data) if ((data) == source) (data) = dest;
#define REPLACE_IF_STRIEQ(data) if (stricmp(data, source.c_str()) == 0) strncpy_s(data, dest.c_str(), dest.size());
#define REPLACE_IF_SCPSTRIEQ(data) if (stricmp(data.c_str(), source.c_str()) == 0) data = dest;
#define REPLACE_IF_EQ(data) { auto iter = replace.find(data); if (iter != replace.end()) (data) = iter->second; }
#define REPLACE_IF_STRIEQ(data) { auto iter = replace.find(data); if (iter != replace.end()) strncpy_s(data, iter->second.c_str(), iter->second.size()); }
//Generates one function for replacing data in a type, which is a map entry of which the key may be replaced. Takes an rvalue reference, used for making a copy and modifying the temporary to then assign it somewhere
#define CHANGE_HELPER_MAP_KEY(name, intype, argtype) template<typename map_t> static typename std::enable_if<std::is_same<typename map_t::value_type, std::pair<const argtype, argtype>>::value, intype>::type name(intype&& pass, map_t replace){ \
const auto it = replace.find(pass.first); \
intype input = { (it == replace.end() ? pass.first : it->second), pass.second }; \
for (const auto& replacement : replace) { \
const argtype& source = replacement.first; \
const argtype& dest = replacement.second;
#define CHANGE_HELPER_MAP_KEY_END } return input; }
intype input = { (it == replace.end() ? pass.first : it->second), pass.second };
#define CHANGE_HELPER_MAP_KEY_END return input; }
//Generates two functions for replacing data in a type. One that takes an rvalue reference, used for making a copy and modifying the temporary to then assign it somewhere, and one which takes an lvalue reference for modifying in-place
#define CHANGE_HELPER(name, intype, argtype) template<typename map_t> static typename std::enable_if<std::is_same<typename map_t::value_type, std::pair<const argtype, argtype>>::value>::type name(intype& input, map_t replace); \
@@ -281,11 +277,8 @@ template<typename map_t> inline static typename std::enable_if<std::is_same<type
name<map_t>(input, replace); \
return std::move(input); \
} \
template<typename map_t> static typename std::enable_if<std::is_same<typename map_t::value_type, std::pair<const argtype, argtype>>::value>::type name(intype& input, map_t replace) {\
for (const auto& replacement : replace) { \
const argtype& source = replacement.first; \
const argtype& dest = replacement.second;
#define CHANGE_HELPER_END }}
template<typename map_t> static typename std::enable_if<std::is_same<typename map_t::value_type, std::pair<const argtype, argtype>>::value>::type name(intype& input, map_t replace) {
#define CHANGE_HELPER_END }
//Change submodel numbers in a submodel
CHANGE_HELPER(change_submodel_numbers, bsp_info, int)
@@ -324,19 +317,15 @@ CHANGE_HELPER_END
//Change submodel name in a subsystem definition
CHANGE_HELPER_MAP_KEY(change_submodel_name, model_read_deferred_tasks::model_subsystem_pair, SCP_string)
SCP_UNUSED(source);
SCP_UNUSED(dest);
CHANGE_HELPER_MAP_KEY_END
//Change subsystem name in an engine subsys definition
CHANGE_HELPER(change_submodel_name, model_read_deferred_tasks::engine_subsystem_pair, SCP_string)
REPLACE_IF_SCPSTRIEQ(input.second.subsystem_name)
REPLACE_IF_EQ(input.second.subsystem_name)
CHANGE_HELPER_END
//Change engine numbers in an engine subsys definition
CHANGE_HELPER_MAP_KEY(change_engine_numbers, model_read_deferred_tasks::engine_subsystem_pair, int)
SCP_UNUSED(source);
SCP_UNUSED(dest);
CHANGE_HELPER_MAP_KEY_END
@@ -392,15 +381,17 @@ void VirtualPOFOperationAddSubmodel::process(polymodel* pm, model_read_deferred_
SCP_vector<int> to_copy_submodels;
bool has_name_collision = false;
if (copyChildren) {
model_iterate_submodel_tree(appendingPM, src_subobj_no, [&to_copy_submodels, &has_name_collision, pm, appendingPM](int submodel, int /*level*/, bool /*isLeaf*/) {
model_iterate_submodel_tree(appendingPM, src_subobj_no, [&to_copy_submodels, &has_name_collision, &renameMap, pm, appendingPM](int submodel, int /*level*/, bool /*isLeaf*/) {
to_copy_submodels.emplace_back(submodel);
if(model_find_submodel_index(pm, appendingPM->submodel[submodel].name) != -1)
auto replace_with_name_it = renameMap.find(appendingPM->submodel[submodel].name);
if(model_find_submodel_index(pm, replace_with_name_it == renameMap.end() ? appendingPM->submodel[submodel].name : replace_with_name_it->second.c_str()) != -1)
has_name_collision = true;
});
}
else {
to_copy_submodels.emplace_back(src_subobj_no);
if (model_find_submodel_index(pm, appendingPM->submodel[src_subobj_no].name) != -1)
auto replace_with_name_it = renameMap.find(appendingPM->submodel[src_subobj_no].name);
if(model_find_submodel_index(pm, replace_with_name_it == renameMap.end() ? appendingPM->submodel[src_subobj_no].name : replace_with_name_it->second.c_str()) != -1)
has_name_collision = true;
}
@@ -650,7 +641,7 @@ void VirtualPOFOperationAddEngine::process(polymodel* pm, model_read_deferred_ta
if (subsystemName) {
if (renameSubsystem) {
const SCP_unordered_map<SCP_string, SCP_string> thrusterSubsysRenameMap{ {*subsystemName, *renameSubsystem} };
const VirtualPOFNameReplacementMap thrusterSubsysRenameMap{ {*subsystemName, *renameSubsystem} };
deferredTasks.engine_subsystems.emplace(change_submodel_name(
change_engine_numbers(model_read_deferred_tasks::engine_subsystem_pair(*it), thrusterReplacementMap),
thrusterSubsysRenameMap));
+1 -1
View File
@@ -736,7 +736,7 @@ void multi_assign_player_ship( int net_player_num, object *objp,int ship_class )
}
// -------------------------------------------------------------------------------------------------
// create_player() is called when a net player needs to be instantiated. The ship that is created
// multi_create_player() is called when a net player needs to be instantiated. The ship that is created
// depends on the parameter ship_class. Note that if ship_class is invalid, the ship default_player_ship
// is used. Returns 1 on success, 0 otherwise
+3 -1
View File
@@ -27,6 +27,8 @@ int Num_pairs_checked = 0;
SCP_vector<int> Collision_sort_list;
static_assert(1 << collision_cache_bitshift > MAX_OBJECTS, "Collision pair caching currently relies on the highest possible objnum being less than 2^collision_cache_bitshift.");
class collider_pair
{
public:
@@ -867,7 +869,7 @@ void obj_collide_pair(object *A, object *B)
}
bool valid = false;
uint key = (OBJ_INDEX(A) << 12) + OBJ_INDEX(B);
uint key = (OBJ_INDEX(A) << collision_cache_bitshift) + OBJ_INDEX(B);
collider_pair* collision_info = &Collision_cached_pairs[key];
+2
View File
@@ -43,6 +43,8 @@ struct collision_info_struct {
#define MIN_LANDING_SOUND_VEL 2.0f
#define LANDING_POS_OFFSET 0.05f
constexpr uint32_t collision_cache_bitshift = 16;
//===============================================================================
// GENERAL COLLISION DETECTION HELPER FUNCTIONS
// These are in CollideGeneral.cpp and are used by one or more of the collision-
+2 -2
View File
@@ -127,10 +127,10 @@ class object
public:
class object *next, *prev; // for linked lists of objects
int signature; // Every object ever has a unique signature...
char type; // what type of object this is... robot, weapon, hostage, powerup, fireball
char type; // what type of object this is... ship, weapon, debris, asteroid, fireball, see OBJ_* defines above
int parent; // This object's parent.
int parent_sig; // This object's parent's signature
int instance; // which instance. ie.. if type is Robot, then this indexes into the Robots array
int instance; // index into the corresponding type array, i.e. if type == OBJ_SHIP then instance indexes the Ships array
flagset<Object::Object_Flags> flags; // misc flags. Call obj_set_flags to change this.
vec3d pos; // absolute x,y,z coordinate of center of object
matrix orient; // orientation of object in world
+16
View File
@@ -196,4 +196,20 @@ void OptionsManager::loadInitialValues()
}
}
void OptionsManager::printValues()
{
mprintf(("Printing in-game options values!\n"));
for (auto& opt : _options) {
// If we're not using in-game options and the option is not a retail option, then skip
// This ensures we only log options that are actually impacting the current game instance
// This code set aside until PR 5895 is merged
//if (!Using_in_game_options && !(opt->getFlags()[options::OptionFlags::RetailBuiltinOption])){
// continue;
//}
mprintf(("Option.%s: %s\n",
opt->getConfigKey().c_str(),
opt->getCurrentValueDescription().display.c_str()));
}
}
} // namespace options
+2
View File
@@ -54,6 +54,8 @@ class OptionsManager {
void discardChanges();
void loadInitialValues();
void printValues();
};
}
+1 -1
View File
@@ -100,7 +100,7 @@ namespace os
//buttons not clickable. So as a hotfix, the dialog has no blocking parent in that case and
//is instead rendered as it's own window
SDL_Window* getDialogParent() {
return !(Cmdline_fullscreen_window || Cmdline_window) ? NULL : os::getSDLMainWindow();
return !(gr_is_viewport_window()) ? NULL : os::getSDLMainWindow();
}
void AssertMessage(const char * text, const char * filename, int linenum, const char * format, ...)
+2 -6
View File
@@ -113,9 +113,7 @@ namespace
case SDL_WINDOWEVENT_FOCUS_LOST:
{
if (fAppActive) {
if (!Cmdline_no_unfocus_pause) {
game_pause();
}
game_pause();
fAppActive = false;
}
@@ -126,9 +124,7 @@ namespace
case SDL_WINDOWEVENT_FOCUS_GAINED:
{
if (!fAppActive) {
if (!Cmdline_no_unfocus_pause) {
game_unpause();
}
game_unpause();
fAppActive = true;
}
+13 -9
View File
@@ -2104,10 +2104,11 @@ int parse_get_line(char *lineout, int max_line_len, const char *start, int max_s
// Goober5000 - added ability to read somewhere other than Parse_text
void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
{
// copy the filename
Assertion(filename, "Filename must not be null!");
if (!filename)
throw parse::ParseException("Invalid filename");
throw parse::FileOpenException("Filename must not be null!");
// copy the filename
strcpy_s(Current_filename_sub, filename);
// if we are paused then processed_text and raw_text must not be NULL!!
@@ -2231,13 +2232,15 @@ void read_raw_file_text(const char *filename, int mode, char *raw_text)
CFILE *mf;
int file_is_encrypted;
Assert(filename);
Assertion(filename, "Filename must not be null!");
if (!filename)
throw parse::FileOpenException("Filename must not be null!");
mf = cfopen(filename, "rb", CFILE_NORMAL, mode);
if (mf == NULL)
{
nprintf(("Error", "Wokka! Error opening file (%s)!\n", filename));
throw parse::ParseException("Failed to open file");
throw parse::FileOpenException("Failed to open file");
}
// read the entire file in
@@ -2245,7 +2248,7 @@ void read_raw_file_text(const char *filename, int mode, char *raw_text)
if(!file_len) {
nprintf(("Error", "Oh noes!! File is empty! (%s)!\n", filename));
throw parse::ParseException("Failed to open file");
throw parse::ParseException("File is empty");
}
// For the possible Latin1 -> UTF-8 conversion we need to reallocate the raw_text at some point and we can only do
@@ -2469,10 +2472,11 @@ void read_file_bytes(const char *filename, int mode, char *raw_bytes)
{
CFILE *mf;
// copy the filename
Assertion(filename, "Filename must not be null!");
if (!filename)
throw parse::ParseException("Invalid filename");
throw parse::FileOpenException("Filename must not be null!");
// copy the filename
strcpy_s(Current_filename_sub, filename);
// if we are paused then raw_bytes must not be NULL!!
@@ -2484,7 +2488,7 @@ void read_file_bytes(const char *filename, int mode, char *raw_bytes)
if (mf == nullptr)
{
nprintf(("Error", "Wokka! Error opening file (%s)!\n", filename));
throw parse::ParseException("Failed to open file");
throw parse::FileOpenException("Failed to open file");
}
// read the entire file in
@@ -2492,7 +2496,7 @@ void read_file_bytes(const char *filename, int mode, char *raw_bytes)
if(!file_len) {
nprintf(("Error", "Oh noes!! File is empty! (%s)!\n", filename));
throw parse::ParseException("Failed to open file");
throw parse::ParseException("File is empty");
}
if (raw_bytes == nullptr) {
+7
View File
@@ -418,6 +418,13 @@ namespace parse
~ParseException() noexcept override = default;
};
class FileOpenException : public ParseException
{
public:
explicit FileOpenException(const std::string& msg) : ParseException(msg) {}
~FileOpenException() noexcept override = default;
};
class VersionException : public std::runtime_error
{
private:
+423 -344
View File
File diff suppressed because it is too large. Load diff
+8
View File
@@ -1243,6 +1243,12 @@ enum sexp_error_check
SEXP_CHECK_INVALID_BOLT_TYPE,
SEXP_CHECK_INVALID_TRAITOR_OVERRIDE,
SEXP_CHECK_INVALID_LUA_GENERAL_ORDER,
SEXP_CHECK_INVALID_SHIP_POINT,
SEXP_CHECK_INVALID_SHIP_WING_SHIPONTEAM_POINT,
SEXP_CHECK_INVALID_SHIP_WING_POINT,
SEXP_CHECK_INVALID_ORDER_RECIPIENT,
SEXP_CHECK_INVALID_SHIP_WING_WHOLETEAM,
SEXP_CHECK_MUST_BE_INTEGER,
};
@@ -1344,6 +1350,8 @@ typedef struct sexp_node {
#define SNF_SPECIAL_ARG_IN_TREE (1<<3)
#define SNF_SPECIAL_ARG_NOT_IN_TREE (1<<4)
#define SNF_CHECKED_ARG_FOR_VAR (1<<5)
#define SNF_CHECKED_NODE_FOR_OPF_POSITIVE (1<<6)
#define SNF_NODE_IS_OPF_POSITIVE (1<<7)
#define SNF_DEFAULT_VALUE SNF_ARGUMENT_VALID
typedef struct sexp_variable {
+3
View File
@@ -68,6 +68,7 @@ ADE_VIRTVAR(MultiLogin, l_Options, "string", "The multiplayer PXO login name", "
if (ADE_SETTING_VAR && (login != nullptr)) {
strcpy_s(Multi_tracker_login, login);
os_config_write_string("PXO", "Login", login);
}
return ade_set_args(L, "s", Multi_tracker_login);
@@ -80,6 +81,7 @@ ADE_VIRTVAR(MultiPassword, l_Options, "string", "The multiplayer PXO login passw
if (ADE_SETTING_VAR && (pswd != nullptr)) {
strcpy_s(Multi_tracker_passwd, pswd);
os_config_write_string("PXO", "Password", pswd);
}
return ade_set_args(L, "b", (strlen(Multi_tracker_passwd) != 0));
@@ -92,6 +94,7 @@ ADE_VIRTVAR(MultiSquad, l_Options, "string", "The multiplayer PXO squad name", "
if (ADE_SETTING_VAR && (squad != nullptr)) {
strcpy_s(Multi_tracker_squad_name, squad);
os_config_write_string("PXO", "SquadName", squad);
}
return ade_set_args(L, "s", Multi_tracker_squad_name);
+42 -4
View File
@@ -259,6 +259,11 @@ ADE_FUNC(playCutscene, l_UserInterface, "string Filename, boolean RestartMusic,
return ADE_RETURN_NIL;
}
ADE_FUNC(isCutscenePlaying, l_UserInterface, nullptr, "Checks if a cutscene is playing.", "boolean", "Returns true if cutscene is playing, false otherwise")
{
return ade_set_args(L, "b", Movie_active);
}
ADE_FUNC(launchURL, l_UserInterface, "string url", "Launches the given URL in a web browser", nullptr, nullptr)
{
const char* url;
@@ -388,6 +393,23 @@ ADE_FUNC(
return ade_set_args(L, "b", player_create_new_pilot(callsign, is_multi, copy_from));
}
ADE_FUNC(unloadPilot,
l_UserInterface_PilotSelect,
nullptr,
"Unloads a player file & associated campaign file. Can not be used outside of pilot select!",
"boolean",
"Returns true if successful, false otherwise")
{
if (gameseq_get_state() == GS_STATE_INITIAL_PLAYER_SELECT) {
Player = nullptr;
Campaign.filename[0] = '\0';
return ADE_RETURN_TRUE;
}
return ADE_RETURN_FALSE;
}
ADE_FUNC(isAutoselect, l_UserInterface_PilotSelect, nullptr,
"Determines if the pilot selection screen should automatically select the default user.", "boolean",
"true if autoselect is enabled, false otherwise")
@@ -1620,12 +1642,28 @@ ADE_LIB_DERIV(l_UserInterface_Cutscenes, "Cutscenes", nullptr, nullptr, l_UserIn
ADE_INDEXER(l_UserInterface_Cutscenes,
"number Index",
"Array of cutscenes",
"custscene_info",
"cutscene_info",
"Cutscene handle, or invalid handle if index is invalid")
{
int idx;
if (!ade_get_args(L, "*i", &idx))
return ade_set_error(L, "s", "");
const char* name;
if (!ade_get_args(L, "*s", &name))
return ade_set_error(L, "o", l_TechRoomCutscene.Set(cutscene_info_h(-1)));
int idx = get_cutscene_index_by_name(name);
if (idx < 0) {
try {
idx = std::stoi(name);
idx--; // Lua->FS2
} catch (const std::exception&) {
// Not a number
return ade_set_error(L, "o", l_TechRoomCutscene.Set(cutscene_info_h(-1)));
}
if (!SCP_vector_inbounds(Cutscenes, idx)) {
return ade_set_error(L, "o", l_TechRoomCutscene.Set(cutscene_info_h(-1)));
}
}
return ade_set_args(L, "o", l_TechRoomCutscene.Set(cutscene_info_h(idx)));
}
+36 -2
View File
@@ -15,15 +15,24 @@ dynamic_sexp_enum_list* lua_enum_h::getEnum() const
return &Dynamic_enums[lua_enum];
}
bool lua_enum_h::isValid() const
{
return SCP_vector_inbounds(Dynamic_enums, lua_enum);
}
ADE_OBJ(l_LuaEnum, lua_enum_h, "LuaEnum", "Lua Enum handle");
ADE_VIRTVAR(Name, l_LuaEnum, "string", "The enum name", "string", "The enum name")
ADE_VIRTVAR(Name, l_LuaEnum, "string", "The enum name", "string", "The enum name or nil if handle is invalid")
{
lua_enum_h lua_enum;
const char* enum_name;
if (!ade_get_args(L, "o|s", l_LuaEnum.Get(&lua_enum), &enum_name))
return ade_set_error(L, "o", l_LuaEnum.Set(lua_enum_h()));
if (!lua_enum.isValid()) {
return ADE_RETURN_NIL;
}
if (ADE_SETTING_VAR) {
LuaError(L, "This property is read only.");
}
@@ -38,6 +47,10 @@ ADE_INDEXER(l_LuaEnum, "number Index", "Array of enum items", "string", "enum it
if (!ade_get_args(L, "o|i", l_LuaEnum.Get(&lua_enum), &idx))
return ADE_RETURN_NIL;
if (!lua_enum.isValid()) {
return ADE_RETURN_NIL;
}
idx--; //Convert from lua 1 based index
if ((idx < 0) || (idx >= static_cast<int>(lua_enum.getEnum()->list.size())))
return ADE_RETURN_NIL;
@@ -45,12 +58,16 @@ ADE_INDEXER(l_LuaEnum, "number Index", "Array of enum items", "string", "enum it
return ade_set_args(L, "s", lua_enum.getEnum()->list[idx].c_str());
}
ADE_FUNC(__len, l_LuaEnum, nullptr, "The number of Lua enum items", "number", "The number of Lua enums item.")
ADE_FUNC(__len, l_LuaEnum, nullptr, "The number of Lua enum items", "number", "The number of Lua enums item or nil if handle is invalid")
{
lua_enum_h lua_enum;
if (!ade_get_args(L, "o", l_LuaEnum.Get(&lua_enum)))
return ade_set_error(L, "i", 0);
if (!lua_enum.isValid()) {
return ADE_RETURN_NIL;
}
return ade_set_args(L, "i", static_cast<int>(lua_enum.getEnum()->list.size()));
}
@@ -66,6 +83,10 @@ ADE_FUNC(addEnumItem,
if (!ade_get_args(L, "os", l_LuaEnum.Get(&lua_enum) , &item_name))
return ADE_RETURN_FALSE;
if (!lua_enum.isValid()) {
return ADE_RETURN_FALSE;
}
for (size_t i = 0; i < lua_enum.getEnum()->list.size(); i++) {
if (!stricmp(item_name, lua_enum.getEnum()->list[i].c_str())) {
return ADE_RETURN_FALSE;
@@ -89,6 +110,10 @@ ADE_FUNC(removeEnumItem,
if (!ade_get_args(L, "os", l_LuaEnum.Get(&lua_enum), &item_name))
return ADE_RETURN_FALSE;
if (!lua_enum.isValid()) {
return ADE_RETURN_FALSE;
}
size_t i;
for (i = 0; i < lua_enum.getEnum()->list.size(); i++) {
@@ -103,5 +128,14 @@ ADE_FUNC(removeEnumItem,
return ADE_RETURN_FALSE;
}
ADE_FUNC(isValid, l_LuaEnum, nullptr, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs")
{
lua_enum_h lua_enum;
if (!ade_get_args(L, "o", l_LuaEnum.Get(&lua_enum)))
return ADE_RETURN_NIL;
return ade_set_args(L, "b", lua_enum.isValid());
}
} // namespace api
} // namespace scripting
+1
View File
@@ -12,6 +12,7 @@ struct lua_enum_h {
lua_enum_h();
explicit lua_enum_h(int idx);
dynamic_sexp_enum_list* getEnum() const;
bool isValid() const;
};
DECLARE_ADE_OBJ(l_LuaEnum, lua_enum_h);
+5 -4
View File
@@ -1267,10 +1267,10 @@ ADE_FUNC(renderTechModel2, l_Shipclass, "number X1, number Y1, number X2, number
ADE_FUNC(renderSelectModel,
l_Shipclass,
"boolean restart, number x, number y, [number width = 629, number height = 355, number = currentEffectSetting]",
"boolean restart, number x, number y, [number width = 629, number height = 355, number currentEffectSetting = default, number zoom = 1.3]",
"Draws the 3D select ship model with the chosen effect at the specified coordinates. Restart should "
"be true on the first frame this is called and false on subsequent frames. Valid selection effects are 1 (fs1) or 2 (fs2), "
"defaults to the mod setting or the model's setting.",
"defaults to the mod setting or the model's setting. Zoom is a multiplier to the model's closeup_zoom value.",
"boolean",
"true if rendered, false if error")
{
@@ -1281,7 +1281,8 @@ ADE_FUNC(renderSelectModel,
int x2 = 629;
int y2 = 355;
int effect = -1;
if (!ade_get_args(L, "obii|iii", l_Shipclass.Get(&idx), &restart, &x1, &y1, &x2, &y2, &effect))
float zoom = 1.3f;
if (!ade_get_args(L, "obii|iiif", l_Shipclass.Get(&idx), &restart, &x1, &y1, &x2, &y2, &effect, &zoom))
return ADE_RETURN_NIL;
if (idx < 0 || idx >= ship_info_size())
@@ -1331,7 +1332,7 @@ ADE_FUNC(renderSelectModel,
y2,
&ShipRot,
&sip->closeup_pos,
sip->closeup_zoom * 1.3f,
sip->closeup_zoom * zoom,
rev_rate,
MR_AUTOCENTER | MR_NO_FOGGING,
GR_RESIZE_NONE,
+6 -6
View File
@@ -15,7 +15,7 @@
#include "network/multiutil.h"
void sexp_beam_or_turret_free_one(ship_subsys *turret, bool is_beam, bool free);
bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, vec3d *turret_pos, vec3d *firing_vec, vec3d *predicted_pos = nullptr, float flak_range_override = 100.0f, bool play_sound = true);
bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, const vec3d *orig_firing_pos, const vec3d *orig_firing_vec, const vec3d *predicted_pos = nullptr, float flak_range_override = 100.0f, bool play_sound = true);
namespace scripting {
namespace api {
@@ -865,7 +865,7 @@ ADE_FUNC(fireWeapon, l_Subsystem, "[number TurretWeaponIndex = 1, number FlakRan
//Get default turret info
vec3d gpos, gvec;
ship_get_global_turret_gun_info(sso->objp, sso->ss, &gpos, &gvec, 1, NULL);
ship_get_global_turret_gun_info(sso->objp, sso->ss, &gpos, false, &gvec, true, nullptr);
if (override_gvec != nullptr)
gvec = *override_gvec;
@@ -896,7 +896,7 @@ ADE_FUNC(rotateTurret, l_Subsystem, "vector Pos, boolean reset=false", "Rotates
auto pmi = model_get_instance(Ships[objp->instance].model_instance_num);
auto pm = model_get(pmi->model_num);
int ret_val = model_rotate_gun(objp, pm, pmi, sso->ss, &pos, reset);
bool ret_val = model_rotate_gun(objp, pm, pmi, sso->ss, reset ? nullptr : &pos);
if (ret_val)
return ADE_RETURN_TRUE;
@@ -961,7 +961,7 @@ ADE_FUNC(
vec3d gpos, gvec;
ship_get_global_turret_gun_info(sso->objp, sso->ss, &gpos, &gvec, 1, NULL);
ship_get_global_turret_gun_info(sso->objp, sso->ss, &gpos, false, &gvec, true, nullptr);
return ade_set_args(L, "oo", l_Vector.Set(gpos), l_Vector.Set(gvec));
}
@@ -1014,9 +1014,9 @@ ADE_FUNC(isInViewFrom, l_Subsystem, "vector from",
get_subsystem_world_pos(sso->objp, sso->ss, &world_pos);
// Disable facing check since the HUD code does the same
int in_sight = ship_subsystem_in_sight(sso->objp, sso->ss, from, &world_pos, 0);
bool in_sight = ship_subsystem_in_sight(sso->objp, sso->ss, from, &world_pos, false);
return ade_set_args(L, "b", in_sight != 0);
return ade_set_args(L, "b", in_sight);
}
ADE_FUNC(isValid, l_Subsystem, NULL, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs")
+20 -10
View File
@@ -24,10 +24,10 @@ cutscene_info_h::cutscene_info_h(int scene) : cutscene(scene) {}
bool cutscene_info_h::IsValid() const
{
return cutscene >= 0;
return SCP_vector_inbounds(Cutscenes, cutscene);
}
cutscene_info* cutscene_info_h::getStage() const
cutscene_info* cutscene_info_h::getScene() const
{
return &Cutscenes[cutscene];
}
@@ -120,7 +120,7 @@ ADE_VIRTVAR(isCampaignMission, l_TechRoomMission, nullptr, "If the mission is ca
}
//**********HANDLE: tech cutscenes
ADE_OBJ(l_TechRoomCutscene, cutscene_info_h, "custscene_info", "Tech Room cutscene handle");
ADE_OBJ(l_TechRoomCutscene, cutscene_info_h, "cutscene_info", "Tech Room cutscene handle");
ADE_VIRTVAR(Name, l_TechRoomCutscene, nullptr, "The name of the cutscene", "string", "The cutscene name")
{
@@ -133,7 +133,7 @@ ADE_VIRTVAR(Name, l_TechRoomCutscene, nullptr, "The name of the cutscene", "stri
LuaError(L, "This property is read only.");
}
return ade_set_args(L, "s", current.getStage()->name);
return ade_set_args(L, "s", current.getScene()->name);
}
ADE_VIRTVAR(Filename, l_TechRoomCutscene, nullptr, "The filename of the cutscene", "string", "The cutscene filename")
@@ -147,7 +147,7 @@ ADE_VIRTVAR(Filename, l_TechRoomCutscene, nullptr, "The filename of the cutscene
LuaError(L, "This property is read only.");
}
return ade_set_args(L, "s", current.getStage()->filename);
return ade_set_args(L, "s", current.getScene()->filename);
}
ADE_VIRTVAR(Description, l_TechRoomCutscene, nullptr, "The cutscene description", "string", "The cutscene description")
@@ -161,7 +161,7 @@ ADE_VIRTVAR(Description, l_TechRoomCutscene, nullptr, "The cutscene description"
LuaError(L, "This property is read only.");
}
return ade_set_args(L, "s", current.getStage()->description);
return ade_set_args(L, "s", current.getScene()->description);
}
ADE_VIRTVAR(isVisible,
@@ -179,8 +179,8 @@ ADE_VIRTVAR(isVisible,
if (ADE_SETTING_VAR) {
LuaError(L, "This property is read only.");
}
if (current.getStage()->flags[Cutscene::Cutscene_Flags::Viewable, Cutscene::Cutscene_Flags::Always_viewable] &&
!current.getStage()->flags[Cutscene::Cutscene_Flags::Never_viewable])
if (current.getScene()->flags[Cutscene::Cutscene_Flags::Viewable, Cutscene::Cutscene_Flags::Always_viewable] &&
!current.getScene()->flags[Cutscene::Cutscene_Flags::Never_viewable])
{
return ade_set_args(L, "b", true);
} else {
@@ -202,7 +202,7 @@ ADE_VIRTVAR(CustomData, l_TechRoomCutscene, nullptr, "Gets the custom data table
auto table = luacpp::LuaTable::create(L);
for (const auto& pair : current.getStage()->custom_data)
for (const auto& pair : current.getScene()->custom_data)
{
table.addValue(pair.first, pair.second);
}
@@ -217,9 +217,19 @@ ADE_FUNC(hasCustomData, l_TechRoomCutscene, nullptr, "Detects whether the cutsce
return ADE_RETURN_NIL;
}
bool result = !current.getStage()->custom_data.empty();
bool result = !current.getScene()->custom_data.empty();
return ade_set_args(L, "b", result);
}
ADE_FUNC(isValid, l_TechRoomCutscene, NULL, "Detects whether cutscene is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs")
{
cutscene_info_h current;
if (!ade_get_args(L, "o", l_TechRoomCutscene.Get(&current))) {
return ADE_RETURN_NIL;
}
return ade_set_args(L, "b", current.IsValid());
}
} // namespace api
} // namespace scripting
+1 -1
View File
@@ -21,7 +21,7 @@ struct cutscene_info_h {
cutscene_info_h();
explicit cutscene_info_h(int scene);
bool IsValid() const;
cutscene_info* getStage() const;
cutscene_info* getScene() const;
};
DECLARE_ADE_OBJ(l_TechRoomMission, sim_mission_h);
+5 -4
View File
@@ -846,11 +846,11 @@ ADE_FUNC(renderTechModel2,
ADE_FUNC(renderSelectModel,
l_Weaponclass,
"number x, number y, [number width = 629, number height = 355, number = currentEffectSetting]",
"number x, number y, [number width = 629, number height = 355, number currentEffectSetting = default, number zoom = 0.65]",
"Draws the 3D select weapon model with the chosen effect at the specified coordinates. Restart should "
"be true on the first frame this is called and false on subsequent frames. Note that primary weapons "
"will not render anything if they do not have a valid pof model defined! Valid selection effects are 1 (fs1) or 2 (fs2), "
"defaults to the mod setting or the model's setting.",
"defaults to the mod setting or the model's setting. Zoom is a multiplier to the model's closeup_zoom value.",
"boolean",
"true if rendered, false if error")
{
@@ -861,7 +861,8 @@ ADE_FUNC(renderSelectModel,
int x2 = 629;
int y2 = 355;
int effect = -1;
if (!ade_get_args(L, "obii|iii", l_Weaponclass.Get(&idx), &restart, &x1, &y1, &x2, &y2, &effect))
float zoom = 0.65f;
if (!ade_get_args(L, "obii|iiif", l_Weaponclass.Get(&idx), &restart, &x1, &y1, &x2, &y2, &effect, &zoom))
return ADE_RETURN_NIL;
if (idx < 0 || idx >= weapon_info_size())
@@ -903,7 +904,7 @@ ADE_FUNC(renderSelectModel,
y2,
&WepRot,
&wip->closeup_pos,
wip->closeup_zoom * 0.65f,
wip->closeup_zoom * zoom,
REVOLUTION_RATE,
MR_IS_MISSILE | MR_AUTOCENTER | MR_NO_FOGGING,
GR_RESIZE_NONE,
+4
View File
@@ -12,6 +12,10 @@ const std::shared_ptr<Hook<>> OnGameInit = Hook<>::Factory("On Game Init",
tl::nullopt,
CHA_GAMEINIT);
const std::shared_ptr<Hook<>> OnSplashEnd = Hook<>::Factory("On Splash End",
"Executed just after the splash screen fades out.",
{});
const std::shared_ptr<Hook<>> OnIntroAboutToPlay = Hook<>::Factory("On Intro About To Play",
"Executed just before the intro movie is played.",
{});
+1
View File
@@ -6,6 +6,7 @@ namespace scripting {
namespace hooks {
extern const std::shared_ptr<Hook<>> OnGameInit;
extern const std::shared_ptr<Hook<>> OnSplashEnd;
extern const std::shared_ptr<Hook<>> OnIntroAboutToPlay;
//The On State Start hook previously used to pass OldState to the conditions, but no semantically sensible condition read the value, so we pretend it has no local condition
extern const std::shared_ptr<OverridableHook<>> OnStateStart;
+12
View File
@@ -152,6 +152,9 @@ class HookImpl : public HookBase {
template <typename... Args>
int run(condition_t condition, detail::HookParameterInstanceList<Args...> argsList = hook_param_list<Args...>()) const
{
if (!Scripting_game_init_run)
return 0;
SCP_vector<SCP_string> paramNames;
argsList.setHookVars(paramNames);
@@ -183,6 +186,9 @@ class HookImpl<void> : public HookBase {
template <typename... Args>
int run(detail::HookParameterInstanceList<Args...> argsList = hook_param_list<Args...>()) const
{
if (!Scripting_game_init_run)
return 0;
SCP_vector<SCP_string> paramNames;
argsList.setHookVars(paramNames);
@@ -239,6 +245,9 @@ class OverridableHookImpl : public Hook<condition_t> {
template <typename... Args>
bool isOverride(condition_t condition, detail::HookParameterInstanceList<Args...> argsList = hook_param_list<Args...>()) const
{
if (!Scripting_game_init_run)
return false;
SCP_vector<SCP_string> paramNames;
argsList.setHookVars(paramNames);
@@ -270,6 +279,9 @@ protected:
template <typename... Args>
bool isOverride(detail::HookParameterInstanceList<Args...> argsList = hook_param_list<Args...>()) const
{
if (!Scripting_game_init_run)
return false;
SCP_vector<SCP_string> paramNames;
argsList.setHookVars(paramNames);
+1 -1
View File
@@ -152,7 +152,7 @@ void LuaFunction::setReference(const LuaReference& ref) {
if (lua_type(L, -1) != LUA_TFUNCTION) {
lua_pop(L, 1);
throw LuaException("Reference does not refere to a function!");
throw LuaException("Reference does not refer to a function!");
} else {
lua_pop(L, 1);
LuaValue::setReference(ref);
+1 -1
View File
@@ -42,7 +42,7 @@ void LuaTable::setReference(const LuaReference& ref) {
if (lua_type(L, -1) != LUA_TTABLE) {
lua_pop(L, 1);
throw LuaException("Reference does not refere to a table!");
throw LuaException("Reference does not refer to a table!");
} else {
lua_pop(L, 1);
LuaValue::setReference(ref);
+3 -1
View File
@@ -55,7 +55,9 @@ lua_State* getMainThread(lua_State* L)
lua_rawget(L, LUA_REGISTRYINDEX);
auto state = lua_tothread(L, -1);
lua_pop(L, 1);
// Need to pop both the thread from L and the main thread to return the stack back to neutral
lua_pop(L, 2);
return state;
}
+1
View File
@@ -32,6 +32,7 @@ using namespace scripting;
script_state Script_system("FS2_Open Scripting");
bool Output_scripting_meta = false;
bool Output_scripting_json = false;
bool Scripting_game_init_run = false;
flag_def_list Script_conditions[] =
{
+1
View File
@@ -350,6 +350,7 @@ void script_init();
extern class script_state Script_system;
extern bool Output_scripting_meta;
extern bool Output_scripting_json;
extern bool Scripting_game_init_run;
//*************************Conditional scripting*************************
+2 -2
View File
@@ -177,7 +177,7 @@ void awacs_update_all_levels()
// < 0.0f : untargetable
// 0.0 - 1.0f : marginally targetable
// >= 1.0f : fully targetable as normal
float awacs_get_level(object *target, ship *viewer, int use_awacs)
float awacs_get_level(const object *target, const ship *viewer, bool use_awacs)
{
Assert(target); // Goober5000
Assert(viewer); // Goober5000
@@ -461,7 +461,7 @@ void team_visibility_update()
// Determine is ship is visible by team
// Goober5000 - now accounts for primitive sensors
int ship_is_visible_by_team(object *target, ship *viewer)
int ship_is_visible_by_team(const object *target, const ship *viewer)
{
Assert(target);
Assert(viewer);
+2 -2
View File
@@ -37,11 +37,11 @@ void awacs_process();
// < 0.0f : untargetable
// 0.0 - 1.0f : marginally targetable
// 1.0f : fully targetable as normal
float awacs_get_level(object *target, ship *viewer, int use_awacs=1);
float awacs_get_level(const object *target, const ship *viewer, bool use_awacs = true);
// Determine if ship is visible by team
// return 1 if ship is fully visible
// return 0 if ship is only partly visible
int ship_is_visible_by_team(object *target, ship *viewer);
int ship_is_visible_by_team(const object *target, const ship *viewer);
#endif
+34 -18
View File
@@ -2941,7 +2941,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
// Goober5000 - if this is a modular table, and we're replacing an existing file name, and the file doesn't exist, don't replace it
if (replace)
if (sip->cockpit_pof_file[0] != '\0')
if (VALID_FNAME(sip->cockpit_pof_file))
if (!model_exists(temp))
valid = false;
@@ -3021,7 +3021,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
// Goober5000 - if this is a modular table, and we're replacing an existing file name, and the file doesn't exist, don't replace it
if (replace)
if (sip->pof_file[0] != '\0')
if (VALID_FNAME(sip->pof_file))
if (!model_exists(temp))
valid = false;
@@ -3041,7 +3041,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
// if this is a modular table, and we're replacing an existing file name, and the file doesn't exist, don't replace it
if (replace)
if (sip->pof_file_tech[0] != '\0')
if (VALID_FNAME(sip->pof_file_tech))
if (!cf_exists_full(temp, CF_TYPE_MODELS))
valid = false;
@@ -3102,7 +3102,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
// Goober5000 - if this is a modular table, and we're replacing an existing file name, and the file doesn't exist, don't replace it
if (replace)
if (sip->pof_file_hud[0] != '\0')
if (VALID_FNAME(sip->pof_file_hud))
if (!cf_exists_full(temp, CF_TYPE_MODELS))
valid = false;
@@ -3380,7 +3380,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
bool valid = true;
if (replace)
if (sip->generic_debris_pof_file[0] != '\0')
if (VALID_FNAME(sip->generic_debris_pof_file))
if (!cf_exists_full(temp, CF_TYPE_MODELS))
valid = false;
@@ -4346,7 +4346,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
{
stuff_vec3d(&sip->closeup_pos);
}
else if (first_time && strlen(sip->pof_file))
else if (first_time && VALID_FNAME(sip->pof_file))
{
//Calculate from the model file. This is inefficient, but whatever
int model_idx = model_load(sip->pof_file, 0, NULL);
@@ -5077,6 +5077,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
}
int n_subsystems = 0;
int n_excess_subsystems = 0;
int cont_flag = 1;
model_subsystem subsystems[MAX_MODEL_SUBSYSTEMS] = {}; // see model.h for max_model_subsystems
for (auto i=0; i<MAX_MODEL_SUBSYSTEMS; i++) {
@@ -5112,7 +5113,8 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
{
if( sip->n_subsystems + n_subsystems >= MAX_MODEL_SUBSYSTEMS )
{
Warning(LOCATION, "Number of subsystems for %s '%s' (%d) exceeds max of %d; only the first %d will be used", info_type_name, sip->name, sip->n_subsystems, n_subsystems, MAX_MODEL_SUBSYSTEMS);
n_excess_subsystems++;
skip_to_start_of_string_one_of({"$Subsystem:", type_name, "#End"});
break;
}
sp = &subsystems[n_subsystems++]; // subsystems a local -- when done, we will malloc and copy
@@ -5465,7 +5467,12 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
default:
UNREACHABLE("This should never happen.\n"); // Impossible return value from required_string_one_of.
}
}
}
// maybe warn
if (n_excess_subsystems > 0) {
Warning(LOCATION, "For %s '%s', number of existing subsystems (%d) plus additional subsystems (%d) exceeds max of %d; only the first %d additional subsystems will be used", info_type_name, sip->name, sip->n_subsystems, n_subsystems + n_excess_subsystems, MAX_MODEL_SUBSYSTEMS, n_subsystems);
}
// when done reading subsystems, malloc and copy the subsystem data to the ship info structure
int orig_n_subsystems = sip->n_subsystems;
@@ -7407,6 +7414,7 @@ void ship_subsys::clear()
targeted_subsys = NULL;
scripting_target_override = false;
last_fired_weapon_info_index = -1;
shared_fire_direction_beam_objnum = -1;
turret_pick_big_attack_point_timestamp = timestamp(0);
turret_big_attack_point = vmd_zero_vector;
@@ -7603,6 +7611,9 @@ static int subsys_set(int objnum, int ignore_subsys_info)
ship_system->disruption_timestamp=timestamp(0);
ship_system->turret_pick_big_attack_point_timestamp = timestamp(0);
ship_system->scripting_target_override = false;
ship_system->last_fired_weapon_info_index = -1;
ship_system->shared_fire_direction_beam_objnum = -1;
vm_vec_zero(&ship_system->turret_big_attack_point);
for(j = 0; j < NUM_TURRET_ORDER_TYPES; j++)
{
@@ -9943,7 +9954,7 @@ static void lethality_decay(ai_info *aip)
if ((aip->target_objnum != -1) && (Objects[aip->target_objnum].type == OBJ_SHIP)) {
//TODO: put this where it belongs, this would involve recompiling *everything* right now
//-WMC
int num_turrets_attacking(object *turret_parent, int target_objnum);
int num_turrets_attacking(const object *turret_parent, int target_objnum);
num_turrets = num_turrets_attacking(&Objects[aip->target_objnum], Ships[aip->shipnum].objnum);
}
nprintf(("lethality", "Player lethality: %.1f, num turrets targeting player: %d\n", aip->lethality, num_turrets));
@@ -10786,15 +10797,20 @@ int ship_create(matrix* orient, vec3d* pos, int ship_type, const char* ship_name
shipp->clear();
shipp->orders_allowed_against = ship_set_default_orders_against();
if (!VALID_FNAME(sip->pof_file))
{
Error(LOCATION, "Cannot create ship %s; pof file is not valid", sip->name);
return -1;
}
sip->model_num = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]); // use the highest detail level
if(strlen(sip->cockpit_pof_file))
if(VALID_FNAME(sip->cockpit_pof_file))
{
sip->cockpit_model_num = model_load(sip->cockpit_pof_file, 0, NULL);
}
// maybe load an optional hud target model
if(strlen(sip->pof_file_hud)){
if(VALID_FNAME(sip->pof_file_hud)){
// check to see if a "real" ship uses this model. if so, load it up for him so that subsystems are setup properly
for(auto it = Ship_info.begin(); it != Ship_info.end(); ++it){
if(!stricmp(it->pof_file, sip->pof_file_hud)){
@@ -10806,7 +10822,7 @@ int ship_create(matrix* orient, vec3d* pos, int ship_type, const char* ship_name
sip->model_num_hud = model_load(sip->pof_file_hud, 0, NULL);
}
if (strlen(sip->generic_debris_pof_file)) {
if (VALID_FNAME(sip->generic_debris_pof_file)) {
sip->generic_debris_model_num = model_load(sip->generic_debris_pof_file, 0, nullptr);
if (sip->generic_debris_model_num >= 0) {
polymodel* pm = model_get(sip->generic_debris_model_num);
@@ -11037,7 +11053,7 @@ static void ship_model_change(int n, int ship_type)
}
if ( sip->cockpit_model_num == -1 ) {
if ( strlen(sip->cockpit_pof_file) ) {
if ( VALID_FNAME(sip->cockpit_pof_file) ) {
sip->cockpit_model_num = model_load(sip->cockpit_pof_file, 0, NULL);
}
}
@@ -14699,7 +14715,7 @@ void ship_get_eye( vec3d *eye_pos, matrix *eye_orient, object *obj, bool do_slew
// element.
eye *ep = &(pm->view_positions[shipp->current_viewpoint]);
if (ep->parent >= 0 && pm->submodel[ep->parent].flags[Model::Submodel_flags::Can_move]) {
if (ep->parent >= 0 && ep->parent < pm->n_models && pm->submodel[ep->parent].flags[Model::Submodel_flags::Can_move]) {
model_instance_local_to_global_point_orient(eye_pos, eye_orient, &ep->pnt, &vmd_identity_matrix, pm, pmi, ep->parent);
vec3d tvec = *eye_pos;
vm_vec_unrotate(eye_pos, &tvec, &obj->orient);
@@ -16407,7 +16423,7 @@ bool ship_can_bay_depart(ship* sp)
// exit: 0 => a valid vector was placed in norm
// !0 => an path normal could not be calculated
//
int ship_return_subsys_path_normal(ship *shipp, ship_subsys *ss, vec3d *gsubpos, vec3d *norm)
int ship_return_subsys_path_normal(const ship *shipp, const ship_subsys *ss, const vec3d *gsubpos, vec3d *norm)
{
if ( ss->system_info->path_num >= 0 ) {
polymodel *pm = NULL;
@@ -16451,7 +16467,7 @@ int ship_return_subsys_path_normal(ship *shipp, ship_subsys *ss, vec3d *gsubpos,
// dot_out => OPTIONAL PARAMETER, output parameter, will return dot between subsys fvec and subsys_to_eye_vec
// (only filled in if do_facing_check is true)
// vec_out => OPTIONAL PARAMETER, vector from eye_pos to absolute subsys_pos. (only filled in if do_facing_check is true)
int ship_subsystem_in_sight(object* objp, ship_subsys* subsys, vec3d *eye_pos, vec3d* subsys_pos, int do_facing_check, float *dot_out, vec3d *vec_out)
bool ship_subsystem_in_sight(const object *objp, const ship_subsys *subsys, const vec3d *eye_pos, const vec3d *subsys_pos, bool do_facing_check, float *dot_out, vec3d *vec_out)
{
float dist, dot;
vec3d terminus, eye_to_pos, subsys_fvec, subsys_to_eye_vec;
@@ -16532,7 +16548,7 @@ ship_subsys *ship_return_next_subsys(ship *shipp, int type, vec3d *attacker_pos)
// Returns the closest subsystem of specified type that is in line of sight.
// Returns null if all subsystems of that type are destroyed or none is in sight.
ship_subsys *ship_get_closest_subsys_in_sight(ship *sp, int subsys_type, vec3d *attacker_pos)
ship_subsys *ship_get_closest_subsys_in_sight(const ship *sp, int subsys_type, const vec3d *attacker_pos)
{
Assert ( subsys_type >= 0 && subsys_type < SUBSYSTEM_MAX );
@@ -18916,7 +18932,7 @@ void ship_update_artillery_lock()
*
* May not work if delta box is large and negative (ie, adjusted box crosses over on itself - min > max)
*/
int check_world_pt_in_expanded_ship_bbox(vec3d *world_pt, object *objp, float delta_box)
int check_world_pt_in_expanded_ship_bbox(const vec3d *world_pt, const object *objp, float delta_box)
{
Assert(objp->type == OBJ_SHIP);
+6 -5
View File
@@ -374,6 +374,7 @@ public:
ship_subsys *targeted_subsys; // subsystem this turret is attacking
bool scripting_target_override;
int last_fired_weapon_info_index; // which weapon class was last fired
int shared_fire_direction_beam_objnum; // reference beam for shared fire direction
int turret_pick_big_attack_point_timestamp; // Next time to pick an attack point for this turret
vec3d turret_big_attack_point; // local coordinate of point for this turret to attack on enemy
@@ -1798,7 +1799,7 @@ extern bool ship_subsystems_blown(const ship *shipp, int type, bool skip_dying_c
extern float ship_get_subsystem_strength(const ship *shipp, int type, bool skip_dying_check = false, bool no_minimum_engine_str = false);
extern ship_subsys *ship_get_subsys(const ship *shipp, const char *subsys_name);
extern int ship_get_num_subsys(ship *shipp);
extern ship_subsys *ship_get_closest_subsys_in_sight(ship *sp, int subsys_type, vec3d *attacker_pos);
extern ship_subsys *ship_get_closest_subsys_in_sight(const ship *sp, int subsys_type, const vec3d *attacker_pos);
//WMC
char *ship_subsys_get_name(ship_subsys *ss);
@@ -1840,8 +1841,8 @@ int ship_navigation_ok_to_warp(ship *sp); // check if ship has navigation power
bool ship_can_warp_full_check(ship *sp); // checks both the warp flags and ship_engine_ok_to_warp() and ship_navigation_ok_to_warp() --wookieejedi
bool ship_can_bay_depart(ship *sp); // checks to see if a ship has a departure location as a bay and if the mothership is present
int ship_return_subsys_path_normal(ship *sp, ship_subsys *ss, vec3d *gsubpos, vec3d *norm);
int ship_subsystem_in_sight(object* objp, ship_subsys* subsys, vec3d *eye_pos, vec3d* subsys_pos, int do_facing_check=1, float *dot_out=NULL, vec3d *vec_out=NULL);
int ship_return_subsys_path_normal(const ship *sp, const ship_subsys *ss, const vec3d *gsubpos, vec3d *norm);
bool ship_subsystem_in_sight(const object *objp, const ship_subsys *subsys, const vec3d *eye_pos, const vec3d *subsys_pos, bool do_facing_check = true, float *dot_out = nullptr, vec3d *vec_out = nullptr);
ship_subsys *ship_return_next_subsys(ship *shipp, int type, vec3d *attacker_pos);
// defines and definition for function to get a random ship of a particular team (any ship,
@@ -1920,7 +1921,7 @@ int is_support_allowed(object *objp, bool do_simple_check = false);
// Stuffs:
// *gpos: absolute position of gun firing point
// *gvec: vector fro *gpos to *targetp
void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vec3d *gpos, vec3d *gvec, int use_angles, vec3d *targetp);
void ship_get_global_turret_gun_info(const object *objp, const ship_subsys *ssp, vec3d *gpos, bool avg_origin, vec3d *gvec, bool use_angles, const vec3d *targetp);
// Given an object and a turret on that object, return the global position and forward vector
// of the turret. The gun normal is the unrotated gun normal, (the center of the FOV cone), not
@@ -1977,7 +1978,7 @@ void ship_replace_active_texture(int ship_index, const char* old_name, const cha
void ship_update_artillery_lock();
// checks if a world point is inside the extended bounding box of a ship
int check_world_pt_in_expanded_ship_bbox(vec3d *world_pt, object *objp, float delta_box);
int check_world_pt_in_expanded_ship_bbox(const vec3d *world_pt, const object *objp, float delta_box);
// returns true if objp is ship and is tagged
int ship_is_tagged(object *objp);
+18 -17
View File
@@ -612,7 +612,7 @@ static void shipfx_actually_warpout(ship *shipp, object *objp)
}
}
void WE_Default::compute_warpout_stuff(float *warp_time, vec3d *warp_pos)
void WE_Default::compute_warpout_stuff(float *ship_move_time, vec3d *warp_pos)
{
Assertion(isValid(), "Warp effect must be valid when compute_warpout_stuff() is called!");
@@ -678,11 +678,8 @@ void WE_Default::compute_warpout_stuff(float *warp_time, vec3d *warp_pos)
ship_move_dist = warp_dist - half_length;
}
// Calculate how long to fly through the effect. Not to get to the effect, just through it.
*warp_time = warping_time;
// Acount for time to get to warp effect, before we actually go through it.
*warp_time += ship_move_dist / warping_speed;
// Calculate time to get to warp effect, before we actually go through it.
*ship_move_time = ship_move_dist / warping_speed;
if (m_portal_objnum < 0)
{
@@ -3481,7 +3478,7 @@ int WE_Default::warpStart()
// done with initial computation; now set up the warp effect
float effect_time = 0.0f;
float open_time, close_time;
if (m_direction == WarpDirection::WARP_IN)
{
// first determine the world center in relation to its position
@@ -3491,14 +3488,16 @@ int WE_Default::warpStart()
// now project the warp effect forward
vm_vec_scale_add( &pos, &pos, &objp->orient.vec.fvec, half_length );
// Effect time is 'SHIPFX_WARP_DELAY' (1.5 secs) seconds to start, warping_time
// for ship to go thru, and 'SHIPFX_WARP_DELAY' (1.5 secs) to go away.
effect_time = warping_time + SHIPFX_WARP_DELAY + SHIPFX_WARP_DELAY;
// for arrivals, opening and closing times are both SHIPFX_WARP_DELAY
open_time = close_time = SHIPFX_WARP_DELAY;
}
else
{
compute_warpout_stuff(&effect_time, &pos);
effect_time += SHIPFX_WARP_DELAY;
// for departures, the opening time is the time it takes for the ship to actually approach the warp effect
// (even if Volition likely intended or assumed that the opening time is always SHIPFX_WARP_DELAY, this is a consequence of the warp effect sequence)
// and the closing time is SHIPFX_WARP_DELAY as normal
compute_warpout_stuff(&open_time, &pos);
close_time = SHIPFX_WARP_DELAY;
// turn off warpin physics in case we're jumping out immediately
objp->phys_info.flags &= ~PF_SUPERCAP_WARP_IN;
@@ -3509,6 +3508,9 @@ int WE_Default::warpStart()
}
}
// Effect time is the time to start, warping_time for ship to go thru, and the time to go away.
float effect_time = open_time + warping_time + close_time;
radius = shipfx_calculate_effect_radius(objp, m_direction);
// cap radius to size of knossos
if (portal_objp != nullptr)
@@ -3525,7 +3527,7 @@ int WE_Default::warpStart()
fireball_type = params->warp_type & WT_FLAG_MASK;
// create fireball
int warp_objnum = fireball_create(&pos, fireball_type, FIREBALL_WARP_EFFECT, m_objnum, radius, (m_direction == WarpDirection::WARP_OUT), nullptr, effect_time, m_ship_info_index, nullptr, 0, 0, params->snd_start, params->snd_end);
int warp_objnum = fireball_create(&pos, fireball_type, FIREBALL_WARP_EFFECT, m_objnum, radius, (m_direction == WarpDirection::WARP_OUT), nullptr, effect_time, m_ship_info_index, nullptr, 0, 0, params->snd_start, params->snd_end, open_time, close_time);
//WMC - bail
// JAS: This must always be created, if not, just warp the ship in/out
@@ -3540,7 +3542,7 @@ int WE_Default::warpStart()
stage_time_start = total_time_start = timestamp();
if (m_direction == WarpDirection::WARP_IN)
{
stage_duration[1] = fl2i(SHIPFX_WARP_DELAY*1000.0f);
stage_duration[1] = fl2i(open_time*1000.0f);
stage_duration[2] = fl2i(warping_time*1000.0f);
stage_time_end = timestamp(stage_duration[1]);
total_time_end = stage_duration[1] + stage_duration[2];
@@ -3556,12 +3558,11 @@ int WE_Default::warpStart()
{
shipp->flags.set(Ship::Ship_Flags::Depart_warp);
// Make the warp effect stage 1 last SHIP_WARP_TIME1 seconds.
// calculate the time when ship is completely warped out or warped in
if ( objp == Player_obj ) {
stage_duration[1] = fl2i(fireball_lifeleft(&Objects[warp_objnum])*1000.0f);
total_time_end = timestamp(fl2i(effect_time*1000.0f));
} else {
total_time_end = timestamp(fl2i(effect_time*1000.0f));
total_time_end = timestamp(fl2i((open_time+warping_time)*1000.0f));
}
// This is a hack to make the ship go at the right speed to go from it's current position to the warp_effect_pos;
+1 -1
View File
@@ -235,7 +235,7 @@ private:
float warping_time; // time to go through the effect
float warping_speed; // speed to go through the effect
void compute_warpout_stuff(float *warp_time, vec3d *warp_pos);
void compute_warpout_stuff(float *ship_move_time, vec3d *warp_pos);
//Total data
int total_time_start;
+1 -1
View File
@@ -1340,7 +1340,7 @@ void ds_set_pitch(int channel_id, float pitch)
*
* @return 0 if sound started successfully, -1 if sound could not be played
*/
ds_sound_handle ds3d_play(int sid, int snd_id, vec3d* pos, vec3d* vel, float min, float max, int looping,
ds_sound_handle ds3d_play(int sid, int snd_id, const vec3d* pos, const vec3d* vel, float min, float max, bool looping,
float max_volume, float estimated_vol, const EnhancedSoundData* enhanced_sound_data,
int priority, bool is_ambient)
{
+1 -1
View File
@@ -100,7 +100,7 @@ int ds_get_sound_index(int channel_id);
// Returns the number of channels that are actually playing
int ds_get_number_channels();
ds_sound_handle ds3d_play(int sid, int snd_id, vec3d* pos, vec3d* vel, float min, float max, int looping,
ds_sound_handle ds3d_play(int sid, int snd_id, const vec3d* pos, const vec3d* vel, float min, float max, bool looping,
float max_volume, float estimated_vol, const EnhancedSoundData* enhanced_sound_data,
int priority = DS_MUST_PLAY, bool is_ambient = false);
+2 -2
View File
@@ -32,7 +32,7 @@
// -1 => failure
//
//
int ds3d_update_buffer(int channel_id, float min, float max, vec3d *pos, vec3d *vel)
int ds3d_update_buffer(int channel_id, float min, float max, const vec3d *pos, const vec3d *vel)
{
if (Cmdline_no_3d_sound) {
nprintf(("Sound", "Aborting ds3d_update_buffer due to Cmdline_no_3d_sound...\n"));
@@ -85,7 +85,7 @@ int ds3d_update_buffer(int channel_id, float min, float max, vec3d *pos, vec3d *
// returns: 0 => success
// -1 => failure
//
int ds3d_update_listener(vec3d *pos, vec3d *vel, matrix *orient)
int ds3d_update_listener(const vec3d *pos, const vec3d *vel, const matrix *orient)
{
if (!ds_initialized) {
return -1;
+2 -2
View File
@@ -12,7 +12,7 @@
#ifndef __DS3D_H__
#define __DS3D_H__
int ds3d_update_listener(vec3d *pos, vec3d *vel, matrix *orient);
int ds3d_update_buffer(int channel, float min, float max, vec3d *pos, vec3d *vel);
int ds3d_update_listener(const vec3d *pos, const vec3d *vel, const matrix *orient);
int ds3d_update_buffer(int channel, float min, float max, const vec3d *pos, const vec3d *vel);
#endif /* __DS3D_H__ */
+3 -3
View File
@@ -656,9 +656,9 @@ MONITOR( Num3DSoundsLoaded )
// returns: -1 => sound could not be played
// n => handle for instance of sound
//
sound_handle snd_play_3d(game_snd* gs, vec3d* source_pos, vec3d* listen_pos, float radius, vec3d* source_vel,
int looping, float vol_scale, int priority, vec3d* /*sound_fvec*/, float range_factor,
int force, bool /*is_ambient*/)
sound_handle snd_play_3d(game_snd* gs, const vec3d* source_pos, const vec3d* listen_pos, float radius, const vec3d* source_vel,
bool looping, float vol_scale, int priority, const vec3d* /*sound_fvec*/, float range_factor,
bool force, bool /*is_ambient*/)
{
vec3d vector_to_sound;
loaded_sound* snd;
+3 -3
View File
@@ -144,9 +144,9 @@ sound_handle snd_play_raw(sound_load_id soundnum, float pan, float vol_scale = 1
// Plays a sound with volume between 0 and 1.0, where 0 is the
// inaudible and 1.0 is the loudest sound in the game. It scales
// the pan and volume relative to the current viewer's location.
sound_handle snd_play_3d(game_snd* gs, vec3d* source_pos, vec3d* listen_pos, float radius = 0.0f, vec3d* vel = nullptr,
int looping = 0, float vol_scale = 1.0f, int priority = SND_PRIORITY_SINGLE_INSTANCE,
vec3d* sound_fvec = nullptr, float range_factor = 1.0f, int force = 0,
sound_handle snd_play_3d(game_snd* gs, const vec3d* source_pos, const vec3d* listen_pos, float radius = 0.0f, const vec3d* vel = nullptr,
bool looping = false, float vol_scale = 1.0f, int priority = SND_PRIORITY_SINGLE_INSTANCE,
const vec3d* sound_fvec = nullptr, float range_factor = 1.0f, bool force = false,
bool is_ambient = false);
// update the given 3d sound with a new position
-2
View File
@@ -789,8 +789,6 @@ add_file_folder("MenuUI"
menuui/fishtank.h
menuui/mainhallmenu.cpp
menuui/mainhallmenu.h
menuui/mainhalltemp.cpp
menuui/mainhalltemp.h
menuui/optionsmenu.cpp
menuui/optionsmenu.h
menuui/optionsmenumulti.cpp
+12 -6
View File
@@ -3011,14 +3011,20 @@ bool stars_background_empty(const background_t &bg)
// Goober5000
void stars_pack_backgrounds()
{
// remove all empty backgrounds
Backgrounds.erase(
std::remove_if(Backgrounds.begin(), Backgrounds.end(), stars_background_empty),
Backgrounds.end());
size_t remove_count = 0;
// remove all empty backgrounds, with a caveat:
// in FRED, make sure we always have at least one background
if (Fred_running && Backgrounds.empty())
stars_add_blank_background(true);
// (note: older code removed all blank backgrounds and re-added one if necessary; but that method caused flag changes to be lost)
Backgrounds.erase(
std::remove_if(Backgrounds.begin(), Backgrounds.end(), [&](const background_t& bg) {
if (stars_background_empty(bg)) {
if (Fred_running && ++remove_count == Backgrounds.size())
return false; // cancel the last removal if FRED is running and if the last removal would result in all backgrounds being removed (i.e. all were blank)
return true;
}
return false;
}), Backgrounds.end());
}
static void render_environment(int i, vec3d *eye_pos, matrix *new_orient, fov_t new_zoom)
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff. Show more