Compare commits

...
Author SHA1 Message Date
SirKnightly 959b4a8e7e Automated build commit 2021-09-11 02:06:19 +00:00
Goober5000 262e4d4489 Merge pull request #3645 from Goober5000/shipname_fix
correct shipname return type
2021-09-08 21:33:00 -04:00
Goober5000 c9d71ce1ab correct shipname return type
The return type of "shipname" is a string, so if the ship can't be found, return an empty string instead of an invalid ship.
2021-09-08 20:57:09 -04:00
Goober5000 e742bd108a Merge pull request #3635 from Goober5000/gc_on_mission_start
run Lua garbage collector on mission start
2021-09-07 14:15:58 -04:00
Goober5000 990d0b7784 run Lua garbage collector on mission start
This might fix #2961.  By explicitly running the garbage collector before a mission starts, this will ensure each mission begins with a clean slate.
2021-09-07 13:50:08 -04:00
Goober5000 7c5371af1a Merge pull request #3624 from Goober5000/fred2_cancel_confirm
confirm cancel in Event Editor
2021-09-05 14:45:14 -04:00
Goober5000 99972a17c6 Merge pull request #3625 from Goober5000/duplicate_squadron_wings
match wing names and aliases on the status gauge
2021-09-05 14:43:27 -04:00
Joshua Glatt 2e743d9cd6 Apply adjust-audio-volume SEXP for effects to looping 3D sounds (#3638) 2021-09-05 11:39:37 -07:00
Goober5000 b7681cfee9 Merge pull request #3636 from Baezon/fix-turret-resetting
Fix turrets not resetting sometimes
2021-09-05 14:31:17 -04:00
Goober5000 5eb924cedc Merge pull request #3639 from Goober5000/handle_invalid_gamesnds
handle invalid gamesnds
2021-09-05 14:29:17 -04:00
Goober5000 d561cd87c4 Merge pull request #3641 from Goober5000/fix_3619
fix nebula range in mission-set-nebula
2021-09-05 14:12:49 -04:00
Goober5000 562522e098 match wing names and aliases on the status gauge
If a wing has a hash symbol, it needs to match the squadron status gauge for the equivalent name without the hash.  This could happen for in-mission jumps or cases where a wing leaves and comes back.

Also remove an obsolete function.
2021-09-05 14:12:08 -04:00
Goober5000 f7bfd637d8 comments 2021-09-05 13:44:02 -04:00
Goober5000 235857ca10 add back fred support 2021-09-05 13:15:34 -04:00
Goober5000 f529fbc633 expand comments 2021-09-05 13:11:33 -04:00
Goober5000 22b16c32d4 Merge pull request #3631 from Goober5000/rewrite_xstr
rewrite XSTR to be more robust
2021-09-05 13:04:41 -04:00
Goober5000 b1298c2b68 address feedback 2021-09-04 18:43:46 -04:00
Goober5000 2ec18afe02 CI 2021-09-04 17:16:33 -04:00
Goober5000 7f742a2145 fix nebula range in mission-set-nebula
The mission-set-nebula sexp never initialized the nebula range, which is usually done in the background editor.  This PR allows the range to be specified, and sets it to 3000 (the same default as in the background editor) if not specified.

Fixes #3619.
2021-09-04 16:37:49 -04:00
Goober5000 b96d9f6e62 address feedback 2021-09-04 15:46:03 -04:00
Goober5000 eaa3fe0979 address feedback 2021-09-03 23:56:46 -04:00
Goober5000 1cd16b7281 string length tweak 2021-09-03 23:16:40 -04:00
Goober5000 44ce5e9b00 handle invalid gamesnds
If an entry in a game sound fails to load, mark the sound as invalid and don't try to load it afterwards.  This is the "permanent fail" fix for #3388.
2021-09-03 19:10:31 -04:00
Asteroth 13b922207b comment cleanup 2021-09-03 00:14:58 -04:00
Asteroth efc686db2d unused variable 2021-09-01 14:33:57 -04:00
Asteroth 2738919cc9 fix turrets not resetting sometimes 2021-09-01 14:29:57 -04:00
Goober5000 7e9121ed8e use blocks instead of goto; avoids skipping over initialization 2021-08-29 22:56:37 -04:00
Goober5000 f0712cfb41 rewrite XSTR to be more robust
The old XSTR parsing code was a bit of a mess and did not even properly enforce its format.  These new routines enforce the actual XSTR format and warn when it is malformed, which is helpful for catching table errors as early as possible.  This fixes #3627 and also catches mismatched quotes and other common XSTR errors.  No more wondering why comments are being mysteriously parsed instead of ignored!
2021-08-29 21:47:15 -04:00
Goober5000 af8422cf7b confirm cancel in Event Editor
This syncs up the Close and Cancel behavior.  In both cases, if there are any changes, FRED will display a prompt.
2021-08-26 16:32:18 -04:00
Asteroth 39a16019e3 Single target multilock missiles should use targeted_subsys (#3618)
* use targeted_subsys if available
2021-08-24 19:45:34 -04:00
Asteroth bbca518524 no chase cam slewing during hyperspace (#3620) 2021-08-24 19:44:07 -04:00
Goober5000 bb28c37829 Merge pull request #3616 from Goober5000/look_at_updates
fixes to look_at and intrinsic rotation
2021-08-22 22:57:26 -04:00
Goober5000 ad2335cfbf fixes to look_at and intrinsic rotation
1) Make sure the `PM_FLAG_HAS_INTRINSIC_ROTATE` flag is only set after submodels pass validation and checking
2) Maintain compatibility with old models that used `$look_at` with the submodel number instead of name
2021-08-22 22:09:18 -04:00
Allen A. Babb 7bf7bf5c1d Merge pull request #3617 from z64555/fix/axis_backups_move_too_much
Fixes a typo in controlconfig that causes an issue with std::move.
2021-08-22 16:18:20 -05:00
z64555 99b7c89883 Fixes a typo that caused an issue for some builds when trying to move the axis binding backups into the bindings during the exit state of the controls config menu. 2021-08-22 15:39:20 -05:00
Goober5000 370419b1a2 Merge pull request #3610 from Goober5000/some_ai_profiles_fixes
some ai_profiles fixes
2021-08-21 21:40:46 -04:00
Goober5000 17d1924897 make CI happy 2021-08-21 20:18:08 -04:00
Goober5000 a351e72895 some ai_profiles fixes
1) Several AI profiles options were getting reset on every iteration through the loop.  They should only be reset when the profile is initially created.
2) Keeping track of AI hits on the player ship was added by Sushi in 9246a2f74d and needs to be guarded by a flag.  This flag has been enabled for mods that set the required FSO version.
2021-08-21 19:53:46 -04:00
Asteroth cd4c6e483a add a few mor cases for checking targeting restrictions (#3605) 2021-08-20 16:11:18 -04:00
Asteroth 82c36b1c59 fix shockwave rotation (#3604) 2021-08-18 14:55:14 -04:00
Asteroth 47f84846e3 fix collision retiming to never be in the future (#3599) 2021-08-12 17:21:22 -04:00
Goober5000 2fe789f87b Merge pull request #3595 from Goober5000/ship_logging
improve logging of ship creation and cleanup
2021-08-11 16:17:09 -04:00
BMagnu f850725c14 Merge pull request #3597 from Baezon/fix-poof-density
Fix for 'anemic' poofs
2021-08-12 01:12:26 +09:00
Goober5000 344eb5a7ed Merge pull request #3596 from Goober5000/fix_fs1_import
fix icon on FS1 mission import
2021-08-11 12:07:24 -04:00
Goober5000 10b357172a improve and expand logging 2021-08-11 02:50:07 -04:00
Asteroth 4892ac7b01 unused param 2021-08-11 00:07:56 -04:00
Asteroth 4474b0087d comments 2021-08-10 23:41:35 -04:00
Goober5000 ee1b6263ba fix icon on FS1 mission import
The SD Demon doesn't use the support ship icon anymore, and hasn't since FSPort switched from FS2-style icons to FS1-style icons.
2021-08-10 23:26:47 -04:00
Baezon 73ced78633 fix poof density 2021-08-10 21:44:02 -04:00
Goober5000 50266c5065 Merge pull request #3580 from JohnAFernandez/qtFRED-CommandBrief-Dialog
qtFRED Command Breifing
2021-08-10 13:55:53 -04:00
JohnAFernandez 00f4797b45 qtFRED Command Breifing
Adds a fully functioning Command Briefing Dialog to qtFRED
2021-08-09 14:23:23 -04:00
BMagnu 2377b5dff2 Merge pull request #3594 from BMagnu/anim_rework_1_perf
Animation Rework Phase 1 Fixes 4 / Performance Issues
2021-08-10 01:33:53 +09:00
Goober5000 f8c41ac377 improve logging of ship creation and cleanup
It's useful for *all* ship cleanup to be logged, not just big/huge ships.  And by extension, it's also useful for ship creation to be logged.
2021-08-09 01:43:07 -04:00
Birk Magnussen aee9dc6910 Fixed bad stepAnimations call, added initial-type optimizations 2021-08-09 14:36:57 +09:00
ngld 2e5a6871c5 Merge pull request #3593 from Goober5000/wrap_no-float16
add a version guard for MSVC compiler bug fix
2021-08-08 11:29:11 +02:00
Goober5000 535b0857b6 add a version guard for MSVC compiler bug fix
Per ngld's suggestion, this adds a version check around the disabling of the qfloat16 overloads.

Follow-up to #3557.
2021-08-07 19:19:02 -04:00
Allen A. Babb 2fa7ddc552 Merge pull request #3481 from MageKing17/bugfix/mainhall-misc-apng-animations
Fix APNG misc mainhall animations.
2021-08-07 13:09:37 -05:00
BMagnu a6f821daee Merge pull request #3592 from Baezon/type-5-targetless-turrets
Handle turret fired type 5 beams without targets properly
2021-08-08 01:47:10 +09:00
Baezon 360198b99f handle turret fired beams without targets properly 2021-08-07 12:20:11 -04:00
Asteroth 7fe62aabcd Add some chase view Improvements (#3571)
* improve chase view cam

* fix comment

* comment description of acceleration
2021-08-05 11:09:08 -04:00
Goober5000 d2859f4979 Merge pull request #3585 from Goober5000/dynamic_fred_poofs
handle nebula poofs dynamically in FRED
2021-08-04 16:58:52 -04:00
Goober5000 643a87a7f1 Merge pull request #3586 from Goober5000/fred_and_teams_and_mantis_2053
ships, wings, etc. can share names with teams
2021-08-04 16:42:09 -04:00
Goober5000 d1d78c980e Merge pull request #3564 from Goober5000/turn_off_wireframe
add Draw Outlines on Selected Ships option
2021-08-04 02:12:21 -04:00
Goober5000 148df94e50 use original intent of colors 2021-08-04 01:42:42 -04:00
Goober5000 d443e9e7d0 might as well fix this too 2021-08-03 22:23:04 -04:00
Goober5000 e1bbbf9f9a ships, wings, etc. can share names with teams
Partial revert of Mantis #2053:
http://scp.indiegames.us/mantis/view.php?id=2053

The patch was speculative and there was never a confirmed bug.  By contrast, there are plenty of existing missions that use Unknown as a ship, wing, or jump node name and work just fine.
2021-08-03 22:06:57 -04:00
Goober5000 fdafe21684 handle nebula poofs dynamically in FRED
Inspired by #3584, this properly saves and restores any number of nebula poofs handled by the game.  FRED will only display checkboxes for the first six, since it still uses the static UI, but everything else is handled behind the scenes.
2021-08-03 21:21:42 -04:00
BMagnu 7ccd13bbce Merge pull request #3584 from BMagnu/fred_poof_clear
FRED: Don't clear poof flags for poof 7 and after
2021-08-04 09:41:56 +09:00
Birk Magnussen 9f45f49ec1 Only clear settable poof bitflags 2021-08-04 00:57:07 +09:00
BMagnu 6b5124a8ad Merge pull request #3582 from BMagnu/anim_rework_1_fixes_3
Animation Rework Phase 1 Fixes #3
2021-08-03 11:15:46 +09:00
Birk Magnussen 1187ebbc2c GCC Warnings 2021-08-02 23:33:26 +09:00
Birk Magnussen d92fab80ef Find turrets by sip name (since index can change) and make pointers shared 2021-08-02 22:35:41 +09:00
BMagnu 1b9dd78233 Merge pull request #3507 from Baezon/poof-overhaul
Overhaul nebula poof system
2021-08-02 18:35:40 +09:00
Baezon ebb5ed9ba6 review tweaks 2021-08-01 13:08:11 -04:00
BMagnu dabdc871a6 Merge pull request #3579 from BMagnu/anim_rework_1_fixes_2
Animation Rework Phase 1 Fixes #2
2021-08-01 14:39:38 +09:00
Birk Magnussen 77a255af63 GCC Warnigns 2021-07-30 18:05:46 +09:00
Birk Magnussen 45c904c25e Fixed memory issue and turret-specific animation handling cases 2021-07-30 17:08:24 +09:00
BMagnu 3d0bd54980 Merge pull request #3578 from BMagnu/anim_rework_1_fixes_1
Anim Rework Phase 1 Fixes
2021-07-30 03:27:39 +09:00
Birk Magnussen 7228244f0c Add forced animation start mode 2021-07-29 20:46:37 +09:00
Birk Magnussen f0f5679731 Fix flipped turret values 2021-07-29 20:21:01 +09:00
Asteroth 1306a23fd4 Merge pull request #3568 from Baezon/fix-turret-flags-no-parse-object
Move some flag setting to subsys_set()
2021-07-28 03:15:58 -04:00
Goober5000 f067a19583 Merge pull request #3576 from Goober5000/override_all_briefing_icons
allow custom briefing icons to always override standard icons
2021-07-28 00:40:42 -04:00
Goober5000 8ad52cd8e0 Merge pull request #3577 from notimaginative/fix_cmdline_password_bug
fix error with -password cmdline option
2021-07-28 00:40:22 -04:00
Taylor Richards a27976bf9a fix error with -password cmdline option 2021-07-27 19:41:01 -04:00
Goober5000 deefbf0860 allow custom briefing icons to always override standard icons
Adds the `$Custom briefing icons always override standard icons:` option to game_settings.tbl.  Implements #3575.
2021-07-27 14:16:58 -04:00
Goober5000 c0f9ad7b3c Merge pull request #3574 from Goober5000/fix_time_to_goal
fix Time To: Unknown on the HUD
2021-07-25 18:17:25 -04:00
Goober5000 482cc7d7a6 fix Time To: Unknown on the HUD
PR #3064 inadvertently caused all goals to display Time To: Unknown, even though time was only supplied for docking and waypoints.  This restores the original behavior where the Time To: line simply wasn't displayed.
2021-07-25 15:29:24 -04:00
JohnAFernandez 18754fafc1 Force Portable Initial Changes (#3562)
Allows us to at least try to use a file path with a unicode character

Typo

Fixup

Update osapi.cpp
2021-07-25 14:18:45 -04:00
Baezon d05b3fa637 handle beam free all in subsys_set 2021-07-25 11:15:01 -04:00
Goober5000 a26367db5f qtfred support 2021-07-24 14:15:06 -04:00
Goober5000 e3617169ca add Draw Outlines on Selected Ships option
This option is helpful because the mesh outlines will often obscure important information like submodel names and turret weapons.
2021-07-24 14:15:06 -04:00
Goober5000 3c487f81f3 Merge pull request #3570 from Goober5000/weapon_list_type_fix
fix WEAPON_LIST_TYPE parsing for -1
2021-07-24 14:12:33 -04:00
Goober5000 878dd4b313 this makes sense too 2021-07-23 20:59:37 -04:00
Goober5000 74d95f01bc fix WEAPON_LIST_TYPE parsing for -1
When an empty string is encountered in the weapon list, it's valid, but the -1 should actually be stored, not skipped.

This bug was preventing ship weapons from being explicitly set to None.
2021-07-23 20:52:29 -04:00
BMagnu 226d51c214 Merge pull request #3500 from BMagnu/anim_rework_1
Animation Overhaul: Phase 1
2021-07-23 23:59:26 +09:00
Asteroth 9855062732 Merge pull request #3567 from Baezon/fix-null-parse-object
Fix a nullptr bug with change_ship_type
2021-07-23 01:01:08 -04:00
Birk Magnussen 5709e24e9a save by PMI id not PMI*, rename 'time' variable 2021-07-23 13:17:11 +09:00
JohnAFernandez 13c410e864 Remove extra AI Profiles call (#3566)
This function has to be called on game/FRED/qtFRED start, and does nothing once it is called.  So there is no reason for it to be hear on mission close.
2021-07-22 22:51:11 -04:00
Birk Magnussen 5f4729bddb Remove Variable Shadowing 2021-07-23 04:38:23 +09:00
Baezon 65fa916cba move some flag setting to subsys_set 2021-07-22 15:29:22 -04:00
Birk Magnussen 473acced54 Apply new initial animations for techroom instances 2021-07-23 04:27:43 +09:00
Birk Magnussen b58b60b555 Fix Constructor Order 2021-07-23 03:31:06 +09:00
BMagnu 9c3b90f403 Merge branch 'master' into anim_rework_1 2021-07-23 03:18:32 +09:00
Birk Magnussen 3b0e02545e Move Animations from ship* to pmi* 2021-07-23 03:12:54 +09:00
Baezon 9e6e1ef123 clang tidy 2021-07-22 12:00:00 -04:00
Baezon 2e4e9855bc ensure p_objp is not null 2021-07-22 01:59:26 -04:00
Asteroth 9adc89a5a2 Merge pull request #3441 from Baezon/ballistic-beams
Add support for ballistic beams
2021-07-21 15:58:09 -04:00
Goober5000 8faa521df9 Merge pull request #3565 from Goober5000/fix_briefing_icon_override
fix briefing icons changing to ships unexpectedly
2021-07-21 12:33:59 -04:00
Baezon a3513c38b6 Merge branch 'master' into ballistic-beams 2021-07-21 02:59:33 -04:00
Goober5000 15901e430f fix briefing icons changing to ships unexpectedly
If the briefing icon doesn't actually show a ship, then don't use the ship icon override.
2021-07-20 23:21:11 -04:00
Asteroth 37584f576c Merge pull request #3561 from Baezon/no-generic-debris-for-subsystems
Minor improvement for generic debris on subsystems
2021-07-19 19:55:44 -04:00
Baezon 3863d9b5a9 dont use generic debris for subsystems 2021-07-17 20:20:54 -04:00
Goober5000 5318fba077 Merge pull request #3558 from Goober5000/qtfred_menu_update
Qtfred menu update
2021-07-17 19:54:06 -04:00
Asteroth d187a6ae99 Merge pull request #3555 from Baezon/firepoints-burst-shots
Add "num firepoints for burst shots" flag
2021-07-17 19:40:04 -04:00
Asteroth 30b35b483b Merge pull request #3556 from Baezon/audiostream-set-volume
Add setVolume function for audio_stream objects
2021-07-17 19:39:33 -04:00
Goober5000 f7f0c2ba21 Merge pull request #3559 from Goober5000/training_message_preservation
Training message preservation
2021-07-17 00:10:51 -04:00
Goober5000 f1362a694d Merge pull request #3560 from Goober5000/log_lcl_weirdness
log when missions are skipped due to XSTR mismatch
2021-07-17 00:09:34 -04:00
Baezon db55349f70 some extra safety and warning for super huge numbers of poofs 2021-07-16 22:30:39 -04:00
Baezon 2d84fdf5c6 Merge remote-tracking branch 'upstream/master' into poof-overhaul 2021-07-16 22:29:56 -04:00
Goober5000 5e7a6ece52 log when missions are skipped due to XSTR mismatch
As a follow-up to #2343, users might wonder why their mission isn't showing up in the tech room, so log a friendly message.
2021-07-16 22:17:36 -04:00
Goober5000 ff62ea010e training messages have additional idiosyncrasies 2021-07-16 22:09:11 -04:00
Goober5000 a14a746a4f don't pre-empt training messages before they finish playing 2021-07-16 20:56:33 -04:00
Goober5000 d4c0c45595 whoops 2021-07-16 00:09:37 -04:00
Goober5000 06eaeca9d7 add Move Ships When Undocking menu option 2021-07-15 23:50:20 -04:00
Goober5000 dd5c92a910 move the show teams code to the right spot and remove the todo 2021-07-15 23:49:27 -04:00
Goober5000 2de1776308 reorder misc menu and fix a couple captions 2021-07-15 23:13:12 -04:00
Goober5000 833b51e423 sync shortcut keys to what they are in FRED 2021-07-15 22:46:57 -04:00
Goober5000 726d66d0ad add the Highlight Selectable Subsystems menu item 2021-07-15 22:17:05 -04:00
Goober5000 c7e2b3c78f add the Clone Marked Objects menu item 2021-07-15 22:15:26 -04:00
Goober5000 97a93162f8 Merge pull request #3557 from Goober5000/qtbug_72073
work around MSVC 2015 and 2017 compiler bug
2021-07-15 00:03:51 -04:00
Goober5000 1fa270ad66 work around MSVC 2015 and 2017 compiler bug
This macro bypasses a compiler bug where MSVC can't distinguish the division symbol.  See https://bugreports.qt.io/browse/QTBUG-72073
2021-07-14 20:13:11 -04:00
Baezon 5b316a562d clarifying comments 2021-07-14 00:22:46 -04:00
Baezon 9c14717b81 use macro version of true/false return 2021-07-14 00:14:08 -04:00
Baezon 0b8bbb3f47 add set volume for audio streams 2021-07-13 15:29:07 -04:00
Goober5000 314833ae61 Merge pull request #3554 from Goober5000/fred_turret_info
FRED turret info
2021-07-11 21:39:46 -04:00
Goober5000 4a4d07872a Merge pull request #3553 from Goober5000/loop_mission_tweak
fix campaign loop in certain cases
2021-07-11 16:10:39 -04:00
Goober5000 fdc33d8ab5 some fixes 2021-07-11 03:52:01 -04:00
Goober5000 c0430f0b55 add menu option to display info for all turrets 2021-07-11 03:42:24 -04:00
Goober5000 0d2d94a49b display weapons along with turrets 2021-07-11 03:33:43 -04:00
Goober5000 4f46fff48c fix campaign loop in certain cases
If the loop mission and the next mission are the same, then don't show the campaign loop screen.  This prevents the campaign from getting into a campaign loop state if it shouldn't, and also allows campaign loops to be conditionally accessible.
2021-07-11 00:49:09 -04:00
Goober5000 1b1d864c59 Merge pull request #3552 from Baezon/fix-secondary-range-check
Fix a secondary range bug from AI multilock
2021-07-09 15:21:32 -04:00
Baezon f1ddfafdf3 fix a bug from ai multilock 2021-07-09 01:55:35 -04:00
Baezon 3481032ae5 add num_firepoints_burst_shots flag 2021-07-08 17:08:56 -04:00
Asteroth 3a4e5d75a3 move the event flag reset to the top of the function to avoid restting anything we just applied (#3551) 2021-07-07 03:44:17 +00:00
PhantomHoover 84b25b52d7 Be smarter about when we try to run scripting hooks: don't even bother trying if no scripts are even registered to run on them. (#3538) 2021-07-02 22:50:53 -04:00
Taylor Richards f2d3aaaf20 Merge pull request #3544 from notimaginative/fix_gtrack_packet_size
bump tracker packet sizes to match server
2021-06-29 00:57:24 -04:00
JohnAFernandez ffbcdab9da Remove Buggy Function and Let Debris End Naturally (#3540)
* Remove Buggy Function and Let Debris end natrually

Removes this retail function that was actually perpetually postponing the death of debris at long distances instead of removing them.  Debris lifetime is suitably short for the small, non-hull pieces (only a few seconds), while large pieces are supposed to be permanent.   It also culls based on player position, which is not the correct approach, since the camera can be free from the player ship.

* Remove Vestigial Variables

These variables are no longer checked for anything now that we allow debris to go through its lifetime.
2021-06-28 08:11:10 -04:00
Goober5000 8db2f374f2 Merge pull request #3545 from Goober5000/fix_3539
fix 3539
2021-06-28 00:58:48 -04:00
Goober5000 46959276f5 fix 3539
If a bad token is found in a string list, just advance the parser until the next token or EOLN.
2021-06-27 21:24:44 -04:00
Taylor Richards d5afd8d79b bump tracker packet sizes to match server
- allow for size of IPv6 game list packet (fixes #3543)

These larger packet sizes aren't required for
FS2/FS2_Open, but just keeps the code in sync.
2021-06-27 13:43:19 -04:00
Goober5000 2a8064a3e9 Merge pull request #3536 from Goober5000/lua_sexp_help
allow characters in SEXP help
2021-06-26 17:41:01 -04:00
Goober5000 9741b36442 allow characters in SEXP help
Certain characters (especially the semicolon) couldn't be used in SEXP help.  By using the same character replacement as in briefings and messages, now they can.
2021-06-25 22:38:31 -04:00
PhantomHoover 0fe59e10ad Inline matrix multiplication ops for performance improvements (#3535) 2021-06-25 22:19:42 -04:00
Asteroth e287c575ce Merge pull request #3534 from Baezon/bugfix-initial-shield-strength
Initialize `n_quadrants` before setting shield strength
2021-06-25 14:24:01 -04:00
Asteroth b14a27cdff fix model point shield handling for mini target shields (#3531) 2021-06-25 00:44:39 -04:00
Baezon 63b0f0a373 bug fix initialize n_quadrants before setting shield strength 2021-06-24 18:44:25 -04:00
Asteroth c464a1cd4c Merge pull request #3532 from Baezon/bugfix-ai-multilock-no-dumbfire
Bugfix with ai multilock and no dumbfire
2021-06-24 18:28:50 -04:00
Asteroth 6889ee856f Merge pull request #3533 from Baezon/bugfix-shield-hit-delay
Initialize shield regen hit delay
2021-06-24 18:28:29 -04:00
Baezon 292f1547c4 initialize shield regen hit delay 2021-06-24 17:38:00 -04:00
PhantomHoover a687fe0aef Reimplement matrix3 multiplication based on a common overloaded operator (#3470) 2021-06-24 17:26:51 +00:00
Baezon 31fa21bbf1 bugfix ai multilock and no dumbfire 2021-06-24 01:29:11 -04:00
Asteroth be6a7f552a Merge pull request #3443 from Baezon/trails-optimize
Optimize trail rendering
2021-06-23 16:59:51 -04:00
Baezon 93da6616f7 Merge remote-tracking branch 'upstream/master' into trails-optimize 2021-06-23 16:04:13 -04:00
Asteroth 4aeaabf337 Merge pull request #3529 from Baezon/shield-regen-hit-delay
Add shield regen hit delay
2021-06-23 16:02:15 -04:00
Asteroth 589071ac46 Merge pull request #3183 from Baezon/ai-multilock
Add AI support for multilock
2021-06-23 02:15:07 -04:00
wookieejedi ccf0e7eb04 Merge pull request #3510 from wookieejedi/add-extra-script-sound-enums
Add More Options to Scripted Persistent Sounds
2021-06-22 18:37:20 -04:00
Baezon adb7d9d195 review tweaks 2021-06-22 16:46:13 -04:00
BMagnu 7a2a4b6640 Merge pull request #3526 from BMagnu/luasexp_subcat_fix
Allow non-unique sexp subcategories
2021-06-22 22:52:10 +09:00
Goober5000 e4f9ca05c5 clang 2021-06-22 02:05:26 -04:00
Taylor Richards 0160fac2ff Refactor hud escort list to be less crazy (#2922)
* refactor hud escort list to be less crazy

 - don't keep rebuilding list
 - add and remove ships more efficiently
 - better handling of player ships in multi (fixes #1674)

* fix sorting issues

 - incomplete priority sort
 - reversed alpha sort

* fix additional sorting issue

* improve safety check in list sort function

* add safety checks to hud_add_ship_to_escort()

this wasn't meant to be used outside of the module so
it was not really safe to use with sexps previously

* remove entries properly in hud_add_remove_ship_escort()
2021-06-22 01:34:06 -04:00
Goober5000 cc2d06d37c correctly handle non-looping sounds when they have finished playing 2021-06-22 01:24:39 -04:00
wookieejedi c5b29c62c6 incorporate feedback
More modifications are still needed though, so keeping in draft status
2021-06-22 00:11:28 -04:00
Baezon 8e2d93211c add shield regen hit delay 2021-06-21 21:01:51 -04:00
Joshua Glatt eca6647dbf Fix leaking of game objects and memory in F3 lab (#3512) 2021-06-21 17:12:51 -07:00
Goober5000 b6e214a4d4 Merge pull request #3528 from Goober5000/language_sexp
add is-language
2021-06-21 15:57:03 -04:00
Goober5000 841a4b8cea add is-language
Checks whether the game is running in the specified language.  Since the fiction viewer doesn't use XSTR-style localization, this SEXP can be used to display different stages for different languages.
2021-06-21 15:00:28 -04:00
m!m 19bc18cb32 Merge pull request #3525 from asarium/build/ccacheTest
Use ccache to speed up some PR builds
2021-06-21 17:50:49 +02:00
Goober5000 823c76ec22 Merge pull request #3064 from z64555/feature/time_to_goal
Feature/time to goal
2021-06-21 11:48:05 -04:00
Goober5000 a75ad91518 Merge pull request #3469 from Goober5000/external_submodel_offset
Add external weapon model angle offset
2021-06-21 11:17:00 -04:00
Birk Magnussen bdefb55ec9 Allow non-unique sexp subcategories 2021-06-21 15:16:17 +09:00
Baezon 00f7f13371 Merge remote-tracking branch 'upstream/master' into ballistic-beams 2021-06-20 22:09:16 -04:00
Baezon 1441cc8e85 review tweaks for ballistic beams 2021-06-20 21:50:11 -04:00
Goober5000 a8a9127d81 Merge pull request #3504 from Goober5000/tstring_check_fix
maintain a copy of the default translations to check against untranslated strings
2021-06-20 21:25:32 -04:00
Joshua Glatt 028168d395 Add in-game options menu options for lightshafts and bloom (#3501)
* Add in-game options menu options for lightshafts and bloom

* move impl of new PostProcessingManager functions to .cpp file
2021-06-20 21:04:34 -04:00
Taylor Richards 7dae8940d7 support opening of web urls on PXO screens (#3485)
* support opening of web urls on PXO screens

(copied out of icculus.org version)

* add delay before allowing more clicks on links

* fix clang-tidy issues
2021-06-20 20:58:55 -04:00
Asteroth 814d44c986 Add weapon optimum range for AI fighters (#3457)
* add weapon optimum range

* review tweaks
2021-06-20 20:52:02 -04:00
Asteroth 99fb2c00ab fix shooter scaling offsets (#3521) 2021-06-20 18:30:09 -04:00
m!m f7c3ab85d3 Merge pull request #3524 from asarium/build/removeDeprecatedCMakeStuff
Remove usage of deprecated CMake module
2021-06-20 18:27:14 +02:00
asarium d03a1007df Use ccache to speed up some PR builds
This sets up ccache to speed up some builds. I hope this also works with
forks since I don't know if those are allowed to use the main repo
cache.
2021-06-20 14:24:11 +02:00
asarium b6fe676072 Remove usage of deprecated CMake module
Also removed all checks for C++11 support since we only support
compilers that have this already.
2021-06-20 12:56:20 +02:00
Goober5000 07f4f4716b handle clang 2021-06-20 02:47:19 -04:00
Asteroth 30e48c1b03 Merge pull request #3511 from Baezon/beam-attenuation-bugfix
Bugfix for beam attenuation damage
2021-06-20 02:31:48 -04:00
Asteroth 352fd3cdfc Merge pull request #3514 from Baezon/negative-hull-repair-kill
Properly handle death due to negative hull repair rate
2021-06-20 02:31:38 -04:00
Goober5000 1becca7cbe accommodate mismatches 2021-06-20 00:45:39 -04:00
Goober5000 07312ff4c0 add code for actually rotating the external models 2021-06-20 00:45:38 -04:00
Goober5000 f9c73e81f2 add code for loading external model angle offset from POF file 2021-06-20 00:45:34 -04:00
Goober5000 e2585a9a0e change secondary_point_reload_pct to be dynamic and to not rely on MAX_SLOTS 2021-06-19 23:23:53 -04:00
Goober5000 d6c65d5139 Merge pull request #3489 from Goober5000/POF_version_adjustment
add some more sophisticated POF version handling
2021-06-19 23:14:00 -04:00
Goober5000 c3551f4361 Merge pull request #3520 from Baezon/downgrade-parse-severity
Restore original error handling of stuff_int_list
2021-06-19 21:43:32 -04:00
Goober5000 d787bd7c6f Merge pull request #3522 from EatThePath/fix-hook-conditions-i-broke
Moved script initialization to take place after all table loads
2021-06-19 21:42:47 -04:00
Baezon 7dd454f563 review changes 2021-06-19 18:39:35 -04:00
Goober5000 f1deee8566 Merge pull request #3354 from Goober5000/gs_state_invalid
actually use GS_STATE_INVALID
2021-06-19 17:49:38 -04:00
David Gibson b24289e918 Moved script initialization to take place after all table loading to facilitate script condition caching. 2021-06-19 12:34:16 -05:00
Baezon 94b1d11f47 restore original error handling of sutff_int_list 2021-06-19 02:58:20 -04:00
Goober5000 c541ac91db Merge pull request #3513 from the-maddin/qt-checked-list-3
Rewrite of CheckedDataListModel
2021-06-19 00:37:40 -04:00
the-maddin eb6f7fb013 Fix some clang stuff 2021-06-18 23:10:56 +02:00
Baezon d2c24efeff properly handle death due to negative hull repair rate 2021-06-18 16:50:55 -04:00
Goober5000 318fed546f Merge pull request #3509 from BMagnu/briefing_shadows
Add shadow map generation for briefing
2021-06-18 15:54:29 -04:00
the-maddin 474f225672 be explicit for msvc
(cherry picked from commit 97e2028337265a5529d0b0a5fb76bb52e1a4ab37)
2021-06-18 20:33:34 +02:00
the-maddin 14337c22b0 Rewrite CheckedDataListModel
for better flexibility & efficiency
2021-06-18 20:21:28 +02:00
Baezon 647334f4ff redundant fwd declaration 2021-06-17 18:39:46 -04:00
wookieejedi 03d51ba400 appease clangtidy 2021-06-17 17:23:01 -04:00
Baezon 3767758ae5 bugfix beam attenuation damage 2021-06-17 17:10:35 -04:00
wookieejedi def814fa4e fix typo 2021-06-17 17:01:53 -04:00
wookieejedi 11c5af39d8 Add More Options to Scripted Persistent Sounds
This adds two new enums to persistent sounds which can be utilized by scripts.
1. OS_PLAY_ON_PLAYER, which allows persistent sounds to play on the player's ship.
2. OS_LOOPING_DISABLED, which allows the scripter to set the sound to play once and not have to then call `removeSound` later. Very handy for playing an assigned sound once with one line.

Tested and everything works as expected.
2021-06-17 16:58:25 -04:00
Birk Magnussen c6b75d3561 Add forgotten Animation Instancing 2021-06-18 04:09:03 +09:00
Goober5000 866a7c40df Merge pull request #3497 from JohnAFernandez/fix-pause-locking
fix missiles locking during pause
2021-06-17 14:58:48 -04:00
Goober5000 5076066627 Merge pull request #3508 from Goober5000/enum_bitops
add bitwise operations with enums
2021-06-17 14:53:37 -04:00
Goober5000 70546b68a5 add bitwise operations with enums
This adds `EnumAND` and `EnumOR` which will return new enums based on the specified operations.  These are handy for enums that correspond to bit fields, such as those used for object persistent sounds.
2021-06-17 14:17:16 -04:00
Baezon a6084ba707 change from radians to degrees 2021-06-17 14:04:47 -04:00
Baezon 27650ef45d proper poof toggling support 2021-06-17 13:15:07 -04:00
Birk Magnussen 4f5fb48f79 Add shadow map generation for briefing 2021-06-17 14:45:54 +09:00
Goober5000 c1d2920526 Merge pull request #3506 from Goober5000/enhance_bitops
some bitwise operation enhancements
2021-06-16 12:17:38 -04:00
Baezon 90558a5858 Merge branch 'master' into poof-overhaul 2021-06-16 03:26:58 -04:00
Birk Magnussen 390f979b7c Small parse improvement + english fixes 2021-06-16 13:51:55 +09:00
wookieejedi 7258861791 Merge pull request #3505 from wookieejedi/add-save-as-shortcut
Add Keyboard Shortcut for `Save As` in FRED
2021-06-15 20:15:22 -04:00
Goober5000 76b944e01d fix tests 2021-06-15 19:53:28 -04:00
Goober5000 8006baec65 Merge pull request #3498 from JohnAFernandez/fix-scripts-crashing-with-bad-mission-data-when-pressing-esc
Add Menu and Mission resetting to scripting state.
2021-06-15 18:15:01 -04:00
Goober5000 443e148869 some bitwise operation enhancements
Allow AND and OR to take up to 10 arguments.  Deprecate addBit and replace with setBit, and add unsetBit.
2021-06-15 17:56:49 -04:00
wookieejedi f9265301af Add Keyboard Shortcut for Save As in FRED
Finally got around to adding in the `Ctrl+Shit+S` shortcut for Save As in FRED. Tested and works as expected.
2021-06-15 16:03:02 -04:00
Birk Magnussen bd6dae97a7 Fixed Code for GCC and C++11 2021-06-16 02:10:30 +09:00
JohnAFernandez 74c6b0c3cb Add escape character for < and > in html output (#3499) 2021-06-15 12:51:40 -04:00
Goober5000 3685a78ea9 make clang happy, and fix a bug in the English handling 2021-06-15 12:40:41 -04:00
Birk Magnussen d0948e67e3 Tidy up some new code into separate functions 2021-06-15 13:27:08 +09:00
JohnAFernandez 3660761d7c Add Menu and Mission resetting to scripting state. 2021-06-14 23:16:10 -04:00
JohnAFernandez e6579fca09 fix missiles locking during pause 2021-06-14 22:37:32 -04:00
Goober5000 da0459feeb Merge pull request #3494 from Baezon/adjust-waypoint-turnrate
Bugfix for waypoint turnrate
2021-06-14 15:07:25 -04:00
Goober5000 b428899d5b Merge pull request #3496 from Goober5000/ui_cutscene
add a scripting function for playing cutscenes in the UI
2021-06-14 14:25:31 -04:00
Goober5000 00ec3431b7 add a scripting function for playing cutscenes in the UI
Implements `common_maybe_play_cutscene` as a scripting function.  Partial fix for #3367.
2021-06-14 13:48:31 -04:00
Goober5000 353d66c17d maintain a copy of the default translations to check against untranslated strings
Bug #3471 occurs because the game only keeps a translation table for the current language.  Since the mission list needs to compare untranslated strings against English strings, this adds a duplicate table for English.  However, it only keeps strings that are NAME_LENGTH or less, because the comparison only needs to be run against mission names.ssss

Fixes #3471.
2021-06-14 02:43:28 -04:00
Goober5000 84ceae724c Merge pull request #3493 from Goober5000/objsnd_bugfix
fix to #3476
2021-06-13 19:27:36 -04:00
Joshua Glatt c47518ec6e karajorma's SEXP containers, Part 3: 'Add/Modify Container' FRED dialog (#3463) 2021-06-13 15:16:27 -07:00
Baezon 76c9583e45 bugfix for waypoint turnrate 2021-06-13 16:33:57 -04:00
Goober5000 131fadfab3 fix to #3476
The default sound position should be the zero-vector, not nullptr.  Also, this adds a bit of extra type safety.
2021-06-13 15:35:44 -04:00
Goober5000 74233c029c Merge pull request #3487 from Goober5000/move_engine_sound
center the persistent sound by default
2021-06-13 00:18:47 -04:00
Goober5000 c1d8834d81 make clang happy 2021-06-11 18:46:03 -04:00
Goober5000 7b69c10e56 add some more sophisticated POF version handling
This adds additional version definitions so that features can be added which support both the aligned and non-aligned POF formats.
2021-06-11 17:53:56 -04:00
Goober5000 c0bb5bc8eb Merge pull request #3326 from Shivansps/arm_fixes_v2
POF 2118 and SLC2 support
2021-06-11 17:43:28 -04:00
m!m 2e56b3ca61 Merge pull request #3382 from asarium/build/readdClang9
Fix missing clang-9 binary in build image
2021-06-11 21:51:12 +02:00
asarium 3c3f79bc8f Fix missing clang-9 binary in build image 2021-06-11 20:54:09 +02:00
Goober5000 462a234e81 Merge pull request #3488 from Goober5000/model_version_warning
add warning for supported POF file versions
2021-06-11 11:33:26 -04:00
Goober5000 eaf14760c4 add warning for supported POF file versions
With several upcoming POF format enhancements (byte alignment, external weapon rotation, submodel translation) it makes sense to warn users if they happen to load an unsupported POF file.
2021-06-11 10:40:21 -04:00
Birk Magnussen a9e5505a7c Added Comments, light refactoring 2021-06-11 13:39:55 +09:00
Asteroth 1525b520d7 Merge pull request #3483 from Baezon/better-damage-type-handling
Improve damage type handling when dealing damage
2021-06-10 18:53:39 -04:00
Goober5000 1843f76d04 center the persistent sound by default
The assumed location of a ship's "EngineSnd" is at its rearmost extent, which is fine for ships with engines, but not suitable for stationary objects such as space stations and comm nodes.  If the ship doesn't have an "engine" subsystem, place the persistent sound in the center.
2021-06-10 18:44:46 -04:00
Goober5000 41cf0b3d52 Merge pull request #3486 from Goober5000/3476_followup
small followup fix for #3476
2021-06-10 17:11:33 -04:00
Goober5000 990b5a3908 small followup fix for #3476
Missed changing the argument type when I changed from int to enum.
2021-06-10 16:38:41 -04:00
Asteroth e4661b00b1 Add Type 5 beams (#2817)
* wip

* basics are working

* turret case is handled

* better fighter beam handling

* missing commit?

* handle missile spawn

* clean up, commenting and ssb handling

* random in sphere and per burst rot counts for "share random" flag

* clean up, clarity and comments

* accidental whitespace changes

* bugfix

* NULL -> nullptr

* more efficiency/clarity

* review tweaks and bugfixes
2021-06-09 18:53:53 -04:00
Baezon 4457471ec5 Merge remote-tracking branch 'upstream/master' into better-damage-type-handling 2021-06-09 17:24:11 -04:00
Goober5000 128107ea43 Merge pull request #3476 from Goober5000/script_obj_sound
add scripting functions to control sounds assigned to objects
2021-06-09 16:00:53 -04:00
Asteroth c840d3e032 Add No_shield_damage_from_ship_collisions flag (#3480)
* add All_ships_take_hull_damage_from_collisions flag

* rename flag and apply flag to debris and asteroid hits
2021-06-09 15:49:35 -04:00
Goober5000 35a09527e5 another #include 2021-06-09 11:52:00 -04:00
Goober5000 20e5f35fb4 include definitions 2021-06-09 11:16:17 -04:00
Goober5000 d217873281 update flags with enums 2021-06-09 10:44:40 -04:00
Baezon 37c5041c03 improve damage type handling when dealing damage 2021-06-09 03:30:52 -04:00
Birk Magnussen fb657c081c Turret fix 2021-06-09 16:04:59 +09:00
Birk Magnussen 7945baa751 Fix turret detection 2021-06-09 15:56:28 +09:00
Birk Magnussen e9a2ff0ebf Fix proper duration handling of segments and full anims 2021-06-09 15:03:46 +09:00
Asteroth 4328b66a28 fix offset initialization (#3468) 2021-06-09 01:38:09 -04:00
Asteroth 11c5bcda74 Merge pull request #3479 from Baezon/improve-particle-alpha
Improve particle alpha handling for small particles
2021-06-08 18:21:52 -04:00
MageKing17 43f9a48cb9 Get a pointer to the generic_anim.
Whoops.
2021-06-08 13:30:21 -07:00
MageKing17 c6e79a61ea Add current_frame safety check to main_hall_mouse_release_region().
There's a similar safety check for opening doors, so we should check here as well.
2021-06-08 12:28:59 -07:00
MageKing17 5d58d9ca7a Reset APNG misc animations properly (fixes #3399).
Changes the `current_frame` checks in generic.cpp so that the last frame can actually be reached (allows the mainhall code to tell the misc animation has finished), and adds a `generic_anim_reset()` function that gets called instead of manually setting `current_frame` and `anim_time` to 0. This should fix #3399.
2021-06-08 12:27:15 -07:00
Birk Magnussen 384b2e20c7 Integrate Initial Type animations 2021-06-08 21:28:15 +09:00
Birk Magnussen 20e742ed61 Generate Animations for initial-type animations 2021-06-08 14:45:35 +09:00
Baezon 29846e2101 make particle alpha distance fade be based in part on radius 2021-06-07 20:50:12 -04:00
Goober5000 a1635cdd76 Merge pull request #3301 from Baezon/beam-substitution
Implement substitution for beams
2021-06-07 11:59:34 -04:00
Goober5000 c80144ce44 Merge pull request #3473 from jg18/sexp-container-1-fixes
A few small fixes to SEXP container definition
2021-06-07 11:23:16 -04:00
Fabian Woltermann 6e11f356ea Merge pull request #3477 from Baezon/fix-accel-weapon-cull
Fix collision culling for accelerating weapons... again
2021-06-07 14:58:14 +02:00
Joshua Glatt 7e4190b97e add any() and none() for ContainerType 2021-06-06 19:41:14 -07:00
Goober5000 65e65b01b6 Merge pull request #3478 from Goober5000/fix_subsystem_stricmp
fix bug in subsystem_stricmp
2021-06-06 14:42:17 -04:00
Joshua Glatt f3773388b9 make ContainerType's '&' overload return ContainerType instead of bool 2021-06-06 11:35:54 -07:00
Goober5000 b05132a64f fix bug in subsystem_stricmp
Here's a bug that's been around ever since version 3.6.  If the subsystem names were not essentially the same, the function wouldn't do the expected string comparison.
2021-06-06 13:59:51 -04:00
Baezon 6c399ccace fix accelerating weapons culling 2021-06-06 12:23:02 -04:00
Goober5000 373a8b940a add scripting functions to assign and remove sounds on objects 2021-06-05 20:03:35 -04:00
Joshua Glatt b4d0db8846 finish implementing bitwise operations for ContainerType 2021-06-05 16:42:55 -07:00
Goober5000 9c0575f173 Merge pull request #3060 from EatThePath/range-aware-secondary-selection
Range aware secondary selection
2021-06-05 19:23:12 -04:00
Goober5000 64f5ad925b Merge pull request #3466 from Baezon/weapon-no-collide
Add "no collide" for weapons
2021-06-05 19:22:52 -04:00
Goober5000 81b06fec41 Merge pull request #3475 from MageKing17/bugfix/superfluous-child-warning
Remove superfluous warning about missing "child" flag.
2021-06-05 19:22:46 -04:00
Goober5000 3c553665aa remove the 'main' argument from obj_snd_assign 2021-06-05 17:40:58 -04:00
MageKing17 f252473b84 Remove superfluous warning about missing "child" flag.
If a weapon has "inherit parent target" but not "child", a Warning will be generated. However, there is no dependency between the two flags; the weapon will function as the modder presumably expects. The Warning even seems to indicate this, by saying "No changes in behavior will occur" (as opposed to other similar warnings, which generally indicate that a flag will be ignored). Since the situation being warned about does not actually cause any issues, the Warning is entirely superfluous. As such, I've removed it outright.
2021-06-05 13:05:40 -07:00
Goober5000 1caf54e909 Merge pull request #3472 from Goober5000/deprecate_script_eval
deprecate script-eval
2021-06-04 21:44:27 -04:00
Birk Magnussen 6aecc4660d Add forced caching of submodelID 2021-06-04 15:52:08 +09:00
Birk Magnussen c915bfd474 Appropriate refactoring of ModelAnimationSubSYSTEM -> model 2021-06-04 15:48:05 +09:00
Birk Magnussen bd2356600f Actually use new constructor 2021-06-04 15:42:31 +09:00
Birk Magnussen 9c6cc422ac Improve Code. Operate on Submodels, not subsystems. 2021-06-04 15:36:32 +09:00
Joshua Glatt 7bd74b2a7b A few small fixes to SEXP container definition 2021-06-03 21:45:16 -07:00
Asteroth 9d5eeb6e08 Merge pull request #3461 from Baezon/random-homing-target-acquisition
Add random homing target acquisition
2021-06-03 20:10:21 -04:00
Goober5000 5ffc05fa85 deprecate script-eval
The script-eval SEXP evaluates all arguments as individual script commands, which is not ideal because most FREDders expect the arguments to be concatenated.  Since the script-eval-block SEXP handles its arguments this way, it makes sense to hide the script-eval SEXP in FRED to prevent confusion.
2021-06-03 19:07:28 -04:00
MageKing17 8ae77e0356 Fix calling split_str() with a maximum line length of 0. (#3465)
Commit 6c2ebab01c (#3439) added a `max_line_length` argument to `split_str()`, but one call had the extra argument added in the wrong spot, and so a previously-pointless-but-harmless `ignored_char` of 0 became a `max_line_length` of 0, resulting (ultimately) in an exception in the unicode code when the iterator tried to decrement at the beginning of a ship's description. This gets rid of the argument entirely, rather than reordering them, because ignoring null characters doesn't really do anything anyway.

I've also added `Assertion`s to check `max_line_length` that should prevent this specific problem from occurring again in the future.
2021-06-03 15:30:34 -04:00
Birk Magnussen a113897c88 Fix some startup logic 2021-06-03 16:51:57 +09:00
Birk Magnussen 4ee04f2492 Fix oversight for ModelAnimationSubsystem 2021-06-03 16:51:56 +09:00
Birk Magnussen 093a471602 Beginnings of new System 2021-06-03 16:51:48 +09:00
Asteroth 22be402de5 remove glowpoint 'depth' (#3455) 2021-06-02 12:54:10 -04:00
Asteroth 84133c42a8 fix ai_aims_from_center flag (#3467) 2021-06-02 12:53:45 -04:00
Joshua Glatt df1c518c3b Validate user-provided values passed to Random::next() (#3464)
* Validate user-provided values passed to Random::next()

* return low value on invalid range

* call LuaError() instead of Warning() in rand32() script function
2021-06-02 12:52:30 -04:00
Asteroth 8ac4c54d43 actually limit based on player like the comment says (#3460) 2021-06-01 22:23:14 -04:00
Asteroth 1445e778f0 Add volume effects (#3446) 2021-06-01 22:20:42 -04:00
Asteroth 2b6041f766 uncouple launch sound from loop sound (#3450) 2021-06-01 22:19:30 -04:00
Baezon 59d30157d8 add no collide for weapons 2021-05-31 22:50:00 -04:00
Joshua Glatt 552201884b karajorma's SEXP containers, Part 1: Definition, parsing, and saving (#3449) 2021-05-31 09:15:48 -07:00
Baezon e053d06a5f add random homing target acquisition 2021-05-30 22:24:24 -04:00
David Gibson f677767846 added safety check assert as requested. 2021-05-30 19:32:23 -05:00
David Gibson 9b35a4e653 Nullpointer and variable name fixes for range aware selection, and moved default weapon max range into a define. 2021-05-30 19:32:23 -05:00
David Gibson 6c0bfd4df5 Tied range aware secondary selection to an aiprofiles flag 2021-05-30 18:58:46 -05:00
David Gibson 102bc28424 Added functionality to get_available_secondary_weapons to screen weapons by range. Controlling flag to be added. 2021-05-30 18:58:46 -05:00
David Gibson 081e34815c corrected misleading comment on get_subsystem_pos 2021-05-30 18:58:46 -05:00
Baezon 6cf5553147 review tweaks 2021-05-30 16:55:50 -04:00
Asteroth 80f4f1abde add weapon ambient sound (#3452) 2021-05-30 16:02:24 -04:00
Taylor Richards 2e30674823 Merge pull request #3386 from notimaginative/add_firewall_status
add firewall status
2021-05-30 10:15:43 -04:00
ngld 448d0457c8 Merge pull request #3454 from ngld/feat/betterPofParserErrors 2021-05-30 01:31:19 +02:00
JohnAFernandez 9d7acbcc9d Update Function Name and comment to describe what it actually is (#3451)
Change find_player_id to find_player_index, since that is what it does
2021-05-29 19:21:36 -04:00
ngld c8cee4cdb1 Provide better error messages for invalid vec3ds in POF files. 2021-05-30 00:21:09 +02:00
the-maddin 21aa608eb6 whitespace cleanup (#3453)
replace spaces with tabs to fix
-Wmisleading-indentation
2021-05-29 21:59:44 +00:00
Goober5000 d6a0d9fb02 Merge pull request #3447 from the-maddin/qt-checked-list-2
qtFred: improvements to my checked list
2021-05-28 22:55:56 -04:00
Asteroth edcdbd02b6 fix bug where shields could be pierced (#3448) 2021-05-28 11:10:18 -04:00
Asteroth d49e24fa0c Add healing weapons (#2873)
* basic proof of concept: healing weapons

* implement healing weapons

* remove unused parameter

* travis

* more travis

* endless travis

* earlier multi client return

* update find_submodel_instance_world_point

* review tweaks
2021-05-25 23:03:20 -04:00
the-maddin e74ebe6b84 fix type warning 2021-05-26 00:56:54 +02:00
the-maddin c197755c0e Improve CheckedDataListModel
Add text coloring support
Add contains/add functions
Row construction efficiency fixes
Whitespace cleanup
2021-05-26 00:06:41 +02:00
Goober5000 ec4de81a79 Merge pull request #3370 from Goober5000/hud_gauge_parameter_type
Hud gauge parameter type
2021-05-24 17:58:05 -04:00
Asteroth c10d6ececf optimize motion debris rendering (#3444) 2021-05-24 17:42:16 -04:00
BMagnu 6c2ebab01c Fix erroneous line break generation for non-ASCII string and high resolutions (#3439)
* Added a byte limit for line length in split function

* Fix call error

* Ádd missing limits import

* Change hardcoded values into defines

* Cleanup default argument
2021-05-24 17:38:58 -04:00
JohnAFernandez 9a682ea32f Fix ai update danger weapon and Add guardrails for other checks (#3387)
* Make sure incoming index is valid.

* Some Extra Guardrails

* One more guardrail

* Fix Typos

Nonsense not nonesense
2021-05-24 21:30:41 +00:00
Goober5000 a5f501b58a Merge pull request #3350 from Baezon/fog-setup-fixes
Some fixes to nebula fog set up
2021-05-24 16:07:11 -04:00
Asteroth 5f8cdb37bd 'const vel' is the important quality here, not 'laser' (#3442) 2021-05-22 22:48:56 -04:00
JohnAFernandez f998215b07 Fix AI targeting itself during EMP (#3431)
To keep the AI code from going crazy, we should keep the AI from targeting itself during/after EMP
2021-05-22 22:37:36 -04:00
Baezon 106b16d5eb initialize variables 2021-05-22 21:17:23 -04:00
Asteroth 61faa3fa9a improve numerical pecision (#3440) 2021-05-21 23:36:17 -04:00
Baezon 3a960db633 optimize trail drawing 2021-05-21 18:23:35 -04:00
Baezon b89d395578 unused variable 2021-05-21 17:23:38 -04:00
Baezon f33b42085e add support for ballistic beams 2021-05-21 14:57:23 -04:00
BMagnu 87f449415f Self-Shadow on 3D-Cockpits (#3437)
* Improve wookieejedi's cockpit shadows

* Rename stray bool

* Make shadow's eye pos explicit

* Tidy up stub declaration

* Add configurable shadows + requests by m!m

* make variable lowercase
2021-05-21 08:15:20 +00:00
wookieejedi a59bb99fc6 Merge pull request #3435 from wookieejedi/expose-shadow-values
Expose Shadow Cascade Distances
2021-05-18 22:20:47 -04:00
wookieejedi ca6cc21be8 incorporate feedback/extra checks 2021-05-18 09:04:56 -04:00
the-maddin b52ab8bd4e gcc11: add missing include (#3434) 2021-05-18 01:27:31 -04:00
Asteroth a5a2f9e3df add extra check for nullptr (#3433) 2021-05-18 01:11:59 -04:00
wookieejedi 40840ee3f9 specify as float tuple 2021-05-17 17:56:48 -04:00
wookieejedi 76af0129f3 Expose Shadow Cascade Distances
The shadows distances were tuned/added by swifty with the following logic:
`// these cascade distances are a result of some arbitrary tuning to give a good balance of quality and banding.`

Mods, especially TCs, may have very different sized ships compared to FS, and as such desire more precise control over shadow cascade distances.

This PR exposes those values with settings in the `game_settings.tbl`.

PR is tested and works as expected.
2021-05-17 17:17:55 -04:00
Goober5000 f91d3dbe17 Merge pull request #3421 from Goober5000/fix_add_data_for_variables
fix add-data for variable names
2021-05-17 11:53:02 -04:00
Goober5000 5ad373502c Merge pull request #3428 from z64555/fix/issue_3427
Fix/issue 3427
2021-05-16 23:22:17 -04:00
Goober5000 37c9ecd0e3 fix add-data for variable names
This properly handles variable additions by calling the replace-variable code on the new variable node immediately after the variable is added.  It's a bolted-on hack but it's the simplest way to fix the issue.  A better fix would be to completely redesign the way the sexp tree handles variables (and everything, really) but that would be a major undertaking.

Fixes #3298.
2021-05-16 23:15:19 -04:00
Tyler Blount b92cbaff64 Merge pull request #3432 from the-maddin/qtfred-win-fixes
Qtfred win fixes
2021-05-16 18:12:31 -06:00
the-maddin ed69a1584b qtfred/win32: Later SCP_mspdbcs_Cleanup (fix crash on shutdown) 2021-05-16 18:25:57 +02:00
the-maddin 789243ea06 qtfred: Replace '/' with platform specific
separator
2021-05-16 18:25:57 +02:00
Fabian Woltermann 2701b557b8 Merge pull request #3426 from The-E/feature/disable_reflect_maps_in_lab
Add an option to switch off reflect maps in the lab
2021-05-14 11:35:11 +02:00
z64555 b4fe5c9cbe * Use temp vars for readibility.
* when using gr_printf_menu, Use format string and va_list instead of the buffer string directly
2021-05-13 16:55:02 -05:00
z64555 f1ee621624 Use menu resolution instead of screen width for positioning the "Conflict!" message. 2021-05-13 13:24:21 -05:00
z64555 bcf87e8a02 When overriding a key, the key should first load in the default binding so that legacy ccfg.tbl's can toggle a modifier (shift or alt) without corrupting the bind. 2021-05-13 13:10:04 -05:00
Fabian Woltermann 3d6fe919da Add a flag to the Lab's render settings to allow Reflect maps to be toggled on or off 2021-05-13 16:02:49 +02:00
Goober5000 2e272cce2d Merge pull request #3360 from EatThePath/stricmp-begone
Remove string comparisons and switch to vector in script conditions
2021-05-10 19:58:20 -04:00
Asteroth 309bfd7825 better beam index warning (#3376) 2021-05-10 18:17:15 -04:00
Goober5000 308794801c Merge pull request #3423 from Goober5000/xstr_id_buf_bump
bump the buffer holding the XSTR id
2021-05-08 17:42:00 -04:00
the-maddin 0b26662755 Qtfred runtime fixes (#3418)
* qtfred: fix crash on shutdown

run shutdown code sooner (lastWindowClosed) to
avoid deallocating invalid glsync

* qtfred: Fix blurry sky background and startup
warning

Only render when window surface is exposed, ensure
first correct frame is rendered asap.

* fix msvc warning

initialize variable

* Clarify comment
2021-05-08 21:31:08 +00:00
Goober5000 8fe55fa659 bump the buffer holding the XSTR id
Turns out some mods really do use XSTR ids higher than 9999.  This bumps the capacity from 4 digits to 7 digits.

Fixes #3422.
2021-05-08 16:35:56 -04:00
Goober5000 9f1e556031 Merge pull request #3420 from Goober5000/fix_3419
remove unneeded warning
2021-05-08 02:13:32 -04:00
Goober5000 d77554767d remove unneeded warning
As pointed out in #3419, subsystems can in fact have 0% maximum hull.

Fixes #3419.
2021-05-07 22:35:13 -04:00
Goober5000 52e2cda4a0 Merge pull request #3416 from Goober5000/fix_3413
use the proper range check
2021-05-05 22:31:36 -04:00
Goober5000 3a888906c5 use the proper range check
The ship index was being checked against the weapon limit.

Fixes #3413.
2021-05-05 21:34:42 -04:00
MageKing17 638a25ea0d Merge pull request #3415 from Goober5000/update_version
bump version to 21.3.x
2021-05-05 17:16:58 -07:00
Goober5000 6d60647a2a bump version to 21.3.x 2021-05-05 19:35:26 -04:00
Goober5000 cd2e69eb3b Merge pull request #3411 from the-maddin/qt-checked-list
QtFred: Model for ListView of checkboxes
2021-05-05 17:37:03 -04:00
Goober5000 e92ba1849b Merge pull request #3410 from the-maddin/qtfred-fix-warnings-05-21
Fix some qtfred warnings
2021-05-05 17:33:52 -04:00
Taylor Richards ce9a0d5e08 add new popup type to handle mission/table validation (#3380)
* add new popup type to handle mission/table validation

* fix error handling when popup gets canceled

* use bool and get rid of Int3()

* use proper Game_mode check to test for standalone
2021-05-05 00:53:57 -04:00
Asteroth d971f34ee2 make all asinf and acosf safe (#3384) 2021-05-05 00:53:24 -04:00
JohnAFernandezandAsteroth 47f6d2b368 Add guardrails to object cleanup during object creation (#3371)
* Prevent Crashes from Weapons being deleted during the collision process

This puts in some safety checks to keep FSO from crashing while doing collision detection when close to the object limit.

* Remove Problematic Assertions and INT3's

Keeps FSO from crashing when low on Object slots from failed weapon and beam creations.

* Simplify Code with a good suggestion

Co-authored-by: Asteroth <[email protected]>

Co-authored-by: Asteroth <[email protected]>
2021-05-05 00:52:11 -04:00
Joshua Glatt 144d484c85 Replace rand() calls with a Random class (#3332) 2021-05-04 18:26:06 -07:00
the-maddin 3c809a9bb5 Fix qtfred runtime warning
Initialize QApplication before call to
QCoreApplication::applicationDirPath as requested
in warning
2021-05-04 17:05:00 +02:00
the-maddin 1c1ee910fb Create model for ListView of Checkboxes
which contain additional undisplayed data. For
example the initialShips/initialWeapons lists,
which refer to the internal indices of player
ships/weapons.
2021-05-04 15:19:10 +02:00
the-maddin 6f2e707f5a Fix qtfred deprecations 2021-05-04 12:49:28 +02:00
the-maddin 3a427d923b Fix deprecation warning
(cherry picked from commit ffa0ac90b8769ba3ac6575fdc2aaad34b195e85e)
2021-05-04 12:48:53 +02:00
Goober5000 a663a7d916 Merge pull request #3408 from Goober5000/for_sexps_known_fix
tweak short-circuiting on the for-* sexps
2021-05-03 11:41:05 -04:00
Goober5000 42d96d967e Merge pull request #3397 from Goober5000/handle_weapon_none
handle weapon "None" properly
2021-05-03 11:40:54 -04:00
Goober5000 a3c5186635 add missing newlines in several mprintf statements (#3409)
Fixes #3407, and I used a regex search to find what I hope are all other examples.
2021-05-02 23:53:40 -04:00
Joshua Glatt cf7fa02e6d Fix variable replacement in text in briefings and debriefings (#3385)
* fix variable replacement in briefings/debriefings
* remove unused local variables
* multi server-side cleanup
* add client-side text replacement in multi
2021-05-02 20:46:56 -07:00
Goober5000 72aa1eebc9 tweak short-circuiting on the for-* sexps
If the `for-players` collection has no valid arguments, don't short-circuit, just to be on the safe side.

Don't short-circuit at all for the `for-ship-*` sexps, because ships can arrive and depart.
2021-04-29 23:52:21 -04:00
Goober5000 28f60df7fa Merge pull request #3406 from MageKing17/bugfix/avoid-negative-lookup-in-loadout
Avoid negative lookup when parsing loadouts.
2021-04-25 19:00:44 -04:00
MageKing17 bed18012d6 Avoid negative lookup when parsing loadouts.
#3271 changed the logic of `stuff_token_list()` in such a way that the supplied index being used to access an array was no longer skipped if it was invalid; naturally, this means that this warning was now being inevitably followed up by an out-of-bounds array access. Adding a `!skip_this_entry` check should hopefully fix the problem while still allowing the loadout count parsing code below it to run, which was presumably the reason the `continue` statement was replaced with the `skip_this_entry` variable in the first place. Fixes #3405.
2021-04-25 11:10:55 -07:00
Goober5000 d43f30053d Merge pull request #3404 from scp-fs2open/revert-3400-apng_fix
Revert "Fix APNG Animation Framecounter"
2021-04-24 22:43:40 -04:00
MageKing17 76dccbe07d Revert "Fix APNG Animation Framecounter" 2021-04-24 18:53:52 -07:00
Goober5000 3eafcde138 Merge pull request #3403 from MageKing17/bugfix/avoid-shiphit-negative-array-access
Avoid out-of-bounds array access if killed by a parentless weapon.
2021-04-24 21:13:38 -04:00
Goober5000 ca7065df46 Merge pull request #3400 from BMagnu/apng_fix
Fix APNG Animation Framecounter
2021-04-24 18:39:39 -04:00
Goober5000 cedf66cb31 Merge pull request #3402 from Baezon/fix-edge-checks
Fix fov_edge_checks
2021-04-24 18:39:32 -04:00
Baezon 03a3fbdc1c fix fov edge checks 2021-04-24 17:10:20 -04:00
MageKing17 9c6661e0e8 Avoid out-of-bounds array access if killed by a parentless weapon.
While there's code to avoid accessing the parent object of a parentless beam, there's no equivalent code to avoid accessing the parent object of a parentless weapon. This fixes this inconsistency.
2021-04-24 13:11:35 -07:00
Birk Magnussen 6bea921fbe Add short explanation 2021-04-24 23:04:00 +09:00
Birk Magnussen 79f5eda0d6 Fix APNG Animation Framecounter 2021-04-24 16:00:09 +09:00
Baezon 4e4c8449fd Merge remote-tracking branch 'upstream/master' into poof-overhaul 2021-04-22 00:06:03 -04:00
Baezon 7980891b48 overhaul poof handling 2021-04-22 00:05:22 -04:00
Goober5000 3aa61dfccb Merge pull request #3396 from Goober5000/is_visible_tweak
make is-ship-visible default to the first player
2021-04-20 00:00:31 -04:00
Goober5000 a6a7482467 handle weapon "None" properly
If the secondary weapon selection is None, the ammo count will be based on an incorrect weapon.  So guard against that.

Fixes #3393.
2021-04-19 23:38:08 -04:00
Tyler Blount 0febfb3545 Merge pull request #3394 from JohnAFernandez/Fix-that-annoying-fred-freezes-when-open-too-long-bug
Keep Fred from freezing
2021-04-19 21:18:48 -06:00
Goober5000 e1c8f5d2ed make is-ship-visible default to the first player
To be more user-friendly, and to accommodate buggy retail missions, make the one-argument version of `is-ship-visible` default to the first player in multiplayer mode..  Also, change the warning to an mprintf.
2021-04-19 23:04:27 -04:00
Goober5000 6ce2a445fe retail -> builtin 2021-04-19 22:28:29 -04:00
Goober5000 96ae629ec6 qtfred 2021-04-19 22:28:29 -04:00
Goober5000 1b9577eb85 add OPF_CUSTOM_HUD_GAUGE and apply it to relevant sexps 2021-04-19 22:28:29 -04:00
Goober5000 beeb18b46c Merge the argument list for OPF_HUD_GAUGE and OPF_HUD_GAUGE_NAME. Call it OPF_RETAIL_HUD_GAUGE. 2021-04-19 22:28:29 -04:00
JohnAFernandez 44343f8d1e Put a bandaid on Fred freezing
Could be because of variable overflows or from `thistime` somehow being much smaller than `lasttime`.

Hard to find the root cause, but manually setting the memory to bad values will no longer cause the freezing with this patch.
2021-04-19 22:00:46 -04:00
Taylor Richards 424c69d1e5 Merge pull request #3379 from notimaginative/fix_chat_error_handling
address issues with error handling in chat_api
2021-04-18 19:46:39 -04:00
JohnAFernandez 6d1354f1f6 Explicitly check that the type is a ship. (#3389)
Not all ships in the ships array are ships (if they were turned into observers), so we need to explicitly check that they are ships.
2021-04-18 05:11:30 +00:00
Tyler Blount d5c4f3e5c7 Merge pull request #3348 from TRBlount/fix/display_name_regression
Strip hashes from explicitly defined display names in tables.
2021-04-17 23:03:54 -06:00
JohnAFernandez 89bd64f4ab Make sure the Netgame state is set to prevent a crash (#3339)
If the client tries to respawn a player when the server is trying to end the game it will lead to a crash on the client side.  This sets the client's game to the correct state when an end game packet is received and adds extra checks before respawning.
2021-04-17 23:49:54 -04:00
Tyler Blount 417739665c Strip hashes from explicitly defined ship and weapon display names 2021-04-17 17:07:41 -06:00
Taylor Richards 11f2920b4f add variant of game update packet to tell PXO we want firewall status 2021-04-17 17:47:15 -04:00
Taylor Richards 9e4ab292ac fix game announcement issue causing first firewall check to fail 2021-04-16 18:35:46 -04:00
Taylor Richards 51addab005 add notification for PXO firewall test 2021-04-16 18:34:48 -04:00
MageKing17 e236a79773 Make an invalid variable name throw an error. (#3373)
There were a few assertions in the SEXP code that would trip if an invalid variable name were specified in a SEXP, but nothing that actually stopped a Release build from performing an out-of-bounds array access if someone made a typo in notepad. Since, as far as I could tell, everything should go through `get_sexp_text_for_variable()` first, I changed its `Assert()` into an `Error()` that should hopefully ensure that we avoid ever doing anything with `Sexp_variables[-1]`.
2021-04-14 21:26:11 -04:00
JohnAFernandez d523f0aef3 Make Sure Special Shield Hitpoint Max Gets Transferred (#3383)
Since the shield strength of the ships is being copied, you need to copy the special shield hitpoints, too, just like for hull.
2021-04-14 21:07:31 -04:00
Taylor Richards 697c009fb3 address issues with error handling in chat_api:
- detect server connection error messages before blindly
   moving to user login (crash fix)
 - detect read errors and fail accordingly (crash fix)
 - allow server disconnect to run even if we aren't connected (cleanup & reset)
2021-04-12 12:59:04 -04:00
JohnAFernandez 1f918fd4c6 Allow Fred to Edit and Allow Command briefs in FRED (#3372)
* Allow Fred to Edit and Allow Command briefs in FRED

They are currently allowed in the game, but not being allowed in Fred was an oversight of mine.

* Allow Command brief saving in qtFRED
2021-04-11 13:50:25 -04:00
Asteroth 3ea7a6d1ba Merge pull request #3369 from Baezon/cull-generic-debris
Properly cull generic debris
2021-04-10 21:44:56 -04:00
David Gibson 743989525b Implemented index caching for hooks refering to ship or weapon info objects. 2021-04-10 20:41:22 -05:00
David Gibson 5bca1417c2 Vectorized the hook condition lists and began chaching information inside the hook at creation to avoid string comparisons in play. CHC_VERSION, CHC_STATE, and CHC_APPLICATION converted. 2021-04-10 20:18:29 -05:00
Baezon 633cd88e83 modernize use nullptr 2021-04-10 19:38:18 -04:00
Baezon 7431aba995 properly cull generic debris 2021-04-09 21:35:26 -04:00
JohnAFernandez 328bebe83a Fix event parsing by setting flags to zero before loading. (#3363) 2021-04-07 23:41:14 -04:00
Goober5000 94e4f3d4cd Merge pull request #3362 from Baezon/no-ship-subsys-lookup
Remove unecessary ship_get_subsys in model_rotate_gun
2021-04-06 10:48:09 -04:00
Baezon b57573c439 remove unecessary ship-get-subsys 2021-04-06 02:24:05 -04:00
Goober5000 0b1d1eed56 Merge pull request #3357 from notimaginative/fix_briefing_music_reset
Only close common_select when we should
2021-04-05 23:52:35 -04:00
Goober5000 123feba557 Merge pull request #3353 from Goober5000/escape_exclamation
make double-character consolidation reusable, and reuse it
2021-04-05 23:50:39 -04:00
Taylor Richards 8c64d73a18 Merge pull request #3358 from notimaginative/fix_main_hall_sounds_in_multi
fix main hall sounds/music not getting stopped on multi clients
2021-04-04 08:19:43 -04:00
Taylor Richards e156f15561 fix main hall sounds/music not getting stopped on multi clients 2021-04-03 11:08:28 -04:00
Taylor Richards 3e1d89ba53 only close common_select when we should 2021-04-03 10:22:11 -04:00
Asteroth 4f7bad2a5c Merge pull request #3347 from Baezon/fireball-lod-fix
Make fireballs default to 1 LOD
2021-04-02 16:01:39 -04:00
Goober5000 dd621e8904 Merge pull request #3351 from PhantomHoover/feature/profile-script-hooks
Add profiling trace for scripting hooks
2021-04-02 15:07:17 -04:00
Goober5000 396d2c55a9 make double-character consolidation reusable, and reuse it
This moves the ## to # escape sequence logic from `mission_process_alt_types` to its own function where it can be used elsewhere (such as for ship class and weapon class display names that need to do the same thing).  Also, this introduces a !! escape sequence for exclamation points that is needed for exclamation points to be used in scripts, as @naomimyselfandi pointed out.
2021-04-02 00:16:19 -04:00
Goober5000 8f5064b353 actually use GS_STATE_INVALID
Make `gameseq_get_state` return GS_STATE_INVALID if the stack is invalid.

With this change, the check for stack validity can be removed in `gr_flip`.
2021-04-02 00:11:45 -04:00
Phantom Hoover 782f6f100d Add profiling trace for scripting hooks 2021-04-01 11:54:09 +01:00
Baezon 6ec1f45997 neb fog set up fixes 2021-04-01 02:17:49 -04:00
Baezon 4f76c441d6 default to 1 lod 2021-03-31 18:00:00 -04:00
Asteroth a2bd895b7b Merge pull request #3344 from Baezon/fix-huge-targeting-regression
Fix regression with huge/small only targeting
2021-03-31 16:47:17 -04:00
JohnAFernandez a4cf53694d Merge pull request #3346 from JohnAFernandez/Fix-3007
Add a safety check to the $On Frame: Hook
2021-03-31 16:46:11 -04:00
JohnAFernandez 6bd7a43e3d Add a safety check to the $On Frame: Hook
So #3007 is a larger bug than just trying to access the player's target.  The real problem is that the game state is not changed from `GS_STATE_GAME_PLAY` during the mission load when restarting the mission.  So the script thinks it's in the mission, tries to access the target and then crashes.  But this won't be the only bug.  Any script that tries to access data on Frame will run into possible problems on mission restart.
2021-03-31 15:25:38 -04:00
Baezon fffc0ebf8b fix name of class #3344 2021-03-30 22:03:25 -04:00
Goober5000 f2426de80b Merge pull request #3340 from Goober5000/num_valid_arguments_fix
make num-valid-arguments work for the for-* sexps
2021-03-30 19:32:26 -04:00
Goober5000 27f6088117 make num-valid-arguments work for the for-* sexps
This adds a hook into the for-* handlers to return the number of valid arguments.  Fixes a bug reported on Discord.
2021-03-30 18:51:24 -04:00
Goober5000 30c76f36ce Merge pull request #3342 from Goober5000/message_list_saving
modify saving of edited messages
2021-03-30 18:42:39 -04:00
Goober5000 35094851f2 modify saving of edited messages
Instead of saving based on the m_cur_msg index, which may have been reset to -1, save on the most recent positive index.  This should keep track of modified text and dropdowns even when m_cur_msg is set to -1 when the message list loses focus.

Fixes #3308.
2021-03-30 17:32:02 -04:00
Fabian Woltermann b562cf822e Merge pull request #3345 from Baezon/fix-strafing-bug
My kingdom for a semicolon!
2021-03-30 09:15:42 +02:00
Baezon 62942557d5 typo 2021-03-30 00:19:53 -04:00
Baezon 4d68bed436 fix huge targeting regression 2021-03-29 22:42:50 -04:00
Asteroth 19d5b8e68e Merge pull request #3329 from PhantomHoover/feature/split-profile-columns
Split frame profile trace into two onscreen columns if needed
2021-03-29 15:34:55 -04:00
Goober5000 9dc50706a7 Merge pull request #3309 from Baezon/turret-alt-math-fix
Update Turret_alt_math for moving subsystems
2021-03-28 18:06:05 -04:00
Goober5000 f2a0e35959 Merge pull request #3333 from Goober5000/no_xstr_entry_suppress
log missing XSTR strings only once
2021-03-28 15:17:24 -04:00
JohnAFernandez e637eea2fb Merge pull request #3337 from JohnAFernandez/Interpolation-Bug
Fix a rare but eventual crash in the interpolation code.
2021-03-28 10:00:47 -04:00
Fabian Woltermann f3e3769056 Merge pull request #3266 from The-E/fix/3250_dynamic_nebula_detail
Make nebula poof detail settings partially dynamic
2021-03-28 13:18:26 +02:00
JohnAFernandez e218f687a9 Fix a rare but eventual crash in the interpolation code.
I don't know when this was introduced but the bug is extremely obvious once someone hit the crash.  It should just be using the timestamp that the packet was received, not trying to put that timestamp into a vector whose index is server frame numbers.
2021-03-28 05:07:27 -04:00
Baezon 5e10fccdbf Merge remote-tracking branch 'upstream/master' into turret-alt-math-fix 2021-03-27 21:12:30 -04:00
Goober5000 12672d0c2b Merge pull request #3310 from Baezon/fix-fov-edge-checks
Fix Fov_edge_checks
2021-03-27 17:22:37 -04:00
Goober5000 de7081fbb3 Merge pull request #3335 from Goober5000/missing_hud_gauge_warning
change reticle log statements to warnings
2021-03-27 15:19:47 -04:00
Goober5000 d73e4d4dec change reticle log statements to warnings
Most guages in hud_guages.tbl will display a Warning if they can't be loaded, but the reticle is an exception - it only prints a log statement.  This PR changes it to be consistent with the rest of the HUD gauges.

Fixes #3090.
2021-03-27 14:19:59 -04:00
Fabian Woltermann aeefcb4657 Remove an unused constant 2021-03-27 16:23:35 +01:00
Fabian Woltermann 023dcfca76 Use the screen size as the break value, remove it from the Neb detail settings 2021-03-27 16:22:20 +01:00
Fabian Woltermann c6e9f954f0 Use the screen size as the break_* value 2021-03-27 16:11:26 +01:00
Fabian Woltermann 961d5eb171 Use bitmap height * 2 to make sure only completely off-screen poofs are culled 2021-03-27 16:11:26 +01:00
Fabian Woltermann 1a66c97c2e After loading the nebula poofs, resize the break_x and break_y values so they are equal to the largest poof bitmap in use.
Also reduce the break alpha value by 90% for all detail levels.
2021-03-27 16:11:26 +01:00
Goober5000 00e2c2ad52 log missing XSTR strings only once
The "No XSTR entry in strings.tbl for index %d" message was previously printed to the log every time the string was accessed, which for commonly-used strings will clutter up the log.  This makes the message only appear once per missing index.
2021-03-26 23:22:06 -04:00
JohnAFernandez 8991fc67cd Merge pull request #3331 from JohnAFernandez/Fix-Multi-Gamestate-Bug
Check for these states
2021-03-26 20:47:40 -04:00
JohnAFernandez 7641abf41c Check for these states
Because GS_STATE_MULTI_MISSION_SYNC is actually shared for a few different multi screens.
2021-03-26 20:18:39 -04:00
Phantom Hoover 871f7a822e Split frame profile trace into two onscreen columns if needed 2021-03-26 20:33:01 +00:00
m!m ca66664eac Merge pull request #3327 from Shivansps/over_alloc_fix
fix over alloc to store lz4 offsets
2021-03-26 18:33:21 +01:00
m!m 4c1b3551b4 Merge pull request #3328 from ngld/fix/keep_stdout
Add FSO_KEEP_STDOUT env var to skip the AttachConsole() call
2021-03-26 18:32:34 +01:00
Asteroth 07d47cb984 Merge pull request #3319 from Baezon/add-fog-color
Allow for setting of nebula fog color explicitly
2021-03-25 21:20:55 -04:00
Allen A. Babb ce9ee8c264 Merge pull request #3324 from z64555/fix/issue_3322
Fixes #3322
2021-03-25 18:43:15 -05:00
ngld 8039650248 Add FSO_KEEP_STDOUT env var to skip the AttachConsole() call
Fixes #3317
2021-03-26 00:03:51 +01:00
Shivansps cd2b578a1b fix over alloc to store offsets 2021-03-25 19:57:48 -03:00
Shivansps 052879b3e4 POF 2118 and SLC2 support 2021-03-25 19:13:12 -03:00
z64555 85ec8251e3 When in bind mode, identify mouse as CID_JOY0 instead of CID_MOUSE. Fixes #3322. 2021-03-25 09:41:37 -05:00
JohnAFernandez 7e51a13c3a Merge pull request #3296 from Shivansps/compressed-files
LZ4 Compressed files Support
2021-03-25 07:08:06 -04:00
Baezon 538b2c6c95 handle lab and use empty string #3319 2021-03-24 13:25:16 -04:00
wookieejedi d3a70e8ed6 Merge pull request #3320 from wookieejedi/disable-spew-whole-ship
Add `no impact debris` for whole ship
2021-03-24 00:56:09 -04:00
Baezon 9dc68849d6 unused variable 2021-03-23 23:59:05 -04:00
wookieejedi b6bab87601 restore original comment 2021-03-23 23:44:03 -04:00
wookieejedi b8d868f828 restore comment 2021-03-23 23:43:31 -04:00
wookieejedi 01e6a57aaa remove un-needed section 2021-03-23 23:41:57 -04:00
wookieejedi e0fe9227aa clarify whitespace 2021-03-23 23:21:40 -04:00
Baezon 803751959a rename variable for no shadowing 2021-03-23 23:20:11 -04:00
wookieejedi 9597b5962e clarify ship override for subsystems debris 2021-03-23 23:16:59 -04:00
wookieejedi 792f74b33e Add no impact debris flag for entire ship model
Previously `no impact debris` flag was only an option for use on subsystems. This PR adds that flag for the entire ship. It also turns off the impact spew of debris for all subsystems if enabled on the ship.
2021-03-23 23:04:08 -04:00
Baezon 5181e71dab Add explicit fog color 2021-03-23 22:57:54 -04:00
JohnAFernandez 3d0e0f80ae One more hiccup 2021-03-23 20:31:04 -04:00
JohnAFernandez 251125913a How many mistakes is it now? No idea. 2021-03-23 20:31:04 -04:00
JohnAFernandez 89d2d6ed46 Fix missile indicators not going away
Add the should be dead flag, in order to properly make the missiles go away, and not just detonate.
2021-03-23 20:31:04 -04:00
JohnAFernandez 06f198afc1 Forgot one 2021-03-23 20:31:04 -04:00
JohnAFernandez 19a289539a More Whitepsace and Dealing with Crazy old CI 2021-03-23 20:31:04 -04:00
JohnAFernandez c14653b391 Whitespace Adjustments 2021-03-23 20:31:04 -04:00
JohnAFernandez 2527bf31c7 Upgrade velocity compression in OO packet
Previously, the velocity could be off by up to 1 m/s.  This gives that packing function one more byte to work with, so that we have a lot more accuracy to work with.
2021-03-23 20:31:04 -04:00
JohnAFernandez 184107283a Properly Set net_signatures for wing ships
Previously wing ships were being given a net signature that was one too large.
2021-03-23 20:31:04 -04:00
JohnAFernandez 42c19cdfee Fix Missile Syncing Between Server and Client 2021-03-23 20:31:04 -04:00
JohnAFernandez 3830534978 Fix implementation of multi_pack_unpack_desired_vel_and_desired_rotvel 2021-03-23 20:31:04 -04:00
JohnAFernandez 48eef6566d Multi Version bump for 21_2
since many of the fixes and features require a multi bump.
2021-03-23 20:31:04 -04:00
the-maddin b253442545 Add -pilot cmdline flag (#3316)
* Cmdline parsing for `-pilot "<name>"`

Add cmdline parameter `-pilot` with required
argument name and export name as global var
`Cmdline_pilot`.

* Expose to scripting `isAutoselect`

Setting `-pilot` flag sets `ui.isAutoselect()` true
(as does `-benchmark_mode`).

* Skip pilot selection

and call `player_finish_select` with the given
player instead.

* More useful error message on pilot load error

Also display callsign

* Add pilot name as scripting variable

* Don't expose pilot flag to launchers

* make stored flag param `const`
2021-03-22 21:14:57 +00:00
Baezon 26314c9b97 Merge remote-tracking branch 'upstream/master' into poof-overhaul 2021-03-22 00:37:17 -04:00
Baezon 5ebb12e47c overhaul poof system 2021-03-22 00:35:31 -04:00
JohnAFernandez 1d5e9794d6 Merge pull request #3263 from BMagnu/fix_unicode_stringstbl
Fix strings.tbl in unicode mode
2021-03-21 17:41:55 -04:00
Asteroth b7a9f545fd Merge pull request #3304 from BMagnu/weapon_penetrate_flag
Add pierce ship weapon flag
2021-03-21 15:12:45 -04:00
JohnAFernandez 9541e244d1 Make sure objects and ships get cleared if leaving mission sync (#3314)
This properly clears ships if you are leaving the sync state for any other state besides the gameplay state.
2021-03-21 14:54:32 -04:00
Asteroth f12d202afe Merge pull request #3278 from Baezon/exponential-fog
Switch to exponential fog and other nebula visibility improvements
2021-03-20 16:45:24 -04:00
Goober5000 507a5f1de8 Merge pull request #3312 from Goober5000/per_mission_chase_view
add mission flag for starting missions in chase view
2021-03-20 15:36:11 -04:00
Baezon 3810b039f5 Merge remote-tracking branch 'upstream/master' into exponential-fog 2021-03-20 10:39:41 -04:00
Baezon 827754d6de minor fixes 2021-03-20 02:31:47 -04:00
Goober5000 d455e17e34 add mission flag for starting missions in chase view
Follow-up to #3305.  Some mods may want one or two missions to start in chase view without having every single mission default to chase view.  So this adds a mission flag (with FRED and qtFRED support) that will enable this on a per-mission basis.
2021-03-20 00:21:27 -04:00
Baezon 7a7999ca56 Merge remote-tracking branch 'upstream/master' into poof-overhaul 2021-03-19 20:43:17 -04:00
Shivansps db360820a9 fix build check 2021-03-19 20:51:09 -03:00
Baezon 804a222041 unused variable 2021-03-19 19:44:11 -04:00
Shivansps 6dab1e464f Merge branch 'compressed-files' of https://github.com/Shivansps/fs2open.github.com into compressed-files 2021-03-19 20:21:02 -03:00
Shivansps 86c45309e8 requested changes and simplified if on random access function 2021-03-19 20:20:58 -03:00
Birk Magnussen 4f4690a482 Fix Names and spawn on destruct 2021-03-20 03:52:54 +09:00
Baezon 1ca60e1d2f fix fov_edge_checks 2021-03-19 11:48:15 -04:00
JohnAFernandez 420c7ec171 Play directive completion sounds for clients (#2900)
* Allow Text to show failure on Clients

Function was accidentally returning false before sending an update on failure.

* Play directive sounds for clients

This adds a short client version of the function that initiates the directive completion sound.

* forgot to remove a failed experiment
2021-03-19 05:39:05 -04:00
Birk Magnussen 011f7e4d6c Change flag to Table entry 2021-03-19 18:30:39 +09:00
Asteroth 8b94e58596 Merge pull request #3284 from Baezon/fix-rand-circle-sphere-bias
Fix probability distribution of vm_vec_random_in_circle/sphere
2021-03-19 02:23:31 -04:00
Asteroth b304ccbe96 Merge pull request #3305 from Strygon/chaseviewtest
Adding "Start missions in Chase View by default" flag in game_settings.tbl
2021-03-19 02:23:08 -04:00
Baezon eb538ffba8 update turret_alt_math for moving subsystems 2021-03-19 02:13:20 -04:00
JohnAFernandez 5bb934119c Limit instances of Client Collisions to Avoid Instant Death (#3164)
* Limit instances of Client Collisions to Avoid Instant Death

* Set timestamps correctly (take 2)

* Handle server check the same everywhere

* Clearer Logic Here
2021-03-18 17:24:08 -04:00
Fabian Woltermann 8c7a8e42b2 Merge pull request #3307 from The-E/fix/3206_submodel_property_parsing
Fix for #3306
2021-03-18 17:29:10 +01:00
Fabian Woltermann 9a06e0727c Make sure we check for any carriage return characters that may sneak in too 2021-03-18 16:52:15 +01:00
Strygon 08603009fb removed the redundancy 2021-03-17 20:40:02 +01:00
JohnAFernandez 609ed4ed94 Remove the fireball limit (#3224)
* Remove the fireball limit

* Handle Feedback

(Without the main PC, so really hoping I don't do any typos)
2021-03-17 14:21:06 -04:00
Strygon 3ab9f85a7a ditto 2021-03-17 17:50:19 +01:00
Strygon 45db675c35 Renaming the bool 2021-03-17 17:49:43 +01:00
Strygon 5b01a68352 Adding a few forgotten parts of the ChaseViewDefault code 2021-03-17 17:39:27 +01:00
Strygon 67cc6a66eb Adding ChaseView Flag functionality
This is where the game would put the player into chaseview/third person if the flag for it is set in game_settings.tbl
2021-03-17 17:38:32 +01:00
Strygon 842c84da7d Adding the actual flag for default chase view 2021-03-17 17:36:25 +01:00
Strygon 6598c25fda Adding support for Chase View flag 2021-03-17 17:35:39 +01:00
Birk Magnussen 2a73fb340f Add pierce ship weapon flag 2021-03-17 17:39:04 +09:00
Baezon edee63491d Merge remote-tracking branch 'upstream/master' into poof-overhaul 2021-03-17 00:46:38 -04:00
Goober5000 d716433848 Merge pull request #3300 from Baezon/fix-flak-substitute-assert
Fix assert with combining flak and non-flak using substitute
2021-03-17 00:34:14 -04:00
Baezon 5bd351d42a initial poof overhaul 2021-03-16 11:33:34 -04:00
Shivansps ad60240eb6 Updated comment header. Feature Complete. 2021-03-16 10:11:48 -03:00
Baezon 29b03cd49f implement beam substitution 2021-03-15 23:16:05 -04:00
Shivansps f8df97b489 fix seek_end on comp_fseek
This where causing the pcx issues
2021-03-15 23:59:39 -03:00
Asteroth 95235b353b Merge pull request #2885 from Baezon/expose-huge-small-targeting
Expose ability to be targeted by 'huge' and 'small only' weapons
2021-03-15 22:32:22 -04:00
Shivansps d5feddfb8a fixed comp_fseek return 2021-03-15 20:08:58 -03:00
Baezon ce1907c52a flak should use normal mflash methods 2021-03-15 18:30:47 -04:00
JohnAFernandez 895dbdddb8 Merge pull request #3295 from Goober5000/spacebar_popup
add text to context popup
2021-03-15 09:44:00 -04:00
Baezon f5e32fa1a2 Merge remote-tracking branch 'upstream/master' into exponential-fog 2021-03-15 02:27:22 -04:00
Shivansps 9fa3baa458 fix typo 2021-03-14 23:43:22 -03:00
Shivansps 4b3760dfb4 more build fixes 2021-03-14 21:17:07 -03:00
Shivansps 142d586a1c and even more build check fixes 2021-03-14 20:29:26 -03:00
Shivansps 95103dfcb8 more build check fixes 2021-03-14 19:57:04 -03:00
Shivansps f50ea26051 Change CI struct name to upper case 2021-03-14 19:36:20 -03:00
Shivansps 983dd0b6ca changes for build check errors 2021-03-14 19:21:13 -03:00
Shivansps 3bdc5e0c27 Merge branch 'master' into compressed-files 2021-03-14 17:57:53 -03:00
Baezon 643cb6a2be update scripting functions 2021-03-14 15:37:43 -04:00
MageKing17 2348d4db64 Merge pull request #3297 from MageKing17/bugfix/sexp-ammo-safety-regression
Fix ammo safety regressions.
2021-03-14 11:45:04 -07:00
MageKing17 77534926c8 Comment clarification. 2021-03-14 11:16:54 -07:00
Shivansps ba6a471054 comment debug statements 2021-03-14 14:55:20 -03:00
MageKing17 47c409a3d5 Fix ammo safety regressions.
When various SEXP functions related to ammo were consolidated back in June (in commit 54409ec928), certain safety catches meant to avoid having too much ammo or the wrong ammo capacity after changing weapons were lost. This commit adds code that should be functionally equivalent.
2021-03-14 10:43:06 -07:00
Shivansps ce44e3522b updated header 2021-03-14 14:35:04 -03:00
Goober5000 593fbdbf21 add text to context popup
The Space Bar is a valid shortcut, so indicate as such.  Fixes #3289.
2021-03-14 12:47:15 -04:00
Shivansps c1b87bc2b6 Add missing CAP to comp goal position.
It should not be needed but at least this stops crashing when you use a unsupported format in compression (like, audio and pcx)
2021-03-14 13:34:13 -03:00
Shivansps 1f164dbb64 Removed isCompressed from compression_info 2021-03-14 11:12:00 -03:00
Shivansps 1275cbfd83 Proper lz4 reference and added position checks
LZ4 moved to lib folder and added cmakelists.
Added several position check to match cfilearchive ones for compressed files.
Changed header check to int.
2021-03-14 10:47:11 -03:00
Goober5000 69be3c8f35 Merge pull request #3293 from Goober5000/hotkey_range_check
simple hotkey range check
2021-03-14 00:04:14 -05:00
Goober5000 85fc1f7cea simple hotkey range check
This prevents the message from firing if the wing index is -1 which would cause an array underflow.  Fixes #3280.
2021-03-13 23:15:16 -05:00
Goober5000 59a5a343c8 Merge pull request #3291 from Baezon/fix-contrail-a_decay_exponent
Peoperly initialize alpha decay exponent for contrails
2021-03-13 22:14:38 -05:00
Baezon a3ce01f250 properly initialize a_decay_exponent 2021-03-13 20:33:54 -05:00
Shivansps 20cccdbf20 Updated Implementation
Moved the compression functions to separated files, added dynamic blocksize support for LZ4 and a decoder buffer to make sure every block is only read from file and decoded only once.
2021-03-13 17:15:04 -03:00
Asteroth 4b64c43ef8 Merge pull request #3274 from Baezon/shooter-damaging-weapons
Add shooter damaging weapons
2021-03-13 15:03:52 -05:00
Asteroth 4307d12762 Merge pull request #3285 from Baezon/fix-fit-and-set-obj-facing
Bugfix for FIT with set-object-facing
2021-03-13 14:59:52 -05:00
Asteroth 0d3d336d65 Merge pull request #3287 from Baezon/fix-aburn-trail
Properly initialize trail alpha exponents for afterburner trails
2021-03-13 14:59:24 -05:00
Shivansps 077338086a Improved implementation
Model load and tech room now works, it crashes at mission load.
2021-03-12 19:58:49 -03:00
Birk Magnussen 0b2ee5f76b Modify other file reading method for new encoding conversion 2021-03-13 07:50:41 +09:00
Baezon 256a047fab fix rand circle and sphere probability distribution 2021-03-12 16:13:52 -05:00
Baezon 222799b1a1 initialize trail alpha exponents for aburn trails 2021-03-12 16:03:50 -05:00
Fabian Woltermann 649fe7e3ce Merge pull request #3286 from Baezon/fix-to-the-fixed-fog
Fix for nebula fogging distance
2021-03-12 10:54:50 +01:00
Baezon 2e5e25dc09 revert #2222 and fix comment 2021-03-11 23:28:23 -05:00
Shivansps 3f9218e671 1st attempt at implementation
I just dumped everything into cfilearchive to avoid a fight with the includes that i cant win for now.

It dosent work, DDS utils complains about DDS filecode.
2021-03-11 20:51:28 -03:00
Birk Magnussen 3003d0589d Change validation to own function 2021-03-12 06:34:27 +09:00
Birk Magnussen 4bd15d43da Fix Warning due to enum class 2021-03-12 06:04:10 +09:00
Birk Magnussen 8df62e99f5 Change Enum to Enum Class 2021-03-12 05:54:25 +09:00
Birk Magnussen d93c9c4220 Adapt parsing exceptions 2021-03-12 05:45:09 +09:00
Birk Magnussen 1b5fd3c5fd Move Conversion functions to util/unicode 2021-03-12 05:42:44 +09:00
Baezon 0612a72c2f fix FIT with set-object-facing 2021-03-11 15:41:55 -05:00
Baezon 22f7ff3443 fix bias from random_circle/sphere 2021-03-11 03:02:24 -05:00
Goober5000 5105f35a31 Merge pull request #3271 from Goober5000/better_parse_lists
rewrite stuffing of lists
2021-03-10 19:19:39 -05:00
Goober5000 f025572110 rewrite stuffing of lists
Consolidate all the functions which parse lists of data.  They're almost the same except for the data itself, so use templates.

As for the templated function which handles the list, boost the robustness by adding additional checking and error messages.
2021-03-10 18:50:12 -05:00
Goober5000 ed2cc47093 Merge pull request #3269 from Goober5000/annotation_fix
Various annotation fixes
2021-03-10 18:49:39 -05:00
Goober5000 4486b9ee10 Merge pull request #2923 from z64555/controls3
Controls3 - Primary/Secondary Bindings
2021-03-10 15:53:52 -05:00
Asteroth e8603e1617 Merge pull request #3247 from Baezon/better-subsys-attacking
Add improved subsystem attacking
2021-03-10 12:21:53 -05:00
Goober5000 f67860a502 Merge pull request #3283 from Goober5000/fix_isdigit_type
fix character type for isdigit
2021-03-10 11:37:54 -05:00
Goober5000 497a0e0805 fix character type for isdigit
Just like the `std::toupper` and `std::tolower` functions (see PR #3226), `std::isdigit` is designed to work with regular `char`, not `unsigned char`.
2021-03-10 10:51:56 -05:00
Goober5000 470a996b11 Merge pull request #3281 from Goober5000/beam_range_fxi
a few turret fixes
2021-03-09 23:09:59 -05:00
Goober5000 5bb9f427b4 a few turret fixes
This correctly calculates the turret position from the center (zero position) of its submodel instead of using the offset from the main model.

This patches a few more parts of the code and also adds a missing locale argument.
2021-03-09 22:39:13 -05:00
Goober5000 52df877ed9 Merge pull request #3276 from Goober5000/special_arg_fix
add small fix from SEXP containers PR
2021-03-09 21:43:28 -05:00
Shivansps 8292d8c44a Revert "Merge branch 'arm_fixes'"
This reverts commit 2ee128cd88, reversing
changes made to a2b349855e.
2021-03-09 20:44:43 -03:00
Asteroth 0eefd352d9 Merge pull request #3264 from Baezon/32-poofs
Increase max nebula poofs to 32
2021-03-08 14:44:26 -05:00
Baezon cf2e3e8641 lightning even with 0 poofs 2021-03-08 10:50:27 -05:00
Baezon 78c79ac88d debug clean up 2021-03-08 01:37:00 -05:00
Baezon 6e54ed3dfc more exponential fog 2021-03-08 00:09:42 -05:00
Baezon f60d2d8e30 comment changes 2021-03-07 23:06:30 -05:00
Goober5000 a12a33b189 Merge pull request #3277 from Goober5000/movable_turret_fov_fix
Turrets on movable submodels
2021-03-07 21:29:28 -05:00
Baezon cf3ce54038 comment clarity 2021-03-07 14:20:55 -05:00
Goober5000 483a632c7f Merge pull request #3243 from Baezon/decouple-asteroids-species
Decouple asteroids from species
2021-03-07 13:13:13 -05:00
Goober5000 9a3fc507d6 Merge pull request #3261 from BMagnu/lua_campaignswitch
Lua add campaign switch function
2021-03-07 13:10:01 -05:00
z64555 19c0f64736 Upgrade a few Assert()'s into Assertion()'s with error messages 2021-03-07 09:56:21 -06:00
z64555 78ac82d6eb Replace Int3()'s with UNREACHABLE
* control_config_bind_btn uses UNREACHABLE macro when an unknown order is passed to it
2021-03-07 09:20:34 -06:00
Baezon 2dd40bcbd8 typo 2021-03-07 02:00:58 -05:00
JohnAFernandez 5da44a9d72 Merge pull request #3178 from Baezon/generic-debris-enhancement
Generic debris expansion and configuration
2021-03-07 00:19:25 -05:00
Goober5000 d75b0f4b4b additional minor cleanup 2021-03-06 20:03:59 -05:00
z64555 5c9582aefc Fix bug where "Clear Conflict" button was disabled on Ship tab unless the conflicted items included an axis. 2021-03-06 12:52:48 -06:00
Goober5000 ab478c4a0e Merge pull request #3275 from Goober5000/uoffset_addendum
addendum to u_offset
2021-03-05 22:44:00 -05:00
Goober5000 01daf25fe9 addendum to u_offset
This improves PR #3257 by updating the frametime after the beam fires, not before, so that the first frame will always be at offset zero
2021-03-05 22:15:55 -05:00
Goober5000 db73c27a53 add small fix from SEXP containers PR
While skimming the differencs in jg18's branch of PR #3253...
https://github.com/jg18/fs2open.github.com/tree/feature/sexp-containers-revised

...I noticed a TODO that is almost certainly a genuine bugfix.  As such, it deserves its own PR.  The line in question was last modified in 2012 by karajorma, and the bugfix should test for the special-arg in the entire actions tree rather than just the first action.
2021-03-05 22:00:51 -05:00
Baezon 3c53b74e5e switch to exponential nebula fog 2021-03-04 23:02:24 -05:00
Birk Magnussen 829e0b8b7c remove bad type modifier 2021-03-04 22:17:16 +09:00
Birk Magnussen 4b60ca467c Refactor encoding functions 2021-03-04 22:08:50 +09:00
Baezon d972f353f1 better species name handling and XSTR 2021-03-03 22:05:12 -05:00
Baezon 9c1d1e64cc better self kill text 2021-03-03 20:22:50 -05:00
Goober5000 abc153e21e Merge pull request #3273 from NovachenFS2/TranslatedAsteroids
Translateable Asteroid Name in Asteroid Fields
2021-03-03 15:26:30 -05:00
Jenna Trassa 9385206e0e Feedback & lowercase debris code 2021-03-03 20:56:24 +01:00
Goober5000 3674f06400 Merge pull request #3272 from NovachenFS2/AsteroidTimer
Translateable Asteroid Impact Time
2021-03-03 14:36:14 -05:00
Goober5000 be780af04d Merge pull request #3268 from BMagnu/replace-texture-sexp
Add replace-texture SEXP
2021-03-03 14:33:21 -05:00
Goober5000 ec1d2e091e Merge pull request #3262 from Goober5000/enable_multi_for_two_sexps
allow two SEXPs to be used in multiplayer that previously couldn't be
2021-03-03 13:59:21 -05:00
Goober5000 106da3c95f allow two SEXPs to be used in multiplayer that previously couldn't be
1) The ship-vanish SEXP (and script function) was already multiplayer-ready
2) The is-ship-visible SEXP was enhanced to add a second argument, which defaults to the player's ship in single-player mode
2021-03-03 13:22:50 -05:00
Jenna Trassa 34649c7543 Translated Asteroid Name in Asteroid Fields 2021-03-03 17:25:37 +01:00
Baezon 0fcfe68164 add self-damaging weapons 2021-03-03 11:08:04 -05:00
Jenna Trassa 8b0e79703a A translateable Asteroid Impact Time 2021-03-03 14:55:29 +01:00
Baezon c3c4c21fc2 typo 2021-03-03 02:26:23 -05:00
Birk Magnussen 8471b8e444 Change Preload Error to Warning 2021-03-03 15:48:47 +09:00
Baezon 8a5483db0b retail name compatability 2021-03-03 01:08:15 -05:00
Baezon 708de001f3 Merge remote-tracking branch 'upstream/master' into decouple-asteroids-species 2021-03-02 19:55:00 -05:00
Birk Magnussen 6cdbb8159b Change SEXP Syntax, and allow Wings 2021-03-03 06:00:47 +09:00
Goober5000 03dbdf7f09 don't display unmodified annotations 2021-03-02 15:09:28 -05:00
m!m 1561a75b13 Merge pull request #3260 from BMagnu/simulatedspeedoverrides_tb
Add scripting API for accessing registered Simulated Speech Overrides
2021-03-02 17:51:49 +01:00
Goober5000 aff1ad40f3 better handling of unexpected characters that come after numbers 2021-03-02 11:31:16 -05:00
Birk Magnussen 19f48e3a10 Tidy up leftover code 2021-03-02 22:17:06 +09:00
Birk Magnussen 2c27707e00 Add replace-texture SEXP 2021-03-02 22:03:28 +09:00
BMagnuandm!m 9a854a7066 Add change to correct typecast
Co-authored-by: m!m <[email protected]>
2021-03-02 17:09:20 +09:00
Asteroth 897492e228 Merge pull request #3257 from BMagnu/beam_reset_offset
Add flag for beams to track uoffset individually
2021-03-02 03:08:02 -05:00
Asteroth 72d7b1bc9c Merge pull request #3259 from BMagnu/lssm_warpeffect
Added flag to select the warp effect type of LSSM's
2021-03-02 03:07:43 -05:00
Goober5000 14a8db0dda check for all possible strings 2021-03-01 21:01:01 -05:00
Goober5000 258ffdf56d a token consisting of a single + or - is not a valid number 2021-03-01 21:00:48 -05:00
Baezon bd5f709e14 must be 32 bits!!! 2021-03-01 12:14:13 -05:00
Baezon b6c29e3ffd increase max poofs to 32 2021-03-01 12:00:37 -05:00
Birk Magnussen b45cb19f3c Add changes to the parse exceptions to work with utf8 encoding 2021-03-02 00:05:12 +09:00
Birk Magnussen e30c919286 Add encoding conversion helpers 2021-03-02 00:03:36 +09:00
Goober5000 c4374d11a9 replace world_to_turret_matrix 2021-03-01 00:02:44 -05:00
Goober5000 3b1aac26e8 remove most of turret_matrix stuff 2021-02-28 23:41:56 -05:00
Birk Magnussen bbc0f08541 Fix Warpout being affected as well. Fix Messages. Fix Init 2021-03-01 13:36:05 +09:00
Goober5000 d4c6c3e145 turret_norm should now be calculated properly 2021-02-28 21:48:15 -05:00
Goober5000 66b7b011c9 Merge pull request #3256 from Goober5000/switch_sexp
add switch and functional-switch operators
2021-02-28 18:00:59 -05:00
Birk Magnussen 2233d2eb9b Reflect used player object 2021-03-01 01:32:13 +09:00
Birk Magnussen e9b2047769 Add function to switch campaign 2021-03-01 01:27:14 +09:00
Birk Magnussen 781aa5c638 Fix warning 2021-03-01 00:55:05 +09:00
m!m b5212d69e4 Merge pull request #3258 from BMagnu/lua_file_cleanup
Changed File not closed error to a warning and automatic file close
2021-02-28 16:44:58 +01:00
Birk Magnussen 03eb74e616 Add scripting API for accessing registeres sso's 2021-03-01 00:32:42 +09:00
Birk Magnussen f0d5c5a499 Remove redundant close 2021-03-01 00:13:49 +09:00
Birk Magnussen 335f2476f9 Added flag to select the warp effect type of lssm's 2021-03-01 00:06:15 +09:00
Birk Magnussen 530916e0fa Changed File not closed error to a warning and automatic file close 2021-02-28 23:47:07 +09:00
Birk Magnussen b4791b3b10 Add flag for beams to track uoffset individually 2021-02-28 14:40:25 +09:00
Baezon 4bdb9165ea bugfix 2021-02-27 22:33:21 -05:00
Goober5000 8d4976dd95 add switch and functional-switch operators
Like the C equivalent, these SEXPs allow one action to be taken, or one value to be returned, based on a numeric expression.
2021-02-27 21:56:51 -05:00
m!m e393503eb2 Merge pull request #3244 from the-maddin/default_volume_issue
Set default volume on audiostream creation
2021-02-27 22:41:03 +01:00
JohnAFernandez 9c4d8f175b Merge pull request #3255 from Baezon/bugfix-trail-texture
Properly initialize trail texture stretch
2021-02-27 14:31:12 -05:00
m!m 002b8a69b1 Merge pull request #3251 from asarium/fix/actionsCompile
Add a CMake platform check for proper non-copyable type support
2021-02-27 20:08:05 +01:00
Baezon 529ba78c04 review tweak 2021-02-27 13:48:26 -05:00
Baezon 772eaa634a properly initialize texture stretch 2021-02-27 13:41:24 -05:00
asarium 7c909c6d69 Fix Visual Studio compilation around non-copyable type
This works around a compiler bug present in most older versions of MSVC
where it absolutely wanted to use a deleted copy constructor even if a
move constructor was available.

This just moves the relevant code into the default constructor.
2021-02-27 19:39:29 +01:00
JohnAFernandez a22f44ce01 Merge pull request #3187 from Baezon/swarm-cleanup
Give swarm missile handling a well-deserved makeover
2021-02-27 00:01:58 -05:00
JohnAFernandez 09671d9907 Merge pull request #3245 from Baezon/disabled-repair-fix
Bugfix for disabled fighters interupting their own support calls
2021-02-26 23:59:03 -05:00
Goober5000 da4b6b2f67 Merge pull request #3252 from Goober5000/main_hall_parsing_fix
when we're finished parsing a main hall, the next main hall should be a new one
2021-02-26 21:55:13 -05:00
the-maddin c0f351d92e fix indents 2021-02-27 00:01:08 +01:00
m!m 8700645d21 Merge pull request #3067 from asarium/lua/fullTypeCoverage
Add full type coverage for return types and variables
2021-02-26 23:06:55 +01:00
Goober5000 3fe59717c5 when we're finished parsing a main hall, the next main hall should be a new one
With modular tables, we might have modified an entry in the middle of the vector, in which case the next index would be an existing main hall.  If the next modular table then created a brand new main hall, it would overwrite that entry.  This makes sure the code always assumes the next main hall will come at the end of the list.
2021-02-26 16:47:05 -05:00
asarium 0fb712ed39 Add full type coverage for return types and variables
This introduces type parsing for all types used within the scripting
documentation. This improves the generated JSON output and also
validates new code via the unit test.
2021-02-26 22:37:37 +01:00
the-maddin 003fa68582 Remove leftover default setting 2021-02-26 22:23:46 +01:00
Baezon e3acb2c56b ai fighter support 2021-02-25 23:45:42 -05:00
Baezon 789f78aefd Merge remote-tracking branch 'upstream/master' into expose-huge-small-targeting 2021-02-25 23:36:59 -05:00
Baezon ec323a3960 combine flags 2021-02-25 23:15:29 -05:00
Baezon 86d01a3b0f Merge remote-tracking branch 'upstream/master' into disabled-repair-fix 2021-02-25 21:22:18 -05:00
Baezon a685b6f051 Merge remote-tracking branch 'upstream/master' into better-subsys-attacking 2021-02-25 21:20:51 -05:00
Goober5000 2ac85f3172 Merge pull request #3246 from Goober5000/back_inserter_include
add missing #include
2021-02-25 15:14:48 -05:00
Goober5000 18a15eed29 add missing #include
On some setups the hierarchy of #include files doesn't catch an <iterator>, so #include it explicitly.
2021-02-25 13:45:07 -05:00
m!m 413633855f Merge pull request #3248 from asarium/build/fixQt
Force aqt to a known good version
2021-02-25 19:11:01 +01:00
asarium ac5eb11c39 Force aqt to a known good version
There was another break in that package so this should hopefully make
our builds more stable.
2021-02-25 18:37:44 +01:00
m!m ca5ab260c9 Merge pull request #3242 from asarium/feature/actionExpression
Add dynamic expression support for actions
2021-02-25 18:17:34 +01:00
Baezon 80ba3b9e9c Merge remote-tracking branch 'upstream/master' into better-subsys-attacking 2021-02-25 02:30:18 -05:00
Baezon 81c771b8ea clean up 2021-02-25 02:13:02 -05:00
Baezon d86723ef07 bugfix disabled fighters calling for support 2021-02-24 16:10:04 -05:00
asarium d589234648 Some final changes 2021-02-24 18:04:19 +01:00
the-maddin 26aab09b8e Update scripting documentation audio_stream:play()
to reflect what the implementation actually does.
2021-02-24 02:16:01 +01:00
JohnAFernandez 55b8f3f642 More cleanup and Int3() refactors (#2744)
* Clean Up ai_attack_object()

There was an unused parameter, and no one has added subsystem targeting (at least to this function) in the past 20 years, so it's time to remove it and make it more readable.  Probably better to adjust that behavior somewhere else anyway.

Also changes an Int3() into

* More Int3 cleaning

Refactor a few Int3()'s in the AI code

* Feedback on is_ignore_object_sub

* Feedback, remove duplicate check, add documentation to preserve why it was there
2021-02-23 19:51:43 -05:00
the-maddin 063854384f Set sane default volumes on audiostream creation.
Looking throughthe code, it appears that
sound volumes are typically set when calling
the "play" function for in-engine uses of sounds.
No default values were, in fact, set explicitly at
creation, so they were initialized to 0.0f.

Scripting documentation, however, refers to
default volumes per type where no volume is
passed. New engine functionality should also
be able to rely on this.

I'm setting sane values found in other
parts of the sound system at stream creation,
to be used or overwritten later.
2021-02-24 01:45:05 +01:00
the-maddin 9ffcd780e9 Deduplicate stream creation code 2021-02-24 01:45:05 +01:00
the-maddin 4125d0bdba Fix stream slot leak
A wrong stream type could lead to a stream handle
being marked as used although no stream is created
2021-02-24 01:44:10 +01:00
the-maddin af29243157 Merge remote-tracking branch 'refs/remotes/upstream/master' 2021-02-23 20:52:40 +01:00
Fabian Woltermann 659d68e9b9 Merge pull request #3167 from EatThePath/cylinder-only-tube-lights
Cylinder only tube lights
2021-02-23 19:11:22 +01:00
m!m 3d856b77d9 Merge pull request #3229 from the-maddin/master
Scripting&Sound: Load sound from memory
2021-02-23 17:57:32 +01:00
Baezon f447fd4e23 unused variables 2021-02-23 01:13:08 -05:00
Baezon 8088cf0ab4 ints to floats 2021-02-23 00:53:10 -05:00
Baezon a69046000e cast to int 2021-02-23 00:12:29 -05:00
Baezon 881da211a2 misc tweaks 2021-02-22 23:23:49 -05:00
Baezon 622a28236e decouple species from asteroids 2021-02-22 22:55:34 -05:00
the-maddin d0edab2c74 Better instance preparation; no MIN macro 2021-02-23 00:47:13 +01:00
Martin Merget c715cf5c65 Merge remote-tracking branch 'refs/remotes/upstream/master' 2021-02-23 00:04:54 +01:00
z64555 1fca306fc9 Address clang-tidy issues. 2021-02-22 14:24:26 -06:00
asarium 02b1e38cd7 Add variables concept to expressions
This also includes the necessary setup work for using "position" and
"direction" in action expressions.
2021-02-22 18:39:06 +01:00
asarium 851a7663a8 Rework actions to use definitions
This introduces action definitions which can then be instantiated to
form actual actions with expressions as their parameters.
2021-02-22 18:02:22 +01:00
asarium bcaa7934c6 Allow implicit conversion from int to float
This is generally a safe operation and will make the system a bit easier
to use.
2021-02-22 18:02:22 +01:00
asarium 1d6052e81e Rework function handling 2021-02-22 18:02:22 +01:00
asarium a5a91b5acf Add Action expressions
This is a parser for a very simple language to allow for execution of
dynamic expressions with return values for use as action parameters. The
only "dynamic" part this currently supports are random ranges but more
features are planned for the future.
2021-02-22 18:02:22 +01:00
asarium aa7ea238b9 Update GTest 2021-02-22 18:02:22 +01:00
m!m 8b23ccdf2e Merge pull request #3061 from asarium/feature/actionSystem
Add dynamic action programming system
2021-02-22 18:00:58 +01:00
Baezon 5004f6ab32 Merge remote-tracking branch 'upstream/master' into ai-multilock 2021-02-21 22:15:31 -05:00
Baezon adea41e690 clarity edits 2021-02-21 21:56:34 -05:00
Baezon d463db3742 Merge remote-tracking branch 'upstream/master' into generic-debris-enhancement 2021-02-21 21:31:59 -05:00
Martin Merget 4305aed38d Factor out duplicate creation/opening code 2021-02-22 01:39:55 +01:00
z64555 f51502e4ae Make qtFred and Fred changes consistent with each other. 2021-02-21 18:16:55 -06:00
the-maddin 34eed12988 Extract shared code from AudioStream::Create and CreateMem
Also cleaned up control flow on failure as far as used macros allow.
2021-02-22 00:58:41 +01:00
the-maddin 60775e73b5 Fix MSVC builds v2 2021-02-21 22:30:17 +01:00
the-maddin e55b246756 Fix MSVC builds 2021-02-21 22:14:39 +01:00
the-maddin 438f48c835 Fix compiler/type issue 2021-02-21 21:51:34 +01:00
asarium 8f88801646 Add dynamic action programming system
This system adds the code necessary for letting content creators specify
complex events by specifying a simple, program-like sequence of
operations that will be executed by the engine when a certain event is
triggered.
2021-02-21 21:29:02 +01:00
the-maddin afc9e8132a renamed memfile functions & struct 2021-02-21 20:53:52 +01:00
the-maddin 371aaa1107 Moved, documented and renamed struct MemFileCursor 2021-02-21 20:46:55 +01:00
the-maddin ab7e6cbbe1 add LuaErrors for invalid input, fix off-by-one 2021-02-21 20:37:02 +01:00
m!m 340edd5ec0 Merge pull request #3239 from asarium/build/updateBuildImage
Update Linux build image and make consistent
2021-02-21 14:36:27 +01:00
asarium 88e6469fb4 Update Linux build image and make consistent
For some reason the last update of the PR pipeline did not update the
other workflows as well so we had some minor inconsistencies.

This should hopefully fix the nightly build issues for real this time.
2021-02-21 14:02:32 +01:00
BMagnu fd60dd342f Add vanish Lua function to some objects (#3238)
* Add vanish function to some objects

* Modify vanish function return. Modify fireball access

* Remove dedunant check for ship vanish
2021-02-21 11:59:49 +00:00
Asteroth dcd38539c2 Merge pull request #3237 from Baezon/fix-turret-inaccuracy
Bugfix to turret_inaccuracy
2021-02-21 00:19:39 -05:00
Baezon 70265c6f63 bugfix turret inaccuracy 2021-02-20 23:41:30 -05:00
JohnAFernandez 19b3f85eea Merge pull request #3220 from Baezon/turret-inaccuracy
Add turret-set-inaccuracy SEXP
2021-02-20 21:07:47 -05:00
JohnAFernandez 873ec133b4 Merge pull request #3204 from Baezon/icons-parsing-overhaul
Overhaul briefing icon parsing
2021-02-20 20:57:47 -05:00
Goober5000 88f6312fd8 Merge pull request #3234 from Goober5000/sim_hull_scripting
add SimHitpointsLeft
2021-02-20 16:51:11 -05:00
Goober5000 69c1398368 add SimHitpointsLeft 2021-02-20 16:13:14 -05:00
m!m 043b1d34c8 Merge pull request #3232 from asarium/fix/linuxNightly
Try to fix linux nightly builds
2021-02-20 21:34:51 +01:00
m!m 1226c894ed Merge pull request #3233 from asarium/build/githubRegistry
Store standalone image on GitHub
2021-02-20 21:34:40 +01:00
asarium 0fb7143f12 Store standalone image on GitHub
Docker Hub is going into a weird direction with their rate limits and
the GitHub Container Registry is better in that regard.

Old images will stay in Docker Hub for the time being though.
2021-02-20 18:34:25 +01:00
Fabian Woltermann 57c593f74e Merge pull request #3231 from Goober5000/fix_eval_numeric_sexp
fix mn.evaluateNumericSEXP
2021-02-20 14:54:53 +01:00
asarium cf3faa35cd Try to fix linux nightly builds
The default Python version appears to be too recent so let's try to have
this action use the python package that is installed in our build image.
2021-02-20 14:44:01 +01:00
Goober5000 2429b99184 fix mn.evaluateNumericSEXP
This is a follow-up to #2687.  The final implementation of evaluateNumericSEXP different from the testing implementation in a small but crucial way that prevented it from actually working.  Since it used `eval_num`, it didn't evaluate the specified SEXP node but rather the CAR (first element) of the node list.  This fixes that by putting the node in a temporary SEXP list so that `eval_num` will look in the right place.
2021-02-20 01:40:55 -05:00
Shivansps 2ee128cd88 Merge branch 'arm_fixes' 2021-02-19 21:02:53 -03:00
Shivansps a2b349855e Merge pull request #1 from scp-fs2open/master
Update fork
2021-02-19 20:53:22 -03:00
Baezon 0f451e6190 more comments 2021-02-19 10:45:32 -05:00
the-maddin 366a96bdd4 Expose audiostreams from memory to LUA
Allow scripts to play any LUA binary string containing valid WAV data.
Useful for TTS data passed in via LUASockets.
2021-02-19 01:22:24 +01:00
the-maddin 8770ad74f8 Add support for audiostream from in-memory sound 2021-02-19 01:19:05 +01:00
the-maddin 68d5467e2a Add support for in memory sound 2021-02-19 01:16:39 +01:00
the-maddin ac2fa61f6e Add support for in-memory sound
Using the memory backend, treat sound in memory like sound in cfiles.
2021-02-19 01:14:40 +01:00
the-maddin 2060a1dabe Add backend for in-memory audio
FFmpegContext can now also hold a cursor over a vector of bytes instead of a cfile to store sound data in it.
Appropriate creation, reading seeking functions included.
2021-02-19 01:10:43 +01:00
Fabian Woltermann b561156f09 Merge pull request #3226 from Goober5000/tocase
additional case functions and checks
2021-02-18 14:08:34 +01:00
Goober5000 f3bca4384f additional case functions and checks
This refactors the tolower/toupper code to use standards-conformant logic, and also adds some helpful utility functions.  Regular tolower/toupper must be supplied a value that can be represented as `unsigned char`, but the std:: functions are a bit more robust.
2021-02-18 04:23:08 -05:00
Fabian Woltermann c00ff88a5a Merge pull request #3223 from Goober5000/case_insensitive_ships
make the ship lookup case insensitive
2021-02-18 09:10:57 +01:00
Goober5000 866b64ff8d maybe this works better 2021-02-17 12:12:17 -05:00
Goober5000 3acd74e894 make the ship lookup case insensitive
Does what it says on the tin.  Fixes #3221.
2021-02-17 11:31:45 -05:00
Goober5000 ffa6e594e7 Merge pull request #3222 from Goober5000/perform_actions_documentation
add clarification
2021-02-16 19:27:30 -05:00
Goober5000 39f9df11f7 add clarification 2021-02-16 18:35:11 -05:00
Baezon a606f2cafa modernize-use-auto 2021-02-16 10:52:56 -05:00
Baezon 592855c35d NULL -> nullptr 2021-02-16 02:27:11 -05:00
Baezon 3797171fb0 Merge remote-tracking branch 'upstream/master' into turret-inaccuracy 2021-02-16 02:23:45 -05:00
Baezon 028e40a43b add turret-set-inaccuracy sexp 2021-02-16 02:21:54 -05:00
Goober5000 36ea4c225b Merge pull request #3219 from Goober5000/model_instance_in_fred
add model instance support to FRED rendering
2021-02-15 09:33:55 -05:00
Goober5000 24df0efb6e add model instance support to FRED rendering
This changes FRED to properly initialize a model instance on ship creation and also use that instance when rendering the ship.  This prevents destroyed submodels from appearing.  Fixes #3212.
2021-02-15 08:34:12 -05:00
Fabian Woltermann db2eba92d5 Merge pull request #3213 from Goober5000/message_list_chain
add send-message-chain
2021-02-15 10:50:05 +01:00
Fabian Woltermann dd0bb3ceca Merge pull request #3214 from Goober5000/fix_event_editor_closing
fix event editor closing on key press
2021-02-15 10:15:18 +01:00
Fabian Woltermann f9da8c7db2 Merge pull request #3217 from Goober5000/fix_event_editor_delete
fix event node deletion behavior
2021-02-15 10:14:51 +01:00
Goober5000 82a93e9e47 fix event node deletion behavior
The defensive coding in #3191 against illegal cut/delete operations was a little too defensive.  This properly allows deletion of events when necessary, and it still guards against a Cut operation being improperly called due to a shortcut key press.

Fixes #3216.
2021-02-14 17:12:22 -05:00
z64555 754f0ea876 Removed unused local variable. 2021-02-14 10:33:49 -06:00
z64555 9145922cb1 Fix default_item to use default_bindings. 2021-02-14 09:59:36 -06:00
z64555 47e19207f9 Merge branch 'master' into controls3
Resolved Conflicts:
	code/controlconfig/controlsconfig.cpp
	code/controlconfig/controlsconfig.h
2021-02-14 09:48:55 -06:00
Goober5000 41f9dbb003 fix event editor closing on key press
Restores the MFC functions that were taken out in #3073.  These functions appeared redundant, but they were actually necessary to intercept the MFC handling of Enter and Escape.  In addition to fixing that behavior, this PR also adds some clarifying comments and renames some functions to prevent confusion.
2021-02-14 01:32:03 -05:00
Goober5000 c067651498 add send-message-chain
Implements send-message-chain as described in issue #3185.  Also fixes the default argument for message priority, and mirrors the default argument fix to qtFRED.
2021-02-14 00:23:57 -05:00
JohnAFernandez 2361152399 Merge pull request #3211 from JohnAFernandez/Messaging_Switch_to_Normal
Make "Normal" the default option for Messages
2021-02-13 23:28:47 -05:00
JohnAFernandez 0318e3f851 Make "Normal" the default option for Messages
Moves messages to the more suitable default of normal.  Also adds clarifying documentation.
2021-02-13 15:56:09 -05:00
Goober5000 d21f7b8915 Merge pull request #3208 from PhantomHoover/fix/misc-rand-sizes
Properly fix static_rand()'s range and account for this in functions that were misusing it
2021-02-13 14:32:18 -05:00
Phantom Hoover 640640b4b4 Properly fix static_rand()'s range and account for this in functions that were misusing it 2021-02-13 16:06:47 +00:00
Fabian Woltermann f87cf7df23 Merge pull request #3207 from Baezon/waypoint-complete-fit-bugfix
Fix for waypoint completion with FIT
2021-02-13 17:01:25 +01:00
Asteroth 616e6a9bc0 Merge pull request #3180 from Baezon/split-collision-light-width
Split beam width for collision purposes and lighting purposes
2021-02-13 02:15:17 -05:00
Goober5000 303787d97c Merge pull request #3206 from Goober5000/retail_hybrid_translation_behavior
restore retail behavior of XSTR
2021-02-12 17:48:23 -05:00
MageKing17 db2bd97669 Merge pull request #3205 from MageKing17/bugfix/controlconfigoverride-default-preset
If the first #ControlConfigOverride isn't named, use "default".
2021-02-12 14:29:01 -08:00
Baezon 5e38f3c0d4 fit waypoint bugfix 2021-02-12 11:12:24 -05:00
Goober5000 1e756d1a67 restore retail behavior of XSTR
This is a follow-up to #2390.  Retail used a weird hybrid approach where internal strings were translated but external strings were not.  This restores that behavior by adding another language, LCL_RETAIL_HYBRID, which is the default.  The game can still be run in completely untranslated or completely translated mode.

All three modes have been tested and translate strings, or not, as expected.  The `$Use tabled strings for the default language:` flag has also been tested and still works.
2021-02-11 19:47:12 -05:00
Baezon ec274d58db clang-tidy 2021-02-10 21:49:49 -05:00
Baezon 682c15d6b1 clang-tidy curse you 2021-02-10 21:41:37 -05:00
MageKing17 29fbebde16 If the first #ControlConfigOverride isn't named, use "default".
This causes it to replace the first preset in the list, which more closely approximates the old behavior.
2021-02-10 17:58:08 -08:00
JohnAFernandez bd7450c4e6 Merge pull request #3198 from Baezon/landmark-logging
Add 'landmark' logging
2021-02-10 20:40:40 -05:00
Baezon b843a5be3a clean up 2021-02-10 18:15:48 -05:00
Baezon be44e287ee overhaul brief icon parsing 2021-02-10 17:36:37 -05:00
Goober5000 af16a6bc42 Merge pull request #3191 from Goober5000/fred_indents_and_colors
Additional FRED features
2021-02-10 09:45:07 -05:00
Fabian Woltermann 6b7429ab5d Merge pull request #3192 from Baezon/turret-forced-target
Add turret-set-forced-target SEXP
2021-02-10 11:39:16 +01:00
Fabian Woltermann d4be72c49b Merge pull request #3203 from JohnAFernandez/Really-fix-hud-config-crash
Actually fix radar range turning to -1 after hud gauge config
2021-02-10 11:36:30 +01:00
JohnAFernandez 86308bbf34 Actually fix radar range turning to -1 after hud gauge config
Problem was caused by negative array index write in this section of code.
2021-02-10 00:05:27 -05:00
Goober5000 3d6124c7ad Merge pull request #3144 from Goober5000/fix_look_at
reimplement look_at
2021-02-09 21:48:52 -05:00
Baezon 6357c2a856 clang tidy 2021-02-07 23:40:14 -05:00
Allen A. Babb 0f1c5e0a42 Merge pull request #3201 from z64555/unhide_chocolate
Unhide chocolate - Revert default-disable of controls unusable with retail data.
2021-02-07 17:02:32 -06:00
z64555 cb692b22ba Add note comment to the CCI_builder. Controls that don't work in retail should be default disabled, but this relies on the coders making them to get it set the first time. 2021-02-07 14:23:44 -06:00
z64555 0b03c3f672 Add me to the cool kids club. Wait, no, not that one. 2021-02-07 14:15:35 -06:00
z64555 15d3dde5fa Unhide non-retail controls by default. Even though retail data can't make use of most of them, some mods may count on their visibility. 2021-02-07 14:06:27 -06:00
Baezon 24947b64d7 split light and collision width 2021-02-06 22:00:45 -05:00
Goober5000 61bb44d07b Merge pull request #3200 from wookieejedi/test-rotation-fix
Fix Bug with Reading Turret Rotation Axes
2021-02-06 14:44:07 -05:00
Goober5000 aa3eb4d42c Merge pull request #3199 from Goober5000/vecmat_projecc
add useful function
2021-02-06 14:19:36 -05:00
wookieejedi 2cdc74264c Fix Bug with Reading Turret Rotation Axes
Multipart turrets that have no rotation specified in PCS don't have `movement_type` assigned properly within `modelread.cpp`, which eventually leads to a fatal crash. This two line fixe properly specifies the `movement_type` and fixes the crash. Tested and confirmed. Thanks to @Goober5000 for the assistance and troubleshooting.
2021-02-06 13:59:23 -05:00
Goober5000 dd92a0f0bb add vm_vec_is_normalized and use it in several places 2021-02-06 13:46:54 -05:00
Goober5000 e699d991d1 add useful function 2021-02-06 00:19:57 -05:00
wookieejedi ede8c6a172 Merge pull request #3197 from wookieejedi/expose-countermeasure-firewait
Expose Additional Countermeasure Options to Modders
2021-02-06 00:08:36 -05:00
JohnAFernandez ba9205cd35 Make sure to pull from the bounds of the vector. 2021-02-05 23:40:24 -05:00
Baezon b7bd8d6fbb moar tweaks 2021-02-05 15:28:14 -05:00
Baezon 7015818ea5 review tweaks 2021-02-05 12:56:03 -05:00
wookieejedi c831235cd3 add warnings 2021-02-05 11:39:50 -05:00
Baezon a6b19706eb 'landmark' logging 2021-02-05 03:49:39 -05:00
wookieejedi de17df207a specify values for AI and use multipliers for fail and succeed 2021-02-04 22:38:06 -05:00
wookieejedi 5116471d8d simplify logic 2021-02-04 13:40:23 -05:00
wookieejedi e01bd28e79 Expose Additional Countermeasure Options to Modders
This PR provides modders with additional options to alter countermeasure values. Previously, countermeasures had a fixed, hardcoded fire wait time of 333 milliseconds, regardless of what `$Fire Wait:` was specified. Also, the AI would wait a fixed delay between firing of failed countermeasure launches and successful countermeasure launches. For successful launches the AI would double the fire wait. Also, Friendly ships would only use a fixed constant value for countermeasure firing chance, and not the value based on their class specific AI profile.

This PR exposes those values with the following options:
•	Add main AI table flag: `Friendly Ships Use AI Profile Countermeasures Firing Chance`. This will allow Friendly ships to use their AI class profile countermeasure firing chance instead of the default fixed value from main AI table.
•	Add value in CM section of weapons table: Countermeasures use fire wait time `+Use Fire Wait`. This is a boolean that allows the countermeasure’s fire wait to actually use what is specified in the `$Fire Wait:` section of that weapon.
•	Add value in CM section weapons table: `+Fail Delay Equals Fire Wait`. The delay between failed countermeasure launches is set to use the fire wait value.
•	Add value in CM section weapons table: `+Successful Launch Delay Multiplier`. This allows modders to set the value that is multiplied to fire wait for successful countermeasure launches.
2021-02-04 13:12:02 -05:00
Asteroth 994e42a27d Merge pull request #3175 from Baezon/trail-decay-exponent
Non-linear Trail decay and Texture stretch
2021-02-04 12:09:22 -05:00
wookieejedi 74c9139c1c Merge pull request #3195 from wookieejedi/fix-retail-cap-ship-speed
Fix speed differential for retail cap ships
2021-02-03 19:26:19 -05:00
wookieejedi 1d7070cf12 add claryifying parentheses 2021-02-03 18:18:04 -05:00
JohnAFernandez 19c19f1262 Remove the Unused Just_updated Flag
This flag is vestigal.  It was likely used in the FS1 codebase and then was not removed by the rushed FS2 multi team.  Whether or not an object uses physics_sim is determined earlier in `multi_oo_is_interp_object()`
2021-02-03 18:00:16 -05:00
wookieejedi 754f4a309c add deprecation check 2021-02-03 17:46:06 -05:00
Asteroth 83bd14586f Merge pull request #3179 from Baezon/empty-sound-fix
Don't search for empty sound names
2021-02-03 17:41:08 -05:00
wookieejedi 828a993eb6 Fix speed differential for retail cap ships
As astuetly determined by @Baezon `the speed differential for retail cap ships is caused by 'ships with no shields can manage ETS', being part of the bugfixes with target version`. This PR adds two flags per Discord suggestion, one for all ships and one for only small ships.
2021-02-03 17:14:39 -05:00
Asteroth 69b319a7d6 Merge pull request #2591 from Baezon/homing-primaries
Add homing primaries
2021-02-02 21:48:16 -05:00
Baezon 6414172898 Merge remote-tracking branch 'upstream/master' into homing-primaries 2021-02-02 21:15:05 -05:00
Baezon e78fccc294 unused variables 2021-02-02 20:49:33 -05:00
Baezon 54f1a86a5a clena up and finalization of subsys targeting 2021-02-02 20:06:40 -05:00
Baezon 8c387d7507 tentative subsys targeting 2021-02-02 11:31:05 -05:00
Baezon 476e916945 Merge remote-tracking branch 'upstream/master' into turret-forced-target 2021-02-01 22:12:50 -05:00
Asteroth 1d131403d5 Merge pull request #3176 from Baezon/damage-incidence-angle
Angle-based damage multiplier
2021-02-01 20:54:20 -05:00
Baezon e7cab7cb42 clang-tidy dont hurt me no mo 2021-02-01 14:24:44 -05:00
Baezon 7b2c8304ff typo 2021-02-01 13:25:44 -05:00
Goober5000 b6314feb29 add comment 2021-02-01 02:24:54 -05:00
Goober5000 a124722966 qtfred 2021-02-01 00:46:17 -05:00
Goober5000 47543ac7d4 implement keyboard shortcuts, implement mission saving for bg color 2021-02-01 00:46:17 -05:00
Goober5000 262b45188a resizable Event Editor 2021-02-01 00:46:17 -05:00
Goober5000 c0446cd03c color stuff works pretty well 2021-02-01 00:46:17 -05:00
Goober5000 bc37fda89e reorganize event annotations a bit
preliminary work for background color and changing indent
implement a sort-of-working background color change
2021-02-01 00:46:17 -05:00
Goober5000 275a439091 Merge pull request #3190 from Goober5000/quick_annotation_fix
mission save bugfix
2021-02-01 00:43:55 -05:00
Goober5000 f475ea4d25 mission save bugfix
Somehow during the annotation merge the save got borked.  This fixes mission saving.
2021-02-01 00:09:55 -05:00
Goober5000 72d2f9f1a1 reimplement look_at
This PR re-implements the look_at feature, which has been broken for many years.  A look-at rotation is a type of intrinsic_rotation, just like dumb-rotations. The same code handles both; the only difference is whether submodel_rotate or submodel_look_at is called to apply the actual movement.

The previous look_at parsing code has been optimized by performing the name-to-submodel resolution when the model is actually parsed, instead of when the first look-at rotation is called.  This avoids the need to store a name in the submodel struct and also avoids cluttering the look_at function.

This PR is essentially bookkeeping, except for the submodel_look_at method.  This method takes advantage of the submodel rotation upgrade and calculates angles in almost the same way as the new turret rotation code.  It's much more elegant, much less hackish, and works for all detail levels.
2021-01-31 14:31:40 -05:00
Goober5000 8ab3cf240b Merge pull request #3189 from Goober5000/fix_compilation_error
fix compilation
2021-01-31 14:30:16 -05:00
Goober5000 450900d2b5 fix compilation
Quick hotfix due to two branches being merged that were both compatible with master but not with each other.
2021-01-31 13:57:12 -05:00
Goober5000 68b6795d5a Merge pull request #3079 from Goober5000/stuff_number
convert float parsing to use strtof
2021-01-31 13:18:27 -05:00
Goober5000 868715867d Merge pull request #3056 from Goober5000/model_rotation_arbitrary_axis
Allow submodels to rotate around an arbitrary axis
2021-01-31 13:17:27 -05:00
Baezon 9b7cc77b55 revert multi compatability :( 2021-01-31 13:11:27 -05:00
Baezon d3355b0783 experimental multi compatability 2021-01-31 12:29:25 -05:00
m!m 6f382ce0d9 Merge pull request #3188 from asarium/fix/stdApiCrash
Fix crash in server REST API when omitting framecap
2021-01-31 17:02:19 +01:00
asarium dbfb5b9d34 Fix crash in server REST API when omitting framecap
Since the return value of the json library is null in that case it
crashed the server when no value was specified.
2021-01-31 16:08:33 +01:00
Allen A. Babb b3edc683fc Merge pull request #2907 from z64555/feature/controls2
Controls 0, 1, and 2 - Vectorize Controls_config, Refactor Control Presets, and Use Undosys for undo/redo actions done in the Controls configuration menu.
2021-01-30 10:04:58 -06:00
Asteroth f1ff6ebf55 Merge pull request #2870 from Baezon/armor-types-beam-weapon
Use armor/damage types for beam-weapon damage
2021-01-30 02:33:13 -05:00
Baezon f95e51b047 dont search for empty names 2021-01-30 00:05:17 -05:00
wookieejedi 213d20f7ef Merge pull request #3177 from Baezon/better-missile-avoid
Improved missile avoidance
2021-01-29 22:47:01 -05:00
Goober5000 a10e456de3 Merge pull request #3073 from Goober5000/fred_comments
Fred comments
2021-01-29 17:38:31 -05:00
Goober5000 5187ea5041 Revert "Disable Qt projects since downloads are broken"
This reverts commit 4aa33c7b3e from PR #3160.
2021-01-29 16:57:05 -05:00
z64555 9b42819dce Address Clang-Tidy issues. 2021-01-29 15:09:54 -06:00
z64555 c1d1a965c6 Think I got all of the index swapped. *Scrolls right* oh. 2021-01-29 04:40:58 -06:00
Baezon 89a8cbcbae review tweaks 2021-01-29 01:04:15 -05:00
z64555 b97e804123 Address signed/unsigned complaint. 2021-01-28 23:54:44 -06:00
Asteroth e0dd807bb4 Merge pull request #3145 from EatThePath/fix-type-d-beam-pulse-lighting
Stop including tube lights between anti-fighter beam pulses
2021-01-29 00:39:27 -05:00
Baezon 888c14aeaf flip max and min 2021-01-28 23:45:06 -05:00
Baezon ab64183981 Merge remote-tracking branch 'upstream/master' into generic-debris-enhancement 2021-01-28 22:57:42 -05:00
Baezon 055784dcff clean up swarm missiles 2021-01-28 22:33:01 -05:00
Baezon 4b3a1a8d84 unused variables 2021-01-28 20:39:29 -05:00
Baezon 91be428596 missing '$' 2021-01-28 20:27:36 -05:00
Baezon 0c0a9e4f26 typo 2021-01-28 20:24:54 -05:00
z64555 d6672d01cf Don't count an item in the reset check if the item is disabled.
* Remove errant Assert()
* Drop leading whitespace of preset $Name field
* Fix do_reset behavior due to incorrect default binding check.  Fix is possibly a backport.
* When $Text is specified for a bind item, set the XStr index to 0.
2021-01-28 19:16:07 -06:00
Goober5000 43ae0e3575 qtfred 2021-01-28 19:59:20 -05:00
Goober5000 2be4818d24 tweak parsing since we want to read annotations generally 2021-01-28 19:17:08 -05:00
Goober5000 9bebdbb575 rename event_comment to event_annotation 2021-01-28 18:56:15 -05:00
Goober5000 04cf220f2d comment capability for FRED
Add FRED comments. These aren't the typical source-code style comments that are embedded in mission or table files; these are regular strings that are stored as mission data. You might think of them as annotations. Right-click on any node in the event tree and click on Edit Comment to add one. The tree node will then show a different icon and the comment will be visible as a tooltip when you mouse over the node text.
2021-01-28 18:25:19 -05:00
Goober5000 61c7cc2856 Merge pull request #3141 from JohnAFernandez/Hud-config-screen-is-weird
Make sure the Hud_config_inited is reset when the screen is exited.
2021-01-28 14:29:29 -05:00
Baezon 824c537bd2 Merge remote-tracking branch 'upstream/master' into ai-multilock 2021-01-28 12:25:35 -05:00
Asteroth d1f0f5002f Merge pull request #2775 from Baezon/burst-seed
Implement Beam Flags and some adjustments to burst handling
2021-01-28 11:51:30 -05:00
Baezon d3d016602d rename beam_flags -> beam_info_flags 2021-01-28 02:46:40 -05:00
Baezon f9e44f545e ignore empty sounds 2021-01-27 23:31:38 -05:00
Baezon 52f57df064 NULL -> nullptr 2021-01-27 23:25:19 -05:00
Baezon 61931520d1 Merge remote-tracking branch 'upstream/master' into generic-debris-enhancement 2021-01-27 23:06:45 -05:00
Baezon 229dd4c63c clean up 2021-01-27 20:59:01 -05:00
Baezon 7515422979 clarity parentheses 2021-01-27 20:14:33 -05:00
Baezon eb1d748907 safety 2021-01-27 17:25:28 -05:00
Baezon b87d3c65a0 Merge remote-tracking branch 'upstream/master' into better-missile-avoid 2021-01-27 15:11:08 -05:00
Baezon 8d841fa1cc clean up 2021-01-27 14:48:41 -05:00
Goober5000 537c4b6f24 Merge pull request #3172 from BMagnu/hotfix_keynames
Fix special char hex codes for utf8
2021-01-27 13:52:15 -05:00
Goober5000 f9a6c86cad Merge pull request #3162 from Goober5000/ship_create_display_name
default display names for script/sexp-created ships
2021-01-27 13:40:16 -05:00
Goober5000 db466048ea Merge pull request #3168 from Goober5000/debris_colgroup_check
add collision group checks to debris
2021-01-27 13:40:05 -05:00
m!m 6952b58846 Merge pull request #3174 from asarium/build/bumpVersion
Bump version
2021-01-27 19:12:45 +01:00
Baezon 284ad2941c Merge remote-tracking branch 'upstream/master' into better-missile-avoid 2021-01-27 12:53:24 -05:00
Baezon 76d295d2bc Merge remote-tracking branch 'upstream/master' into damage-incidence-angle 2021-01-27 12:47:55 -05:00
Baezon 66c38c43c6 clean up 2021-01-27 12:43:32 -05:00
asarium 2161ab563d Bump version 2021-01-27 17:55:37 +01:00
Baezon 6cd5510bd1 clean up 2021-01-27 10:49:32 -05:00
Birk Magnussen 25cca192db Switch Key Names from internal encoding to unicode in utf8 mode 2021-01-26 19:09:20 +09:00
Birk Magnussen e6067f756a Fix special char hex codes for utf8 2021-01-26 13:53:25 +09:00
Goober5000 6d9e9a2e3d add collision group checks to debris
Collision groups were checked between ships, weapons, and beams, but not debris.  This adds debris.
2021-01-24 01:07:11 -05:00
David Gibson 373d119f1a modernize nullptr for tidy 2021-01-23 21:58:47 -06:00
David Gibson 7cb2338325 removed now-superflous element counting loop from gr_opengl_deferred_lighting_finish 2021-01-23 21:10:54 -06:00
David Gibson ddcc94b0da Extended the tube light cylinder volumes for deferred lighting to encompass the end cap spheres, removed those spheres, and adjusted the shader handling of tube lights to account for this. 2021-01-23 20:56:07 -06:00
David Gibson 9a866434b8 Removed the end light volumes from the defered lighting setup for tubelights. 2021-01-23 17:31:12 -06:00
Goober5000 37e92e01b6 default display names for script/sexp-created ships
Now that the hash truncation logic is part of the ship display name, ships created by script or sexp should check this as well.
2021-01-22 23:06:48 -05:00
m!m 60047116fd Merge pull request #3160 from asarium/ci/disableQt
Disable Qt projects since downloads are broken
2021-01-22 16:54:54 +01:00
asarium 4aa33c7b3e Disable Qt projects since downloads are broken 2021-01-22 16:26:44 +01:00
m!m 373dbeb63f Merge pull request #3159 from Baezon/previous-bank-fix
Fix a bug with previous_bank and ship class swapping
2021-01-21 22:06:30 +01:00
m!m 361cc76c30 Merge pull request #3154 from Goober5000/redo_3149
fix a rare crash in the tech room
2021-01-21 21:47:53 +01:00
Baezon 3f27183d6d also reset previous bank 2021-01-21 01:04:13 -05:00
Baezon 4e5c74d729 add trail alpha decay exponent 2021-01-19 22:44:08 -05:00
Goober5000 3d976f53b4 fix a rare crash in the tech room
The tech room loop checked that all the lines could fit in the available screen real estate, but it didn't check to see that the number of lines was less than the array capacity.  Normally this never happened, but certain mods which use smaller fonts will overflow the limit.  This adds a proper bounds check and also increases the bounds so that more of the smaller lines can be used.
2021-01-17 11:21:41 -05:00
m!m e5af389803 Merge pull request #3150 from scp-fs2open/revert-3149-fix_techroom_crash
Revert "fix a rare crash in the tech room"
2021-01-17 17:14:30 +01:00
m!m ba0f2e7f83 Revert "fix a rare crash in the tech room" 2021-01-17 11:55:59 +01:00
Goober5000 f0987a4ad1 Merge pull request #3149 from Goober5000/fix_techroom_crash
fix a rare crash in the tech room
2021-01-17 02:15:55 -05:00
Goober5000 61dd4356f0 fix a rare crash in the tech room
The tech room loop checked that all the lines could fit in the available screen real estate, but it didn't check to see that the number of lines was less than the array capacity.  Normally this never happened, but certain mods which use smaller fonts will overflow the limit.  This adds a proper bounds check and also increases the bounds so that more of the smaller lines can be used.
2021-01-17 01:21:52 -05:00
David Gibson d9895455cd Addressed feedback and tidy issues with AA beam pulse lighting fixes 2021-01-15 13:09:43 -06:00
Baezon d9a7e786b5 Merge remote-tracking branch 'upstream/master' into burst-seed 2021-01-15 12:52:25 -05:00
David Gibson 40e7ce012e =Added some important brackets I negligently omitted 2021-01-15 00:28:59 -06:00
David Gibson 31227c659a added a check in beam_move_all_post() to avoid adding tube lights when anti-fighter beams are between pulses. Also added documenting comments to beam_type_b_get_status 2021-01-14 23:59:13 -06:00
JohnAFernandez 679f7f20d7 Switch the correct ammo variable.
The previous pr was supposed to fix what ammo capacity the ship was reloaded to, but instead it was changing how much ammo was in the magazine.
2021-01-14 09:23:52 -05:00
JohnAFernandez ff9b2fd6cd Make sure the Hud_config_inited is reset when the screen is exited. 2021-01-13 21:20:27 -05:00
Fabian Woltermann a939a73c14 Merge pull request #3139 from z64555/hud_config
More safety checks for the HUD_config struct
2021-01-13 22:20:43 +01:00
Goober5000 487aad8bbe a bit of cleanup and reorganization/simplification 2021-01-13 14:01:34 -05:00
Goober5000 554bbb884d remove angles from the submodel instance 2021-01-13 14:01:34 -05:00
Goober5000 020f14fb51 allow rotating submodels to rotate around an arbitrary axis 2021-01-13 14:01:28 -05:00
Goober5000 98e501acc7 remove the orientation field for submodels
Change the `orientation` field to `frame_of_reference` and remove it from submodel instance calculations.
2021-01-13 13:52:48 -05:00
Goober5000 3f5ad1239d Merge pull request #3140 from Goober5000/techroom_update
make model_set_up_techroom_instance a bit more robust
2021-01-13 13:51:47 -05:00
z64555 4e715352c9 Increment int strikes per CSG warning, as done in PLR code
* Use ReleaseWarnring instead of Warning
* Use correct upper boundry check logic on HUD_config::mail_color
2021-01-13 12:48:24 -06:00
Goober5000 5f538fa514 make model_set_up_techroom_instance a bit more robust
Follow-up to #3137.  I missed two important details in the first implementation: the triggered animation needs to be an "initial position" type, and `model_update_instance` needs to be run on all subobjects, not just turrets.

This should really fix #3123.  I still can't see the destroyed collector, but the Rahu no longer flickers when it spins around.
2021-01-13 13:17:31 -05:00
Goober5000 506ba911d0 Merge pull request #3137 from Goober5000/tech_room_instance
properly use model instances for tech room and briefing popups
2021-01-13 11:03:25 -05:00
z64555 f44cfa06c8 Adds more safety checks for PLR and CSG reading for the HUD_config structure.
* Adds a #define for the HC_color array size
* Doxy's the HUD_config struct
2021-01-12 23:12:20 -06:00
Goober5000 eeb6f5b091 properly use model instances for tech room and briefing popups
These two situations mostly relied on the obsolete model code which didn't include model instances.  Data for submodel positions and for whether a submodel is blown off are now stored exclusively in model instances, not the model itself.

This fixes #3124.
2021-01-12 23:15:37 -05:00
Fabian Woltermann 18cb07dc72 Merge pull request #3134 from EatThePath/tube-light-attenuation-recalc
Properly recalculate tube light attenuation
2021-01-11 20:28:19 +01:00
JohnAFernandez 3a4df0d89a Send them to Github instead of Mantis 2021-01-11 14:06:03 -05:00
JohnAFernandez d33f9db1f0 Move Standalone Running statement to after the init of Debug Statements 2021-01-11 14:05:01 -05:00
JohnAFernandez 75b26ef94b Fix unfocused multi interpolation inaccuracies (#3129)
Remove One Source of Client and Server having Different Timing

Multiplayer *must not* have its time affected by being in the background, because their clocks will come out of sync and movement from an out of focus multiplayer instance will appear to move in slow motion.  This fixes the timing discrepancy by only doing os_sleep when unfocused when in single player.
2021-01-11 14:04:23 -05:00
JohnAFernandez 398f4d9053 Remove Unused ClientDamage Commandline Option (#3132)
This option was likely part of an attempt to fix multi that never went anywhere. We now have rollback, which is on by default.
2021-01-11 14:02:52 -05:00
David Gibson 069947e397 Added a recalculation of tube light attenuation in fragment shader after proper distance is determined. fixes slash beam and penetration lighting issues 2021-01-11 11:55:45 -06:00
Fabian Woltermann 580d67eca7 Merge pull request #3133 from Baezon/slow-weapon-collide-fix
Fix collision culling for slow/accelerating weapons
2021-01-11 09:15:53 +01:00
Baezon 4bf93e08df fix collision culling for slow weapons 2021-01-11 00:45:42 -05:00
Fabian Woltermann 6c475130d1 Merge pull request #3126 from The-E/fix/ambient_light_in_lab
When setting the ambient factor in the lab, recalculate ambient lighting
2021-01-10 20:11:13 +01:00
Fabian Woltermann 6f0dec4169 When setting the ambient factor in the lab, recalculate ambient lighting 2021-01-10 19:44:24 +01:00
Goober5000 3b99402a0a Merge pull request #3121 from Goober5000/background_warning
make FRED validate all backgrounds, not just the active one
2021-01-10 09:21:45 -05:00
Fabian Woltermann 6c6ade116f Merge pull request #3120 from Goober5000/fix_3101
fix 3101
2021-01-10 12:43:47 +01:00
Fabian Woltermann 4f6c834587 Merge pull request #3122 from Baezon/glowpoint-timing-fix
Fix a bug with glowpoint timing
2021-01-10 12:42:42 +01:00
Baezon c486843e5f fix a bug with glowpoint timing 2021-01-10 03:11:25 -05:00
Goober5000 ec93ac4c0e fix 3101
Always update the ship class and loadout information in a ship's parse object, even if it is present at mission start.
2021-01-10 01:53:14 -05:00
Goober5000 2307d05086 make FRED validate all backgrounds, not just the active one
The background validation checks are somewhat convoluted, and misleading with multiple backgrounds, so this PR disables the standard main-background check if FRED is running.  It then adds a new FRED-specific background check that covers all backgrounds.

Additionally, this PR fixes a bug in an error message in `stars_get_first_valid_background`.

Fixes #3116.
2021-01-10 01:48:43 -05:00
Goober5000 d3d48ee6fa Merge pull request #3093 from Baezon/script-target-bracket-fix
Fix a crash with scripted targeting brackets
2021-01-08 18:13:44 -05:00
Baezon ac7bdbf269 fix bug in scripted targeting brackets 2021-01-08 17:30:28 -05:00
Goober5000 ddf00588d6 Merge pull request #3076 from Goober5000/hash_stuff
preprocess alt names before mission start
2021-01-08 16:03:10 -05:00
Goober5000 365ba2029e remove temporary function 2021-01-08 09:53:11 -05:00
Goober5000 02cdb106ba convert number parsing to use atoi and strtof
One small detail that was overlooked in PR #3021... KeldorKatarn's old code used `strtod` while the new code used `stuff_float`.  This wouldn't be a problem except that a couple MVP models use annoyingly formatted floats like 8.9e-08.  So I decided to convert `stuff_float` to use `strtof` and do some more upgrades while I was at it.
2021-01-08 09:25:25 -05:00
m!m 85a2a8f11e Merge pull request #3092 from Goober5000/strtof_patch
interim patch for get_user_vec3d_value
2021-01-08 10:47:42 +01:00
Goober5000 e76fdea8b9 interim patch for get_user_vec3d_value
As described in #3079, the old parsing of fvec and uvec used strtod and some POFs rely on this format.  This is a quickfix that uses the new parsing method for only `get_user_vec3d_value`.
2021-01-07 18:08:27 -05:00
Goober5000 726ce953c7 Merge pull request #3089 from Goober5000/fix_submodel_debris
de-parent live debris
2021-01-07 15:24:49 -05:00
JohnAFernandez a545a4ec27 Merge pull request #3087 from Baezon/lock-restriction-safety
Safety for lock restriction handling
2021-01-07 15:01:31 -05:00
Fabian Woltermann fc0662a9c4 Merge pull request #3080 from JohnAFernandez/Vectorize-Tech-Lists
Vectorize tech lists
2021-01-07 15:16:09 +01:00
JohnAFernandez e5685ded86 Keep the lists from being cleared early by removing unneeded the loops. 2021-01-07 00:33:21 -05:00
JohnAFernandez 226cbf1204 The true fix, check the correct value 2021-01-07 00:32:52 -05:00
Goober5000 043466309a de-parent live debris
It appears that live debris is not supposed to have a parent submodel; see the retail NTF Boadicea.  This removes the parent from any live debris that might have it.

This should fix #3078.
2021-01-07 00:02:49 -05:00
Baezon 766624600d update name 2021-01-06 18:04:59 -05:00
Baezon dab9a41f45 remove redundant check 2021-01-06 17:45:32 -05:00
m!m 51d8ab13d8 Merge pull request #3088 from EatThePath/fix-empty-secondaries-locking
off-by-one fix in early exit update_aspect_lock_information()
2021-01-06 23:25:53 +01:00
Baezon b93d30e12b unused variable 2021-01-06 14:46:44 -05:00
David Gibson 945db25578 corrected off-by-one issue in the early exit condition of update_aspect_lock_information() 2021-01-06 13:46:05 -06:00
Baezon 67de007ecb lock restrictions safety 2021-01-06 14:28:27 -05:00
JohnAFernandez e9a9b3b978 Merge pull request #3085 from Baezon/docking-collision-bugfix
Safety for check_for_docking_collision
2021-01-06 14:03:00 -05:00
Baezon e10522a2c7 safety for check_for_docking_collision 2021-01-06 13:18:27 -05:00
JohnAFernandez 9f33b432fc Simply this check 2021-01-05 00:07:07 -05:00
JohnAFernandez 7a0578a797 Ok CI, I'll use nullptr 2021-01-04 22:14:36 -05:00
JohnAFernandez b81825326c Signed Vs Unsigned
AKA C'Mon CI!
2021-01-03 21:54:31 -05:00
JohnAFernandez b8c2c975c0 Change to a pointer
So that we can release bitmaps and animations properly.
2021-01-03 21:36:50 -05:00
JohnAFernandez dc0272e664 Fixes Part 1 2021-01-03 20:56:30 -05:00
JohnAFernandez 3e329d64dd Check the correct value in the animation code 2021-01-03 15:15:51 -05:00
JohnAFernandez 882ac2b1a1 Possibly Fix Memory Corruption Issue
Remove Unused Var
2021-01-03 15:14:11 -05:00
Goober5000 b3153df6be preprocess alt names before mission start
For compatibility purposes some mods want alt names to truncate at the hash symbol. But we can't actually do that at mission load,
since we have to save them again in FRED. So this commit processes the names just before the mission starts.

To further complicate things, some mods actually want the hash to be displayed. So this commit introduces the double-hash as an escape sequence for a single hash.
2021-01-02 14:31:59 -05:00
z64555 bc8e16b37b Address change requst. 2021-01-01 22:41:43 -06:00
z64555 79e4c9b4e3 Satisfy change requests 2021-01-01 19:14:20 -06:00
z64555 70969c4475 Address requests by Goober5000 2020-12-31 15:39:21 -06:00
z64555 ecbb6a12f8 update feature to use eval_ship() instead of sexp_get_ship_from_node() 2020-12-31 14:58:33 -06:00
z64555 218059ffbb Merge branch 'add_ai_seconds_to_waypoint_sexp' of https://github.com/tcrayford/fs2open.github.com into feature/time_to_goal 2020-12-31 13:15:35 -06:00
Baezon c65ae50b81 version 1 of generic debris enhancements 2020-12-17 01:58:00 -05:00
Baezon 95de0c6833 version 1 of ai multilock 2020-12-14 20:41:06 -05:00
Baezon ab2c08392e in-progress 2020-12-14 04:29:55 -05:00
z64555 89a2c4dbcb Missed another break. 2020-12-12 14:47:15 -06:00
z64555 2cd557e483 Expletives and default breaks. 2020-12-12 14:20:07 -06:00
z64555 e5df64ab75 Address uninitialized variable issue. 2020-12-12 12:20:54 -06:00
z64555 52b0b19d11 Update qtFred sexp_tree::get_listing_opf_keypress 2020-12-12 11:39:47 -06:00
z64555 36a2355e86 Address exceedingly frivolous signed/unsigned mismatch errors. 2020-12-11 17:44:45 -06:00
z64555 351e8e0c44 Address short/int size mismatches. 2020-12-11 15:59:31 -06:00
z64555 91e0d8f578 QoL: Highlight/select the found key, be it primary or secondary, should the search mode find it. 2020-12-11 12:41:46 -06:00
z64555 f8f26e3b23 Fix bug where search mode wouldn't detect joy buttons correclty.
* Fix bug where joy hats were ignored.
2020-12-11 12:41:46 -06:00
z64555 873a3c3442 Fix bug with displaying joy button text
* Fix a number of warnings
2020-12-11 12:41:46 -06:00
z64555 1318b814d3 Fix out-of-bounds error due to axis controls index. 2020-12-11 12:41:45 -06:00
z64555 4c8f5f789c Grey out empty/unbound items
* Joy axis bindings now use the same color logic as the keys and buttons
2020-12-11 12:41:45 -06:00
z64555 b9b922b235 Fix bug where int conflict wasn't incremented for unselected items.
* Fix color highlighting of mouse-over lines
* Fix color highlighting of selected bindings
* Fix bug when trying to select an item while the selected line is an axis control
* Allow user to select an empty item
* Expand hitbox of items so players aren't required to click exactly on the text to select it.
2020-12-11 12:41:45 -06:00
z64555 716b4a3205 Moves the listbox draw function out of do_frame()
* Adjusts listbox positioning and widths of the binding columns for better display.
* Adds DCF tools to adjust the text positioning in the listbox
2020-12-11 12:41:44 -06:00
z64555 3c592b799f Fix bug where the "disallowed key" popup would spam you, blocking you from doing a meaningful bind. 2020-12-11 12:41:44 -06:00
z64555 c8c8a8d896 Fix bug where unbound items were flagged as conflicted.
* Fix bug where Cc_lines was accessed after being cleared. (Incorrectly assumed control_config_cancel_exit() was an exit point)
2020-12-11 12:41:43 -06:00
z64555 12b19a32bd Fix bug where unbound joy buttons would persist as a valid binding. 2020-12-11 12:41:43 -06:00
z64555 40e114351c Miscellaneous fixes to allow building.
* declare CCB before CC_bind.
* update various functions to use new binding aggregate classes.
2020-12-11 12:41:43 -06:00
z64555 704c600ef0 update control_config_clear() 2020-12-11 12:41:42 -06:00
z64555 5dde26fc33 Update check_control_used() to use primary and secondary. 2020-12-11 12:41:42 -06:00
z64555 cb5f80b0d5 Complete refactor of control_config_do_frame()
* Add textify method to CC_bind
* Replace string printing of key and joy bindings with primary and secondary bindings.
2020-12-11 12:41:42 -06:00
z64555 32636968bd Refactor browse mode in control_config_do_frame()
* Allow joy button presses to dismiss help overlay while in browse mode
* Refactor selected item cycling
2020-12-11 12:41:41 -06:00
z64555 e546a75c6b Fix possible bug where joy btn was ignored in bind mode
* Refactor search mode in control_config_do_frame()
* Dismiss help overlay in search mode when joy button is pressed
* Fix possible bug where nav buttons were not reset in search mode
2020-12-11 12:41:41 -06:00
z64555 9b0aab3f82 replace CCI::joy_id and ::key_id with ::primary and ::secondary (through inheritance of CCB)
* Add convenience methods to CCB
* Convert CC_bind into a class and define convenience methods
* Set underlying type of enum CID to short
* controlconfigdefaults.tbl: Should the CCI be disabled by an override, clear its binding.
* Control config menu: Should the help overlay be active during bind mode, dismiss it, don't bind anything, and stay in bind mode (fixes inconstant behavior)
* Refactor bind mode handling in control_config_do_frame()
2020-12-11 12:41:40 -06:00
z64555 7f2baec387 Remove Control_config::key_default and ::joy_default, redirect calls to the default presets 2020-12-11 12:41:40 -06:00
z64555 392148f84b Fix crash in unit tests due to uninitialized Control_config vector. 2020-12-11 12:33:45 -06:00
z64555 577ae95026 Use ints instead of size_t because CI said so. 2020-12-11 07:40:42 -06:00
z64555 968d76431f Missing an amp. 2020-12-11 07:40:42 -06:00
z64555 c2408839d1 Fix out of bounds error due to dynamic containers cleared at wrong spot. 2020-12-11 07:40:41 -06:00
z64555 a03af2e4a9 Re-enables the undo feature in the controls config menu. 2020-12-11 07:40:41 -06:00
z64555 aec7589b94 Tweak Undo_item::save to correctly save a reference as a pointer
* Remove two uncessary Assets.  operator new should throw an exception should it fail.
* Move template functions into .h.  This resolves instancing problems when trying to define them in a .cpp
* Add Undo_system::empty() and ::empty_redo() for checking if an ::undo() or ::redo() is possible.
2020-12-11 07:40:41 -06:00
z64555 e1974d1b57 Fix potential issue where Undo_stacks delete tracked items when going out of scope, even after the items were passed onto an Undo_system 2020-12-11 07:40:40 -06:00
z64555 bc7809de43 Undo_system::save_stack() now ignores empty stacks passed to it. No need to check for stack size before saving.
* Implement Undo_stack::size()
* Spellcheck in undosys.h
2020-12-11 07:40:40 -06:00
z64555 f23a72e228 Minor changes 2020-12-11 07:40:40 -06:00
z64555 45a45a4eff Remove licensing. 2020-12-11 07:40:39 -06:00
z64555 7e9ce1c9f7 Initial draft of the instruction documentation
Fix two typos

Adds Undo_system::clear()

Whoops! Can't just simply clear the stacks,gotta clean stuff off the heap first.
2020-12-11 07:40:39 -06:00
z64555 dce37d529c Adds ability to save a stack of undo operations as a single undo-op within the system 2020-12-11 07:40:39 -06:00
z64555 f528b3caa5 Changes the system so that the undo() and redo() functions return references to the changed item and its container (if it was specified at launch). This should allow client code to quickly find and access it on its end by comparing pointer values.
Since the pair is returned instead of the size of the undo/redo stacks, stack size information has been moved to their own getter functions.
2020-12-11 07:40:38 -06:00
z64555 492320f8d3 z64555's Undo system! This is a generic undo system, so you can save any single item type (not C arrays) and do undo/redo stuff on them. 2020-12-11 07:40:38 -06:00
z64555 636b4b12e4 Vectorize control_config::Conflicts 2020-12-11 07:40:14 -06:00
z64555 93c96b8943 Re-enable display of current selected preset. 2020-12-11 07:40:14 -06:00
z64555 d1f94c7b92 Fix overwrite check.
Add check for cfile open correctly.
2020-12-11 07:40:13 -06:00
z64555 74ffcb2db8 Print "NONE" instead of "-1" For unbound keys. 2020-12-11 07:40:13 -06:00
z64555 c7112fd897 Adds function to write controlconfigdefaults.tbl using the default preset at time of call.
* Adds DC function 'write_ccd' to write the .tbl
* Adds DC function 'load_ccd' to reload the .tbl
2020-12-11 07:40:13 -06:00
z64555 1bb9ed8832 Remove unused variable for catch clause. 2020-12-11 07:40:13 -06:00
z64555 23f85da586 Move typedef after the declaration of the struct it uses. 2020-12-11 07:40:12 -06:00
z64555 f163f56c10 Cleanup controlsconfig.h and refactor some loops. 2020-12-11 07:40:12 -06:00
z64555 9c03e8c069 Remove dead comment. 2020-12-11 07:40:12 -06:00
z64555 92a76c05ee Adds ability for mod devs to change the naming of a control as it shows up in the config menu.
* Presets start from a clean slate, while Overrides inherite from hardcode defaults
2020-12-11 07:40:11 -06:00
z64555 730ab835a8 Add #ControlConfigPreset section to controlconfigdefault.tbl and allow multiple sections in the .tbl
* This is a section that only configures the bindings for a preset.  Allowing mod devs to add presets instead of just the overrides
* Added ability to resume parsing should a Binding not be found
2020-12-11 07:40:11 -06:00
z64555 ef89a4aa38 Refactor and cleanup control_config_common_load_overrides.
* Mod devs now have the option to override hardcoded defaults
2020-12-11 07:40:11 -06:00
z64555 f9474cf773 Refactor defaults.
* Hardcoded default bindings are now a preset
* rename controls_config_init_bindings() to control_config_common_init_bindings() for consistancy
2020-12-11 07:40:10 -06:00
z64555 96382b47e2 Initial work on presets and default keybinds. Defaults will be moved into a preset instead of lingering in the bindings vector, this may cause issues with training messages that reference controls by default keybinds. 2020-12-11 07:40:10 -06:00
z64555 99dfdf07a2 Refactor control_config_list_prepare()
Default Disable Non-retail controls
Expand CC_lines buffer to include NUM_JOY_AXIS_ACTIONS
2020-12-11 07:39:30 -06:00
z64555 62fa76d65e Fix wrong reference to array size.
Init Cc_lines, which is now dynamic
De-init Cc_lines and Control_config_backup when closing the menu.
2020-12-11 07:39:30 -06:00
z64555 1a482cab4a Remove Joy_table declaration for later PR.. 2020-12-11 07:39:30 -06:00
z64555 ec61af7e1c Revert obliteration of magic number. 2020-12-11 07:39:29 -06:00
z64555 d8d4e9ed2c Disable Presets and Undo capabilities. Only reset_all was spared.
* Revert Joy_axis_action_index changes.  Feature to be added later
* Strip out most of the preset and undo capability.  Only the backup ability survived.
2020-12-11 07:39:29 -06:00
z64555 38df849e6e Change Control_config array to an SCP_vector
* Hardcoded bindings are done at run-time instead of assignment
* Created a map that should retain backward compatibility with controlconfigdefaults.tbl and playerfiles, since a reorganization of the controls in the code is planned and would break compatibility.
2020-12-11 07:39:28 -06:00
Baezon d06eedf13c testing 2020-12-10 18:49:05 -05:00
Baezon a3d3be11fb initial testing version 2020-12-10 18:41:36 -05:00
Baezon be76cd727a implement turret-forced-target 2020-12-08 00:07:23 -05:00
Baezon 641cf48073 implement damage incidence angle 2020-12-01 12:37:22 -05:00
Baezon 2dbaf53e49 no more travis wooo 2020-11-20 14:10:27 -05:00
Baezon 849b4c57b6 no warning 2020-11-20 12:03:34 -05:00
Baezon de0933e1b9 expose targeting sizes 2020-11-20 12:03:34 -05:00
Baezon 8c9a6d049e use armor types for beam-weapon damage 2020-11-06 23:40:02 -05:00
Baezon 799fa27d36 clarity comment 2020-10-02 23:14:45 -04:00
Baezon 01cd5a34b9 travis 2020-10-02 12:04:45 -04:00
Baezon 9b3905a4bb v1 2020-10-02 02:53:13 -04:00
Shivansps 1239ec47e3 Smart pointers and disabled some messages
Added smart pointers to bsp_in, bsp_out, sldc_tree and slc2_tree as requested.

I also changed the #error to #pragma message in a attempt to remove the false positive error.

Also did minor code indent fixes-
2020-08-30 13:45:43 -03:00
Shivansps eb1814e303 SLC2 and POF version 2118 support
Changed SLDC system to SLC2, this replaced the char at the beginning of the node for an int in order for the tree to be aligned.
Added BSP_Defpoints data alignment and SLDC to SLC2 conversion for pof version < 2118.
2020-08-27 20:10:29 -03:00
Baezon b4ec1b32ba better subtype determiner 2020-08-05 01:54:47 -04:00
Baezon 27544e781a Revert "no homing when out of range"
This reverts commit eebfb7eadb.
2020-07-23 21:12:51 -04:00
Baezon eebfb7eadb no homing when out of range 2020-07-23 12:30:28 -04:00
Baezon e6938bd14d verison 1 2020-07-22 23:48:53 -04:00
Tom Crayford 6ce29b5c6a add seconds-to-goal
returns the number of seconds between a designated ship and its goal.
Uses `ship_return_seconds_to_goal`, but only returns 0 in the invalid
cases.
2019-09-23 11:12:18 +01:00
Tom Crayford bc99331d47 refactor ship_return_time_to_goal to be more general 2019-09-22 22:00:13 +01:00
567 changed files with 44913 additions and 16623 deletions

No files matched your search

+2
View File
@@ -6,3 +6,5 @@ code/executor/ @asarium
code/scpui/ @asarium
code/scripting/ @asarium
code/controlconfig/ @z64555
+3 -1
View File
@@ -15,7 +15,7 @@ jobs:
configuration: [FastDebug, Release]
name: Linux
runs-on: ubuntu-latest
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
steps:
- name: Cache Qt
id: cache-qt-lin
@@ -29,6 +29,8 @@ jobs:
version: ${{ env.QT_VERSION }}
dir: ${{ github.workspace }}/..
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
setup-python: 'false'
aqtversion: ==1.1.3
- uses: actions/checkout@v1
name: Checkout
+3 -1
View File
@@ -39,7 +39,7 @@ jobs:
name: Linux
needs: create_release
runs-on: ubuntu-latest
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
steps:
- name: Cache Qt
id: cache-qt-lin
@@ -53,6 +53,8 @@ jobs:
version: ${{ env.QT_VERSION }}
dir: ${{ github.workspace }}/..
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
setup-python: 'false'
aqtversion: ==1.1.3
- uses: actions/checkout@v1
name: Checkout
+3 -1
View File
@@ -15,7 +15,7 @@ jobs:
configuration: [FastDebug, Release]
name: Linux
runs-on: ubuntu-latest
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
steps:
- name: Cache Qt
id: cache-qt-lin
@@ -29,6 +29,8 @@ jobs:
version: ${{ env.QT_VERSION }}
dir: ${{ github.workspace }}/..
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
setup-python: 'false'
aqtversion: ==1.1.3
- uses: actions/checkout@v1
name: Checkout
+62
View File
@@ -0,0 +1,62 @@
name: Build caches for master
on:
push:
branches:
- master
env:
QT_VERSION: 5.12.9
jobs:
build_linux:
strategy:
matrix:
configuration: [Debug, Release]
compiler: [gcc-5, gcc-10]
cmake_options: [""]
include:
# Also include configurations that check if the code compiles without the graphics backends
- configuration: Debug
compiler: gcc-10
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
- configuration: Release
compiler: gcc-10
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-11e553c
steps:
- name: Cache Qt
id: cache-qt-lin
uses: actions/cache@v1
with:
path: ${{ github.workspace }}/../Qt
key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: ${{ env.QT_VERSION }}
dir: ${{ github.workspace }}/..
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
setup-python: 'false'
aqtversion: ==1.1.3
- uses: actions/checkout@v1
name: Checkout
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ runner.os }}-${{ matrix.configuration }}-${{ matrix.compiler }}
- name: Configure CMake
env:
CONFIGURATION: ${{ matrix.configuration }}
COMPILER: ${{ matrix.compiler }}
JOB_CMAKE_OPTIONS: ${{ matrix.cmake_options }}
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
- name: Compile
working-directory: ./build
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja -k 20 all
+7 -1
View File
@@ -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-e977d36
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
steps:
- name: Cache Qt
id: cache-qt-lin
@@ -37,12 +37,18 @@ jobs:
dir: ${{ github.workspace }}/..
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
setup-python: 'false'
aqtversion: ==1.1.3
- uses: actions/checkout@v1
name: Checkout
with:
submodules: true
fetch-depth: 0
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ runner.os }}-${{ matrix.configuration }}-${{ matrix.compiler }}
- name: Configure CMake
env:
CONFIGURATION: ${{ matrix.configuration }}
@@ -3,7 +3,8 @@ name: Publish Standalone docker image
on:
push:
tags:
- '*'
- 'nightly_*'
- 'release_*'
jobs:
build_images:
@@ -12,13 +13,9 @@ jobs:
configuration: [FastDebug, Release]
include:
- configuration: FastDebug
latest_tag: latest_debug
nightly_tag: nightly_debug
release_tag: release_debug
tag_suffix: -debug
- configuration: Release
latest_tag: latest
nightly_tag: nightly
release_tag: release
tag_suffix: ""
fail-fast: false
runs-on: ubuntu-latest
steps:
@@ -26,23 +23,30 @@ jobs:
name: Checkout
with:
submodules: true
- name: Docker login
run: echo ${{ secrets.DOCKER_ACCESSTOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
- name: Docker build
run: docker build --build-arg build_type=${{ matrix.configuration }} -f docker/standalone/Dockerfile -t ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} .
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Push latest tag
run: |
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }}
- name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: ghcr.io/scp-fs2open/standalone_server
tag-latest: false
- name: Push nightly tag
if: contains(github.ref, 'refs/tags/nightly_')
run: |
docker tag ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.nightly_tag }}
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.nightly_tag }}
- name: Push release tag
if: contains(github.ref, 'refs/tags/release_')
run: |
docker tag ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.release_tag }}
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.release_tag }}
- name: Login to GitHub Docker Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.CR_PAT }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: .
file: ./docker/standalone/Dockerfile
push: true
tags: ${{ steps.docker_meta.outputs.tags }}${{ matrix.tag_suffix }}
labels: ${{ steps.docker_meta.outputs.labels }}
build-args: |
build_type=${{ matrix.configuration }}
+2 -1
View File
@@ -32,4 +32,5 @@ export LD_LIBRARY_PATH
cmake -G Ninja -DFSO_FATAL_WARNINGS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON $CMAKE_OPTIONS \
-DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_BUILD_APPIMAGE=ON -DCMAKE_BUILD_TYPE=$CONFIGURATION \
-DFFMPEG_USE_PRECOMPILED=ON -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=ON ..
-DFFMPEG_USE_PRECOMPILED=ON -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=ON \
-DSHADERS_ENABLE_COMPILATION=ON ..
-12
View File
@@ -1,15 +1,3 @@
include(WriteCompilerDetectionHeader)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
set(CMAKE_CXX_STANDARD 11)
get_property(CMAKE_CXX_KNOWN_FEATURES GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
write_compiler_detection_header(
FILE "${GENERATED_SOURCE_DIR}/scp_compiler_detection.h"
PREFIX SCP
COMPILERS AppleClang Clang GNU MSVC
FEATURES ${CMAKE_CXX_KNOWN_FEATURES}
VERSION 3.2
)
-2
View File
@@ -21,8 +21,6 @@ endif()
set(CMAKE_EXTRA_INCLUDE_FILES "cstddef")
CHECK_TYPE_SIZE("std::max_align_t" STD_MAX_ALIGN_T LANGUAGE CXX)
set(CMAKE_EXTRA_INCLUDE_FILES "type_traits")
CHECK_TYPE_SIZE("std::is_trivially_copyable<int>" STD_IS_TRIVIALLY_COPYABLE LANGUAGE CXX)
set(CMAKE_EXTRA_INCLUDE_FILES)
CHECK_TYPE_SIZE("char32_t" CHAR32_T LANGUAGE CXX)
-2
View File
@@ -8,8 +8,6 @@
#cmakedefine SCP_HAVE_MAX_ALIGN_T
#cmakedefine SCP_HAVE_STD_MAX_ALIGN_T
#cmakedefine01 HAVE_STD_IS_TRIVIALLY_COPYABLE
#cmakedefine01 IS_64BIT
#cmakedefine01 SCP_HAVE_STRCASECMP
+2 -2
View File
@@ -6,8 +6,8 @@ if (EXISTS "${PROJECT_SOURCE_DIR}/version_override.cmake")
include("${PROJECT_SOURCE_DIR}/version_override.cmake")
endif()
set_if_not_defined(FSO_VERSION_MAJOR 20)
set_if_not_defined(FSO_VERSION_MINOR 1)
set_if_not_defined(FSO_VERSION_MAJOR 21)
set_if_not_defined(FSO_VERSION_MINOR 3)
set_if_not_defined(FSO_VERSION_BUILD 0)
# A numeric revision e.g. the CI build number
+3
View File
@@ -26,6 +26,7 @@ TARGET_LINK_LIBRARIES(code PUBLIC openal)
TARGET_LINK_LIBRARIES(code PUBLIC ${LUA_LIBS})
TARGET_LINK_LIBRARIES(code PUBLIC ${PNG_LIBS})
TARGET_LINK_LIBRARIES(code PUBLIC ${JPEG_LIBS})
TARGET_LINK_LIBRARIES(code PUBLIC lz4)
TARGET_LINK_LIBRARIES(code PUBLIC sdl2)
@@ -63,6 +64,8 @@ ENDIF(FSO_USE_SPEECH)
TARGET_LINK_LIBRARIES(code PUBLIC jansson)
target_link_libraries(code PUBLIC variant)
# Headers for standalone "gui" on UNIX
IF(UNIX)
TARGET_LINK_LIBRARIES(code PUBLIC ${MONGOOSE_LIBS})
+8
View File
@@ -0,0 +1,8 @@
#include "Action.h"
namespace actions {
Action::~Action() = default;
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "common.h"
namespace actions {
enum class ActionResult
{
Finished,
Errored,
Wait
};
class Action {
public:
virtual ~Action() = 0;
virtual ActionResult execute(ProgramLocals& locals) const = 0;
virtual std::unique_ptr<Action> clone() const = 0;
};
} // namespace actions
+21
View File
@@ -0,0 +1,21 @@
#include "ActionDefinition.h"
namespace actions {
ActionDefinition::ActionDefinition(SCP_string name, SCP_vector<ActionParameter> parameters)
: m_name(std::move(name)), m_parameters(std::move(parameters))
{
}
ActionDefinition::~ActionDefinition() = default;
const SCP_string& ActionDefinition::getName() const
{
return m_name;
}
const SCP_vector<ActionParameter>& ActionDefinition::getParameters() const
{
return m_parameters;
}
} // namespace actions
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include "Action.h"
#include "actions/expression/ActionExpression.h"
#include "actions/expression/Value.h"
namespace actions {
struct ActionParameter {
SCP_string name;
expression::ValueType type;
};
class ActionDefinition {
public:
ActionDefinition(SCP_string name, SCP_vector<ActionParameter> parameters);
virtual ~ActionDefinition();
const SCP_string& getName() const;
const SCP_vector<actions::ActionParameter>& getParameters() const;
virtual flagset<ProgramContextFlags> getRequiredContext() const = 0;
virtual std::unique_ptr<Action> createAction(
const SCP_unordered_map<SCP_string, expression::ActionExpression>& parameterExpressions) const = 0;
private:
SCP_string m_name;
SCP_vector<ActionParameter> m_parameters;
};
} // namespace actions
+145
View File
@@ -0,0 +1,145 @@
#include "ActionDefinitionManager.h"
#include "BuiltinActionDefinition.h"
#include "actions/types/MoveToSubmodel.h"
#include "actions/types/ParticleEffectAction.h"
#include "actions/types/PlaySoundAction.h"
#include "actions/types/SetDirectionAction.h"
#include "actions/types/SetPositionAction.h"
#include "actions/types/WaitAction.h"
#include "utils/join_string.h"
namespace actions {
namespace {
void parseActionParameters(const SCP_vector<ActionParameter>& params,
SCP_unordered_map<SCP_string, expression::ActionExpression>& parameterExpressions,
const expression::ParseContext& context)
{
// We do not impose a set order of the parameters so we need to do something similar to the outer loop and
// check for every possibility in a loop.
for (size_t i = 0; i < params.size(); ++i) {
// We might be doing some empty loops here in case of errors but there is a bound so this will finish
// eventually
for (const auto& parameter : params) {
// Do not check for parameters which we already found
if (parameterExpressions.find(parameter.name) != parameterExpressions.end()) {
continue;
}
const auto paramString = "+" + parameter.name + ":";
if (!optional_string(paramString.c_str())) {
continue;
}
// We found our parameter!
parameterExpressions[parameter.name] =
expression::ActionExpression::parseFromTable(parameter.type, context);
break;
}
}
}
} // namespace
const ActionDefinitionManager& ActionDefinitionManager::instance()
{
static const ActionDefinitionManager manager;
return manager;
}
void ActionDefinitionManager::addDefinition(std::unique_ptr<ActionDefinition> def)
{
m_definitions.push_back(std::move(def));
}
std::unique_ptr<Action> ActionDefinitionManager::parseAction(const flagset<ProgramContextFlags>& context_flags,
const expression::ParseContext& context) const
{
// We search through all definitions for every time we want to check for an action. This is not the most efficient
// way to handle this since it results in an O(n^2) runtime. A better alternative would be to either construct a
// regex with all the accepted actions as alternatives (although runtime of this is also doubtful) or peek at the
// next token, extract the relevant part (The stuff between "+" and ":") and then look that up in a map. Then we
// would get down to O(n).
// Since this only happens at parse time, this is good enough for now.
for (const auto& actionDef : m_definitions) {
const auto parseString = "+" + actionDef->getName() + ":";
if (!optional_string(parseString.c_str())) {
continue;
}
SCP_unordered_map<SCP_string, expression::ActionExpression> parameterExpressions;
const auto& params = actionDef->getParameters();
if (params.size() == 1) {
// Single parameters are expected to be placed directly after the action name
parameterExpressions[params.front().name] =
expression::ActionExpression::parseFromTable(params.front().type, context);
} else {
parseActionParameters(params, parameterExpressions, context);
if (parameterExpressions.size() != params.size()) {
SCP_vector<ActionParameter> missingParameters;
std::copy_if(params.cbegin(),
params.cend(),
std::back_inserter(missingParameters),
[&parameterExpressions](const ActionParameter& param) {
const auto iter = parameterExpressions.find(param.name);
return iter == parameterExpressions.cend();
});
SCP_vector<SCP_string> missingParameterNames;
std::transform(missingParameters.cbegin(),
missingParameters.cend(),
std::back_inserter(missingParameterNames),
[](const ActionParameter& param) { return "+" + param.name; });
const auto namesList = util::join_container(missingParameterNames, ", ");
error_display(0, "Missing parameters for action '%s': %s", parseString.c_str(), namesList.c_str());
// Fix up the parameter list so that we can continue parsing
for (const auto& missingParam : missingParameters) {
parameterExpressions[missingParam.name] = expression::ActionExpression();
}
}
}
auto actionInstance = actionDef->createAction(parameterExpressions);
const auto requiredFlags = actionDef->getRequiredContext();
if ((context_flags & requiredFlags) != requiredFlags) {
// We are missing at least one context flag so this action is not valid here. We still return the action
// here though since that is the only way we can keep parsing the table without throwing random error
// messages at the user. However, this will trigger an Assertion (in debug) or crashes when the action is
// actually run later.
error_display(0,
"The action %s is not available in the current context. This will lead to an Assertion failure when "
"this action is triggered.",
parseString.c_str());
}
return actionInstance;
}
return std::unique_ptr<Action>();
}
ActionDefinitionManager::ActionDefinitionManager()
{
addDefinition(
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::MoveToSubmodel>("Move To Subobject")));
addDefinition(std::unique_ptr<ActionDefinition>(
new BuiltinActionDefinition<types::ParticleEffectAction>("Start Particle Effect")));
addDefinition(
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::PlaySoundAction>("Play 3D Sound")));
addDefinition(
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetDirectionAction>("Set Direction")));
addDefinition(
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetPositionAction>("Set Position")));
addDefinition(std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::WaitAction>("Wait")));
}
} // namespace actions
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "ActionDefinition.h"
namespace actions {
class ActionDefinitionManager {
public:
// This type is supposed to be global so it may not be copied
ActionDefinitionManager(const ActionDefinitionManager&) = delete;
ActionDefinitionManager& operator=(const ActionDefinitionManager&) = delete;
ActionDefinitionManager(ActionDefinitionManager&&) noexcept = default;
ActionDefinitionManager& operator=(ActionDefinitionManager&&) noexcept = default;
std::unique_ptr<Action> parseAction(const flagset<ProgramContextFlags>& context_flags,
const expression::ParseContext& context) const;
static const ActionDefinitionManager& instance();
private:
ActionDefinitionManager();
void addDefinition(std::unique_ptr<ActionDefinition> def);
SCP_vector<std::unique_ptr<ActionDefinition>> m_definitions;
};
} // namespace actions
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include "ActionDefinition.h"
#include "expression/Value.h"
namespace actions {
template <typename TAction>
class BuiltinActionDefinition : public ActionDefinition {
using ActionValueType = typename TAction::ValueType;
public:
BuiltinActionDefinition(SCP_string name)
: ActionDefinition(std::move(name), {{"parameter", expression::ValueTypeTraits<ActionValueType>::type}})
{
}
~BuiltinActionDefinition() override = default;
flagset<ProgramContextFlags> getRequiredContext() const override
{
return flagset<ProgramContextFlags>(TAction::getRequiredExecutionContextFlags());
}
std::unique_ptr<Action> createAction(
const SCP_unordered_map<SCP_string, expression::ActionExpression>& parameterExpressions) const override
{
const auto paramIter = parameterExpressions.find("parameter");
// This should have been caught earlier
Assertion(paramIter != parameterExpressions.cend(), "Could not find built-in parameter!");
return std::unique_ptr<Action>(new TAction(paramIter->second.template asTyped<ActionValueType>()));
}
};
} // namespace actions
+174
View File
@@ -0,0 +1,174 @@
#include "Program.h"
#include "Action.h"
#include "actions/ActionDefinitionManager.h"
#include "executor/global_executors.h"
#include "tracing/Monitor.h"
#include "tracing/categories.h"
#include "tracing/tracing.h"
namespace {
using namespace actions;
tracing::Monitor<int> RunningProgramsMonitor("RunningPrograms", 0);
struct ProgramRunner {
tracing::RunningCounter runningCounter{RunningProgramsMonitor};
ProgramInstance instance;
explicit ProgramRunner(const ProgramInstance& _instance) : instance(_instance) {}
executor::Executor::CallbackResult operator()()
{
TRACE_SCOPE(tracing::ProgramStepOne);
auto res = instance.step();
switch (res) {
case ProgramState::Running:
// If the program is still running then we need to reschedule our execution
return executor::Executor::CallbackResult::Reschedule;
case ProgramState::Done:
case ProgramState::Errored:
default:
return executor::Executor::CallbackResult::Done;
}
}
};
} // namespace
namespace actions {
ProgramInstance::ProgramInstance(const actions::Program* program) : _program(program) {}
ProgramState ProgramInstance::step()
{
if (!_locals.host.IsValid()) {
return ProgramState::Done;
}
auto& actions = _program->getActions();
for (; _currentInstruction < actions.size(); ++_currentInstruction) {
auto& action = actions[_currentInstruction];
auto result = action->execute(_locals);
if (result == ActionResult::Errored) {
return ProgramState::Errored;
}
if (result == ActionResult::Wait) {
// Action is not done yet, call again next frame
return ProgramState::Running;
}
// Action finished, run next action in program
}
// End of program reached, we are done now
return ProgramState::Done;
}
ProgramLocals& ProgramInstance::locals()
{
return _locals;
}
const ProgramLocals& ProgramInstance::locals() const
{
return _locals;
}
const SCP_vector<std::unique_ptr<actions::Action>>& Program::getActions() const
{
return _actions;
}
Program::Program() = default;
ProgramInstance Program::newInstance() const
{
return ProgramInstance(this);
}
void Program::parseActions(const flagset<ProgramContextFlags>& context_flags, const expression::ParseContext& context)
{
while (true) {
auto action = ActionDefinitionManager::instance().parseAction(context_flags, context);
if (!action) {
break;
}
_actions.push_back(std::move(action));
}
}
Program::Program(const Program& other) { *this = other; }
Program& Program::operator=(const Program& other)
{
_actions.clear();
_actions.reserve(other._actions.size());
for (auto& action : other._actions) {
_actions.emplace_back(action->clone());
}
return *this;
}
Program& Program::operator=(Program&&) noexcept = default;
Program::Program(Program&&) noexcept = default;
bool ProgramSet::isEmpty() const
{
return _programs.empty();
}
ProgramSet::ProgramSet() = default;
ProgramSet::ProgramSet(const flagset<ProgramContextFlags>& context_flags) : _contextFlags(context_flags) {}
void ProgramSet::start(object* objp, const vec3d* local_pos, const matrix* local_orient, int submodel) const
{
if (_contextFlags[ProgramContextFlags::HasObject]) {
Assertion(objp != nullptr, "Invalid object pointer supplied.");
}
if (_contextFlags[ProgramContextFlags::HasSubobject]) {
Assertion(submodel >= 0, "Invalid subobject supplied.");
}
for (const auto& prog : _programs) {
auto instance = prog.newInstance();
instance.locals().host = object_h(objp);
instance.locals().localPosition = *local_pos;
instance.locals().localOrient = *local_orient;
instance.locals().hostSubobject = submodel;
instance.locals().variables = getDefaultTableVariables();
// Just use the executor system to take care of running this program
executor::OnSimulationExecutor->post(ProgramRunner(instance));
}
}
ProgramSet ProgramSet::parseProgramSet(const char* tag, const flagset<ProgramContextFlags>& context_flags)
{
ProgramSet set(context_flags);
const auto parseContext = getTableParseContext();
while (optional_string(tag)) {
Program p;
p.parseActions(context_flags, parseContext);
set._programs.push_back(std::move(p));
}
return set;
}
} // namespace actions
+112
View File
@@ -0,0 +1,112 @@
#pragma once
#include "globalincs/pstypes.h"
#include "Action.h"
#include "common.h"
namespace actions {
class Program;
class ProgramSet;
enum class ProgramState
{
Running,
Done,
Errored
};
/**
* @brief An instance of a program execution
*
* A program instance contains all information related to a single execution of a program. A program instance is
* independent from other instances. Local variables used by actions are stored in a specific structure which can be
* used by actions to keep track of state in this specific instance.
*/
class ProgramInstance {
size_t _currentInstruction = 0;
ProgramLocals _locals;
const Program* _program = nullptr;
public:
explicit ProgramInstance(const Program* program);
ProgramState step();
ProgramLocals& locals();
const ProgramLocals& locals() const;
};
/**
* @brief A program specified in a table
*
* A program is a sequential collection of Actions which are executed in sequence.
*/
class Program {
SCP_vector<std::unique_ptr<Action>> _actions;
public:
Program();
Program(const Program& other);
Program& operator=(const Program& other);
Program(Program&& other) noexcept;
Program& operator=(Program&& other) noexcept;
const SCP_vector<std::unique_ptr<actions::Action>>& getActions() const;
/**
* @brief Creates a new program instance from this program
*
* The program instance is initialized to its initial values.
*
* @return The new program instance
*/
ProgramInstance newInstance() const;
void parseActions(const flagset<ProgramContextFlags>& context_flags, const expression::ParseContext& context);
};
/**
* @brief A set of parsed programs
*
* Programs are parsed in sets of zero or more programs to make it possible for the content creator to split up
* programs.
*/
class ProgramSet {
flagset<ProgramContextFlags> _contextFlags;
SCP_vector<Program> _programs;
public:
ProgramSet();
explicit ProgramSet(const flagset<ProgramContextFlags>& context_flags);
bool isEmpty() const;
/**
* @brief Starts the execution of all programs within this set with the specified parameters
* @param objp The object on which this program is executed
* @param local_pos The local position relative to the object or the subsystem if specified
* @param local_orien The local orientation relative to the object or the subsystem if specified
* @param submodel The submodel of the model to which this program is attached to.
*/
void start(object* objp, const vec3d* local_pos, const matrix* local_orient, int submodel = -1) const;
/**
* @brief Parses a program set of zero or more programs
*
* The tag is entirely optional and if it doesn't appear in the current table then an empty set will be returned
* which does nothing if started.
*
* @param tag The tag which is required before a program can be specified
* @param context_flags A set of flags which specify in which environment the program will be used. This is used
* mainly for validating the used actions.
* @return The parsed program set.
*/
static ProgramSet parseProgramSet(const char* tag, const flagset<ProgramContextFlags>& context_flags = {});
};
} // namespace actions
+27
View File
@@ -0,0 +1,27 @@
#include "common.h"
namespace actions {
expression::ParseContext getTableParseContext()
{
expression::ParseContext ctx;
auto& locals = ctx.variables.addScope("locals");
locals.addMember("position", expression::ValueType::Vector);
locals.addMember("direction", expression::ValueType::Vector);
return ctx;
}
expression::ProgramVariables getDefaultTableVariables()
{
expression::ProgramVariables vars;
vars.setValue({"locals", "position"}, expression::Value(vmd_zero_vector));
vars.setValue({"locals", "direction"}, expression::Value(vmd_zero_vector));
return vars;
}
} // namespace actions
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include "globalincs/flagset.h"
#include "actions/expression/ParseContext.h"
#include "actions/expression/ProgramVariables.h"
#include "math/vecmat.h"
#include "object/object.h"
class object;
class ship_subsys;
namespace actions {
struct ProgramLocals {
object_h host;
vec3d localPosition = vmd_zero_vector;
matrix localOrient = vmd_identity_matrix;
int hostSubobject = -1;
int waitTimestamp = 0;
expression::ProgramVariables variables;
};
/**
* @brief A list of flags for altering the program parsing behavior
*
* This should be used for differentiating between different program environments when validating the program (e.g. a
* particle effect requires an object host).
*/
// clang-format off
FLAG_LIST(ProgramContextFlags) {
HasObject = 0, //!< The program will be started with an object host
HasSubobject, //!< The program will be started with a subobject host
NUM_VALUES
};
// clang-format on
/**
* @brief Constructs the "table" parse context
*
* This is the context that is available when parsing a program from an external table (e.g. ships or weapons).
*
* @return The parse context
*/
expression::ParseContext getTableParseContext();
/**
* @brief Gets a program variables instance initialized with the default values
*
* The values are consistent with what is required for the default table parse context;
*
* @return The program variables
*/
expression::ProgramVariables getDefaultTableVariables();
} // namespace actions
@@ -0,0 +1,66 @@
#include "ActionExpression.h"
#include "ExpressionParser.h"
#include "TypeDefinition.h"
#include "actions/expression/nodes/AbstractExpression.h"
namespace actions {
namespace expression {
namespace {
SCP_string getTypeName(ValueType type)
{
const auto& typeDef = TypeDefinition::forValueType(type);
return typeDef.getName();
}
} // namespace
ActionExpression ActionExpression::parseFromTable(ValueType expectedReturnType, const ParseContext& context)
{
SCP_string expressionText;
stuff_string(expressionText, F_NAME);
ExpressionParser parser(expressionText);
auto expression = parser.parse(context);
if (!expression) {
error_display(0, "Failed to parse action expression:\n%s", parser.getErrorText().c_str());
return ActionExpression();
}
auto type = expression->getExpressionType();
// Check if the type this evaluates to matches what we expect
if (!checkTypeWithImplicitConversion(type, expectedReturnType)) {
error_display(0,
"Expression evaluates to type <%s> but <%s> was expected!",
getTypeName(type).c_str(),
getTypeName(expectedReturnType).c_str());
return ActionExpression();
}
// Everything is valid
return ActionExpression(expression);
}
Value ActionExpression::execute(const ProgramVariables& variables) const
{
// Do not crash on invalid expressions
if (!isValid()) {
return Value();
}
return m_expression->execute(variables);
}
bool ActionExpression::isValid() const
{
return m_expression != nullptr;
}
} // namespace expression
} // namespace actions
@@ -0,0 +1,83 @@
#pragma once
#include "ParseContext.h"
#include "ProgramVariables.h"
#include "TypeDefinition.h"
#include "parse/parselo.h"
namespace actions {
namespace expression {
namespace nodes {
class AbstractExpression;
}
template <typename T>
class TypedActionExpression;
/**
* @brief An expression that can be executed to compute a value at runtime
*
* This is the basis of having dynamic values in actions by allowing mods to specify complex expressions that make use
* of variables and functions for computing their values at runtime.
*/
class ActionExpression {
public:
ActionExpression() = default;
Value execute(const ProgramVariables& variables) const;
bool isValid() const;
template <typename T>
TypedActionExpression<T> asTyped() const;
static ActionExpression parseFromTable(ValueType expectedReturnType, const ParseContext& context);
protected:
explicit ActionExpression(std::shared_ptr<nodes::AbstractExpression> expression)
: m_expression(std::move(expression))
{
}
std::shared_ptr<nodes::AbstractExpression> m_expression;
};
/**
* @brief An action expression with a specific type specified at compile time
*
* This is most useful for built-in actions that have the required type fixed at compile time.
*
* @tparam T The type of the expression value.
*/
template <typename T>
class TypedActionExpression {
public:
explicit TypedActionExpression(ActionExpression expression) : m_expression(std::move(expression)) {}
T execute(const ProgramVariables& variables) const
{
// Do not crash on invalid expressions
if (!m_expression.isValid()) {
return T();
}
return m_expression.execute(variables).template get<T>();
}
static TypedActionExpression<T> parseFromTable(const ParseContext& context)
{
return ActionExpression::parseFromTable(ValueTypeTraits<T>::type, context).template asTyped<T>();
}
private:
ActionExpression m_expression;
};
template <typename T>
TypedActionExpression<T> ActionExpression::asTyped() const
{
return TypedActionExpression<T>(*this);
}
} // namespace expression
} // namespace actions
@@ -0,0 +1,194 @@
#include "ExpressionParser.h"
#include "actions/expression/nodes/FunctionCallExpression.h"
#include "actions/expression/nodes/LiteralExpression.h"
#include "actions/expression/nodes/RandomRangeExpression.h"
#include "actions/expression/nodes/VariableReferenceExpression.h"
#include "actions/expression/nodes/VectorConstructorExpression.h"
#include "libs/antlr/ErrorListener.h"
PUSH_SUPPRESS_WARNINGS
#include "action_expression/generated/ActionExpressionLexer.h"
#include "action_expression/generated/ActionExpressionParser.h"
#include "action_expression/generated/ActionExpressionVisitor.h"
POP_SUPPRESS_WARNINGS
namespace actions {
namespace expression {
namespace {
class ExpressionBuilderVisitor : public ActionExpressionVisitor {
public:
antlrcpp::Any visitExpression_main(ActionExpressionParser::Expression_mainContext* context) override
{
return visit(context->expression());
}
antlrcpp::Any visitExpression(ActionExpressionParser::ExpressionContext* context) override
{
if (context->value_expression() != nullptr) {
return visit(context->value_expression());
}
if (context->parenthesis_expression()) {
return visit(context->parenthesis_expression());
}
if (context->random_range_expression()) {
return visit(context->random_range_expression());
}
if (context->variable_reference_expression()) {
return visit(context->variable_reference_expression());
}
// This is an operator so treat it as one.
antlr4::Token* operatorTok;
SCP_string operatorText;
if (context->PLUS() != nullptr) {
operatorTok = context->PLUS()->getSymbol();
operatorText = context->PLUS()->getText();
} else {
operatorTok = context->MINUS()->getSymbol();
operatorText = context->MINUS()->getText();
}
const auto leftExpr = visit(context->expression(0)).as<std::shared_ptr<nodes::AbstractExpression>>();
const auto rightExpr = visit(context->expression(1)).as<std::shared_ptr<nodes::AbstractExpression>>();
auto operatorExpression = std::make_shared<nodes::FunctionCallExpression>(operatorTok,
true,
std::move(operatorText),
SCP_vector<std::shared_ptr<nodes::AbstractExpression>>{leftExpr, rightExpr});
return std::static_pointer_cast<nodes::AbstractExpression>(operatorExpression);
}
antlrcpp::Any visitVariable_reference_expression(
ActionExpressionParser::Variable_reference_expressionContext* context) override
{
const auto identifiers = context->IDENTIFIER();
SCP_vector<SCP_string> partNames;
partNames.reserve(identifiers.size());
std::transform(identifiers.begin(),
identifiers.end(),
std::back_inserter(partNames),
[](antlr4::tree::TerminalNode* node) { return node->getText(); });
SCP_vector<antlr4::Token*> partTokens;
partTokens.reserve(identifiers.size());
std::transform(identifiers.begin(),
identifiers.end(),
std::back_inserter(partTokens),
[](antlr4::tree::TerminalNode* node) { return node->getSymbol(); });
auto expression =
std::make_shared<nodes::VariableReferenceExpression>(std::move(partNames), std::move(partTokens));
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
}
antlrcpp::Any visitRandom_range_expression(ActionExpressionParser::Random_range_expressionContext* context) override
{
auto expression = std::make_shared<nodes::RandomRangeExpression>(context->RAND_L_PAREN()->getSymbol(),
visit(context->expression(0)).as<std::shared_ptr<nodes::AbstractExpression>>(),
visit(context->expression(1)).as<std::shared_ptr<nodes::AbstractExpression>>());
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
}
antlrcpp::Any visitLiteral_expression(ActionExpressionParser::Literal_expressionContext* context) override
{
std::shared_ptr<nodes::LiteralExpression> expression;
if (context->FLOAT() != nullptr) {
expression = std::make_shared<nodes::LiteralExpression>(context->FLOAT()->getSymbol(),
Value(std::stof(context->FLOAT()->getText())));
} else if (context->INT() != nullptr) {
expression = std::make_shared<nodes::LiteralExpression>(context->INT()->getSymbol(),
Value(std::stoi(context->INT()->getText())));
} else {
const auto idText = context->STRING()->getText();
// Cut off the first and last characters since those are the < > braces
expression = std::make_shared<nodes::LiteralExpression>(context->STRING()->getSymbol(),
Value(idText.substr(1, idText.size() - 2)));
}
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
}
antlrcpp::Any visitParenthesis_expression(ActionExpressionParser::Parenthesis_expressionContext* context) override
{
return visit(context->expression());
}
antlrcpp::Any visitValue_expression(ActionExpressionParser::Value_expressionContext* context) override
{
return visitChildren(context);
}
antlrcpp::Any visitVec3d_constructor(ActionExpressionParser::Vec3d_constructorContext* context) override
{
auto xExpression = visit(context->expression(0)).as<std::shared_ptr<nodes::AbstractExpression>>();
auto yExpression = visit(context->expression(1)).as<std::shared_ptr<nodes::AbstractExpression>>();
auto zExpression = visit(context->expression(2)).as<std::shared_ptr<nodes::AbstractExpression>>();
auto expression = std::make_shared<nodes::VectorConstructorExpression>(context->L_PAREN()->getSymbol(),
xExpression,
yExpression,
zExpression);
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
}
};
} // namespace
ExpressionParser::ExpressionParser(SCP_string expressionText) : m_expressionText(std::move(expressionText)) {}
std::shared_ptr<nodes::AbstractExpression> ExpressionParser::parse(const ParseContext& context)
{
antlr4::ANTLRInputStream input(m_expressionText);
ActionExpressionLexer lexer(&input);
antlr4::CommonTokenStream tokens(&lexer);
::ActionExpressionParser parser(&tokens);
// By default we log to stderr which we do not want
parser.removeErrorListeners();
libs::antlr::ErrorListener errListener;
parser.addErrorListener(&errListener);
antlr4::tree::ParseTree* tree = parser.expression_main();
std::shared_ptr<nodes::AbstractExpression> exprReturn;
if (errListener.diagnostics.empty()) {
// Only do semantic checking if syntax is valid
ExpressionBuilderVisitor builder;
exprReturn = tree->accept(&builder).as<std::shared_ptr<nodes::AbstractExpression>>();
if (!exprReturn->validate(&parser, context)) {
exprReturn = nullptr;
} else {
exprReturn->validationDone();
}
}
if (!errListener.diagnostics.empty()) {
SCP_stringstream outStream;
for (const auto& diag : errListener.diagnostics) {
SCP_string tokenUnderline;
if (diag.tokenLength > 1) {
tokenUnderline = SCP_string(diag.tokenLength - 1, '~');
}
outStream << m_expressionText << "\n"
<< SCP_string(diag.columnInLine, ' ') << "^" << tokenUnderline << "\n"
<< diag.errorMessage << "\n";
}
m_errorText = outStream.str();
return nullptr;
}
return exprReturn;
}
const SCP_string& ExpressionParser::getErrorText() const
{
return m_errorText;
}
} // namespace expression
} // namespace actions
@@ -0,0 +1,27 @@
#pragma once
#include "globalincs/pstypes.h"
#include "ParseContext.h"
#include "actions/expression/nodes/AbstractExpression.h"
namespace actions {
namespace expression {
class ExpressionParser {
public:
ExpressionParser(SCP_string expressionText);
std::shared_ptr<nodes::AbstractExpression> parse(const ParseContext& context);
const SCP_string& getErrorText() const;
private:
SCP_string m_expressionText;
SCP_string m_errorText;
};
} // namespace expression
} // namespace actions
@@ -0,0 +1,90 @@
#include "FunctionManager.h"
#include "TypeDefinition.h"
#include "math/vecmat.h"
namespace actions {
namespace expression {
namespace {
template <typename T>
Value value_plus(const SCP_vector<Value>& parameters)
{
return Value(parameters[0].get<T>() + parameters[1].get<T>());
}
template <typename T>
Value value_minus(const SCP_vector<Value>& parameters)
{
return Value(parameters[0].get<T>() - parameters[1].get<T>());
}
} // namespace
const FunctionManager& FunctionManager::instance()
{
static const FunctionManager manager;
return manager;
}
void FunctionManager::addOperator(const SCP_string& name,
std::initializer_list<ValueType> parameterTypes,
ValueType returnType,
FunctionImplementation implementation)
{
m_operators[name].push_back({name, parameterTypes, returnType, std::move(implementation)});
}
const FunctionDefinition* FunctionManager::findOperator(const SCP_string& name,
const SCP_vector<ValueType>& parameters) const
{
const auto iter = m_operators.find(name);
if (iter == m_operators.end()) {
return nullptr;
}
// Prefer exact matches
for (const auto& funcDef : iter->second) {
if (funcDef.parameterTypes == parameters) {
return &funcDef;
}
}
// Now check if there are matches with implicit conversions
for (const auto& funcDef : iter->second) {
// Reject functions with different amount of elements
if (funcDef.parameterTypes.size() != parameters.size()) {
continue;
}
auto funcParamIter = funcDef.parameterTypes.cbegin();
auto actualParamIter = parameters.cbegin();
bool match = true;
for (; funcParamIter != funcDef.parameterTypes.cend(); ++funcParamIter, ++actualParamIter) {
if (!checkTypeWithImplicitConversion(*actualParamIter, *funcParamIter)) {
// Also doesn't match with implicit conversions
match = false;
break;
}
}
if (match) {
return &funcDef;
}
}
return nullptr;
}
FunctionManager::FunctionManager()
{
addOperator("+", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_plus<int>);
addOperator("+", {ValueType::Float, ValueType::Float}, ValueType::Float, value_plus<float>);
addOperator("+", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_plus<vec3d>);
addOperator("-", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_minus<int>);
addOperator("-", {ValueType::Float, ValueType::Float}, ValueType::Float, value_minus<float>);
addOperator("-", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_minus<vec3d>);
}
} // namespace expression
} // namespace actions
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include "Value.h"
namespace actions {
namespace expression {
using FunctionImplementation = std::function<Value(const SCP_vector<Value>& parameters)>;
struct FunctionDefinition {
SCP_string name;
SCP_vector<ValueType> parameterTypes;
ValueType returnType;
FunctionImplementation implementation;
};
class FunctionManager {
public:
// This type is supposed to be global so it may not be copied
FunctionManager(const FunctionManager&) = delete;
FunctionManager& operator=(const FunctionManager&) = delete;
FunctionManager(FunctionManager&&) noexcept = default;
FunctionManager& operator=(FunctionManager&&) noexcept = default;
void addOperator(const SCP_string& name,
std::initializer_list<ValueType> parameterTypes,
ValueType returnType,
FunctionImplementation implementation);
const FunctionDefinition* findOperator(const SCP_string& name, const SCP_vector<ValueType>& parameterTypes) const;
static const FunctionManager& instance();
private:
FunctionManager();
SCP_unordered_map<SCP_string, SCP_vector<FunctionDefinition>> m_operators;
};
} // namespace expression
} // namespace actions
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "ProgramVariables.h"
namespace actions {
namespace expression {
struct ParseContext {
ProgramVariablesDefinition variables;
};
} // namespace expression
} // namespace actions
@@ -0,0 +1,61 @@
#include "ProgramVariables.h"
namespace actions {
namespace expression {
ProgramVariablesDefinition& ProgramVariablesDefinition::addScope(const SCP_string& name)
{
auto scope = std::unique_ptr<ProgramVariablesDefinition>(new ProgramVariablesDefinition());
auto scopePtr = scope.get();
m_scopes.insert(std::make_pair(name, std::move(scope)));
return *scopePtr;
}
void ProgramVariablesDefinition::addMember(const SCP_string& name, ValueType type)
{
m_memberTypes[name] = type;
}
ProgramVariablesDefinition* ProgramVariablesDefinition::getScope(const SCP_string& name) const
{
const auto iter = m_scopes.find(name);
if (iter == m_scopes.cend()) {
return nullptr;
}
return iter->second.get();
}
ValueType ProgramVariablesDefinition::getMemberType(const SCP_string& name) const
{
const auto iter = m_memberTypes.find(name);
if (iter == m_memberTypes.cend()) {
return ValueType::Invalid;
}
return iter->second;
}
Value ProgramVariables::getValue(std::initializer_list<const char*> memberPathParts) const
{
return getValue<decltype(memberPathParts)>(memberPathParts);
}
void ProgramVariables::setValue(std::initializer_list<const char*> memberPathParts, Value value)
{
setValue<decltype(memberPathParts)>(memberPathParts, std::move(value));
}
Value ProgramVariables::getValue(const SCP_string& fullMemberName) const
{
const auto iter = m_values.find(fullMemberName);
// This should have been validated earlier
Assertion(iter != m_values.end(), "Could not find member %s in variables!", fullMemberName.c_str());
return iter->second;
}
} // namespace expression
} // namespace actions
@@ -0,0 +1,61 @@
#pragma once
#include "Value.h"
#include "utils/join_string.h"
namespace actions {
namespace expression {
struct ProgramVariables {
public:
template <typename TContainer>
static SCP_string getMemberName(const TContainer& memberPathParts)
{
return util::join_container(memberPathParts, ".");
}
template <typename TContainer>
Value getValue(const TContainer& memberPathParts) const
{
const auto name = getMemberName(memberPathParts);
return getValue(name);
}
Value getValue(std::initializer_list<const char*> memberPathParts) const;
Value getValue(const SCP_string& fullMemberName) const;
template <typename TContainer>
void setValue(const TContainer& memberPathParts, Value value)
{
const auto name = getMemberName(memberPathParts);
m_values[name] = std::move(value);
}
void setValue(std::initializer_list<const char*> memberPathParts, Value value);
private:
SCP_unordered_map<SCP_string, Value> m_values;
};
class ProgramVariablesDefinition {
public:
ProgramVariablesDefinition& addScope(const SCP_string& name);
void addMember(const SCP_string& name, ValueType type);
ProgramVariablesDefinition* getScope(const SCP_string& name) const;
ValueType getMemberType(const SCP_string& name) const;
private:
SCP_unordered_map<SCP_string, std::unique_ptr<ProgramVariablesDefinition>> m_scopes;
SCP_unordered_map<SCP_string, ValueType> m_memberTypes;
};
} // namespace expression
} // namespace actions
@@ -0,0 +1,60 @@
#include "TypeDefinition.h"
namespace actions {
namespace expression {
// Integers can be safely converted to floats (for the most part)
TypeDefinition TypeDefinition::s_integer("Integer", {ValueType::Float});
// No implicit conversions here
TypeDefinition TypeDefinition::s_float("Float", {});
TypeDefinition TypeDefinition::s_vector("Vector", {});
// Identifiers are a technicality needed for making non-math values work but they have no further use other than their
// string content
TypeDefinition TypeDefinition::s_identifier("Identifier", {});
TypeDefinition::TypeDefinition(SCP_string name, SCP_vector<actions::expression::ValueType> allowedImplicitConversion)
: m_name(std::move(name)), m_allowedImplicitConversions(std::move(allowedImplicitConversion))
{
}
const SCP_string& TypeDefinition::getName() const
{
return m_name;
}
const SCP_vector<actions::expression::ValueType>& TypeDefinition::getAllowedImplicitConversions() const
{
return m_allowedImplicitConversions;
}
const TypeDefinition& TypeDefinition::forValueType(ValueType type)
{
switch (type) {
case ValueType::Integer:
return s_integer;
case ValueType::Float:
return s_float;
case ValueType::Vector:
return s_vector;
case ValueType::String:
return s_identifier;
default:
UNREACHABLE("Invalid value type!");
return s_integer; // Make compiler happy
}
}
bool checkTypeWithImplicitConversion(ValueType currentType, ValueType expectedType)
{
if (currentType == expectedType) {
// No need to consider implicit conversions;
return true;
}
const auto& currentTypeDef = TypeDefinition::forValueType(currentType);
const auto& allowedConversions = currentTypeDef.getAllowedImplicitConversions();
// For now, no transitiv conversions are allowed
return std::find(allowedConversions.cbegin(), allowedConversions.cend(), expectedType) != allowedConversions.cend();
}
} // namespace expression
} // namespace actions
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include "Value.h"
namespace actions {
namespace expression {
class TypeDefinition {
public:
TypeDefinition(SCP_string name, SCP_vector<actions::expression::ValueType> allowedImplicitConversion);
const SCP_string& getName() const;
const SCP_vector<actions::expression::ValueType>& getAllowedImplicitConversions() const;
static const TypeDefinition& forValueType(ValueType type);
// This type is supposed to be global so it may not be copied
TypeDefinition(const TypeDefinition&) = delete;
TypeDefinition& operator=(const TypeDefinition&) = delete;
TypeDefinition(TypeDefinition&&) = delete;
TypeDefinition& operator=(TypeDefinition&&) = delete;
private:
SCP_string m_name;
// The types this type is allowed to implicitly convert to
SCP_vector<ValueType> m_allowedImplicitConversions;
static TypeDefinition s_integer;
static TypeDefinition s_float;
static TypeDefinition s_vector;
static TypeDefinition s_identifier;
};
bool checkTypeWithImplicitConversion(ValueType currentType, ValueType expectedType);
} // namespace expression
} // namespace actions
+80
View File
@@ -0,0 +1,80 @@
#include "Value.h"
#include "math/vecmat.h"
namespace actions {
namespace expression {
Value::Value(int val) : m_value(val) {}
Value::Value(float val) : m_value(val) {}
Value::Value(const vec3d& val) : m_value(val) {}
Value::Value(SCP_string val) : m_value(std::move(val)) {}
ValueType actions::expression::Value::getType() const
{
switch (m_value.index()) {
case 1:
return ValueType::Integer;
case 2:
return ValueType::Float;
case 3:
return ValueType::Vector;
case 4:
return ValueType::String;
default:
return ValueType::Invalid;
}
}
int Value::getInteger() const
{
return get<int>();
}
float Value::getFloat() const
{
return get<float>();
}
vec3d Value::getVector() const
{
return get<vec3d>();
}
SCP_string Value::getIdentifier() const
{
return get<SCP_string>();
}
bool operator==(const Value& lhs, const Value& rhs)
{
return lhs.m_value == rhs.m_value;
}
bool operator!=(const Value& lhs, const Value& rhs)
{
return !(rhs == lhs);
}
std::ostream& operator<<(std::ostream& os, const Value& value)
{
os << "Value<";
switch (value.getType()) {
case ValueType::Invalid:
os << "INVALID";
break;
case ValueType::Integer:
os << value.get<int>();
break;
case ValueType::Float:
os << value.get<float>();
break;
case ValueType::Vector:
os << value.get<vec3d>();
break;
case ValueType::String:
os << value.get<SCP_string>();
break;
}
os << ">";
return os;
}
} // namespace expression
} // namespace actions
+104
View File
@@ -0,0 +1,104 @@
#pragma once
#include "globalincs/pstypes.h"
#include <mpark/variant.hpp>
namespace actions {
namespace expression {
namespace detail {
template <typename ToType>
struct implicit_conversion {
template <typename T>
ToType operator()(const T&)
{
throw std::runtime_error("Invalid implicit conversion.");
}
};
template<>
struct implicit_conversion<float> {
inline float operator()(int val) {
return i2fl(val);
}
template <typename T>
float operator()(const T&)
{
throw std::runtime_error("Invalid implicit conversion.");
}
};
} // namespace detail
enum class ValueType
{
Invalid,
Integer,
Float,
Vector,
String,
};
template <typename T>
struct ValueTypeTraits;
template <>
struct ValueTypeTraits<int> {
static constexpr ValueType type = ValueType::Integer;
};
template <>
struct ValueTypeTraits<float> {
static constexpr ValueType type = ValueType::Float;
};
template <>
struct ValueTypeTraits<vec3d> {
static constexpr ValueType type = ValueType::Vector;
};
template <>
struct ValueTypeTraits<SCP_string> {
static constexpr ValueType type = ValueType::String;
};
class Value {
public:
Value() = default;
explicit Value(int val);
explicit Value(float val);
explicit Value(const vec3d& val);
explicit Value(SCP_string val);
ValueType getType() const;
int getInteger() const;
float getFloat() const;
vec3d getVector() const;
SCP_string getIdentifier() const;
template <typename T>
T get() const
{
const auto currentType = getType();
const auto expectedType = ValueTypeTraits<T>::type;
if (currentType == expectedType) {
// No conversion necessary
return mpark::get<T>(m_value);
}
return mpark::visit(detail::implicit_conversion<T>{}, m_value);
}
friend bool operator==(const Value& lhs, const Value& rhs);
friend bool operator!=(const Value& lhs, const Value& rhs);
friend std::ostream& operator<<(std::ostream& os, const Value& value);
private:
mpark::variant<mpark::monostate, int, float, vec3d, SCP_string> m_value;
};
} // namespace expression
} // namespace actions
@@ -0,0 +1,31 @@
#include "AbstractExpression.h"
namespace actions {
namespace expression {
namespace nodes {
AbstractExpression::AbstractExpression(antlr4::Token* token) : m_token(token) {}
AbstractExpression::~AbstractExpression() = default;
antlr4::Token* AbstractExpression::getToken() const
{
return m_token;
}
ValueType AbstractExpression::getExpressionType() const
{
// Since expressions are immutable, caching this will be more efficient
if (m_cachedType == ValueType::Invalid) {
m_cachedType = determineReturnType();
}
return m_cachedType;
}
void AbstractExpression::validationDone()
{
// This pointer will become invalid after this so reset it now to avoid issues
m_token = nullptr;
}
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,38 @@
#pragma once
#include "actions/expression/ParseContext.h"
#include "actions/expression/Value.h"
#include <Parser.h>
#include <Token.h>
namespace actions {
namespace expression {
namespace nodes {
class AbstractExpression {
public:
AbstractExpression(antlr4::Token* token);
virtual ~AbstractExpression();
virtual Value execute(const ProgramVariables& variables) const = 0;
virtual bool validate(antlr4::Parser* parser, const ParseContext& context) = 0;
virtual void validationDone();
ValueType getExpressionType() const;
antlr4::Token* getToken() const;
protected:
virtual ValueType determineReturnType() const = 0;
antlr4::Token* m_token = nullptr;
mutable ValueType m_cachedType = ValueType::Invalid;
};
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,95 @@
#include "FunctionCallExpression.h"
#include "actions/expression/TypeDefinition.h"
namespace actions {
namespace expression {
namespace nodes {
FunctionCallExpression::FunctionCallExpression(antlr4::Token* token,
bool isOperator,
SCP_string functionName,
SCP_vector<std::shared_ptr<AbstractExpression>> parameterExpressions)
: AbstractExpression(token), m_isOperator(isOperator), m_functionName(std::move(functionName)),
m_parameterExpressions(std::move(parameterExpressions))
{
}
FunctionCallExpression::~FunctionCallExpression() = default;
Value FunctionCallExpression::execute(const ProgramVariables& variables) const
{
SCP_vector<Value> parameterValues;
parameterValues.reserve(m_parameterExpressions.size());
std::transform(m_parameterExpressions.cbegin(),
m_parameterExpressions.cend(),
std::back_inserter(parameterValues),
[&variables](const std::shared_ptr<AbstractExpression>& expression) { return expression->execute(variables); });
return m_functionDef->implementation(parameterValues);
}
bool FunctionCallExpression::validate(antlr4::Parser* parser, const ParseContext& context)
{
bool valid = true;
for (const auto& paramExpression : m_parameterExpressions) {
valid &= paramExpression->validate(parser, context);
}
if (!valid) {
// Exit early if there were errors since the values below are meaningless otherwise
return false;
}
Assertion(m_isOperator, "Non-operator functions are not supported yet!");
SCP_vector<ValueType> parameterTypes;
std::transform(m_parameterExpressions.cbegin(),
m_parameterExpressions.cend(),
std::back_inserter(parameterTypes),
[](const std::shared_ptr<AbstractExpression>& expression) { return expression->getExpressionType(); });
const auto functionDef = FunctionManager::instance().findOperator(m_functionName, parameterTypes);
if (functionDef == nullptr) {
SCP_stringstream paramListStream;
if (!parameterTypes.empty()) {
paramListStream << "<" << TypeDefinition::forValueType(parameterTypes.front()).getName() << ">";
for (auto iter = parameterTypes.cbegin() + 1; iter != parameterTypes.cend(); ++iter) {
paramListStream << ", <" << TypeDefinition::forValueType(*iter).getName() << ">";
}
}
parser->notifyErrorListeners(m_token,
SCP_string("Could not find a definition for the ") + (m_isOperator ? "operator" : "function") + " '" +
m_functionName + "' with parameters (" + paramListStream.str() + ")",
nullptr);
return false;
}
m_functionDef = functionDef;
return true;
}
ValueType FunctionCallExpression::determineReturnType() const
{
return m_functionDef->returnType;
}
void FunctionCallExpression::validationDone()
{
AbstractExpression::validationDone();
// We no longer need this so we can save some memory
m_functionName.clear();
m_functionName.shrink_to_fit();
for (const auto& paramExpression : m_parameterExpressions) {
paramExpression->validationDone();
}
}
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,38 @@
#pragma once
#include "AbstractExpression.h"
#include "actions/expression/FunctionManager.h"
#include "actions/expression/Value.h"
namespace actions {
namespace expression {
namespace nodes {
class FunctionCallExpression : public AbstractExpression {
public:
FunctionCallExpression(antlr4::Token* token,
bool isOperator,
SCP_string functionName,
SCP_vector<std::shared_ptr<AbstractExpression>> parameterExpressions);
~FunctionCallExpression() override;
Value execute(const ProgramVariables& variables) const override;
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
void validationDone() override;
protected:
ValueType determineReturnType() const override;
private:
bool m_isOperator;
SCP_string m_functionName;
const FunctionDefinition* m_functionDef = nullptr;
SCP_vector<std::shared_ptr<AbstractExpression>> m_parameterExpressions;
};
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,30 @@
#include "LiteralExpression.h"
namespace actions {
namespace expression {
namespace nodes {
LiteralExpression::LiteralExpression(antlr4::Token* token, Value literalValue)
: AbstractExpression(token), m_literalValue(std::move(literalValue))
{
}
LiteralExpression::~LiteralExpression() = default;
Value LiteralExpression::execute(const ProgramVariables& /*variables*/) const
{
return m_literalValue;
}
bool LiteralExpression::validate(antlr4::Parser* /*parser*/, const ParseContext& /*context*/)
{
// Literals are always valid
return true;
}
ValueType LiteralExpression::determineReturnType() const
{
return m_literalValue.getType();
}
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,28 @@
#pragma once
#include "AbstractExpression.h"
#include "actions/expression/Value.h"
namespace actions {
namespace expression {
namespace nodes {
class LiteralExpression : public AbstractExpression {
public:
LiteralExpression(antlr4::Token* token, Value literalValue);
~LiteralExpression() override;
Value execute(const ProgramVariables& variables) const override;
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
protected:
ValueType determineReturnType() const override;
private:
Value m_literalValue;
};
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,108 @@
#include "RandomRangeExpression.h"
#include "actions/expression/TypeDefinition.h"
#include "utils/RandomRange.h"
namespace actions {
namespace expression {
namespace nodes {
namespace {
template <typename T>
Value executeHelper(const Value& leftVal, const Value& rightVal)
{
auto leftContentVal = leftVal.get<T>();
auto rightContentVal = rightVal.get<T>();
if (leftContentVal == rightContentVal) {
// no need to do random stuff here
return Value(rightContentVal);
}
if (rightContentVal < leftContentVal) {
// We can fix this issue
std::swap(leftContentVal, rightContentVal);
}
// Might be a bit inefficient not to cache this somehow but the bounds can be dynamic
return Value(util::UniformRange<T>(leftContentVal, rightContentVal).next());
}
} // namespace
RandomRangeExpression::RandomRangeExpression(antlr4::Token* token,
std::shared_ptr<AbstractExpression> leftExpression,
std::shared_ptr<AbstractExpression> rightExpression)
: AbstractExpression(token), m_leftExpression(std::move(leftExpression)),
m_rightExpression(std::move(rightExpression))
{
}
RandomRangeExpression::~RandomRangeExpression() = default;
Value RandomRangeExpression::execute(const ProgramVariables& variables) const
{
const auto leftVal = m_leftExpression->execute(variables);
const auto rightVal = m_rightExpression->execute(variables);
if (leftVal.getType() == ValueType::Integer) {
return executeHelper<int>(leftVal, rightVal);
} else {
return executeHelper<float>(leftVal, rightVal);
}
}
bool RandomRangeExpression::validate(antlr4::Parser* parser, const ParseContext& context)
{
bool valid = true;
valid &= m_leftExpression->validate(parser, context);
valid &= m_rightExpression->validate(parser, context);
if (!valid) {
// Exit early if there were errors since the values below are meaningless otherwise
return false;
}
const auto leftType = m_leftExpression->getExpressionType();
const auto rightType = m_rightExpression->getExpressionType();
const auto& leftTypedef = TypeDefinition::forValueType(leftType);
const auto& rightTypedef = TypeDefinition::forValueType(rightType);
// Both values must be the same
if (leftType != rightType) {
parser->notifyErrorListeners(m_rightExpression->getToken(),
"Inconsistent types for random range. Must be the same but got <" + leftTypedef.getName() + "> and <" +
rightTypedef.getName() + ">.",
nullptr);
return false;
}
// And we only support floats or integers
if (leftType != ValueType::Float && leftType != ValueType::Integer) {
parser->notifyErrorListeners(m_leftExpression->getToken(),
"Invalid parameter type for random range. Must be <Float> or <Integer> but got <" + leftTypedef.getName() +
">.",
nullptr);
return false;
}
// Everything is fine.
return true;
}
ValueType RandomRangeExpression::determineReturnType() const
{
// Return type is the same as the contained return types
return m_leftExpression->getExpressionType();
}
void RandomRangeExpression::validationDone()
{
AbstractExpression::validationDone();
m_leftExpression->validationDone();
m_rightExpression->validationDone();
}
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,30 @@
#pragma once
#include "AbstractExpression.h"
namespace actions {
namespace expression {
namespace nodes {
class RandomRangeExpression : public AbstractExpression {
public:
RandomRangeExpression(antlr4::Token* token,
std::shared_ptr<AbstractExpression> leftExpression,
std::shared_ptr<AbstractExpression> rightExpression);
~RandomRangeExpression() override;
Value execute(const ProgramVariables& variables) const override;
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
void validationDone() override;
protected:
ValueType determineReturnType() const override;
private:
std::shared_ptr<AbstractExpression> m_leftExpression;
std::shared_ptr<AbstractExpression> m_rightExpression;
};
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,69 @@
#include "VariableReferenceExpression.h"
namespace actions {
namespace expression {
namespace nodes {
VariableReferenceExpression::VariableReferenceExpression(SCP_vector<SCP_string> references,
SCP_vector<antlr4::Token*> referenceTokens)
: AbstractExpression(referenceTokens.back()), m_references(std::move(references)),
m_referenceTokens(std::move(referenceTokens))
{
}
VariableReferenceExpression::~VariableReferenceExpression() = default;
Value VariableReferenceExpression::execute(const ProgramVariables& variables) const
{
return variables.getValue(m_fullVariablePath);
}
bool VariableReferenceExpression::validate(antlr4::Parser* parser, const ParseContext& context)
{
// Start out in the global scope
auto currentScope = &context.variables;
SCP_string currentScopeName("<global>");
for (size_t i = 0; i < m_references.size() - 1; ++i) {
const auto nextScope = currentScope->getScope(m_references[i]);
if (nextScope == nullptr) {
parser->notifyErrorListeners(m_referenceTokens[i],
"Could not find scope " + m_references[i] + " in scope " + currentScopeName + ".",
nullptr);
return false;
}
currentScope = nextScope;
currentScopeName = m_references[i];
}
auto variableType = currentScope->getMemberType(m_references.back());
if (variableType == ValueType::Invalid) {
parser->notifyErrorListeners(m_referenceTokens.back(),
"Could not find variable " + m_references.back() + " in scope " + currentScopeName + ".",
nullptr);
return false;
}
// Cache this so we don't duplicate all this code in determineReturnType()
m_variableType = variableType;
m_fullVariablePath = ProgramVariables::getMemberName(m_references);
return true;
}
ValueType VariableReferenceExpression::determineReturnType() const
{
return m_variableType;
}
void VariableReferenceExpression::validationDone()
{
AbstractExpression::validationDone();
// Clear these since they are not necessary anymore
m_references.clear();
m_referenceTokens.clear();
}
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,32 @@
#pragma once
#include "AbstractExpression.h"
namespace actions {
namespace expression {
namespace nodes {
class VariableReferenceExpression : public AbstractExpression {
public:
VariableReferenceExpression(const SCP_vector<SCP_string> references,
SCP_vector<antlr4::Token*> referenceTokens);
~VariableReferenceExpression() override;
Value execute(const ProgramVariables& variables) const override;
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
void validationDone() override;
protected:
ValueType determineReturnType() const override;
private:
ValueType m_variableType = ValueType::Invalid;
SCP_string m_fullVariablePath;
SCP_vector<SCP_string> m_references;
SCP_vector<antlr4::Token*> m_referenceTokens;
};
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,87 @@
#include "VectorConstructorExpression.h"
#include "actions/expression/TypeDefinition.h"
namespace actions {
namespace expression {
namespace nodes {
VectorConstructorExpression::VectorConstructorExpression(antlr4::Token* token,
std::shared_ptr<AbstractExpression> xExpression,
std::shared_ptr<AbstractExpression> yExpression,
std::shared_ptr<AbstractExpression> zExpression)
: AbstractExpression(token), m_xExpression(std::move(xExpression)), m_yExpression(std::move(yExpression)),
m_zExpression(std::move(zExpression))
{
}
VectorConstructorExpression::~VectorConstructorExpression() = default;
Value VectorConstructorExpression::execute(const ProgramVariables& variables) const
{
auto xValue = m_xExpression->execute(variables);
auto yValue = m_yExpression->execute(variables);
auto zValue = m_zExpression->execute(variables);
vec3d vecVal;
vecVal.xyz.x = xValue.getFloat();
vecVal.xyz.y = yValue.getFloat();
vecVal.xyz.z = zValue.getFloat();
return Value(vecVal);
}
bool VectorConstructorExpression::validate(antlr4::Parser* parser, const ParseContext& context)
{
bool valid = true;
valid &= m_xExpression->validate(parser, context);
valid &= m_yExpression->validate(parser, context);
valid &= m_zExpression->validate(parser, context);
if (!valid) {
// Exit early if there were errors since the values below are meaningless otherwise
return false;
}
if (!checkTypeWithImplicitConversion(m_xExpression->getExpressionType(), ValueType::Float)) {
const auto& typeDef = TypeDefinition::forValueType(m_xExpression->getExpressionType());
parser->notifyErrorListeners(m_xExpression->getToken(),
"Invalid value type for vector constructor. Expected <Float>, got <" + typeDef.getName() + ">",
nullptr);
valid = false;
}
if (!checkTypeWithImplicitConversion(m_yExpression->getExpressionType(), ValueType::Float)) {
const auto& typeDef = TypeDefinition::forValueType(m_yExpression->getExpressionType());
parser->notifyErrorListeners(m_yExpression->getToken(),
"Invalid value type for vector constructor. Expected <Float>, got <" + typeDef.getName() + ">",
nullptr);
valid = false;
}
if (!checkTypeWithImplicitConversion(m_zExpression->getExpressionType(), ValueType::Float)) {
const auto& typeDef = TypeDefinition::forValueType(m_zExpression->getExpressionType());
parser->notifyErrorListeners(m_zExpression->getToken(),
"Invalid value type for vector constructor. Expected <Float>, got <" + typeDef.getName() + ">",
nullptr);
valid = false;
}
return valid;
}
ValueType VectorConstructorExpression::determineReturnType() const
{
// No need to look at the expressions here since we will always be a vector
return ValueType::Vector;
}
void VectorConstructorExpression::validationDone()
{
AbstractExpression::validationDone();
m_xExpression->validationDone();
m_yExpression->validationDone();
m_zExpression->validationDone();
}
} // namespace nodes
} // namespace expression
} // namespace actions
@@ -0,0 +1,34 @@
#pragma once
#include "AbstractExpression.h"
#include "actions/expression/Value.h"
namespace actions {
namespace expression {
namespace nodes {
class VectorConstructorExpression : public AbstractExpression {
public:
VectorConstructorExpression(antlr4::Token* token,
std::shared_ptr<AbstractExpression> xExpression,
std::shared_ptr<AbstractExpression> yExpression,
std::shared_ptr<AbstractExpression> zExpression);
~VectorConstructorExpression() override;
Value execute(const ProgramVariables& variables) const override;
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
void validationDone() override;
protected:
ValueType determineReturnType() const override;
private:
std::shared_ptr<AbstractExpression> m_xExpression;
std::shared_ptr<AbstractExpression> m_yExpression;
std::shared_ptr<AbstractExpression> m_zExpression;
};
} // namespace nodes
} // namespace expression
} // namespace actions
+79
View File
@@ -0,0 +1,79 @@
#include "MoveToSubmodel.h"
#include "math/vecmat.h"
#include "parse/parselo.h"
#include "ship/ship.h"
#include <utility>
namespace actions {
namespace types {
flagset<ProgramContextFlags> MoveToSubmodel::getRequiredExecutionContextFlags()
{
return flagset<ProgramContextFlags>{ProgramContextFlags::HasObject, ProgramContextFlags::HasSubobject};
}
MoveToSubmodel::MoveToSubmodel(expression::TypedActionExpression<ValueType> subObjectExpression)
: m_subObjectExpression(std::move(subObjectExpression))
{
}
MoveToSubmodel::~MoveToSubmodel() = default;
ActionResult MoveToSubmodel::execute(ProgramLocals& locals) const
{
// The calling code should ensure that this never happens
Assertion(locals.hostSubobject >= 0, "Did not have a valid host subobject.");
auto instance = object_get_model_instance(locals.host.objp);
Assertion(instance != -1, "Model instances are required if a host subobject is specified.");
auto pmi = model_get_instance(instance);
auto pm = model_get(pmi->model_num);
const auto destinationSubObject = m_subObjectExpression.execute(locals.variables);
bool goToParent = false;
if (destinationSubObject == "<parent>") {
goToParent = true;
}
if (goToParent) {
// Special case when we simply want to go to our parent without explicitly specifying its name
auto parentSubobject = pm->submodel[locals.hostSubobject].parent;
if (parentSubobject < 0) {
Warning(LOCATION,
"Current program subobject %s on model %s has no parent!",
pm->submodel[locals.hostSubobject].name,
pm->filename);
return ActionResult::Errored;
}
// We have a valid parent so this move will work properly
locals.hostSubobject = parentSubobject;
return ActionResult::Finished;
}
// We need to do a linear search for the right subobject
for (int i = 0; i < pm->n_models; ++i) {
const auto submodel = pm->submodel[i];
if (subsystem_stricmp(destinationSubObject.c_str(), submodel.name) == 0) {
// Found something!
locals.hostSubobject = i;
return ActionResult::Finished;
}
}
Warning(LOCATION, "Could not find subobject %s in model %s!", destinationSubObject.c_str(), pm->filename);
return ActionResult::Errored;
}
std::unique_ptr<Action> MoveToSubmodel::clone() const
{
return std::unique_ptr<Action>(new MoveToSubmodel(*this));
}
} // namespace types
} // namespace actions
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "globalincs/flagset.h"
#include "actions/Action.h"
#include "actions/expression/ActionExpression.h"
namespace actions {
namespace types {
class MoveToSubmodel : public Action {
public:
using ValueType = SCP_string;
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
explicit MoveToSubmodel(expression::TypedActionExpression<ValueType> subObjectExpression);
~MoveToSubmodel() override;
ActionResult execute(ProgramLocals& locals) const override;
std::unique_ptr<Action> clone() const override;
private:
expression::TypedActionExpression<ValueType> m_subObjectExpression;
};
} // namespace types
} // namespace actions
@@ -0,0 +1,76 @@
#include "ParticleEffectAction.h"
#include "math/vecmat.h"
#include "parse/parselo.h"
#include "ship/ship.h"
#include <utility>
namespace actions {
namespace types {
flagset<ProgramContextFlags> ParticleEffectAction::getRequiredExecutionContextFlags()
{
return flagset<ProgramContextFlags>{ProgramContextFlags::HasObject};
}
ParticleEffectAction::ParticleEffectAction(expression::TypedActionExpression<ValueType> effectExpression)
: m_effectExpression(std::move(effectExpression))
{
}
ParticleEffectAction::~ParticleEffectAction() = default;
ActionResult ParticleEffectAction::execute(ProgramLocals& locals) const
{
auto effectIdx = particle::ParticleManager::get()->getEffectByName(m_effectExpression.execute(locals.variables));
if (!effectIdx.isValid()) {
// In case the parsing code failed
return ActionResult::Errored;
}
using namespace particle;
auto source = ParticleManager::get()->createSource(effectIdx);
vec3d local_pos;
matrix local_orient;
if (locals.hostSubobject != -1) {
auto instance = object_get_model_instance(locals.host.objp);
Assertion(instance != -1, "Model instances are required if a host subobject is specified.");
auto pmi = model_get_instance(instance);
auto pm = model_get(pmi->model_num);
find_submodel_instance_point_orient(&local_pos,
&local_orient,
pm,
pmi,
locals.hostSubobject,
&locals.localPosition,
&locals.localOrient);
} else {
local_pos = locals.localPosition;
local_orient = locals.localOrient;
}
local_pos += locals.variables.getValue({"locals", "position"}).getVector();
auto direction = locals.variables.getValue({"locals", "direction"}).getVector();
source.moveToObject(locals.host.objp, &local_pos);
source.setOrientationFromNormalizedVec(&direction, true);
source.finish();
return ActionResult::Finished;
}
std::unique_ptr<Action> ParticleEffectAction::clone() const
{
return std::unique_ptr<Action>(new ParticleEffectAction(*this));
}
} // namespace types
} // namespace actions
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "actions/Action.h"
#include "actions/expression/ActionExpression.h"
#include "particle/ParticleManager.h"
namespace actions {
namespace types {
class ParticleEffectAction : public Action {
public:
using ValueType = SCP_string;
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
explicit ParticleEffectAction(expression::TypedActionExpression<ValueType> effectExpression);
~ParticleEffectAction() override;
ActionResult execute(ProgramLocals& locals) const override;
std::unique_ptr<Action> clone() const override;
private:
expression::TypedActionExpression<ValueType> m_effectExpression;
};
} // namespace types
} // namespace actions
+65
View File
@@ -0,0 +1,65 @@
//
//
#include "PlaySoundAction.h"
#include "ship/ship.h"
#include <utility>
namespace actions {
namespace types {
flagset<ProgramContextFlags> PlaySoundAction::getRequiredExecutionContextFlags()
{
return flagset<ProgramContextFlags>{ProgramContextFlags::HasObject};
}
PlaySoundAction::PlaySoundAction(expression::TypedActionExpression<ValueType> soundIdExpression)
: m_soundIdExpression(std::move(soundIdExpression))
{
}
PlaySoundAction::~PlaySoundAction() = default;
ActionResult PlaySoundAction::execute(ProgramLocals& locals) const
{
vec3d local_pos;
matrix local_orient;
if (locals.hostSubobject != -1) {
auto instance = object_get_model_instance(locals.host.objp);
Assertion(instance != -1, "Model instances are required if a host subobject is specified.");
auto pmi = model_get_instance(instance);
auto pm = model_get(pmi->model_num);
find_submodel_instance_point_orient(&local_pos,
&local_orient,
pm,
pmi,
locals.hostSubobject,
&locals.localPosition,
&locals.localOrient);
} else {
local_pos = locals.localPosition;
local_orient = locals.localOrient;
}
local_pos += locals.variables.getValue({"locals", "position"}).getVector();
vec3d global_pos;
vm_vec_unrotate(&global_pos, &local_pos, &locals.host.objp->orient);
global_pos += locals.host.objp->pos;
const auto soundId = gamesnd_get_by_name(m_soundIdExpression.execute(locals.variables).c_str());
// Sound is not attached to the host but the object sound system currently only supports persistent sounds
snd_play_3d(gamesnd_get_game_sound(soundId), &global_pos, &Eye_position);
return ActionResult::Finished;
}
std::unique_ptr<Action> PlaySoundAction::clone() const
{
return std::unique_ptr<Action>(new PlaySoundAction(*this));
}
} // namespace types
} // namespace actions
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "actions/Action.h"
#include "actions/expression/ActionExpression.h"
#include "gamesnd/gamesnd.h"
namespace actions {
namespace types {
class PlaySoundAction : public Action {
public:
using ValueType = SCP_string;
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
explicit PlaySoundAction(expression::TypedActionExpression<ValueType> soundIdExpression);
~PlaySoundAction() override;
ActionResult execute(ProgramLocals& locals) const override;
std::unique_ptr<Action> clone() const override;
private:
expression::TypedActionExpression<ValueType> m_soundIdExpression;
};
} // namespace types
} // namespace actions
+39
View File
@@ -0,0 +1,39 @@
#include "SetDirectionAction.h"
#include "math/vecmat.h"
#include "parse/parselo.h"
#include <utility>
namespace actions {
namespace types {
flagset<ProgramContextFlags> SetDirectionAction::getRequiredExecutionContextFlags()
{
return flagset<ProgramContextFlags>{};
}
SetDirectionAction::SetDirectionAction(expression::TypedActionExpression<vec3d> newDirExpression)
: m_newDirExpression(std::move(newDirExpression))
{
}
SetDirectionAction::~SetDirectionAction() = default;
ActionResult SetDirectionAction::execute(ProgramLocals& locals) const
{
auto dir = m_newDirExpression.execute(locals.variables);
vm_vec_normalize_safe(&dir);
locals.variables.setValue({"locals", "direction"}, expression::Value(dir));
return ActionResult::Finished;
}
std::unique_ptr<Action> SetDirectionAction::clone() const
{
return std::unique_ptr<Action>(new SetDirectionAction(*this));
}
} // namespace types
} // namespace actions
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "actions/Action.h"
#include "actions/expression/ActionExpression.h"
namespace actions {
namespace types {
class SetDirectionAction : public Action {
public:
using ValueType = vec3d;
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
SetDirectionAction(expression::TypedActionExpression<ValueType> newDirExpression);
~SetDirectionAction() override;
ActionResult execute(ProgramLocals& locals) const override;
std::unique_ptr<Action> clone() const override;
private:
expression::TypedActionExpression<ValueType> m_newDirExpression;
};
} // namespace types
} // namespace actions
+34
View File
@@ -0,0 +1,34 @@
#include "SetPositionAction.h"
#include "parse/parselo.h"
#include <utility>
namespace actions {
namespace types {
flagset<ProgramContextFlags> SetPositionAction::getRequiredExecutionContextFlags()
{
return flagset<ProgramContextFlags>{};
}
SetPositionAction::SetPositionAction(expression::TypedActionExpression<ValueType> newPositionExpression)
: m_newPosExpression(std::move(newPositionExpression))
{
}
SetPositionAction::~SetPositionAction() = default;
ActionResult SetPositionAction::execute(ProgramLocals& locals) const
{
locals.variables.setValue({"locals", "position"}, expression::Value(m_newPosExpression.execute(locals.variables)));
return ActionResult::Finished;
}
std::unique_ptr<Action> SetPositionAction::clone() const
{
return std::unique_ptr<Action>(new SetPositionAction(*this));
}
} // namespace types
} // namespace actions
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "actions/Action.h"
#include "actions/expression/ActionExpression.h"
namespace actions {
namespace types {
class SetPositionAction : public Action {
public:
using ValueType = vec3d;
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
SetPositionAction(expression::TypedActionExpression<ValueType> newPositionExpression);
~SetPositionAction() override;
ActionResult execute(ProgramLocals& locals) const override;
std::unique_ptr<Action> clone() const override;
private:
expression::TypedActionExpression<vec3d> m_newPosExpression;
};
} // namespace types
} // namespace actions
+48
View File
@@ -0,0 +1,48 @@
#include "WaitAction.h"
#include "io/timer.h"
#include "parse/parselo.h"
namespace actions {
namespace types {
flagset<ProgramContextFlags> WaitAction::getRequiredExecutionContextFlags()
{
return flagset<ProgramContextFlags>{};
}
WaitAction::WaitAction(expression::TypedActionExpression<float> waitTimeExpression)
: _waitTimeExpression(std::move(waitTimeExpression))
{
}
WaitAction::~WaitAction() = default;
ActionResult WaitAction::execute(actions::ProgramLocals& locals) const
{
if (!timestamp_valid(locals.waitTimestamp)) {
auto waitTime = _waitTimeExpression.execute(locals.variables);
// Catch errors in the expression
if (waitTime < 0.001f) {
waitTime = 0.001f;
}
locals.waitTimestamp = timestamp(fl2i(waitTime * TIMESTAMP_FREQUENCY));
}
if (!timestamp_elapsed(locals.waitTimestamp)) {
// Wait until the timestamp is elapsed
return ActionResult::Wait;
}
// Timestamp is elapsed. The timestamp is elapsed so that timestamp_valid above returns false for the next wait
// action
locals.waitTimestamp = 0;
return ActionResult::Finished;
}
std::unique_ptr<Action> WaitAction::clone() const
{
return std::unique_ptr<Action>(new WaitAction(*this));
}
} // namespace types
} // namespace actions
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "actions/Action.h"
#include "actions/common.h"
#include "actions/expression/ActionExpression.h"
#include "utils/RandomRange.h"
namespace actions {
namespace types {
class WaitAction : public Action {
public:
using ValueType = float;
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
explicit WaitAction(expression::TypedActionExpression<ValueType> waitTimeExpression);
~WaitAction() override;
ActionResult execute(ProgramLocals& locals) const override;
std::unique_ptr<Action> clone() const override;
private:
expression::TypedActionExpression<ValueType> _waitTimeExpression;
};
} // namespace types
} // namespace actions
+4 -1
View File
@@ -491,6 +491,9 @@ typedef struct ai_info {
int ai_override_rot_timestamp; // mark for when to end the current rotational maneuver override
int form_obj_slotnum; // for flying in formation object mode, the position in the formation
int multilock_check_timestamp; // when to check for multilock next
SCP_vector<std::pair<int, ship_subsys*>> ai_missile_locks_firing; // a list of missile locks (locked objnum, locked subsys) the ai is currently firing
} ai_info;
// Goober5000
@@ -561,7 +564,7 @@ const char *ai_get_goal_target_name(const char *name, int *index);
void ai_clear_goal_target_names();
extern void init_ai_system(void);
extern void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp, int ship_info_index = -1);
extern void ai_attack_object(object *attacker, object *attacked, int ship_info_index = -1);
extern void ai_evade_object(object *evader, object *evaded);
extern void ai_ignore_object(object *ignorer, object *ignored, int ignore_new);
extern void ai_ignore_wing(object *ignorer, int wingnum);
+9 -2
View File
@@ -5,7 +5,7 @@
namespace AI {
FLAG_LIST(AI_Flags) {
Formation_wing, // Fly in formation as part of wing.
Formation_wing, // Fly in formation as part of wing. Also used when flying waypoints.
Awaiting_repair, // Awaiting a repair ship.
Being_repaired, // Currently docked with repair ship.
Repairing, // Repairing a ship (or going to repair a ship)
@@ -130,9 +130,16 @@ namespace AI {
Use_only_single_fov_for_turrets,
No_turning_directional_bias,
Use_axial_turnrate_differences,
nonshielded_ships_can_manage_ets,
all_nonshielded_ships_can_manage_ets,
fightercraft_nonshielded_ships_can_manage_ets,
Better_collision_avoidance,
Require_exact_los,
Improved_missile_avoidance,
Friendlies_use_countermeasure_firechance,
Improved_subsystem_attack_pathing,
Fixed_ship_weapon_collision,
No_shield_damage_from_ship_collisions,
Reset_last_hit_target_time_for_player_hits,
NUM_VALUES
};
+69 -9
View File
@@ -58,6 +58,26 @@ int ai_path_type_match(char *p)
return -1;
}
const char* AI_secondary_range_aware_select_modes[] = {
"retail",
"aware",
};
int Num_ai_secondary_range_aware_select_modes = sizeof(AI_secondary_range_aware_select_modes) / sizeof(char*);
int ai_secondary_range_select_mode_match(char* p)
{
int i;
for (i = 0; i < Num_ai_secondary_range_aware_select_modes; i++)
{
if (!stricmp(AI_secondary_range_aware_select_modes[i], p))
return i;
}
return -1;
}
void parse_ai_profiles_tbl(const char *filename)
{
int i;
@@ -448,7 +468,6 @@ void parse_ai_profiles_tbl(const char *filename)
set_flag(profile, "$firing requires exact los:", AI::Profile_Flags::Require_exact_los);
profile->ai_path_mode = AI_PATH_MODE_NORMAL;
if (optional_string("$ai path mode:"))
{
stuff_string(buf, F_NAME, NAME_LENGTH);
@@ -481,14 +500,12 @@ void parse_ai_profiles_tbl(const char *filename)
if (path_radii >= Minimum_subsystem_path_pt_dist) {
profile->subsystem_path_radii = path_radii;
} else {
mprintf(("Warning: \"$override radius for subsystem path points:\" should be >= %i (read %i). Value will not be used. ", Minimum_subsystem_path_pt_dist, path_radii));
mprintf(("Warning: \"$override radius for subsystem path points:\" should be >= %i (read %i). Value will not be used.\n", Minimum_subsystem_path_pt_dist, path_radii));
}
}
set_flag(profile, "$use POF radius for subsystem path points:", AI::Profile_Flags::Use_subsystem_path_point_radii);
profile->bay_arrive_speed_mult = 1.0f;
profile->bay_depart_speed_mult = 1.0f;
if (optional_string("$bay arrive speed multiplier:")) {
stuff_float(&profile->bay_arrive_speed_mult);
}
@@ -520,15 +537,50 @@ void parse_ai_profiles_tbl(const char *filename)
profile->second_order_lead_predict_factor = 0;
}
}
if (optional_string("$ships with no shields can manage ETS:"))
{
mprintf(("Warning: \"$ships with no shields can manage ETS\" flag is deprecated in favor of \"$any ship with no shields can manage ETS\"\n"));
bool temp;
stuff_boolean(&temp);
profile->flags.set(AI::Profile_Flags::all_nonshielded_ships_can_manage_ets, temp);
}
set_flag(profile, "$no directional bias for missile and ship turning:", AI::Profile_Flags::No_turning_directional_bias);
set_flag(profile, "$respect ship axial turnrate differences:", AI::Profile_Flags::Use_axial_turnrate_differences);
set_flag(profile, "$ships with no shields can manage ETS:", AI::Profile_Flags::nonshielded_ships_can_manage_ets);
set_flag(profile, "$any ship with no shields can manage ETS:", AI::Profile_Flags::all_nonshielded_ships_can_manage_ets);
set_flag(profile, "$fighters/bombers with no shields can manage ETS:", AI::Profile_Flags::fightercraft_nonshielded_ships_can_manage_ets);
set_flag(profile, "$better combat collision avoidance for fightercraft:", AI::Profile_Flags::Better_collision_avoidance);
set_flag(profile, "$improved missile avoidance for fightercraft:", AI::Profile_Flags::Improved_missile_avoidance);
set_flag(profile, "$friendly ships use AI profile countermeasure chance:", AI::Profile_Flags::Friendlies_use_countermeasure_firechance);
set_flag(profile, "$improved subsystem attack pathing:", AI::Profile_Flags::Improved_subsystem_attack_pathing);
set_flag(profile, "$fixed ship-weapon collisions:", AI::Profile_Flags::Fixed_ship_weapon_collision);
//Intention is to expand this feature to include a preference for close or long range weapons
//hence using something besides a simple flag.
if (optional_string("$AI secondary range awareness:"))
{
stuff_string(buf, F_NAME, NAME_LENGTH);
int j = ai_secondary_range_select_mode_match(buf);
if (j >= 0) {
profile->ai_range_aware_secondary_select_mode = j;
}
else {
Warning(LOCATION, "Invalid ai secondary range awareness mode '%s' specified", buf);
}
}
set_flag(profile, "$no shield damage from ship collisions:", AI::Profile_Flags::No_shield_damage_from_ship_collisions);
set_flag(profile, "$reset last_hit_target_time for player hits:", AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits);
// if we've been through once already and are at the same place, force a move
if (saved_Mp && (saved_Mp == Mp))
{
@@ -600,11 +652,12 @@ void ai_profile_t::reset()
flags.reset();
ai_path_mode = 0;
ai_path_mode = AI_PATH_MODE_NORMAL;
subsystem_path_radii = 0;
bay_arrive_speed_mult = 0;
bay_depart_speed_mult = 0;
bay_arrive_speed_mult = 1.0f;
bay_depart_speed_mult = 1.0f;
second_order_lead_predict_factor = 0;
ai_range_aware_secondary_select_mode = AI_RANGE_AWARE_SEC_SEL_MODE_RETAIL;
for (int i = 0; i < NUM_SKILL_LEVELS; ++i) {
max_incoming_asteroids[i] = 0;
@@ -676,10 +729,17 @@ void ai_profile_t::reset()
flags.set(AI::Profile_Flags::Fix_ai_path_order_bug);
flags.set(AI::Profile_Flags::Aspect_invulnerability_fix);
flags.set(AI::Profile_Flags::Use_actual_primary_range);
flags.set(AI::Profile_Flags::nonshielded_ships_can_manage_ets);
flags.set(AI::Profile_Flags::fightercraft_nonshielded_ships_can_manage_ets);
}
// this flag has been enabled ever since 3.7.2
if (mod_supports_version(3, 7, 2)) {
flags.set(AI::Profile_Flags::Fix_ramming_stationary_targets_bug);
}
// and this flag has been enabled ever since 3.6.10
if (mod_supports_version(3, 6, 10)) {
flags.set(AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits);
}
if (mod_supports_version(21, 4, 0)) {
flags.set(AI::Profile_Flags::Fixed_ship_weapon_collision);
}
}
+6
View File
@@ -17,6 +17,9 @@
// AI Path types
#define AI_PATH_MODE_NORMAL 0
#define AI_PATH_MODE_ALT1 1
#define AI_RANGE_AWARE_SEC_SEL_MODE_RETAIL 0
#define AI_RANGE_AWARE_SEC_SEL_MODE_AWARE 1
#define MAX_AI_PROFILES 5
@@ -107,6 +110,9 @@ public:
// How much 0-1 of a second-order lead prediction factor to add to lead indicators. Affects only the HUD indicator, and autoaim.
float second_order_lead_predict_factor;
//Controls if the AI is dumb enough to keep trying to use out-of-range secondaries.
int ai_range_aware_secondary_select_mode;
void reset();
};
+158 -16
View File
@@ -17,12 +17,13 @@
#include "io/timer.h"
#include "math/staticrand.h"
#include "mission/missionparse.h"
#include "object/objcollide.h"
#include "object/object.h"
#include "ship/afterburner.h"
#include "ship/ship.h"
#include "utils/Random.h"
#include "weapon/weapon.h"
#ifdef _MSC_VER
#pragma optimize("", off)
#pragma auto_inline(off)
@@ -529,6 +530,112 @@ int ai_big_maybe_follow_subsys_path(int do_dot_check)
return 0;
}
bool ai_new_maybe_reposition_attack_subsys() {
ai_info* aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
if (aip->target_objnum < 0 || aip->targeted_subsys == nullptr)
return false;
object* target_objp = &Objects[aip->target_objnum];
// dont bother with this if you're still far away
if (vm_vec_dist(&Pl_objp->pos, &target_objp->pos) > target_objp->radius * 2.0f + 2000.0f)
return false;
vec3d geye, gsubpos;
eye* ep;
polymodel* pm = model_get(Ship_info[Ships[Pl_objp->instance].ship_info_index].model_num);
// get world pos of eye (stored in geye)
ep = &(pm->view_positions[Ships[Pl_objp->instance].current_viewpoint]);
model_find_world_point(&geye, &ep->pnt, pm->id, 0, &Pl_objp->orient, &Pl_objp->pos);
// get world pos of subsystem
vm_vec_unrotate(&gsubpos, &aip->targeted_subsys->system_info->pnt, &En_objp->orient);
vm_vec_add2(&gsubpos, &En_objp->pos);
// you're in sight! shoot it!
if (ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, 0))
return false;
// not in sight, gotta get there
vec3d tgt2pl = Pl_objp->pos - target_objp->pos;
vec3d tgt2subsys = gsubpos - target_objp->pos;
float pl_dist = vm_vec_mag(&tgt2pl);
float subsys_dist = vm_vec_mag(&tgt2subsys) * 1.3f; // add a bit to this, we don't want to go directly at the subsys, we want to go above it
float angle2subsys = vm_vec_delta_ang(&tgt2pl, &tgt2subsys, nullptr);
float angle2goal = angle2subsys;
if (angle2goal > 1.2f) // let's not try to go more than about 70 degrees around the ship
angle2goal = 1.2f;
// we'll start by travelling along the sphere at our current distance around the ship
// remember that goal_pos will be relative to our target_ship (so we can scale in and out easily)
vec3d goal_pos, cross;
vm_vec_cross(&cross, &tgt2pl, &tgt2subsys);
vm_vec_normalize(&cross);
vm_vec_negate(&cross);
vm_rot_point_around_line(&goal_pos, &tgt2pl, angle2goal, &vmd_zero_vector, &cross);
// then scale in (or out) towards our subsys
vec3d world_goal_pos;
float new_radius = ((subsys_dist - pl_dist) * (angle2goal / angle2subsys)) + pl_dist;
vm_vec_copy_scale(&world_goal_pos, &goal_pos, new_radius / pl_dist);
world_goal_pos += target_objp->pos;
vec3d* good_pos = nullptr;
if (pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f)) {
// ok best case won't work
if (pl_dist > subsys_dist) {
//we're farther away than the subsys, let's try at our current distance
world_goal_pos = goal_pos + target_objp->pos;
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
good_pos = &world_goal_pos;
}
// ok, let's start moving it away and see if that's better
for (float i = 1.0f; i < 4.0f; i += 1.0f) {
if (good_pos)
break;
vm_vec_copy_scale(&world_goal_pos, &goal_pos, (new_radius / pl_dist) * powf(1.3f,i));
world_goal_pos += target_objp->pos;
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
good_pos = &world_goal_pos;
}
// welp this sucks, just run away i guess
if (!good_pos) {
vm_vec_copy_normalize(&world_goal_pos, &tgt2pl);
world_goal_pos *= target_objp->radius + pl_dist;
world_goal_pos += target_objp->pos;
good_pos = &world_goal_pos;
}
}
else
good_pos = &world_goal_pos;
ai_turn_towards_vector(good_pos, Pl_objp, nullptr, nullptr, 0.0f, 0);
float subsys_distance = vm_vec_dist(&Pl_objp->pos, &gsubpos);
// full speed until you're close, then slow down a bit
accelerate_ship(aip, subsys_distance > 200.0f ? 1.0f : subsys_distance / 200.0f);
if (!(Ships[Pl_objp->instance].flags[Ship::Ship_Flags::Afterburner_locked])) {
if (subsys_distance > 300.0f) {
if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
afterburners_start(Pl_objp);
aip->afterburner_stop_time = Missiontime + 2 * F1_0;
}
}
else
afterburners_stop(Pl_objp);
}
return true;
}
// This function is only called from ai_big_chase_attack() when a ship is flying very slowly and
// attacking a big ship. The ship should scan for enemy fighter/bombers... if any are close, then
// return 1, otherwise return 0;
@@ -637,7 +744,10 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
}
// see if Pl_objp needs to reposition to get a good shot at subsystem which is being attacked
if ( ai_big_maybe_follow_subsys_path() ) {
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
if (ai_new_maybe_reposition_attack_subsys())
return;
} else if ( ai_big_maybe_follow_subsys_path() ) {
if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags[AI::AI_Flags::Kamikaze])) {
afterburners_stop(Pl_objp);
}
@@ -667,11 +777,25 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
ai_turn_towards_vector(enemy_pos, Pl_objp, nullptr, rel_pos, 0.0f, 0, rvec);
// set an optimal range if applicable
float optimal_range = 0.0f;
ship_weapon* weapons = &Ships[Pl_objp->instance].weapons;
weapon_info* wip = nullptr;
if (weapons->num_primary_banks >= 1 && weapons->current_primary_bank >= 0) {
wip = &Weapon_info[weapons->primary_bank_weapons[weapons->current_primary_bank]];
} else if (weapons->num_secondary_banks >= 1 && weapons->current_secondary_bank >= 0) {
wip = &Weapon_info[weapons->secondary_bank_weapons[weapons->current_secondary_bank]];
}
if (wip != nullptr && wip->optimum_range > 0)
optimal_range = wip->optimum_range;
// calc range of primary weapon
weapon_travel_dist = ai_get_weapon_dist(&Ships[Pl_objp->instance].weapons);
if ( aip->targeted_subsys != NULL ) {
if (dist_to_enemy > (weapon_travel_dist-20)) {
if (dist_to_enemy > (optimal_range > 0.0f ? optimal_range : (weapon_travel_dist-20))) {
accelerate_ship(aip, 1.0f);
ship *shipp = &Ships[Pl_objp->instance];
@@ -699,7 +823,7 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
} else if (dot_to_enemy < 0.75f) {
accel = 0.75f;
} else {
if (dist_to_enemy > weapon_travel_dist/2.0f) {
if (dist_to_enemy > (optimal_range > 0.0f ? optimal_range : (weapon_travel_dist/2.0f))) {
accel = 1.0f;
} else {
accel = 1.0f - dot_to_enemy;
@@ -757,7 +881,14 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
swp = &Ships[Pl_objp->instance].weapons;
if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/MAX(1.0f, En_objp->radius + dist_to_enemy)) {
float target_radius = En_objp->radius;
// normal subsystem attacking uses whether or not its on a path and skips this function entirely to stop "over-eager" firing
// since the improved method doesnt use paths, refine the target radius to stop over-eager shooting
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing] && aip->targeted_subsys != nullptr)
target_radius = aip->targeted_subsys->system_info->radius;
if (dot_to_enemy > 0.95f - 0.5f * target_radius/MAX(1.0f, target_radius + dist_to_enemy)) {
aip->time_enemy_in_range += flFrametime;
// Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
@@ -797,7 +928,7 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
}
}
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank]) && weapon_target_satisfies_lock_restrictions(swip, En_objp)) {
float firing_range;
if (swip->wi_flags[Weapon::Info_Flags::Local_ssm])
firing_range=swip->lssm_lock_range;
@@ -827,10 +958,11 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
} else {
t = swip->fire_wait;
if ((swip->burst_shots > 0) && (swip->burst_flags[Weapon::Burst_Flags::Random_length])) {
swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
swp->burst_counter[current_bank_adjusted] = Random::next(swip->burst_shots);
} else {
swp->burst_counter[current_bank_adjusted] = 0;
}
swp->burst_seed[current_bank_adjusted] = Random::next();
}
} else {
if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
@@ -839,10 +971,11 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
} else {
t = set_secondary_fire_delay(aip, temp_shipp, swip, false);
if ((swip->burst_shots > 0) && (swip->burst_flags[Weapon::Burst_Flags::Random_length])) {
swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
swp->burst_counter[current_bank_adjusted] = Random::next(swip->burst_shots);
} else {
swp->burst_counter[current_bank_adjusted] = 0;
}
swp->burst_seed[current_bank_adjusted] = Random::next();
}
}
swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
@@ -1039,7 +1172,10 @@ void ai_big_chase()
float dist_normal_to_enemy;
if (vm_vec_mag_squared(&aip->big_attack_surface_normal) > 0.9) {
dist_normal_to_enemy = fl_abs((dist_to_enemy * vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing])
dist_normal_to_enemy = fl_abs((dist_to_enemy / vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
else // I'm fairly sure this retail code VVV meant to use / instead of *
dist_normal_to_enemy = fl_abs((dist_to_enemy * vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
} else {
// don;t have normal so use a conservative value here
dist_normal_to_enemy = 0.3f * dist_to_enemy;
@@ -1316,13 +1452,17 @@ void ai_big_strafe_attack()
ai_info *aip;
vec3d target_pos;
vec3d rand_vec;
float target_dist, target_dot, accel, t;
float accel, t;
float target_dist = 0.0f;
float target_dot = 0.0f;
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
if ( ai_big_maybe_follow_subsys_path(0) ) {
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
if (ai_new_maybe_reposition_attack_subsys())
return;
} else if (ai_big_maybe_follow_subsys_path(0))
return;
}
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
if ( ai_big_strafe_maybe_retreat(&target_pos) )
@@ -1345,7 +1485,7 @@ void ai_big_strafe_attack()
if ( t > 0.0f && t < 1.5f ) {
// set up goal_point for avoid path to turn towards
aip->goal_point = Pl_objp->pos;
switch(rand()%4) {
switch(Random::next(4)) {
case 0:
vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec, 2.0f);
break;
@@ -1439,9 +1579,11 @@ void ai_big_strafe_glide_attack()
flight_dot_to_enemy = vm_vec_dot(&target_objp->pos, &norm_vel_vec); //Angle between flight path and target ship
//If we're following a path, then we shouldn't be doing glide strafe
if ( ai_big_maybe_follow_subsys_path(0) ) {
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
if (ai_new_maybe_reposition_attack_subsys())
return;
} else if (ai_big_maybe_follow_subsys_path(0))
return;
}
//Gets a point on the target ship to attack, as well as distance and the angle between the nose of the attacker and that point.
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
@@ -1575,7 +1717,7 @@ void ai_big_strafe_retreat1()
if (Missiontime - aip->submode_start_time > fl2f(1.50f)) {
// set up goal_point for avoid path to turn towards
aip->prev_goal_point = Pl_objp->pos;
switch(rand()%4) {
switch(Random::next(4)) {
case 0:
vm_vec_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec);
break;
+396 -165
View File
File diff suppressed because it is too large. Load diff
+10 -8
View File
@@ -293,9 +293,11 @@ void ai_clear_ship_goals( ai_info *aip )
}
// add scripting hook for 'On Goals Cleared' --wookieejedi
Script_system.SetHookObject("Ship", &Objects[Ships[aip->shipnum].objnum]);
Script_system.RunCondition(CHA_ONGOALSCLEARED);
Script_system.RemHookVars({"Ship"});
if (Script_system.IsActiveAction(CHA_ONGOALSCLEARED)) {
Script_system.SetHookObject("Ship", &Objects[Ships[aip->shipnum].objnum]);
Script_system.RunCondition(CHA_ONGOALSCLEARED);
Script_system.RemHookVars({"Ship"});
}
}
void ai_clear_wing_goals( wing *wingp )
@@ -2147,14 +2149,14 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
// Mike -- debug code!
// If a ship has a subobject on it, attack that instead of the main ship!
ai_attack_object( objp, other_obj, NULL);
ai_attack_object( objp, other_obj);
break;
case AI_GOAL_CHASE_WEAPON:
Assert( Weapons[current_goal->target_instance].objnum >= 0 );
other_obj = &Objects[Weapons[current_goal->target_instance].objnum];
Assert( other_obj->signature == current_goal->target_signature );
ai_attack_object( objp, other_obj, NULL );
ai_attack_object( objp, other_obj);
break;
case AI_GOAL_GUARD:
@@ -2279,7 +2281,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
shipnum = ship_name_lookup( current_goal->target_name );
Assert( shipnum >= 0 );
other_obj = &Objects[Ships[shipnum].objnum];
ai_attack_object( objp, other_obj, NULL);
ai_attack_object( objp, other_obj);
ai_set_attack_subsystem( objp, current_goal->ai_submode ); // submode stored the subsystem type
if (current_goal->ai_mode != AI_GOAL_DESTROY_SUBSYSTEM) {
if (aip->target_objnum != -1) {
@@ -2301,14 +2303,14 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
break;
case AI_GOAL_CHASE_ANY:
ai_attack_object( objp, NULL, NULL );
ai_attack_object( objp, nullptr);
break;
// chase-ship-class is chase-any but restricted to a subset of ships
case AI_GOAL_CHASE_SHIP_CLASS:
shipnum = ship_info_lookup( current_goal->target_name );
Assertion( shipnum >= 0, "The target of AI_GOAL_CHASE_SHIP_CLASS must refer to a valid ship class!" );
ai_attack_object( objp, NULL, NULL, shipnum );
ai_attack_object( objp, nullptr, shipnum );
break;
case AI_GOAL_WARP: {
+1 -1
View File
@@ -32,6 +32,6 @@ int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int second
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, ship_subsys* turret);
//Does all the stuff needed to aim and fire a turret.
void ai_fire_from_turret(ship *shipp, ship_subsys *ss);
void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss);
#endif
+173 -192
View File
@@ -18,6 +18,7 @@
#include "render/3d.h"
#include "ship/ship.h"
#include "ship/shipfx.h"
#include "utils/Random.h"
#include "weapon/beam.h"
#include "weapon/flak.h"
#include "weapon/muzzleflash.h"
@@ -87,6 +88,18 @@ typedef struct eval_enemy_obj_struct {
int nearest_objnum;
} eval_enemy_obj_struct;
// the current world orientation of the turret matrix, corresponding to its fvec and uvec defined in the model
// is NOT affected by the turret's current aiming
void turret_instance_find_world_orient(matrix* out_mat, int model_instance_num, int submodel_num, const matrix* objorient)
{
auto pmi = model_get_instance(model_instance_num);
auto pm = model_get(pmi->model_num);
vec3d fvec, uvec;
model_instance_find_world_dir(&fvec, &pm->submodel[submodel_num].frame_of_reference.vec.fvec, pm, pmi, pm->submodel[submodel_num].parent, objorient);
model_instance_find_world_dir(&uvec, &pm->submodel[submodel_num].frame_of_reference.vec.uvec, pm, pmi, pm->submodel[submodel_num].parent, objorient);
vm_vector_2_matrix_norm(out_mat, &fvec, &uvec);
}
/**
* Is object in turret field of view?
*
@@ -98,117 +111,53 @@ typedef struct eval_enemy_obj_struct {
*
* @return 1 if objp is in fov of the specified turret. Otherwise return 0.
*/
int object_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, float dist)
bool object_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, float dist)
{
vec3d v2e;
float size_mod;
bool in_fov;
bool in_fov = false;
if (ss->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
int model_num;
switch (objp->type) {
case OBJ_SHIP:
model_num = Ship_info[Ships[objp->instance].ship_info_index].model_num;
break;
case OBJ_ASTEROID:
model_num = Asteroid_info[Asteroids[objp->instance].asteroid_type].model_num[Asteroids[objp->instance].asteroid_subtype];
break;
default:
vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
size_mod = objp->radius / (dist + objp->radius);
in_fov = turret_fov_test(ss, tvec, &v2e, size_mod);
return in_fov;
}
auto pm = model_get(model_num);
for (int i = 0; i < 8; i++) {
vec3d bbox_point;
vm_vec_unrotate(&bbox_point, &pm->bounding_box[i], &objp->orient);
bbox_point += objp->pos;
vm_vec_normalized_dir(&v2e, &bbox_point, tpos);
in_fov = turret_fov_test(ss, tvec, &v2e, -0.2f);
if (in_fov)
return true;
}
}
// if the bbox points method didn't work (or fov_edge_checks isn't on)
// try the normal method
vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
size_mod = objp->radius / (dist + objp->radius);
in_fov = turret_fov_test(ss, tvec, &v2e, size_mod);
if ( in_fov ) {
return 1;
}
return 0;
}
bool is_object_radius_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, vec3d *v2e, vec3d *predicted_pos, float distance)
{
float target_dist = distance;
if (distance == 0.0f)
target_dist = vm_vec_dist(predicted_pos,tpos);
if (object_in_turret_fov(objp, ss, tvec, tpos, target_dist + objp->radius)) {
// so the targeted spot in not in fov but the enemy + radius is
// lets align the darn gun and try shooting there
vec3d temp_vec;
float multiplier = 0;
model_subsystem *tp = ss->system_info;
// project v2e_from_turret to turret normal
// substract resultant vector from the temp_vec (v2e_from_turret)
// adjust z component as necessary
// calculate multiplier for the resultant vector
// use multiplier and the z component and compose a new vector
float dot = vm_vec_dot(v2e, tvec);
vm_vec_scale_add(&temp_vec, v2e, tvec, -dot);
if (IS_VEC_NULL_SQ_SAFE(&temp_vec)) {
// return false, target is perfectly aligned over or below the turret
// safe bet is to allow turret to reacquire better target
return false;
}
// normalize the vec, it needs to be done regardless
vm_vec_normalize(&temp_vec);
bool fix_elevation = false;
bool fix_base_rot = false;
if (dot < tp->turret_fov) {
dot = tp->turret_fov;
fix_elevation = true;
}
if (dot > tp->turret_max_fov) {
dot = tp->turret_max_fov;
fix_elevation = true;
}
if (tp->flags[Model::Subsystem_Flags::Turret_alt_math]) {
vec3d temp_vec2;
vm_vec_rotate(&temp_vec2, &temp_vec, &ss->world_to_turret_matrix);
// now in turrets frame of reference
// check if math is actually possible
if (!((temp_vec2.xyz.x == 0) && (temp_vec2.xyz.y == 0))) {
float temp_z = temp_vec2.xyz.z;
temp_vec2.xyz.z = 0.0f;
// make sure null vecs wont happen
if (!IS_VEC_NULL_SQ_SAFE(&temp_vec2)) {
vm_vec_normalize(&temp_vec2);
// only do this if it actually is required
if (-temp_vec2.xyz.y < tp->turret_y_fov) {
float check_pos = 1;
fix_base_rot = true;
temp_vec2.xyz.y = -tp->turret_y_fov;
if (temp_vec2.xyz.x < 0)
check_pos = -1;
temp_vec2.xyz.x = check_pos * sqrtf(1 - (temp_vec2.xyz.y*temp_vec2.xyz.y));
// restore z component
float scaler = sqrtf(1 - (temp_z*temp_z));
vm_vec_scale(&temp_vec2,scaler);
temp_vec2.xyz.z = temp_z;
// back to world frame
vm_vec_unrotate(&temp_vec, &temp_vec2, &ss->world_to_turret_matrix);
}
}
}
}
if (fix_elevation || fix_base_rot) {
if (fix_elevation) {
multiplier = sqrtf(1 - (dot*dot));
// keep the temp_vec scaled with the tweaked vector
vm_vec_scale(&temp_vec, multiplier);
}
vm_vec_scale_add(v2e, &temp_vec, tvec, dot);
// and we are done with v2e...
vm_vec_scale_add(predicted_pos, tpos, v2e, target_dist);
// and we are done with predicted position
return true;
} else {
mprintf(("Warning: Function 'is_object_radius_in_turret_fov' was called\nwithout need to fix turret alignments\n"));
return false;
}
}
// outside of the expanded radii, unable to align, return false
return false;
return in_fov;
}
/**
@@ -555,6 +504,7 @@ void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
if ( objp->type == OBJ_SHIP ) {
shipp = &Ships[objp->instance];
ship_info* sip = &Ship_info[shipp->ship_info_index];
// check on enemy team
if ( !iff_matches_mask(shipp->team, eeo->enemy_team_mask) ) {
@@ -594,16 +544,18 @@ void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
}
}
// don't shoot at small ships if we shouldn't
// don't shoot at big ships with huge weapons unless they have the flag
if (eeo->eeo_flags & EEOF_BIG_ONLY) {
if (!(Ship_info[shipp->ship_info_index].is_big_or_huge())) {
if (sip->class_type == -1 || !(Ship_types[sip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only])) {
return;
}
}
// ^ Note the difference between these checks
// V "small only" EXCLUDES only big ships, "huge" INCLUDES only big ships
// don't shoot at big ships if we shouldn't
// don't shoot at ships ignored by small weapons if this is a small weapon
if (eeo->eeo_flags & EEOF_SMALL_ONLY) {
if ((Ship_info[shipp->ship_info_index].is_big_or_huge())) {
if (sip->class_type >= 0 && (Ship_types[sip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only])) {
return;
}
}
@@ -1179,23 +1131,18 @@ int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d
if (!skip) {
if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
ship_info* esip = &Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index];
// check for huge weapon and huge ship
if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].is_big_or_huge()) ) {
if ( !big_only_flag || (esip->class_type >= 0 && Ship_types[esip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only]) ) {
// check for tagged only and tagged ship
if ( tagged_only_flag && ship_is_tagged(&Objects[aip->target_objnum]) ) {
// select new target if aip->target_objnum is out of field of view
vec3d v2e;
float dist;
bool in_fov;
dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
in_fov = turret_fov_test(turret_subsys, tvec, &v2e);
if (turret_subsys->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
if (in_fov == false)
if (object_in_turret_fov(&Objects[aip->target_objnum], turret_subsys, tvec, tpos, dist + Objects[aip->target_objnum].radius))
in_fov = true;
}
// MODIFY FOR ATTACKING BIG SHIP
// dot += (0.5f * Objects[aip->target_objnum].radius / dist);
if (in_fov) {
@@ -1230,20 +1177,22 @@ int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d
* of the turret.
*
* @param objp Pointer to object
* @param tp Turrent model system on that object
* @param tp Turret model system on that object
* @param gpos [Output] Global absolute position of gun firing point
* @param gvec [Output] Global vector
*
* @note The gun normal is the unrotated gun normal, (the center of the FOV cone), not
* the actual gun normal given using the current turret heading. But it _is_ rotated into the model's orientation
* in global space.
* @note2 Because of this, both single-part and multi-part turrets are treated the same way; no need to find the multi-part's gun submodel.
*/
void ship_get_global_turret_info(object *objp, model_subsystem *tp, vec3d *gpos, vec3d *gvec)
void ship_get_global_turret_info(const object *objp, const model_subsystem *tp, vec3d *gpos, vec3d *gvec)
{
// vm_vec_unrotate(gpos, &tp->turret_avg_firing_point, &objp->orient);
vm_vec_unrotate(gpos, &tp->pnt, &objp->orient);
vm_vec_add2(gpos, &objp->pos);
vm_vec_unrotate(gvec, &tp->turret_norm, &objp->orient);
auto model_instance_num = Ships[objp->instance].model_instance_num;
if (gpos)
model_instance_find_world_point(gpos, &vmd_zero_vector, model_instance_num, tp->subobj_num, &objp->orient, &objp->pos);
if (gvec)
model_instance_find_world_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);
@@ -1303,7 +1252,8 @@ void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vec3d *gpos
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())) {
vm_vec_unrotate(&turret_norm, &tp->turret_norm, &objp->orient);
// the turret norm here is from the perspective of the base submodel, not the gun submodel
model_instance_find_world_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 {
@@ -1434,18 +1384,13 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
in_fov = turret_fov_test(ss, gvec, &v2e);
if (ss->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
if (in_fov == false)
in_fov = is_object_radius_in_turret_fov(&Objects[ss->turret_enemy_objnum], ss, gvec, &gun_pos, &v2e, predicted_enemy_pos, 0.0f);
}
if (in_fov) {
ret_val = model_rotate_gun(objp, pm, pmi, tp, predicted_enemy_pos);
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, tp, predicted_enemy_pos, true);
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
}
} 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->system_info, predicted_enemy_pos, true);
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
}
// by default "ret_val" should be set to 1 for multi-part turrets, and 0 for single-part turrets
@@ -1477,7 +1422,7 @@ float aifft_compute_turret_dot(object *objp, object *enemy_objp, vec3d *abs_gunp
if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
vec3d turret_norm;
vm_vec_unrotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
model_instance_find_world_dir(&turret_norm, &turret_subsysp->system_info->turret_norm, Ships[objp->instance].model_instance_num, turret_subsysp->system_info->subobj_num, &objp->orient, true);
float dot_return = vm_vec_dot(&turret_norm, &vector_out);
if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags[AI::Profile_Flags::Smart_subsystem_targeting_for_turrets]) {
@@ -1547,8 +1492,7 @@ ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *en
// Compute absolute gun position.
vec3d abs_gun_pos;
vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
vm_vec_add2(&abs_gun_pos, &objp->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())
@@ -1661,8 +1605,10 @@ void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsy
{
Assert(weapon_num < MAX_SHIP_WEAPONS);
float wait = 1000.0f;
// should subtract 1 from firepoints to match behavior of wip->burst_shots
int burst_shots = (wip->burst_flags[Weapon::Burst_Flags::Num_firepoints_burst_shots] ? turret->system_info->turret_num_firing_points - 1 : wip->burst_shots);
if (wip->burst_shots > turret->weapons.burst_counter[weapon_num]) {
if (burst_shots > turret->weapons.burst_counter[weapon_num]) {
wait *= wip->burst_delay;
turret->weapons.burst_counter[weapon_num]++;
} else {
@@ -1672,10 +1618,12 @@ void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsy
} else {
wait *= wip->fire_wait;
}
if ((wip->burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Random_length])) {
turret->weapons.burst_counter[weapon_num] = (myrand() % wip->burst_shots);
if ((burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Random_length])) {
turret->weapons.burst_counter[weapon_num] = Random::next(burst_shots);
turret->weapons.burst_seed[weapon_num] = Random::next();
} else {
turret->weapons.burst_counter[weapon_num] = 0;
turret->weapons.burst_seed[weapon_num] = Random::next();
}
}
@@ -1686,7 +1634,7 @@ void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsy
fs_dest = &turret->weapons.next_secondary_fire_stamp[weapon_num - MAX_SHIP_PRIMARY_BANKS];
//Check for the new cooldown flag
if(!((wip->wi_flags[Weapon::Info_Flags::Same_turret_cooldown]) || ((wip->burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Fast_firing]))))
if(!((wip->wi_flags[Weapon::Info_Flags::Same_turret_cooldown]) || ((burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Fast_firing]))))
{
// make side even for team vs. team
@@ -1769,7 +1717,7 @@ 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 *turret_fvec, vec3d *predicted_pos = NULL, float flak_range_override = 100.0f)
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)
{
matrix turret_orient;
int weapon_objnum;
@@ -1834,8 +1782,25 @@ 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)) {
vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
turret->turret_last_fire_direction = *turret_fvec;
ship_weapon* swp = &turret->weapons;
turret->turret_last_fire_direction = *firing_vec;
if (turret->turret_inaccuracy > 0.0f)
vm_vec_random_cone(firing_vec, firing_vec, turret->turret_inaccuracy);
vm_vector_2_matrix(&turret_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];
int old_burst_counter = swp->burst_counter[weapon_num];
// only used by type 5 beams
if (turret->weapons.burst_counter[weapon_num] == 0) {
swp->per_burst_rot += wip->b_info.t5info.per_burst_rot;
if (swp->per_burst_rot > PI2)
swp->per_burst_rot -= PI2;
else if (swp->per_burst_rot < -PI2)
swp->per_burst_rot += PI2;
}
// set next fire timestamp for the turret
if (last_shot_in_salvo)
@@ -1861,7 +1826,10 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
else
fire_info.target_subsys = NULL;
fire_info.turret = turret;
fire_info.burst_seed = old_burst_seed;
fire_info.fire_method = BFM_TURRET_FIRED;
fire_info.per_burst_rotation = swp->per_burst_rot;
fire_info.burst_index = old_burst_counter;
// fire a beam weapon
weapon_objnum = beam_fire(&fire_info);
@@ -1872,9 +1840,11 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
parent_ship->last_fired_turret = turret;
turret->last_fired_weapon_info_index = turret_weapon_class;
Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", nullptr, "Beam", objp, "Target", &Objects[turret->turret_enemy_objnum]);
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum]);
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
if (Script_system.IsActiveAction(CHA_ONTURRETFIRED)) {
Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", nullptr, "Beam", objp, "Target", &Objects[turret->turret_enemy_objnum]);
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum]);
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
}
}
turret->flags.set(Ship::Subsystem_Flags::Has_fired); //set fired flag for scripting -nike
@@ -1882,7 +1852,6 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
}
// don't fire swam, but set up swarm info instead
else if ((wip->wi_flags[Weapon::Info_Flags::Swarm]) || (wip->wi_flags[Weapon::Info_Flags::Corkscrew])) {
ship_weapon *swp = &turret->weapons;
if (swp->current_secondary_bank < 0) {
swp->current_secondary_bank = 0;
}
@@ -1908,8 +1877,6 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
else {
for (int i = 0; i < wip->shots; i++) {
if (turret->system_info->flags[Model::Subsystem_Flags::Turret_use_ammo]) {
ship_weapon *swp;
swp = &turret->weapons;
int bank_to_fire, num_slots = turret->system_info->turret_num_firing_points;
if (wip->subtype == WP_LASER) {
int points;
@@ -1989,7 +1956,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
}
// 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, turret_fvec, 1, NULL);
ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, turret_pos, firing_vec, 1, nullptr);
weapon_objnum = weapon_create( turret_pos, &turret_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);
@@ -2007,14 +1974,14 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
// AL 1-6-97: Store pointer to turret subsystem
wp->turret_subsys = turret;
Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", objp, "Beam", nullptr, "Target", &Objects[turret->turret_enemy_objnum]);
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum]);
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
if (Script_system.IsActiveAction(CHA_ONTURRETFIRED)) {
Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", objp, "Beam", nullptr, "Target", &Objects[turret->turret_enemy_objnum]);
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum]);
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
}
// if the gun is a flak gun
if (wip->wi_flags[Weapon::Info_Flags::Flak]) {
// show a muzzle flash
flak_muzzle_flash(turret_pos, turret_fvec, &Objects[parent_ship->objnum].phys_info, turret_weapon_class);
if (wip->wi_flags[Weapon::Info_Flags::Flak]) {
if(predicted_pos != NULL)
{
@@ -2029,9 +1996,10 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
flak_set_range(objp, flak_range_override);
}
}
// otherwise just do mflash if the weapon has it
else if (wip->muzzle_flash >= 0) {
mflash_create(turret_pos, turret_fvec, &Objects[parent_ship->objnum].phys_info, wip->muzzle_flash);
// 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);
}
// in multiplayer (and the master), then send a turret fired packet.
@@ -2124,9 +2092,11 @@ void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
Ships[Objects[tsi->parent_objnum].instance].last_fired_turret = tsi->turret;
tsi->turret->last_fired_weapon_info_index = tsi->weapon_class;
Script_system.SetHookObjects(4, "Ship", &Objects[tsi->parent_objnum], "Weapon", &Objects[weapon_objnum], "Beam", nullptr, "Target", &Objects[tsi->turret->turret_enemy_objnum]);
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[tsi->parent_objnum]);
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
if (Script_system.IsActiveAction(CHA_ONTURRETFIRED)) {
Script_system.SetHookObjects(4, "Ship", &Objects[tsi->parent_objnum], "Weapon", &Objects[weapon_objnum], "Beam", nullptr, "Target", &Objects[tsi->turret->turret_enemy_objnum]);
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[tsi->parent_objnum]);
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
}
// muzzle flash?
if (Weapon_info[tsi->weapon_class].muzzle_flash >= 0) {
@@ -2157,13 +2127,15 @@ int Num_find_turret_enemy = 0;
int Num_turrets_fired = 0;
/**
* Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
* Previously called ai_fire_from_turret()
* Given a ship and a turret subsystem, handle all its behavior, mostly targeting and shooting but also
* all turret movement and resetting when idle
*/
extern int Nebula_sec_range;
void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
{
float weapon_firing_range;
float WeaponMinRange; // *Weapon minimum firing range -Et1
float weapon_min_range; // *Weapon minimum firing range -Et1
vec3d v2e;
object *lep; // Last enemy pointer
model_subsystem *tp = ss->system_info;
@@ -2235,6 +2207,8 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
{
ss->turret_enemy_objnum = -1;
lep = nullptr;
ss->flags.remove(Ship::Subsystem_Flags::Forced_target);
ss->flags.remove(Ship::Subsystem_Flags::Forced_subsys_target);
}
Assert((shipp->objnum >= 0) && (shipp->objnum < MAX_OBJECTS));
@@ -2242,25 +2216,10 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
objp = &Objects[shipp->objnum];
Assert(objp->type == OBJ_SHIP);
// Wanderer - make sure turrets already have all the data
if ( !(tp->flags[Model::Subsystem_Flags::Turret_matrix]) )
{
if (!(tp->turret_gun_sobj == tp->subobj_num))
{
auto pmi = model_get_instance(shipp->model_instance_num);
auto pm = model_get(pmi->model_num);
model_make_turret_matrix(pm, pmi, tp);
}
}
// Use the turret info for all guns, not one gun in particular.
vec3d gvec, gpos;
ship_get_global_turret_info(objp, tp, &gpos, &gvec);
if (tp->flags[Model::Subsystem_Flags::Turret_alt_math]) {
vm_matrix_x_matrix( &ss->world_to_turret_matrix, &objp->orient, &tp->turret_matrix );
}
// 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);
@@ -2351,12 +2310,12 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm])
{
weapon_firing_range = wip->lssm_lock_range;
WeaponMinRange = 0.0f;
weapon_min_range = 0.0f;
}
else
{
weapon_firing_range = MIN(wip->lifetime * wip->max_speed, wip->weapon_range);
WeaponMinRange = wip->WeaponMinRange;
weapon_min_range = wip->weapon_min_range;
}
// if beam weapon in nebula and target not tagged, decrease firing range
@@ -2369,19 +2328,21 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
}
// Don't try to fire beyond weapon_limit_range (or within min range)
if (dist_to_enemy < WeaponMinRange || dist_to_enemy > weapon_firing_range) {
if (dist_to_enemy < weapon_min_range || dist_to_enemy > weapon_firing_range) {
// it's possible another weapon is in range, but if not,
// we will end up selecting a new target
continue;
}
ship_info* esip = lep->type != OBJ_SHIP ? nullptr : &Ship_info[Ships[lep->instance].ship_info_index];
// If targeted a small ship and have a huge weapon, don't fire. But this shouldn't happen, as a small ship should not get selected.
if ( wip->wi_flags[Weapon::Info_Flags::Huge] ) {
if ( lep->type != OBJ_SHIP ) {
tentative_return = true;
continue;
}
if ( !(Ship_info[Ships[lep->instance].ship_info_index].is_big_or_huge()) ) {
if ( esip->class_type >= 0 && !(Ship_types[esip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only])) {
tentative_return = true;
continue;
}
@@ -2389,7 +2350,8 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
// Similar check for small weapons
if ( wip->wi_flags[Weapon::Info_Flags::Small_only] ) {
if ( (lep->type == OBJ_SHIP) && Ship_info[Ships[lep->instance].ship_info_index].is_big_or_huge() ) {
if ( (lep->type == OBJ_SHIP) && esip->class_type >= 0 &&
(Ship_types[esip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only])) {
tentative_return = true;
continue;
}
@@ -2433,6 +2395,11 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
tentative_return = true;
continue;
}
if (!weapon_target_satisfies_lock_restrictions(wip, lep)) {
tentative_return = true;
continue;
}
}
else {
// check tagged-only for bombs only if the flag is set; see Mantis #3114
@@ -2453,8 +2420,10 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
//none of our guns can hit the enemy, so find a new enemy
if (num_valid == 0) {
ss->turret_enemy_objnum = -1;
ss->turret_time_enemy_in_range = 0.0f;
if (!ss->flags[Ship::Subsystem_Flags::Forced_target]) {
ss->turret_enemy_objnum = -1;
ss->turret_time_enemy_in_range = 0.0f;
}
// in the original code, we returned where the "tentative return" variable was set,
// so return for real now, since we don't have a reason to remain in the function
@@ -2462,8 +2431,16 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
return;
}
// if we've got Forced_subsys_target targeted_subsys *really* shouldnt be null but just in case...
if (ss->flags[Ship::Subsystem_Flags::Forced_subsys_target] && ss->targeted_subsys != nullptr) {
if (ss->targeted_subsys->current_hits <= 0.0f) {
ss->flags.remove(Ship::Subsystem_Flags::Forced_subsys_target);
ss->flags.remove(Ship::Subsystem_Flags::Forced_target);
}
}
// Maybe pick a new enemy, unless targeting has been taken over by scripting
if ( turret_should_pick_new_target(ss) && !ss->scripting_target_override ) {
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);
@@ -2736,7 +2713,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
{
// If nothing is OK to fire (lost track of the target?)
// reset the target (so we don't continue to track what we can't hit)
if (tp->flags[Model::Subsystem_Flags::Turret_only_target_if_can_fire])
if (tp->flags[Model::Subsystem_Flags::Turret_only_target_if_can_fire] && !(ss->flags[Ship::Subsystem_Flags::Forced_target]))
{
ss->turret_enemy_objnum = -1; // Reset enemy objnum, find a new one next frame.
ss->turret_time_enemy_in_range = 0.0f;
@@ -2771,7 +2748,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
}
}
}
else
else if (!(ss->flags[Ship::Subsystem_Flags::Forced_target]))
{
// Lost him!
ss->turret_enemy_objnum = -1; // Reset enemy objnum, find a new one next frame.
@@ -2794,16 +2771,20 @@ bool turret_adv_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mo
model_subsystem *tp = ss->system_info;
float dot = vm_vec_dot(v2e, gvec);
if (((dot + size_mod) >= tp->turret_fov) && ((dot - size_mod) <= tp->turret_max_fov)) {
object* objp = &Objects[ss->parent_objnum];
matrix turret_matrix;
turret_instance_find_world_orient(&turret_matrix, Ships[objp->instance].model_instance_num, tp->subobj_num, &objp->orient);
vec3d of_dst;
vm_vec_rotate( &of_dst, v2e, &ss->world_to_turret_matrix );
if ((of_dst.xyz.x == 0) && (of_dst.xyz.y == 0)) {
return true;
} else {
of_dst.xyz.z = 0;
vm_vec_rotate(&of_dst, v2e, &turret_matrix);
if ((of_dst.xyz.x == 0) && (of_dst.xyz.z == 0))
return true;
else {
of_dst.xyz.y = 0;
if (!IS_VEC_NULL_SQ_SAFE(&of_dst)) {
vm_vec_normalize(&of_dst);
// now we have 2d vector with lenght of 1 that points at the targets direction after being rotated to turrets FOR
if ((-of_dst.xyz.y + size_mod) >= tp->turret_y_fov)
if ((of_dst.xyz.z + size_mod) >= tp->turret_base_fov)
return true;
}
}
@@ -2814,7 +2795,7 @@ bool turret_adv_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mo
bool turret_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
{
bool in_fov = false;
if (ss->system_info->flags[Model::Subsystem_Flags::Turret_alt_math])
if (ss->system_info->flags[Model::Subsystem_Flags::Turret_restricted_fov])
in_fov = turret_adv_fov_test(ss, gvec, v2e, size_mod);
else
in_fov = turret_std_fov_test(ss, gvec, v2e, size_mod);
+131 -138
View File
@@ -38,7 +38,6 @@
#include "ship/ship.h"
#include "ship/shipfx.h"
#include "ship/shiphit.h"
#include "species_defs/species_defs.h"
#include "stats/scoring.h"
#include "weapon/weapon.h"
@@ -50,9 +49,9 @@ asteroid_obj Asteroid_objs[MAX_ASTEROID_OBJS]; // array used to store asteroid o
asteroid_obj Asteroid_obj_list; // head of linked list of asteroid_obj structs
// used for randomly generating debris type when there are multiple sizes.
#define SMALL_DEBRIS_WEIGHT 8
#define MEDIUM_DEBRIS_WEIGHT 4
#define LARGE_DEBRIS_WEIGHT 1
const float SMALL_DEBRIS_WEIGHT = 8.0f;
const float MEDIUM_DEBRIS_WEIGHT = 4.0f;
const float LARGE_DEBRIS_WEIGHT = 1.0f;
int Asteroids_enabled = 1;
int Num_asteroids = 0;
@@ -257,7 +256,7 @@ object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int asteroi
return NULL;
}
if((asteroid_type < 0) || (asteroid_type >= (((int)Species_info.size() + 1) * NUM_DEBRIS_SIZES))) {
if((asteroid_type < 0) || (asteroid_type >= (int)Asteroid_info.size())) {
return NULL;
}
@@ -325,7 +324,7 @@ object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int asteroi
asteroid_default_flagset += Object::Object_Flags::Physics;
asteroid_default_flagset += Object::Object_Flags::Collides;
objnum = obj_create(OBJ_ASTEROID, -1, n, &orient, &pos, radius, asteroid_default_flagset);
objnum = obj_create(OBJ_ASTEROID, -1, n, &orient, &pos, radius, asteroid_default_flagset, false);
if ( (objnum == -1) || (objnum >= MAX_OBJECTS) ) {
mprintf(("Couldn't create asteroid -- out of object slots\n"));
@@ -488,31 +487,6 @@ static void asteroid_load(int asteroid_info_index, int asteroid_subtype)
}
}
/**
* Returns a weight that depends on asteroid size.
*
* The weight is then used to determine the frequencty of different sizes of ship debris
*/
static int get_debris_weight(int ship_debris_index)
{
int size = ship_debris_index % NUM_DEBRIS_SIZES;
switch (size)
{
case ASTEROID_TYPE_SMALL:
return SMALL_DEBRIS_WEIGHT;
case ASTEROID_TYPE_MEDIUM:
return MEDIUM_DEBRIS_WEIGHT;
case ASTEROID_TYPE_LARGE:
return LARGE_DEBRIS_WEIGHT;
default:
Int3();
return 1;
}
}
/**
* Create all the asteroids for the mission
*/
@@ -522,14 +496,14 @@ void asteroid_create_all()
// ship_debris_odds_table keeps track of debris type of the next debris piece
// each different type (size) of debris piece has a diffenent weight, smaller weighted more heavily than larger.
// choose next type from table ship_debris_odds_table by rand()%max_weighted_range,
// choose next type from table ship_debris_odds_table by Random::next()%max_weighted_range,
// the threshold *below* which the debris type is selected.
struct {
int random_threshold;
float random_threshold;
int debris_type;
} ship_debris_odds_table[MAX_ACTIVE_DEBRIS_TYPES];
int max_weighted_range = 0;
float max_weighted_range = 0.0f;
if (!Asteroids_enabled)
return;
@@ -552,7 +526,7 @@ void asteroid_create_all()
// Calculate the odds table
for (idx=0; idx<num_debris_types; idx++) {
int debris_weight = get_debris_weight(Asteroid_field.field_debris_type[idx]);
float debris_weight = Asteroid_info[Asteroid_field.field_debris_type[idx]].spawn_weight;
ship_debris_odds_table[idx].random_threshold = max_weighted_range + debris_weight;
ship_debris_odds_table[idx].debris_type = Asteroid_field.field_debris_type[idx];
max_weighted_range += debris_weight;
@@ -590,7 +564,7 @@ void asteroid_create_all()
// For asteroid, load only large asteroids
// get a valid subtype
int subtype = rand() % NUM_DEBRIS_POFS;
int subtype = Random::next(NUM_DEBRIS_POFS);
while (Asteroid_field.field_debris_type[subtype] == -1) {
subtype = (subtype + 1) % NUM_DEBRIS_POFS;
}
@@ -599,7 +573,7 @@ void asteroid_create_all()
} else {
Assert(num_debris_types > 0);
int rand_choice = rand() % max_weighted_range;
float rand_choice = frand() * max_weighted_range;
for (idx=0; idx<MAX_ACTIVE_DEBRIS_TYPES; idx++) {
// for ship debris, choose type according to odds table
@@ -784,7 +758,7 @@ static void maybe_throw_asteroid(int count)
for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
object *A = &Objects[so->objnum];
if (so->objnum == Asteroid_throw_objnum) {
int subtype = rand() % NUM_DEBRIS_POFS;
int subtype = Random::next(NUM_DEBRIS_POFS);
while (Asteroid_field.field_debris_type[subtype] == -1) {
subtype = (subtype + 1) % NUM_DEBRIS_POFS;
}
@@ -1045,13 +1019,13 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
smi->collision_checked = false;
// set angles for last frame (need to set to prev to get p0)
angles copy_angles = smi->angs;
matrix copy_matrix = smi->canonical_orient;
// find the start and end positions of the sphere in submodel RF
smi->angs = smi->prev_angs;
smi->canonical_orient = smi->canonical_prev_orient;
world_find_model_instance_point(&p0, &light_obj->last_pos, pm, pmi, submodel, &heavy_obj->last_orient, &heavy_obj->last_pos);
smi->angs = copy_angles;
smi->canonical_orient = copy_matrix;
world_find_model_instance_point(&p1, &light_obj->pos, pm, pmi, submodel, &heavy_obj->orient, &heavy_obj->pos);
mc.p0 = &p0;
@@ -1069,7 +1043,7 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
// set up asteroid_hit_info for rotating submodel
if (asteroid_hit_info->edge_hit == 0) {
model_instance_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
model_instance_find_world_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
}
// find position in submodel RF of light object at collison
@@ -1101,7 +1075,7 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
// get collision normal if not edge hit
if (asteroid_hit_info->edge_hit == 0) {
model_instance_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
model_instance_find_world_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
}
// find position in submodel RF of light object at collison
@@ -1350,7 +1324,7 @@ static void asteroid_do_area_effect(object *asteroid_objp)
if ( weapon_area_calc_damage(ship_objp, &asteroid_objp->pos, asip->inner_rad, asip->outer_rad, asip->blast, asip->damage, &blast, &damage, asip->outer_rad) == -1 )
continue;
ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage );
ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage, asip->damage_type_idx);
weapon_area_apply_blast(NULL, ship_objp, &asteroid_objp->pos, blast, 0);
} // end for
}
@@ -1458,9 +1432,14 @@ static void asteroid_maybe_break_up(object *pasteroid_obj)
if ( timestamp_elapsed(asp->final_death_time) ) {
vec3d relvec, vfh, tvec;
bool skip = false;
bool hooked = Script_system.IsActiveAction(CHA_DEATH);
Script_system.SetHookObject("Self", pasteroid_obj);
if(!Script_system.IsConditionOverride(CHA_DEATH, pasteroid_obj))
if (hooked) {
Script_system.SetHookObject("Self", pasteroid_obj);
skip = Script_system.IsConditionOverride(CHA_DEATH, pasteroid_obj);
}
if (!skip)
{
pasteroid_obj->flags.set(Object::Object_Flags::Should_be_dead);
@@ -1510,7 +1489,7 @@ static void asteroid_maybe_break_up(object *pasteroid_obj)
int num_roids_var = split->max - split->min;
if (num_roids_var > 0)
num_roids += rand() % num_roids_var;
num_roids += Random::next(num_roids_var);
if (num_roids > 0)
for (int i=0; i<num_roids; i++)
@@ -1565,8 +1544,10 @@ static void asteroid_maybe_break_up(object *pasteroid_obj)
}
asp->final_death_time = timestamp(-1);
}
Script_system.RunCondition(CHA_DEATH, pasteroid_obj);
Script_system.RemHookVar("Self");
if (hooked) {
Script_system.RunCondition(CHA_DEATH, pasteroid_obj);
Script_system.RemHookVar("Self");
}
}
}
@@ -1788,7 +1769,7 @@ static void asteroid_parse_section(asteroid_info *asip)
}
required_string("$Detail distance:");
asip->num_detail_levels = stuff_int_list(asip->detail_distance, MAX_ASTEROID_DETAIL_LEVELS, RAW_INTEGER_TYPE);
asip->num_detail_levels = (int)stuff_int_list(asip->detail_distance, MAX_ASTEROID_DETAIL_LEVELS, RAW_INTEGER_TYPE);
required_string("$Max Speed:");
stuff_float(&asip->max_speed);
@@ -1802,7 +1783,7 @@ static void asteroid_parse_section(asteroid_info *asip)
if(optional_string("$Explosion Animations:")){
int temp[MAX_FIREBALL_TYPES];
int parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
auto parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
asip->explosion_bitmap_anims.clear();
asip->explosion_bitmap_anims.insert(asip->explosion_bitmap_anims.begin(), temp, temp+parsed_ints);
}
@@ -1849,20 +1830,81 @@ static void asteroid_parse_section(asteroid_info *asip)
} else
Warning(LOCATION, "Invalid asteroid reference %i used for $Split in asteroids table, ignoring.", split_type);
}
if (optional_string("$Spawn Weight:")) {
stuff_float(&asip->spawn_weight);
if (asip->spawn_weight <= 0.0f) {
Warning(LOCATION, "Spawn weight for asteroid '%s' must be greater than 0", asip->name);
asip->spawn_weight = 1.0f;
}
} else {
switch (Asteroid_info.size() % NUM_DEBRIS_SIZES)
{
case ASTEROID_TYPE_SMALL:
asip->spawn_weight = SMALL_DEBRIS_WEIGHT;
break;
case ASTEROID_TYPE_MEDIUM:
asip->spawn_weight = MEDIUM_DEBRIS_WEIGHT;
break;
case ASTEROID_TYPE_LARGE:
asip->spawn_weight = LARGE_DEBRIS_WEIGHT;
break;
default:
UNREACHABLE("Here be dragons");
}
}
}
// changes the name to "[species] Debris" if it had a name like "[species] debris #"
void maybe_change_asteroid_name(asteroid_info* asip) {
SCP_string name = asip->name;
size_t split = std::string::npos;
for (species_info species : Species_info) {
if (name.compare(0, strlen(species.species_name), species.species_name) == 0) {
split = strlen(species.species_name);
break;
}
}
if (split == std::string::npos)
return;
SCP_string remaining_name = name.substr(split + 1, name.length());
split = remaining_name.find(' ');
if (split == std::string::npos)
return;
SCP_string debris = remaining_name.substr(0, split);
SCP_string num = remaining_name.substr(split + 1, remaining_name.length());
if (stricmp(debris.c_str(), "Debris") != 0)
return;
if (num.empty() || std::find_if(num.begin(), num.end(), [](char c) {
return !std::isdigit(c, SCP_default_locale);
}) != num.end())
return;
// make sure this asteroid would correspond the 'species section' of the old style retail asteroids
if (Asteroid_info.size() < NUM_DEBRIS_SIZES + Species_info.size() * NUM_DEBRIS_SIZES && Asteroid_info.size() >= NUM_DEBRIS_SIZES) {
int idx = (int)(Asteroid_info.size()) / NUM_DEBRIS_SIZES - 1;
strcpy_s(asip->name, Species_info[idx].species_name);
strcat(asip->name, " ");
strcat(asip->name, XSTR("debris", 348));
}
}
/**
Read in data from asteroid.tbl into Asteroid_info[] array.
After this function is completes, the Asteroid_info[] array will
contain exactly three asteroids per species plus exactly three
generic asteroids. These three asteroids are a set
of small, medium, and large asteroids in that exact order.
If asteroid.tbl contains too many or too few asteroids as per
the rules above this function will abort the engine with an
error message stating the number expected and it will write
to the debug.log how everything was parsed.
After this function is complete, the Asteroid_info[] array
will have at least 3 generic asteroids plus whatever else
special ones were added in the table. The generic asteroids are
a set of small, medium, and large asteroids in that exact order.
Note that by saying "asteroid" this code is talking about
the asteroids and the debris that make up asteroid fields
@@ -1874,12 +1916,6 @@ that is being protected.
static void asteroid_parse_tbl()
{
char impact_ani_file[MAX_FILENAME_LEN];
// How did we get here without having any species defined?
Assertion(!Species_info.empty(),
"Cannot parse asteroids/debris if there "
"are no species for them to belong to."
);
try
{
@@ -1889,8 +1925,6 @@ static void asteroid_parse_tbl()
required_string("#Asteroid Types");
size_t tally = 0;
size_t max_asteroids =
NUM_DEBRIS_SIZES + Species_info.size() * NUM_DEBRIS_SIZES;
#ifndef NDEBUG
SCP_vector<SCP_string> parsed_asteroids;
@@ -1903,82 +1937,41 @@ static void asteroid_parse_tbl()
asteroid_parse_section(&new_asteroid);
size_t species = tally / NUM_DEBRIS_SIZES;
if (tally >= max_asteroids)
{
#ifdef NDEBUG
// Bump the warning count in release so they get something
// even if the message never gets displayed.
Warning(LOCATION, "Ignoring extra asteroid/debris");
#else
SCP_string msg("Ignoring extra asteroid/debris '");
msg.append(new_asteroid.name);
msg.append("'\n");
Warning(LOCATION, "%s", msg.c_str());
parsed_asteroids.push_back(msg);
#endif
}
else
{
maybe_change_asteroid_name(&new_asteroid);
#ifndef NDEBUG
SCP_string msg;
msg.append("Parsing asteroid: '");
msg.append(new_asteroid.name);
msg.append("' as a '");
msg.append((species == 0) ? "generic" : Species_info[species - 1].species_name);
msg.append("'");
switch (tally % NUM_DEBRIS_SIZES) {
case ASTEROID_TYPE_SMALL:
msg.append(" small\n");
break;
case ASTEROID_TYPE_MEDIUM:
msg.append(" medium\n");
break;
case ASTEROID_TYPE_LARGE:
msg.append(" large\n");
break;
default:
Error(LOCATION, "Get a coder! Math has broken!\n"
"Important numbers:\n"
"\ttally: " SIZE_T_ARG "\n"
"\tNUM_DEBRIS_SIZES: %d\n",
tally, NUM_DEBRIS_SIZES
);
msg.append(" unknown\n");
}
parsed_asteroids.push_back(msg);
#endif
Asteroid_info.push_back(new_asteroid);
SCP_string msg;
msg.append("Parsing asteroid: '");
msg.append(new_asteroid.name);
msg.append("' as a '");
msg.append((tally >= NUM_DEBRIS_SIZES) ? "generic" : "special");
msg.append("'");
switch (tally % NUM_DEBRIS_SIZES) {
case ASTEROID_TYPE_SMALL:
msg.append(" small\n");
break;
case ASTEROID_TYPE_MEDIUM:
msg.append(" medium\n");
break;
case ASTEROID_TYPE_LARGE:
msg.append(" large\n");
break;
default:
Error(LOCATION, "Get a coder! Math has broken!\n"
"Important numbers:\n"
"\ttally: " SIZE_T_ARG "\n"
"\tNUM_DEBRIS_SIZES: %d\n",
tally, NUM_DEBRIS_SIZES
);
msg.append(" unknown\n");
}
parsed_asteroids.push_back(msg);
#endif
Asteroid_info.push_back(new_asteroid);
tally++;
}
required_string("#End");
if (tally != max_asteroids)
{
#ifndef NDEBUG
mprintf(("Asteroid.tbl as parsed:\n"));
for (SCP_vector<SCP_string>::iterator iter = parsed_asteroids.begin();
iter != parsed_asteroids.end(); ++iter)
{
mprintf(("%s\n", iter->c_str()));
}
#endif
Error(LOCATION,
"Found " SIZE_T_ARG " asteroids/debris when " SIZE_T_ARG " expected\n\n"
"<Number expected> = <Number of species> * %d + %d generic asteroids\n"
SIZE_T_ARG " = " SIZE_T_ARG "*%d + %d\n\n"
#ifdef NDEBUG
"Run a debug build to see a list of all parsed asteroids\n",
#else
"See the debug.log for a listing of all parsed asteroids\n",
#endif
tally, max_asteroids,
NUM_DEBRIS_SIZES, NUM_DEBRIS_SIZES,
max_asteroids, Species_info.size(), NUM_DEBRIS_SIZES, NUM_DEBRIS_SIZES
);
}
Asteroid_impact_explosion_ani = -1;
required_string("$Impact Explosion:");
stuff_string(impact_ani_file, F_NAME, MAX_FILENAME_LEN);
+2
View File
@@ -73,6 +73,8 @@ public:
int model_num[NUM_DEBRIS_POFS];
SCP_vector<int> explosion_bitmap_anims;
float fireball_radius_multiplier; // the model radius is multiplied by this to determine the fireball size
SCP_string display_name; // only used for hud targeting display and for 'ship' asteroids
float spawn_weight; // ship asteroids only, relative proportion to spawn compared to other types in its asteroid field
asteroid_info( )
: num_detail_levels( 0 ), max_speed( 0 ), damage_type_idx( 0 ),
+3 -2
View File
@@ -716,7 +716,7 @@ bool StartAutopilot()
vm_vec_scale(&pos, 5*speed_cap*tc_factor); // pos is now scaled by 5 times the speed (5 seconds ahead)
vm_vec_add(&pos, &pos, &Autopilot_flight_leader->pos); // pos is now 5*speed cap in front of player ship
switch (myrand()%24)
switch (Random::next(24))
// 8 different ways of getting perp points
// 4 of which will not be used when capships are present (anything below, or straight above)
{
@@ -795,7 +795,8 @@ bool StartAutopilot()
vm_vec_scale(&perp, Autopilot_flight_leader->radius+radius);
// randomly scale up/down by up to 20%
j = 20-myrand()%40; // [-20,20]
// jg18 - original comment said [-20,20] but preserving existing behavior
j = Random::next(-19, 20);
vm_vec_scale(&perp, 1.0f+(float(j)/100.0f));
vm_vec_add(&cameraPos, &pos, &perp);
+7 -5
View File
@@ -287,7 +287,7 @@ static int _cfile_chdir(const char *new_dir, const char *cur_dir __UNUSED)
const char *colon = strchr(new_dir, ':');
if (colon) {
if (!cfile_chdrive(tolower(*(colon - 1)) - 'a' + 1, 1))
if (!cfile_chdrive(SCP_tolower(*(colon - 1)) - 'a' + 1, 1))
return 1;
path = colon + 1;
@@ -305,7 +305,7 @@ static int _cfile_chdir(const char *new_dir, const char *cur_dir __UNUSED)
status = _chdir(path);
if (status != 0) {
#ifdef _WIN32
cfile_chdrive(tolower(cur_dir[0]) - 'a' + 1, 1);
cfile_chdrive(SCP_tolower(cur_dir[0]) - 'a' + 1, 1);
#endif /* _WIN32 */
return 2;
}
@@ -866,6 +866,7 @@ static int cfget_cfile_block()
cfile->data = nullptr;
cfile->fp = nullptr;
cfile->type = CFILE_BLOCK_USED;
cf_clear_compression_info(cfile);
return i;
}
}
@@ -921,7 +922,7 @@ int cfclose( CFILE * cfile )
} else {
// VP do nothing
}
cf_clear_compression_info(cfile);
cfile->type = CFILE_BLOCK_UNUSED;
return result;
}
@@ -973,7 +974,7 @@ static CFILE *cf_open_fill_cfblock(const char* source, int line, const char* ori
if(pos == -1L)
pos = 0;
cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)), 0 );
cf_check_compression(cfp);
return cfp;
}
}
@@ -1008,7 +1009,7 @@ static CFILE *cf_open_packed_cfblock(const char* source, int line, const char* o
cfp->line_num = line;
cf_init_lowlevel_read_code(cfp,offset, size, 0 );
cf_check_compression(cfp);
return cfp;
}
@@ -1052,6 +1053,7 @@ static CFILE *cf_open_mapped_fill_cfblock(const char* source, int line, const ch
cfp->line_num = line;
cf_init_lowlevel_read_code(cfp, 0, 0, 0 );
#if defined _WIN32
cfp->hMapFile = CreateFileMapping(cfp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (cfp->hMapFile == NULL) {
+47 -8
View File
@@ -24,6 +24,7 @@
#include "cfile/cfile.h"
#include "cfile/cfilearchive.h"
#include "cfile/cfilecompression.h"
#include "luaconf.h"
#include <sstream>
@@ -33,7 +34,6 @@
#define CHECK_POSITION
// Called once to setup the low-level reading code.
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos )
{
Assert(cfile != nullptr);
@@ -54,6 +54,33 @@ void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size,
}
}
//This function checks the file header to see if the file is compressed, if so it fills the correct compression info.
void cf_check_compression(CFILE* cfile)
{
if (cfile->size <= 16)
return;
int header=cfread_int(cfile);
cfseek(cfile, 0, SEEK_SET);
if (comp_check_header(header) == COMP_HEADER_MATCH)
comp_create_ci(cfile, header);
}
//This function is called to cleanup compression info when the cfile handle is reused.
void cf_clear_compression_info(CFILE* cfile)
{
if (cfile->compression_info.header != 0)
{
free(cfile->compression_info.offsets);
free(cfile->compression_info.decoder_buffer);
cfile->compression_info.offsets = nullptr;
cfile->compression_info.decoder_buffer = nullptr;
cfile->compression_info.header = 0;
cfile->compression_info.block_size = 0;
cfile->compression_info.last_decoded_block_pos = 0;
cfile->compression_info.last_decoded_block_bytes = 0;
cfile->compression_info.num_offsets = 0;
}
}
// cfeof() Tests for end-of-file on a stream
@@ -61,11 +88,11 @@ void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size,
// returns a nonzero value after the first read operation that attempts to read
// past the end of the file. It returns 0 if the current position is not end of file.
// There is no error return.
int cfeof(CFILE *cfile)
{
Assert(cfile != NULL);
if (cfile->compression_info.header != 0)
return comp_feof(cfile);
int result = 0;
#if defined(CHECK_POSITION) && !defined(NDEBUG)
@@ -92,6 +119,8 @@ int cfeof(CFILE *cfile)
int cftell( CFILE * cfile )
{
Assert(cfile != NULL);
if (cfile->compression_info.header != 0)
return (int)comp_ftell(cfile);
#if defined(CHECK_POSITION) && !defined(NDEBUG)
if (cfile->fp) {
@@ -117,6 +146,9 @@ int cfseek( CFILE *cfile, int offset, int where )
Assert(cfile != NULL);
if (cfile->compression_info.header != 0)
return comp_fseek(cfile, offset, where);
// TODO: seek to offset in memory mapped file
Assert( !cfile->mem_mapped );
@@ -202,23 +234,31 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
// This is a file from memory
bytes_read = size;
memcpy(buf, reinterpret_cast<const char*>(cfile->data) + cfile->raw_position, size);
} else if (cfile->compression_info.header != 0) {
bytes_read = comp_fread(cfile, reinterpret_cast<char*>(buf),size);
if (bytes_read != size)
{
mprintf(("\nFile: %s decompression error. Result was: %d expected: %d\n", cfile->original_filename.c_str(), (int)bytes_read, (int)size));
Assertion(bytes_read == size, "File decompression error!");
}
} else {
bytes_read = fread( buf, 1, size, cfile->fp );
bytes_read = fread(buf, 1, size, cfile->fp);
}
if ( bytes_read > 0 ) {
cfile->raw_position += bytes_read;
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
}
#if defined(CHECK_POSITION) && !defined(NDEBUG)
if (cfile->fp) {
//Raw position is not the fp position with compressed files
if (cfile->fp && cfile->compression_info.header == 0) {
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
Assert(tmp_offset==cfile->raw_position);
Assert(tmp_offset == cfile->raw_position);
}
#endif
return (int)(bytes_read / elsize);
}
int cfread_lua_number(double *buf, CFILE *cfile)
@@ -266,4 +306,3 @@ int cfread_lua_number(double *buf, CFILE *cfile)
return items_read;
}
+19 -2
View File
@@ -7,7 +7,6 @@
*
*/
#ifndef _CFILEARCHIVE_H
#define _CFILEARCHIVE_H
@@ -23,6 +22,17 @@
#define CFILE_BLOCK_UNUSED 0
#define CFILE_BLOCK_USED 1
struct COMPRESSION_INFO {
int header = 0;
size_t compressed_size = 0;
int block_size = 0;
int num_offsets = 0;
int* offsets = nullptr;
char* decoder_buffer = nullptr;
int last_decoded_block_pos = 0;
int last_decoded_block_bytes = 0;
};
struct CFILE {
int type = CFILE_BLOCK_UNUSED; // CFILE_BLOCK_UNUSED, CFILE_BLOCK_USED
int dir_type; // directory location
@@ -44,12 +54,19 @@ struct CFILE {
SCP_string original_filename;
const char* source_file;
int line_num;
COMPRESSION_INFO compression_info;
};
#define MAX_CFILE_BLOCKS 64
extern std::array<CFILE, MAX_CFILE_BLOCKS> Cfile_block_list;
// Called once to setup the low-level reading code.
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos );
void cf_init_lowlevel_read_code(CFILE* cfile, size_t lib_offset, size_t size, size_t pos);
// This checks if the file is compressed or not, and creates the proper compression info if so.
void cf_check_compression(CFILE* cfile);
// Used to clear compression info data and free dynamic memory used by compression
void cf_clear_compression_info(CFILE* cfile);
#endif
+206
View File
@@ -0,0 +1,206 @@
#define _CFILE_INTERNAL
#include <cstdlib>
#include <cstring>
#include <cstdio>
#ifdef _WIN32
#include <io.h>
#include <direct.h>
#include <windows.h>
#include <winbase.h>
#endif
#include "lz4.h"
#include "cfilecompression.h"
#include "cfilearchive.h"
/*INTERNAL FUNCTIONS*/
/*LZ41*/
void lz41_load_offsets(CFILE* cf);
size_t lz41_stream_random_access(CFILE* cf, char* bytes_out, size_t offset, size_t length);
void lz41_create_ci(CFILE* cf, int header);
/*MISC*/
int fso_fseek(CFILE* cfile, int offset, int where);
/*END OF INTERNAL FUNCTIONS*/
int comp_check_header(int header)
{
if (LZ41_FILE_HEADER == header)
return COMP_HEADER_MATCH;
return COMP_HEADER_MISMATCH;
}
void comp_create_ci(CFILE* cf, int header)
{
//mprintf(("(CI)Compressed File Opened: %s \n", cf->original_filename.c_str()));
if (LZ41_FILE_HEADER == header)
lz41_create_ci(cf, header);
//mprintf(("(CI)Uncompressed FileSize: %d \n", cf->size));
//mprintf(("(CI)Compressed FileSize: %d \n", cf->compression_info.compressed_size));
//mprintf(("(CI)Block Size: %d \n", cf->compression_info.block_size));
}
size_t comp_fread(CFILE* cf, char* buffer, size_t length)
{
/* Check the request to be at least 1 byte */
Assertion(length > 0, "Invalid length requested.");
/* Check that we are not requesting to read beyond end of file */
Assertion(cf->raw_position + length <= cf->size, "Invalid length requested.");
if (LZ41_FILE_HEADER == cf->compression_info.header)
return lz41_stream_random_access(cf, buffer, cf->raw_position, length);
return 0;
}
size_t comp_ftell(CFILE* cf)
{
return cf->raw_position;
}
int comp_feof(CFILE* cf)
{
if (cf->raw_position >= cf->size)
return 1;
else
return 0;
}
int comp_fseek(CFILE* cf, int offset, int where)
{
size_t goal_position;
switch (where) {
case SEEK_SET: goal_position = offset; break;
case SEEK_CUR: goal_position = cf->raw_position + offset; break;
case SEEK_END: goal_position = cf->size + offset; break;
default: return 1;
}
// Make sure we don't seek beyond the end of the file
CAP(goal_position,(size_t)0, cf->size);
cf->raw_position = goal_position;
return 0;
}
//Special fseek for compressed files handled by FSO, only SEEK_SET and SEEK_END is supported.
int fso_fseek(CFILE* cfile, int offset, int where)
{
size_t goal_position;
switch (where) {
case SEEK_SET: goal_position = offset + cfile->lib_offset; break;
case SEEK_END: goal_position = cfile->compression_info.compressed_size + offset + cfile->lib_offset; break;
default: return 0;
}
// Make sure we don't seek beyond the end of the file
CAP(goal_position, cfile->lib_offset, cfile->lib_offset + cfile->compression_info.compressed_size);
int result = 0;
if (cfile->fp)
{
result = fseek(cfile->fp, (long)goal_position, SEEK_SET);
Assertion(goal_position >= cfile->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cfile->lib_offset);
}
return result;
}
void lz41_create_ci(CFILE* cf, int header)
{
cf->compression_info.header = header;
cf->compression_info.compressed_size = cf->size;
fso_fseek(cf, (int)sizeof(int)*-3, SEEK_END);
auto fNumoffsets = fread(&cf->compression_info.num_offsets, sizeof(int), 1, cf->fp);
auto fSize = fread(&cf->size, sizeof(int), 1, cf->fp);
auto fBsize = fread(&cf->compression_info.block_size, sizeof(int), 1, cf->fp);
Assertion(cf->compression_info.num_offsets > 0, "Invalid number of offsets, compressed file is possibly in the wrong format or corrupted.");
#if !defined(NDEBUG)
Assertion(cf->size > 4, "Invalid filesize, compressed file is possibly in the wrong format or corrupted.");
Assertion(cf->compression_info.block_size > 16, "Invalid block size, compressed file is possibly in the wrong format or corrupted.");
Assertion(fNumoffsets == 1, "Error while reading the number of offsets, compressed file is possibly in the wrong format or corrupted.");
Assertion(fSize == 1, "Error while reading original filesize, compressed file is possibly in the wrong format or corrupted.");
Assertion(fBsize == 1, "Error while reading block size, compressed file is possibly in the wrong format or corrupted.");
#endif
cf->compression_info.decoder_buffer = (char*)malloc(LZ4_compressBound(cf->compression_info.block_size));
cf->compression_info.last_decoded_block_pos = 0;
cf->compression_info.last_decoded_block_bytes = 0;
lz41_load_offsets(cf);
}
void lz41_load_offsets(CFILE* cf)
{
cf->compression_info.offsets = (int*)malloc(cf->compression_info.num_offsets * sizeof(int));
int block;
int* offsets_ptr = cf->compression_info.offsets;
/* Seek to the first offset position, remember to consider the trailing ints */
fso_fseek(cf, ( ( sizeof(int) * cf->compression_info.num_offsets ) * -1 ) - (sizeof(int)*3 ), SEEK_END);
for (block = 0; block < cf->compression_info.num_offsets; ++block)
{
auto bytes_read = fread(offsets_ptr++, sizeof(int), 1, cf->fp);
Assertion(bytes_read == 1, "Error reading offset list.");
}
}
size_t lz41_stream_random_access(CFILE* cf, char* bytes_out, size_t offset, size_t length)
{
LZ4_streamDecode_t lz4_stream_decode_body;
LZ4_streamDecode_t* lz4_stream_decode = &lz4_stream_decode_body;
/* The blocks (current_block to end_block) contain the data we want */
size_t current_block = offset / cf->compression_info.block_size;
size_t end_block = ((offset + length - 1) / cf->compression_info.block_size) + 1;
size_t written_bytes = 0;
if (cf->compression_info.num_offsets <= (int)end_block)
return (size_t)LZ41_OFFSETS_MISMATCH;
/* Seek to the first block to read, if it matches the cached block, search the next one instead */
if (cf->compression_info.last_decoded_block_pos == cf->compression_info.offsets[current_block] && current_block + 1 <= end_block)
fso_fseek(cf, cf->compression_info.offsets[current_block+1], SEEK_SET);
else
fso_fseek(cf, cf->compression_info.offsets[current_block], SEEK_SET);
offset = offset % cf->compression_info.block_size;
char* cmp_buf = (char*)malloc(LZ4_compressBound(cf->compression_info.block_size));
/* Start decoding */
for (; current_block < end_block; ++current_block)
{
/* Only read and decode if the requested block is not the cached one */
if (cf->compression_info.last_decoded_block_pos != cf->compression_info.offsets[current_block])
{
cf->compression_info.last_decoded_block_pos = cf->compression_info.offsets[current_block];
/* The difference in offsets is the size of the block */
int cmp_bytes = cf->compression_info.offsets[current_block + 1] - cf->compression_info.offsets[current_block];
auto bytes_read = fread(cmp_buf, cmp_bytes, 1, cf->fp);
Assertion(bytes_read == 1, "Error reading from compressed file.");
cf->compression_info.last_decoded_block_bytes = LZ4_decompress_safe_continue(lz4_stream_decode, cmp_buf, cf->compression_info.decoder_buffer, cmp_bytes, cf->compression_info.block_size);
if (cf->compression_info.last_decoded_block_bytes <= 0)
{
free(cmp_buf);
return (size_t)LZ41_DECOMPRESSION_ERROR;
}
}
/* Write out the part of the data we care about from buffer */
size_t block_length = (length < (cf->compression_info.last_decoded_block_bytes - offset) ? length : (cf->compression_info.last_decoded_block_bytes - offset));
memcpy(bytes_out + written_bytes, cf->compression_info.decoder_buffer + offset, (size_t)block_length);
written_bytes += block_length;
offset = 0;
length -= block_length;
}
free(cmp_buf);
return written_bytes;
}
+66
View File
@@ -0,0 +1,66 @@
/*
ShivanSpS - Compressed files support for FSO. Many thanks to ngld, taylor and everyone elsewho helped me in getting this done.
-The file header is a version, this is used to tell FSO how to decompress that file, always use 4 chars to mantain alignment.
-The header ID can be used to add diferent revisions to LZ41 decompression system or to add other compression format supports whiout breaking compatibility.
-The system uses a offset list to record the position of every block in file, this list, along with the number of offsets, original filesize,
and block size, must be written by the compressor app.
-A decoder cache is used to store the last decoded block, this ensures each block is read and decoded only once in sequential reads. This cache
is a little bit bigger than the block size, a higher block size means less overhead added to the file, but it also means a little more ram will
be used during decompression.
-All this dynamic memory is assigned at cfopen() and it is cleared on cfclose().
................................char[4]..........(n ints)...(int)..........(int)..........(int)
-COMPRESSED FILE DATA STUCTURE: HEADER|N BLOCKS|N OFFSETS|NUM_OFFSETS|ORIGINAL_FILESIZE|BLOCK_SIZE
*/
#ifndef _CFILECOMPRESSION_H
#define _CFILECOMPRESSION_H
struct CFILE;
/*LZ41*/
#define LZ41_FILE_HEADER 0x31345A4C // "14ZL" -> "LZ41", inverted
#define LZ41_DECOMPRESSION_ERROR -1
#define LZ41_MAX_BLOCKS_OVERFLOW -2
#define LZ41_HEADER_MISMATCH -3
#define LZ41_OFFSETS_MISMATCH -4
/******/
#define COMP_HEADER_MATCH 1
#define COMP_HEADER_MISMATCH 0
/*
Returns COMP_HEADER_MATCH if header is a valid compressed file header,
returns COMP_HEADER_MISMATCH if it dosent.
*/
int comp_check_header(int header);
/*
This is called to generate the correct compression_info data
after the file has been indentified as a compressed file.
This must be done before calling any other function.
*/
void comp_create_ci(CFILE* cf, int header);
/*
Read X bytes from the uncompressed file starting from X offset.
Returns the amount of bytes read, and 0 or lower to indicate errors.
*/
size_t comp_fread(CFILE* cf, char* buffer, size_t length);
/*
Returns the current uncompressed file position.
*/
size_t comp_ftell(CFILE* cf);
/*
Returns 1 of the uncompressed file has been completely read, otherwise it returns a 0.
*/
int comp_feof(CFILE* cf);
/*
Used to move the uncompressed file current position.
*/
int comp_fseek(CFILE* cf, int offset, int where);
#endif
+14 -14
View File
@@ -246,7 +246,7 @@ Flag exe_params[] =
{ "-noninteractive", "Disables interactive dialogs", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noninteractive", },
{ "-no_unfocused_pause","Don't pause if the window isn't focused", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_unfocused_pause", },
{ "-benchmark_mode", "Puts the game into benchmark mode", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-benchmark_mode", },
{ "-profile_frame_time","Profile frame time", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_time", },
{ "-profile_frame_time","Profile frame time", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_time", },
{ "-profile_write_file", "Write profiling information to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_write_file", },
{ "-json_profiling", "Generate JSON profiling output", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-json_profiling", },
{ "-debug_window", "Enable the debug window", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-debug_window", },
@@ -274,7 +274,6 @@ cmdline_parm gameclosed_arg("-closed", NULL, AT_NONE); // Cmdline_closed_game
cmdline_parm gamerestricted_arg("-restricted", NULL, AT_NONE); // Cmdline_restricted_game
cmdline_parm port_arg("-port", "Multiplayer network port", AT_INT);
cmdline_parm multilog_arg("-multilog", NULL, AT_NONE); // Cmdline_multi_log
cmdline_parm client_dodamage("-clientdamage", NULL, AT_NONE); // Cmdline_client_dodamage
cmdline_parm pof_spew("-pofspew", NULL, AT_NONE); // Cmdline_spew_pof_info
cmdline_parm weapon_spew("-weaponspew", nullptr, AT_STRING); // Cmdline_spew_weapon_stats
cmdline_parm mouse_coords("-coords", NULL, AT_NONE); // Cmdline_mouse_coords
@@ -287,7 +286,6 @@ char *Cmdline_game_password = NULL;
char *Cmdline_rank_above = NULL;
char *Cmdline_rank_below = NULL;
int Cmdline_cd_check = 1;
int Cmdline_client_dodamage = 0;
int Cmdline_closed_game = 0;
int Cmdline_freespace_no_music = 0;
int Cmdline_freespace_no_sound = 0;
@@ -481,6 +479,7 @@ cmdline_parm reparse_mainhall_arg("-reparse_mainhall", NULL, AT_NONE); //Cmdline
cmdline_parm frame_profile_write_file("-profile_write_file", NULL, AT_NONE); // Cmdline_profile_write_file
cmdline_parm no_unfocused_pause_arg("-no_unfocused_pause", NULL, AT_NONE); //Cmdline_no_unfocus_pause
cmdline_parm benchmark_mode_arg("-benchmark_mode", NULL, AT_NONE); //Cmdline_benchmark_mode
cmdline_parm pilot_arg("-pilot", nullptr, AT_STRING); //Cmdline_pilot
cmdline_parm noninteractive_arg("-noninteractive", NULL, AT_NONE); //Cmdline_noninteractive
cmdline_parm json_profiling("-json_profiling", NULL, AT_NONE); //Cmdline_json_profiling
cmdline_parm show_video_info("-show_video_info", NULL, AT_NONE); //Cmdline_show_video_info
@@ -512,6 +511,7 @@ int Cmdline_reparse_mainhall = 0;
bool Cmdline_profile_write_file = false;
bool Cmdline_no_unfocus_pause = false;
bool Cmdline_benchmark_mode = false;
const char *Cmdline_pilot = nullptr;
bool Cmdline_noninteractive = false;
bool Cmdline_json_profiling = false;
bool Cmdline_frame_profile = false;
@@ -812,7 +812,7 @@ void os_validate_parms(int argc, char *argv[])
if (!stricmp(token, "-help") || !stricmp(token, "--help") || !stricmp(token, "-h") || !stricmp(token, "-?")) {
printf("FreeSpace 2 Open, version %s\n", FS_VERSION_FULL);
printf("Website: http://scp.indiegames.us\n");
printf("Mantis (bug reporting): http://scp.indiegames.us/mantis/\n\n");
printf("Github (bug reporting): https://github.com/scp-fs2open/fs2open.github.com/issues\n\n");
printf("Usage: fs2_open [options]\n");
// not the prettiest thing but the job gets done
@@ -1619,7 +1619,7 @@ bool SetCmdlineParams()
Cmdline_game_name = gamename_arg.str();
// be sure that this string fits in our limits
if ( strlen(Cmdline_game_name) > MAX_GAMENAME_LEN ) {
if ( strlen(Cmdline_game_name) >= MAX_GAMENAME_LEN ) {
Cmdline_game_name[MAX_GAMENAME_LEN-1] = '\0';
}
}
@@ -1629,8 +1629,9 @@ bool SetCmdlineParams()
Cmdline_game_password = gamepassword_arg.str();
// be sure that this string fits in our limits
if ( strlen(Cmdline_game_name) > MAX_PASSWD_LEN ) {
Cmdline_game_name[MAX_PASSWD_LEN-1] = '\0';
if ( strlen(Cmdline_game_password) >= MAX_PASSWD_LEN ) {
ReleaseWarning(LOCATION, "Multi game password is longer than max of %d charaters and will be trimmed to fit!", MAX_PASSWD_LEN-1);
Cmdline_game_password[MAX_PASSWD_LEN-1] = '\0';
}
}
@@ -1663,12 +1664,6 @@ bool SetCmdlineParams()
Cmdline_multi_log = 1;
}
// maybe use old-school client damage
if(client_dodamage.found()){
Cmdline_client_dodamage = 1;
}
// spew pof info
if(pof_spew.found()){
Cmdline_spew_pof_info = 1;
@@ -1721,7 +1716,7 @@ bool SetCmdlineParams()
sprintf(override, "{\"width\":%d,\"height\":%d}", width, height);
options::OptionsManager::instance()->setOverride("Graphics.Resolution", override);
} else {
mprintf(("Failed to parse -res parameter \"%s\". Must be in format \"<width>x<height>\".", Cmdline_res));
mprintf(("Failed to parse -res parameter \"%s\". Must be in format \"<width>x<height>\".\n", Cmdline_res));
}
}
if(center_res_arg.found()){
@@ -2131,6 +2126,11 @@ bool SetCmdlineParams()
Cmdline_benchmark_mode = true;
}
if (pilot_arg.found())
{
Cmdline_pilot = pilot_arg.str();
}
if (noninteractive_arg.found())
{
Cmdline_noninteractive = true;
+1 -1
View File
@@ -30,7 +30,6 @@ extern char *Cmdline_game_password;
extern char *Cmdline_rank_above;
extern char *Cmdline_rank_below;
extern int Cmdline_cd_check;
extern int Cmdline_client_dodamage;
extern int Cmdline_closed_game;
extern int Cmdline_freespace_no_music;
extern int Cmdline_freespace_no_sound;
@@ -144,6 +143,7 @@ extern int Cmdline_reparse_mainhall;
extern bool Cmdline_profile_write_file;
extern bool Cmdline_no_unfocus_pause;
extern bool Cmdline_benchmark_mode;
extern const char *Cmdline_pilot;
extern bool Cmdline_noninteractive;
extern bool Cmdline_json_profiling;
extern bool Cmdline_frame_profile;
File diff suppressed because it is too large. Load diff
+336 -28
View File
@@ -28,12 +28,27 @@ enum Joy_axis_index {
JOY_RZ_AXIS
};
/*!
* Controller index enumeration
* @details For use with hardcoded bindings and scripting API to allow human-readable translation
* @note These enums are hardcoded so that an int value of 0 would be JOY0
*/
enum CID : short {
CID_NONE = -3, // No device bound
CID_KEYBOARD = -2, // button belongs to keyboard
CID_MOUSE = -1, // to mouse
CID_JOY0 = 0, // to Joy0
CID_JOY1 = 1, // to Joy1 (Throttle?)
CID_JOY2 = 2, // to Joy2 (Pedals?)
CID_JOY3 = 3 // to Joy3 (Head tracker?)
// Any additional joystick is an int >3
};
/*!
* These are used to index a corresponding (analog) action, namely controlling the orientation angles and throttle.
*/
enum Joy_axis_action_index {
JOY_HEADING_AXIS =0,
JOY_HEADING_AXIS =0, //
JOY_PITCH_AXIS,
JOY_BANK_AXIS,
JOY_ABS_THROTTLE_AXIS,
@@ -45,7 +60,16 @@ enum Joy_axis_action_index {
NUM_JOY_AXIS_ACTIONS //!< The total number of actions an axis may map to
};
/*!
* Joystick action modes
* note: should this be in joy.h?
*/
enum Joy_axis_action_mode {
JAAM_ABS, //!< Absolute mode. Axis position = output value
JAAM_REL, //!< Relative mode. Axis postion away from its center adds or subtracts an output value register.
JAAM_BTN_NEG, //!< Button mode, negative side. Axis position in the negative side will trigger a button action
JAAM_BTN_POS, //!< Button mode, positive side. Axis position in the positive side will trigger a button action
};
/*!
* Control Configuration Types. Namely differ in how the control is activated
@@ -55,27 +79,12 @@ enum CC_type {
CC_TYPE_CONTINUOUS //!< A continous control that is activated as long as the key or button is held down
};
/*!
* Control configuration item type.
*/
typedef struct config_item {
short key_default; //!< default key bound to action
short joy_default; //!< default joystick button bound to action
char tab; //!< what tab (category) it belongs in
int indexXSTR; //!< what string index we should use to translate this with an XSTR
const char *text; //!< describes the action in the config screen
char type; //!< manner control should be checked in
short key_id; //!< actual key bound to action
short joy_id; //!< joystick button bound to action
int used; //!< has control been used yet in mission? If so, this is the timestamp
bool disabled; //!< whether this action should be available at all
bool continuous_ongoing;//!< whether this action is a continuous one and is currently ongoing
} config_item;
/*!
* All available actions
* This is the value of the id field in config_item
* The first group of items are ship targeting.
*
* Note: Do not adjust the order or numeric value
*/
enum IoActionId {
TARGET_NEXT =0, //!< target next
@@ -252,7 +261,7 @@ enum IoActionId {
DOWN_SLIDE_THRUST =111, //!< DOWN_SLIDE_THRUST
HUD_TARGETBOX_TOGGLE_WIREFRAME =112, //!< HUD_TARGETBOX_TOGGLE_WIREFRAME
VIEW_TOPDOWN =113, //!< VIEW_TOPDOWN
VIEW_TRACK_TARGET =114, //!< VIEW_TRACK_TARGET
VIEW_TRACK_TARGET =114, //!< VIEW_TRACK_TARGET (Switfty) Toggle for VM_Track
//!< @n
//!< AutoPilot - Kazan
@@ -285,10 +294,200 @@ enum IoActionId {
CCFG_MAX //!< The total number of defined control actions (or last define + 1)
};
class CCB;
/*!
* A singular button binding
*/
class CC_bind {
public:
CID cid = CID_NONE; //!< Which controller this belongs to
short btn = -1; //!< Which button to index; If cid == CID_KEYBOARD, this is a key hash.
public:
CC_bind() = default;
CC_bind(const CC_bind &A) = default;
CC_bind(CID _cid, short _btn) : cid(_cid), btn(_btn) {};
CC_bind& operator=(const CC_bind &A);
/*!
* Checks if this CC_bind is equal to the given CC_bind
*/
bool operator==(const CC_bind &B) const;
/*!
* Checks if this CC_bind is equal to either in the pair
*/
bool operator==(const CCB &pair) const;
/*!
* Checks if this CC_bind is not equal to the given CC_bind
*/
bool operator!=(const CC_bind &B) const;
/*!
* Checks if this CC_bind is not equal to either in the pair
*/
bool operator!=(const CCB &pair) const;
/*!
* Clears the binding
*/
void clear();
/*!
* True if not bound
*/
bool empty() const;
/*!
* Returns a human-readable string of this binding
*/
SCP_string textify() const;
};
/*!
* A pair of bindings.
* @note Please don't set the bindings directly, use ::take() instead.
*/
class CCB {
public:
CC_bind first; // The primary binding
CC_bind second; // The secondary binding
public:
CCB() = default;
CCB(const CCB& A) = default;
/*!
* Returns true if nothing is bound
*/
bool empty() const;
/*!
* Takes a given binding
*
* @param[in] A The bind to take
* @param[in] order 0 is primary, 1 is secondary, -1 is overwrite existing bind (if any)
*
* @note Should there be a binding to the same controller as the given binding, it is cleared
*/
void take(CC_bind A, int order);
/*!
* Clears both bindings
*/
void clear();
/*!
* Gets the value of btn if either CC_bind has the given CID
*
* @returns the btn of the bound controller, or
* @returns -1 if that controller is not bound
*/
short get_btn(CID) const;
/*!
* Assigns the contents of the given CCB to this CCB
*/
CCB& operator=(const CCB&);
/*!
* Returns True if this CCB's first isn't empty and the given CCB has a binding equal to it
*/
bool has_first(const CCB&) const;
/*!
* Returns True if this CCB's second isn't empty and the given CCB has a binding equal to it
*/
bool has_second(const CCB&) const;
};
/*!
* A preset, a collection of bindings for use in Control_config with an associated name
*/
class CC_preset {
public:
SCP_vector<CCB> bindings;
SCP_string name;
};
/*!
* Control configuration item type.
* @detail Contains binding info, documentation, behavior, etc. for a single control
*/
class CCI : public CCB {
public:
// Inherited from CCB
// CC_bind first;
// CC_bind second;
// Items Set in menu
char tab; //!< what tab (category) it belongs in
int indexXSTR; //!< what string index we should use to translate this with an XSTR 0 = None, 1= Use item index + CONTROL_CONFIG_XSTR, 2 <= use CCI::indexXSTR directly
SCP_string text; //!< describes the action in the config screen
CC_type type; //!< manner control should be checked in
// Items used during gameplay
int used; //!< has control been used yet in mission? If so, this is the timestamp
bool disabled = true; //!< whether this action should be available at all
bool continuous_ongoing; //!< whether this action is a continuous one and is currently ongoing
public:
CCI() = default;
CCI(const CCI& A) = default;
/*!
* Assigns the contents of the given CCI to this CCI
*/
CCI& operator=(const CCI&);
/*!
* @brief Takes the bindings of the given CCB, but leaves all other members alone
*/
CCI& operator=(const CCB&);
};
/*!
* Builder predicate to populate a ControlConfig vector with hardcoded default bindings.
*/
class CCI_builder {
public:
/*!
* Initilizes the given ControlConfig vector
*/
CCI_builder(SCP_vector<CCI>& _ControlConfig);
/*!
* Start a chain of factory methods. If there are any pre-init work to be done, that's done here.
*/
CCI_builder& start();
/*!
* End a chain of factory methods. If there any post-init work to be done, that's done here.
*/
void end();
/*!
* Assigns the hardcoded binding to the given action
* @note This differs from the original hardcode from :V: in the hopes of it being more intuitive to future IoAction additions
*/
CCI_builder& operator()(IoActionId action_id, short key_default, short joy_default, char tab, int indexXSTR, const char *text, CC_type type, bool disabled = false);
private:
CCI_builder(); // Only one builder per Control Config, so a default constructor is useless
SCP_vector<CCI>& ControlConfig;
};
extern int Failed_key_index;
extern int Axis_map_to[];
extern int Invert_axis[];
extern int Axis_map_to[]; // Array to map an axis action to a joy axis. size() = NUM_JOY_AXIS_ACTIONS
extern int Axis_map_to_defaults[];
extern int Invert_axis[]; // Array to hold inversion bools for a joy axis. size() = JOY_NUM_AXES
extern int Invert_axis_defaults[];
extern int Joy_dead_zone_size;
@@ -296,23 +495,76 @@ extern int Joy_sensitivity;
extern int Control_config_overlay_id;
extern config_item Control_config[]; //!< Stores the keyboard configuration
extern SCP_vector<config_item*> Control_config_presets; // tabled control presets; pointers to config_item arrays
extern SCP_vector<SCP_string> Control_config_preset_names; // names for Control_config_presets (identical order of items)
extern SCP_vector<CCI> Control_config; //!< Stores the keyboard configuration
extern SCP_vector<CC_preset> Control_config_presets; // tabled control presets; pointers to config_item arrays
extern const char **Scan_code_text;
extern const char **Joy_button_text;
void control_config_common_init(); //!< initialize common control config stuff - call at game startup after localization has been initialized
void control_config_common_close(); //!< close common control config stuff - call at game shutdown
/*!
* @brief Checks if either binding in the CCB has the given cid
*
* @returns 0 if its the first element, or
* @returns 1 if its the second element, or
* @returns -1 if neither
*/
int is_cid_either(CID, const CCB);
/*!
* @brief initialize common control config stuff - call at game startup after localization has been initialized
*/
void control_config_common_init();
/*!
* @brief close common control config stuff - call at game shutdown
*/
void control_config_common_close();
/*!
* @brief init config menu
*/
void control_config_init();
/*!
* @brief do a frame of the config menu
*/
void control_config_do_frame(float frametime);
/*!
* @brief close config menu
*/
void control_config_close();
/*!
* @brief Cancel configuration of controls, revert any changes, return to previous menu/game state
*/
void control_config_cancel_exit();
void control_config_reset_defaults(int presetnum=-1);
/*!
* @brief Resets all controls to values within the currently selected preset
*/
void control_config_reset_defaults();
/*!
* Returns the IoActionId of a control bound to the given key
*
* @param[in] key The key combo to look for
* @param[in] find_override If true, return the IoActionId of a control that has this key as its default
* @details If find_override is set to true, then this returns the index of the action
*/
int translate_key_to_index(const char *key, bool find_override=true);
/*!
* @brief Given the system default key 'key', return the current key that is bound to that function.
*
* @param[in] key The default key combo (as string) to a certain control
*
* @returns The key combo (as string) currently bound to the control
*
* @details Both the 'key' and the return value are descriptive strings that can be displayed
* directly to the user. If 'key' isn't a real key, is not normally bound to anything,
* or there is no key currently bound to the function, NULL is returned.
*/
char *translate_key(char *key);
/**
@@ -334,14 +586,70 @@ const char *textify_scancode(int code);
*/
const char *textify_scancode_universal(int code);
/*!
* @brief Checks how long a control has been active
*
* @param[in] id The IoActionId of the control to check
*
* @returns Time, in milliseconds, that the control has been active.
*
* @note This function is only to be used on CC_CONTINOUS type controls, or it will trigger an assertion
*/
float check_control_timef(int id);
/**
* @brief Wrapper for check_control_used. Allows the game to ignore the key if told to do so by the ignore-key SEXP.
*
* @brief id The IoActionId of the control to check
* @brief key The key combo to check against the control.
*
* @returns 0 If the control wasn't used, or
* @returns 1 If the control was used
*/
int check_control(int id, int key = -1);
/**
* @brief Gets the scaled reading for all control axes.
*
* @param[out] h heading
* @param[out] p pitch
* @param[out] b bank
* @param[out] ta Throttle - Absolute
* @param[out] tr Throttle - Relative
*
* @note None of the input pointers may be nullptr
*/
void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr);
/**
* @brief Markes the given control (by IoActionId) as used
*
* @details Updates the ::used timestamp, triggers a script hook, and marks ::continous_ongoing as true
*/
void control_used(int id);
/**
* @brief Clears the bindings of all controls
*/
void control_config_clear();
/**
* @brief debug function used to indicate number of controls checked
*/
void control_check_indicate();
/**
* @brief Clears the used timestamp of all controls
*/
void control_config_clear_used_status();
/**
* @brief Applies sensitivity multiplier and deadzone histerisis to the raw axis value
*
* @param[in] raw The raw axis value to transform
*
* @return The transformed value
*/
int joy_get_scaled_reading(int raw);
#endif
File diff suppressed because it is too large. Load diff
+2 -3
View File
@@ -93,13 +93,12 @@ class Decoder {
bool m_decoding;
size_t m_queueSize = 0;
Decoder(const Decoder&) SCP_DELETED_FUNCTION;
Decoder& operator=(const Decoder&) SCP_DELETED_FUNCTION;
protected:
Decoder();
public:
Decoder(const Decoder&) = delete;
Decoder& operator=(const Decoder&) = delete;
virtual ~Decoder();
+1 -1
View File
@@ -613,7 +613,7 @@ void cutscenes_screen_do_frame()
if (src)
{
Text_size = split_str(src, Cutscene_desc_coords[gr_screen.res][2], Text_line_size, Text_lines,
Cutscene_max_text_lines[gr_screen.res]);
Cutscene_max_text_lines[gr_screen.res], MAX_TEXT_LINE_LEN);
Assert(Text_size >= 0 && Text_size < Cutscene_max_text_lines[gr_screen.res]);
}
}
+3 -3
View File
@@ -286,7 +286,7 @@ std::unique_ptr<DecoderStatus> initializeStatus(std::unique_ptr<InputStream>& st
if (status->subtitleStream == nullptr) {
// We don't have an external subtitle stream or loading from that file has failed
auto& current_language = Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang];
auto& current_language = Lcl_languages[lcl_get_current_lang_index()];
for (uint32_t i = 0; i < ctx->nb_streams; ++i) {
auto test_stream = ctx->streams[i];
@@ -428,7 +428,7 @@ std::unique_ptr<InputStream> openInputStream(const SCP_string& name) {
std::unique_ptr<InputStream> openSubtitleStream(const SCP_string& name)
{
auto& current_language = Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang];
auto& current_language = Lcl_languages[lcl_get_current_lang_index()];
for (auto ext : CHECKED_SUBT_EXTENSIONS) {
SCP_string fileName;
@@ -453,7 +453,7 @@ bool FFMPEGDecoder::initialize(const SCP_string& fileName, const PlaybackPropert
{
SCP_string movieName = fileName;
// First make the file name lower case
std::transform(movieName.begin(), movieName.end(), movieName.begin(), [](char c) { return (char)::tolower(c); });
SCP_tolower(movieName);
// Then remove the extension
size_t dotPos = movieName.find('.');
+4 -6
View File
@@ -45,10 +45,8 @@ struct DecoderStatus {
~DecoderStatus();
private:
DecoderStatus(const DecoderStatus&) SCP_DELETED_FUNCTION;
DecoderStatus& operator=(const DecoderStatus&) SCP_DELETED_FUNCTION;
DecoderStatus(const DecoderStatus&) = delete;
DecoderStatus& operator=(const DecoderStatus&) = delete;
};
template<typename Frame>
@@ -67,8 +65,8 @@ class FFMPEGStreamDecoder {
explicit FFMPEGStreamDecoder(DecoderStatus* status);
virtual ~FFMPEGStreamDecoder();
FFMPEGStreamDecoder(const FFMPEGStreamDecoder&) SCP_DELETED_FUNCTION;
FFMPEGStreamDecoder& operator=(const FFMPEGStreamDecoder&) SCP_DELETED_FUNCTION;
FFMPEGStreamDecoder(const FFMPEGStreamDecoder&) = delete;
FFMPEGStreamDecoder& operator=(const FFMPEGStreamDecoder&) = delete;
virtual void decodePacket(AVPacket* packet) = 0;
+85 -89
View File
@@ -30,8 +30,9 @@
#include "ship/ship.h"
#include "ship/shipfx.h"
#include "species_defs/species_defs.h"
#include "weapon/weapon.h"
#include "tracing/Monitor.h"
#include "utils/Random.h"
#include "weapon/weapon.h"
#define MIN_RADIUS_FOR_PERSISTANT_DEBRIS 50 // ship radius at which debris from it becomes persistant
#define DEBRIS_SOUND_DELAY 2000 // time to start debris sound after created
@@ -51,8 +52,6 @@ int Debris_model = -1;
int Debris_vaporize_model = -1;
int Debris_num_submodels = 0;
#define MAX_DEBRIS_DIST 10000.0f // Debris goes away if it's this far away.
#define DEBRIS_DISTANCE_CHECK_TIME (10*1000) // Check every 10 seconds.
#define DEBRIS_INDEX(dp) (int)(dp-Debris.data())
const auto OnDebrisCreatedHook = scripting::Hook::Factory(
@@ -75,7 +74,7 @@ static void debris_start_death_roll(object *debris_obj, debris *debris_p)
int fireball_type = fireball_ship_explosion_type(&Ship_info[debris_p->ship_info_index]);
if(fireball_type < 0) {
fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
fireball_type = FIREBALL_EXPLOSION_LARGE1 + Random::next(FIREBALL_NUM_LARGE_EXPLOSIONS);
}
fireball_create( &debris_obj->pos, fireball_type, FIREBALL_LARGE_EXPLOSION, OBJ_INDEX(debris_obj), debris_obj->radius*1.75f);
@@ -196,34 +195,6 @@ void debris_delete( object * obj )
db->objnum = -1;
}
/**
* If debris piece *db is far away from all players, make it go away very soon.
* In single player game, delete if MAX_DEBRIS_DIST from player.
* In multiplayer game, delete if MAX_DEBRIS_DIST from all players.
*/
void maybe_delete_debris(debris *db)
{
object *objp;
if (Player_obj != nullptr && timestamp_elapsed(db->next_distance_check) && timestamp_elapsed(db->must_survive_until)) {
if (!(Game_mode & GM_MULTIPLAYER)) { // In single player game, just check against player.
if (vm_vec_dist_quick(&Player_obj->pos, &Objects[db->objnum].pos) > MAX_DEBRIS_DIST)
db->lifeleft = 0.1f;
else
db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
} else {
for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
if (objp->flags[Object::Object_Flags::Player_ship]) {
if (vm_vec_dist_quick(&objp->pos, &Objects[db->objnum].pos) < MAX_DEBRIS_DIST) {
db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
return;
}
}
}
db->lifeleft = 0.1f;
}
}
}
/**
* Do various updates to debris: check if time to die, start fireballs
@@ -246,7 +217,7 @@ void debris_process_post(object * obj, float frame_time)
radar_plot_object( obj );
if ( timestamp_elapsed(db->sound_delay) ) {
obj_snd_assign(objnum, db->ambient_sound, &vmd_zero_vector, 0);
obj_snd_assign(objnum, db->ambient_sound, &vmd_zero_vector);
db->sound_delay = 0;
}
}
@@ -260,8 +231,6 @@ void debris_process_post(object * obj, float frame_time)
}
}
maybe_delete_debris(db); // Make this debris go away if it's very far away.
// ================== DO THE ELECTRIC ARCING STUFF =====================
if ( db->arc_frequency <= 0 ) {
return; // If arc_frequency <= 0, this piece has no arcs on it
@@ -274,7 +243,7 @@ void debris_process_post(object * obj, float frame_time)
if (db->is_hull) {
int n, n_arcs = ((rand()>>5) % 3)+1; // Create 1-3 sparks
int n, n_arcs = Random::next(1, 3); // Create 1-3 sparks
vec3d v1, v2, v3, v4;
@@ -283,8 +252,7 @@ void debris_process_post(object * obj, float frame_time)
n = 0;
int a = 100, b = 1000;
int lifetime = (myrand()%((b)-(a)+1))+(a);
int lifetime = Random::next(100, 1000);
// Create the spark effects
for (int i=0; i<MAX_DEBRIS_ARCS; ++i) {
@@ -351,8 +319,8 @@ void debris_process_post(object * obj, float frame_time)
db->arc_timestamp[i] = timestamp(-1);
} else {
// Maybe move a vertex.... 20% of the time maybe?
int mr = myrand();
if ( mr < RAND_MAX/5 ) {
int mr = Random::next();
if ( mr < Random::MAX_VALUE/5 ) {
vec3d v1, v2;
submodel_get_two_random_points_better(db->model_num, db->submodel_num, &v1, &v2);
@@ -424,17 +392,6 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
sip = &Ship_info[shipp->ship_info_index];
vaporize = (shipp->flags[Ship::Ship_Flags::Vaporize]);
if ( !hull_flag ) {
// Make vaporize debris seen from farther away
float dist = vm_vec_dist_quick( pos, &Eye_position );
if (vaporize) {
dist /= 2.0f;
}
if ( dist > 200.0f ) {
return NULL;
}
}
// try to maintain our soft limit
if (hull_flag && (Num_hull_pieces >= SOFT_LIMIT_DEBRIS_PIECES)) {
// cause oldest hull debris chunk to blow up
@@ -457,8 +414,40 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
db = &Debris[n];
//WMC - We must survive until now, at least.
db->must_survive_until = timestamp();
if (!hull_flag) {
if (model_num >= 0) {
db->model_num = model_num;
db->submodel_num = Random::next(sip->generic_debris_num_submodels);
}
else if (vaporize) {
db->model_num = Debris_vaporize_model;
db->submodel_num = Random::next(Debris_num_submodels);
}
else {
db->model_num = Debris_model;
db->submodel_num = Random::next(Debris_num_submodels);
}
}
else {
db->model_num = model_num;
db->submodel_num = submodel_num;
}
db->model_instance_num = model_create_instance(false, db->model_num);
float radius = submodel_get_radius(db->model_num, db->submodel_num);
// if its generic, maybe cull it if its too small and far
if (!hull_flag) {
float dist = vm_vec_dist_quick(pos, &Eye_position);
// Make vaporize debris seen from farther away
if (vaporize) {
dist /= 2.0f;
}
if (dist > radius * 200.0f) {
return nullptr;
}
}
if(hull_flag && sip->debris_min_lifetime >= 0.0f && sip->debris_max_lifetime >= 0.0f)
{
@@ -468,14 +457,16 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
{
// Create Debris piece n!
if ( hull_flag ) {
if (rand() < RAND_MAX/6) // Make some pieces blow up shortly after explosion.
db->lifeleft = 2.0f * (myrand() * RAND_MAX_1f) + 0.5f;
if (Random::next() < (Random::MAX_VALUE/6)) // Make some pieces blow up shortly after explosion.
db->lifeleft = 2.0f * (frand()) + 0.5f;
else {
db->flags.set(Debris_Flags::DoNotExpire);
db->lifeleft = -1.0f; // large hull pieces stay around forever
}
} else {
db->lifeleft = (myrand() * RAND_MAX_1f) * 2.0f + 0.1f;
} else {
// small non-hull pieces should stick around longer the larger they are
// sqrtf should make sure its never too crazy long
db->lifeleft = (frand() * 2.0f + 0.1f) * sqrtf(radius);
}
}
@@ -484,12 +475,10 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
{
if(sip->debris_min_lifetime >= 0.0f && sip->debris_max_lifetime >= 0.0f)
{
db->must_survive_until = timestamp(fl2i(sip->debris_min_lifetime * 1000.0f));
db->lifeleft = (( sip->debris_max_lifetime - sip->debris_min_lifetime ) * frand()) + sip->debris_min_lifetime;
}
else if(sip->debris_min_lifetime >= 0.0f)
{
db->must_survive_until = timestamp(fl2i(sip->debris_min_lifetime * 1000.0f));
if(db->lifeleft < sip->debris_min_lifetime)
db->lifeleft = sip->debris_min_lifetime;
}
@@ -514,7 +503,6 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
db->fire_timeout = 0; // if not changed, timestamp_elapsed() will return false
db->time_started = Missiontime;
db->species = Ship_info[shipp->ship_info_index].species;
db->next_distance_check = (myrand() % 2000) + 4*DEBRIS_DISTANCE_CHECK_TIME;
db->parent_alt_name = shipp->alt_type_index;
db->damage_mult = 1.0f;
@@ -533,25 +521,9 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
db->arc_frequency = 0;
}
if ( model_num < 0 ) {
if (vaporize) {
db->model_num = Debris_vaporize_model;
} else {
db->model_num = Debris_model;
}
db->submodel_num = (myrand()>>4) % Debris_num_submodels;
} else {
db->model_num = model_num;
db->submodel_num = submodel_num;
}
db->model_instance_num = model_create_instance(false, db->model_num);
float radius = submodel_get_radius( db->model_num, db->submodel_num );
db->next_fireball = timestamp_rand(500,2000); //start one 1/2 - 2 secs later
if ( pos == NULL )
if ( pos == nullptr )
pos = &source_obj->pos;
flagset<Object::Object_Flags> default_flags;
@@ -559,10 +531,18 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
default_flags.set(Object::Object_Flags::Physics);
default_flags.set(Object::Object_Flags::Collides, hull_flag != 0);
objnum = obj_create( OBJ_DEBRIS, parent_objnum, n, &source_obj->orient, pos, radius, default_flags);
matrix orient = source_obj->orient;
if (!db->is_hull) {
// non-hull debris has no relation to its parent orientation
vec3d rand;
vm_vec_rand_vec(&rand);
vm_vector_2_matrix(&orient, &rand);
}
objnum = obj_create(OBJ_DEBRIS, parent_objnum, n, &orient, pos, radius, default_flags, false);
if ( objnum == -1 ) {
mprintf(("Couldn't create debris object -- out of object slots\n"));
return NULL;
return nullptr;
}
db->objnum = objnum;
@@ -612,7 +592,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
if ( hull_flag ) {
float t;
scale = exp_force * i2fl((myrand()%20) + 10); // for radial_vel away from location of blast center
scale = exp_force * i2fl(Random::next(10, 29)); // for radial_vel away from location of blast center
db->sound_delay = timestamp(DEBRIS_SOUND_DELAY);
// set up physics mass and I_inv for hull debris pieces
@@ -624,7 +604,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
// limit the amount of time that fireballs appear
// let fireball length be linked to radius of ship. Range is .33 radius => 3.33 radius seconds.
t = 1000*Objects[db->source_objnum].radius/3 + myrand()%(fl2i(1000*3*Objects[db->source_objnum].radius));
t = 1000*Objects[db->source_objnum].radius/3 + Random::next(fl2i(1000*3*Objects[db->source_objnum].radius));
db->fire_timeout = timestamp(fl2i(t)); // fireballs last from 5 - 30 seconds
if ( Objects[db->source_objnum].radius < MIN_RADIUS_FOR_PERSISTANT_DEBRIS ) {
@@ -635,7 +615,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
}
}
else {
scale = exp_force * i2fl((myrand()%20) + 10); // for radial_vel away from blast center (non-hull)
scale = exp_force * i2fl(Random::next(10, 29)); // for radial_vel away from blast center (non-hull)
}
if ( vm_vec_mag_squared( &to_center ) < 0.1f ) {
@@ -662,7 +642,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
radius = 1.0f;
}
scale = ( 6.0f + i2fl(myrand()%4) ) / radius;
scale = (6.0f + i2fl(Random::next(4))) / radius;
vm_vec_rand_vec_quick(&rotvel);
vm_vec_scale(&rotvel, scale);
@@ -899,13 +879,13 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
smi->collision_checked = false;
// set angles for last frame (need to set to prev to get p0)
angles copy_angles = smi->angs;
matrix copy_matrix = smi->canonical_orient;
// find the start and end positions of the sphere in submodel RF
smi->angs = smi->prev_angs;
smi->canonical_orient = smi->canonical_prev_orient;
world_find_model_instance_point(&p0, &light_obj->last_pos, pm, pmi, submodel, &heavy_obj->last_orient, &heavy_obj->last_pos);
smi->angs = copy_angles;
smi->canonical_orient = copy_matrix;
world_find_model_instance_point(&p1, &light_obj->pos, pm, pmi, submodel, &heavy_obj->orient, &heavy_obj->pos);
mc.p0 = &p0;
@@ -924,7 +904,7 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
// set up debris_hit_info for rotating submodel
if (debris_hit_info->edge_hit == 0) {
model_instance_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
model_instance_find_world_dir(&debris_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
}
// find position in submodel RF of light object at collison
@@ -956,7 +936,7 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
// get collision normal if not edge hit
if (debris_hit_info->edge_hit == 0) {
model_instance_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
model_instance_find_world_dir(&debris_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
}
// find position in submodel RF of light object at collison
@@ -1127,7 +1107,7 @@ void debris_render(object * obj, model_draw_list *scene)
MONITOR_INC(NumHullDebrisRend, 1);
} else {
MONITOR_INC(NumSmallDebrisRend, 1);
render_info.set_flags(MR_NO_LIGHTING);
// render_info.set_flags(MR_NO_LIGHTING);
}
submodel_render_queue( &render_info, scene, pm, pmi, db->submodel_num, &obj->orient, &obj->pos );
@@ -1146,3 +1126,19 @@ bool debris_is_vaporized(debris *db)
{
return db->model_num == Debris_vaporize_model;
}
void create_generic_debris(object* ship_objp, vec3d* pos, float min_num_debris, float max_num_debris, float speed_mult, bool use_ship_debris) {
Assertion(ship_objp->type == OBJ_SHIP, "create_generic_debris called for a non-ship, only ships can spew debris!");
if (ship_objp->type != OBJ_SHIP)
return;
float num_debris = frand_range(min_num_debris, max_num_debris);
num_debris *= (Detail.num_small_debris + 0.5f) / 4.5f;
vec3d create_pos = *pos;
for (int i = 0; i < num_debris; i++) {
int model_num = use_ship_debris ? Ship_info[Ships[ship_objp->instance].ship_info_index].generic_debris_model_num : -1;
debris_create(ship_objp, model_num, -1, &create_pos, pos, 0, speed_mult);
}
}
+5 -3
View File
@@ -40,17 +40,15 @@ typedef struct debris {
gamesnd_id ambient_sound; // Ambient looping sound
int objnum = -1; // What object this is linked to
float lifeleft; // When 0 or less object dies
int must_survive_until; //WMC - timestamp of earliest point that it can be murthered.
int model_num; // What model this uses
int model_instance_num; // What model instance this uses - needed for arcs
int submodel_num; // What submodel this uses
int next_fireball; // When to start a fireball
int is_hull; // indicates a large hull chunk of debris
int is_hull; // indicates whether this is a collideable, destructable piece of debris from the model, or just a generic debris fragment
int species; // What species this is from. -1 if don't care.
int fire_timeout; // timestamp that holds time for fireballs to stop appearing
int sound_delay; // timestamp to signal when sound should start
fix time_started; // time when debris was created
int next_distance_check; // timestamp to determine whether to delete this piece of debris.
vec3d arc_pts[MAX_DEBRIS_ARCS][2]; // The endpoints of each arc
int arc_timestamp[MAX_DEBRIS_ARCS]; // When this times out, the spark goes away. -1 is not used
@@ -82,4 +80,8 @@ void debris_remove_from_hull_list(debris *db);
bool debris_is_generic(debris *db);
bool debris_is_vaporized(debris *db);
// creates a burst of generic debris at hitpos from ship_objp, with a random number between min and max
// use_ship_debris is for whether the ship's generic debris should be used, or simply debris01.pof
void create_generic_debris(object* ship_objp, vec3d* hitpos, float min_num_debris, float max_num_debris, float speed_mult, bool use_ship_debris);
#endif // _DEBRIS_H
+9 -1
View File
@@ -65,15 +65,23 @@ void GetLightInfo(vec3 position, out vec3 lightDir, out float attenuation)
if (lightType == LT_TUBE) { // Tube light
float beamLength = length(beamVec);
vec3 beamDir = beamVec / beamLength;
//The actual 'lighting element' is shorter than the light volume cylinder
//To compensate the light is moved forward along the beam one radius and the length shortened
//this allows room for clean falloff of the light past the ends of beams.
vec3 adjustedLightPos = lightPosition - (beamDir * lightRadius);
beamLength = beamLength - (lightRadius * 2.0);
//adjustments having been made, lightdir needs recalculating
lightDir = adjustedLightPos - position.xyz;
// Get nearest point on line
float neardist = clamp(dot(lightDir, beamDir), 0.0, beamLength);
// Move back from the endpoint of the beam along the beam by the distance we calculated
vec3 nearest = lightPosition - beamDir * neardist;
vec3 nearest = adjustedLightPos - beamDir * neardist;
lightDir = nearest - position.xyz;
dist = length(lightDir);
if(dist > lightRadius) {
discard;
}
attenuation = 1.0 - clamp(dist / lightRadius, 0.0, 1.0);
} else if (lightType == LT_CONE) {
float coneDot = dot(normalize(-lightDir), coneDir);
if(dualCone) {
+4 -6
View File
@@ -12,7 +12,7 @@ layout (std140) uniform genericData {
vec3 fog_color;
float fog_start;
float fog_scale;
float fog_density;
float zNear;
float zFar;
};
@@ -23,17 +23,15 @@ void main()
// If the scene is in fog then we change the color we operate on based on the depth of the pixel
float depth_val = texture(depth_tex, fragTexCoord.xy).x;
// Transform depth value from [-1, 1] to [0, 1]
float depth_normalized = fma(depth_val, 0.5, 0.5);
// Transform depth value from [0, 1] to [-1, 1]
float depth_normalized = 2 * depth_val - 1;
// Now we compute the depth value in projection space using the clipping plane information
float view_depth = 2.0 * zNear * zFar / (zFar + zNear - depth_normalized * (zFar - zNear));
if (isinf(view_depth)) {
fragOut0.rgb = color_in.rgb;
} else {
// This is the same formula that is also used by the main shader
float fog_dist = clamp((view_depth - fog_start) * 0.75 * fog_scale, 0.0, 1.0);
float fog_dist = clamp(1 - pow(fog_density, view_depth - fog_start) , 0.0, 1.0);
vec3 finalFogColor = srgb_to_linear(fog_color);
fragOut0.rgb = mix(color_in.rgb, finalFogColor, fog_dist);
@@ -221,6 +221,7 @@ $AI:
+Can Form Wing: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Freighter
$Counts for Alone: YES
@@ -250,6 +251,7 @@ $AI:
+Active docks: ( "cargo" )
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: AWACS
$Counts for Alone: YES
@@ -277,6 +279,7 @@ $AI:
+Can Form Wing: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Gas Miner
$Counts for Alone: YES
@@ -304,6 +307,7 @@ $AI:
+Can Form Wing: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Cruiser
$Counts for Alone: YES
@@ -331,6 +335,7 @@ $AI:
+Can Form Wing: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Corvette
$Counts for Alone: YES
@@ -358,6 +363,7 @@ $AI:
+Can Form Wing: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Capital
$Counts for Alone: YES
@@ -386,6 +392,7 @@ $AI:
+Can Form Wing: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Super Cap
$Counts for Alone: YES
@@ -412,6 +419,7 @@ $AI:
+Can Form Wing: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Drydock
$Counts for Alone: YES
@@ -436,6 +444,7 @@ $AI:
+Turrets attack this: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Knossos Device
$Counts for Alone: YES
@@ -458,5 +467,6 @@ $AI:
+Turrets attack this: YES
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
#End
+104 -106
View File
@@ -14,6 +14,7 @@
#include "graphics/tmapper.h"
#include "localization/localize.h"
#include "model/model.h"
#include "nebula/neb.h"
#include "object/object.h"
#include "options/Option.h"
#include "parse/parselo.h"
@@ -31,13 +32,15 @@ int Knossos_warp_ani_used;
#define MAX_WARP_LOD 0
fireball Fireballs[MAX_FIREBALLS];
constexpr int INTITIAL_FIREBALL_CONTAINTER_SIZE = 256;
SCP_vector<fireball> Fireballs;
SCP_vector<int> Unused_fireball_indices;
fireball_info Fireball_info[MAX_FIREBALL_TYPES];
int fireball_used[MAX_FIREBALL_TYPES];
int Num_fireballs = 0;
int Num_fireball_types = 0;
bool fireballs_inited = false;
@@ -284,11 +287,13 @@ static void parse_fireball_tbl(const char *table_filename)
}
}
bool first_time;
// now select our entry accordingly...
// are we using a previous entry?
if (existing_idx >= 0)
{
fi = &Fireball_info[existing_idx];
first_time = false;
}
// we are creating a new entry, so set some defaults
else
@@ -312,6 +317,7 @@ static void parse_fireball_tbl(const char *table_filename)
fireball_set_default_warp_attributes(Num_fireball_types);
Num_fireball_types++;
first_time = true;
}
// copy over what we already parsed
@@ -319,6 +325,7 @@ static void parse_fireball_tbl(const char *table_filename)
strcpy_s(fi->unique_id, unique_id);
strcpy_s(fi->lod[0].filename, fireball_filename);
// Do we have a LOD num?
if (optional_string("$LOD:"))
{
@@ -326,6 +333,9 @@ static void parse_fireball_tbl(const char *table_filename)
if (fi->lod_count > MAX_FIREBALL_LOD)
fi->lod_count = MAX_FIREBALL_LOD;
} else if (first_time) {
//assume a LOD of at least 1
fi->lod_count = 1;
}
// check for particular lighting color
@@ -435,8 +445,6 @@ void fireball_load_data()
// This will get called at the start of each level.
void fireball_init()
{
int i;
if ( !fireballs_inited ) {
// Do all the processing that happens only once
fireball_parse_tbl();
@@ -446,10 +454,10 @@ void fireball_init()
}
// Reset everything between levels
Num_fireballs = 0;
for (i=0; i<MAX_FIREBALLS; i++ ) {
Fireballs[i].objnum = -1;
}
Fireballs.clear();
Fireballs.reserve(INTITIAL_FIREBALL_CONTAINTER_SIZE);
Unused_fireball_indices.clear();
Unused_fireball_indices.reserve(INTITIAL_FIREBALL_CONTAINTER_SIZE);
// Goober5000 - reset Knossos warp flag
Knossos_warp_ani_used = 0;
@@ -467,13 +475,15 @@ void fireball_delete( object * obj )
fireball *fb;
num = obj->instance;
// Make sure the new system works fine.
Assert(obj->instance > -1);
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
fb = &Fireballs[num];
Assert( fb->objnum == OBJ_INDEX(obj));
Fireballs[num].objnum = -1;
Num_fireballs--;
Assert( Num_fireballs >= 0 );
Unused_fireball_indices.push_back(num);
}
/**
@@ -481,13 +491,9 @@ void fireball_delete( object * obj )
*/
void fireball_delete_all()
{
fireball *fb;
int i;
for ( i = 0; i < MAX_FIREBALLS; i++ ) {
fb = &Fireballs[i];
if ( fb->objnum != -1 ) {
obj_delete(fb->objnum);
for (auto& current_fireball : Fireballs) {
if ( current_fireball.objnum != -1 ) {
obj_delete(current_fireball.objnum);
}
}
}
@@ -499,6 +505,8 @@ void fireball_set_framenum(int num)
fireball_info *fd;
fireball_lod *fl;
Assert(static_cast<int>(Fireballs.size()) > num);
fb = &Fireballs[num];
fd = &Fireball_info[Fireballs[num].fireball_info_index];
@@ -537,6 +545,9 @@ int fireball_is_perishable(object * obj)
num = obj->instance;
objnum = OBJ_INDEX(obj);
// Make sure the new system works fine.
Assert(obj->instance > -1);
Assert((int)Fireballs.size() > obj->instance);
Assert( Fireballs[num].objnum == objnum );
fb = &Fireballs[num];
@@ -553,43 +564,6 @@ int fireball_is_perishable(object * obj)
return 0;
}
/**
* There are too many fireballs, so delete the oldest small one
* to free up a slot.
*
* @return The fireball slot freed.
*/
int fireball_free_one()
{
fireball *fb;
int i;
int oldest_objnum = -1, oldest_slotnum = -1;
float lifeleft, oldest_lifeleft = 0.0f;
for ( i = 0; i < MAX_FIREBALLS; i++ ) {
fb = &Fireballs[i];
// only remove the ones that aren't warp effects
if ( (fb->objnum >= 0) && fireball_is_perishable(&Objects[fb->objnum]) ) {
lifeleft = fb->total_time - fb->time_elapsed;
if ( (oldest_objnum < 0) || (lifeleft < oldest_lifeleft) ) {
oldest_slotnum = i;
oldest_lifeleft = lifeleft;
oldest_objnum = fb->objnum;
}
break;
}
}
if ( oldest_objnum > -1 ) {
obj_delete(oldest_objnum);
}
return oldest_slotnum;
}
int fireball_is_warp(object * obj)
{
int num, objnum;
@@ -597,6 +571,9 @@ int fireball_is_warp(object * obj)
num = obj->instance;
objnum = OBJ_INDEX(obj);
// Make sure the new system works fine.
Assert(obj->instance > -1);
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
Assert( Fireballs[num].objnum == objnum );
fb = &Fireballs[num];
@@ -641,6 +618,9 @@ void fireball_process_post(object * obj, float frame_time)
num = obj->instance;
objnum = OBJ_INDEX(obj);
// Make sure the new system works fine.
Assert(obj->instance > -1);
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
Assert( Fireballs[num].objnum == objnum );
fb = &Fireballs[num];
@@ -665,6 +645,10 @@ float fireball_lifeleft( object *obj )
num = obj->instance;
objnum = OBJ_INDEX(obj);
// Make sure the new system works fine.
Assert(obj->instance > -1);
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
Assert( Fireballs[num].objnum == objnum );
fb = &Fireballs[num];
@@ -680,6 +664,10 @@ float fireball_lifeleft_percent( object *obj )
int num, objnum;
fireball *fb;
// Make sure the new system works fine.
Assert(obj->instance > -1);
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
num = obj->instance;
objnum = OBJ_INDEX(obj);
Assert( Fireballs[num].objnum == objnum );
@@ -781,10 +769,8 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
{
int n, objnum, fb_lod;
object *obj;
fireball *fb;
fireball_info *fd;
fireball_lod *fl;
Assert( fireball_type > -1 );
Assert( fireball_type < Num_fireball_types );
@@ -800,23 +786,21 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
}
}
if ( (Num_fireballs >= MAX_FIREBALLS) || (Num_objects >= MAX_OBJECTS) ) {
// out of slots, so free one up.
n = fireball_free_one();
if ( n < 0 ) {
return -1;
}
} else {
for ( n = 0; n < MAX_FIREBALLS; n++ ) {
if ( Fireballs[n].objnum < 0 ) {
break;
}
}
Assert( n != MAX_FIREBALLS );
if (Num_objects >= MAX_OBJECTS) {
return -1;
}
fb = &Fireballs[n];
if (!Unused_fireball_indices.empty()) {
n = Unused_fireball_indices.back();
Unused_fireball_indices.pop_back();
}
else {
n = static_cast<int>(Fireballs.size());
Fireballs.emplace_back();
}
fireball* new_fireball = &Fireballs[n];
// get an lod to use
fb_lod = fireball_get_lod(pos, fd, size);
@@ -833,13 +817,13 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
}
fl = &fd->lod[fb_lod];
fb->lod = (char)fb_lod;
new_fireball->lod = (char)fb_lod;
fb->flags = extra_flags;
fb->warp_open_sound_index = warp_open_sound;
fb->warp_close_sound_index = warp_close_sound;
fb->warp_open_duration = (warp_open_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_open_duration;
fb->warp_close_duration = (warp_close_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_close_duration;
new_fireball->flags = extra_flags;
new_fireball->warp_open_sound_index = warp_open_sound;
new_fireball->warp_close_sound_index = warp_close_sound;
new_fireball->warp_open_duration = (warp_open_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_open_duration;
new_fireball->warp_close_duration = (warp_close_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_close_duration;
matrix orient;
if(orient_override != NULL){
@@ -854,60 +838,55 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
flagset<Object::Object_Flags> default_flags;
default_flags.set(Object::Object_Flags::Renders);
objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, default_flags);
if (objnum < 0) {
Int3(); // Get John, we ran out of objects for fireballs
return objnum;
}
objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, default_flags, false);
obj = &Objects[objnum];
fb->fireball_info_index = fireball_type;
fb->fireball_render_type = render_type;
fb->time_elapsed = 0.0f;
fb->objnum = objnum;
fb->current_bitmap = -1;
new_fireball->fireball_info_index = fireball_type;
new_fireball->fireball_render_type = render_type;
new_fireball->time_elapsed = 0.0f;
new_fireball->objnum = objnum;
new_fireball->current_bitmap = -1;
switch( fb->fireball_render_type ) {
switch( new_fireball->fireball_render_type ) {
case FIREBALL_MEDIUM_EXPLOSION:
fb->orient = (myrand()>>8) & 7; // 0 - 7
new_fireball->orient = Random::next() & 7; // 0 - 7
break;
case FIREBALL_LARGE_EXPLOSION:
fb->orient = (myrand()>>8) % 360; // 0 - 359
new_fireball->orient = Random::next(360); // 0 - 359
break;
case FIREBALL_WARP_EFFECT:
// Play sound effect for warp hole opening up
fireball_play_warphole_open_sound(ship_class, fb);
fireball_play_warphole_open_sound(ship_class, new_fireball);
// warp in type
if (reverse) {
fb->orient = 1;
new_fireball->orient = 1;
// if warp out, then reverse the orientation
vm_vec_scale( &obj->orient.vec.fvec, -1.0f ); // Reverse the forward vector
vm_vec_scale( &obj->orient.vec.rvec, -1.0f ); // Reverse the right vector
} else {
fb->orient = 0;
new_fireball->orient = 0;
}
break;
default:
Int3();
UNREACHABLE("Bad type set in fireball_create");
break;
}
if ( fb->fireball_render_type == FIREBALL_WARP_EFFECT ) {
if ( new_fireball->fireball_render_type == FIREBALL_WARP_EFFECT ) {
Assert( warp_lifetime >= 4.0f ); // Warp lifetime must be at least 4 seconds!
if ( warp_lifetime < 4.0f )
warp_lifetime = 4.0f;
fb->total_time = warp_lifetime; // in seconds
new_fireball->total_time = warp_lifetime; // in seconds
} else {
fb->total_time = i2fl(fl->num_frames) / fl->fps; // in seconds
new_fireball->total_time = i2fl(fl->num_frames) / fl->fps; // in seconds
}
fireball_set_framenum(n);
if ( velocity ) {
@@ -923,7 +902,6 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
vm_vec_zero(&obj->phys_info.max_rotvel);
}
Num_fireballs++;
return objnum;
}
@@ -1008,9 +986,9 @@ int fireball_ship_explosion_type(ship_info *sip)
}
if(ship_fireballs > 0){
index = sip->explosion_bitmap_anims[rand()%ship_fireballs];
index = sip->explosion_bitmap_anims[Random::next(ship_fireballs)];
} else if(objecttype_fireballs > 0){
index = Ship_types[sip->class_type].explosion_bitmap_anims[rand()%objecttype_fireballs];
index = Ship_types[sip->class_type].explosion_bitmap_anims[Random::next(objecttype_fireballs)];
}
return index;
@@ -1027,7 +1005,7 @@ int fireball_asteroid_explosion_type(asteroid_info *aip)
int roid_fireballs = (int)aip->explosion_bitmap_anims.size();
if (roid_fireballs > 0) {
index = aip->explosion_bitmap_anims[rand()%roid_fireballs];
index = aip->explosion_bitmap_anims[Random::next(roid_fireballs)];
}
return index;
@@ -1058,24 +1036,33 @@ void fireball_render(object* obj, model_draw_list *scene)
MONITOR_INC( NumFireballsRend, 1 );
// Make sure the new system works fine.
Assert(obj->instance > -1);
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
num = obj->instance;
fb = &Fireballs[num];
if ( Fireballs[num].current_bitmap < 0 )
if ( fb->current_bitmap < 0 )
return;
float alpha = 1.0f;
if (The_mission.flags[Mission::Mission_Flags::Fullneb] && Neb_affects_fireballs)
alpha *= neb2_get_fog_visibility(&obj->pos, NEB_FOG_VISIBILITY_MULT_FIREBALL(obj->radius));
g3_transfer_vertex(&p, &obj->pos);
switch ( fb->fireball_render_type ) {
case FIREBALL_MEDIUM_EXPLOSION: {
batching_add_volume_bitmap(Fireballs[num].current_bitmap, &p, fb->orient, obj->radius);
batching_add_volume_bitmap(fb->current_bitmap, &p, fb->orient, obj->radius, alpha);
}
break;
case FIREBALL_LARGE_EXPLOSION: {
// Make the big explosions rotate with the viewer.
batching_add_volume_bitmap_rotated(Fireballs[num].current_bitmap, &p, (i2fl(fb->orient)*PI) / 180.0f, obj->radius);
batching_add_volume_bitmap_rotated(fb->current_bitmap, &p, (i2fl(fb->orient)*PI) / 180.0f, obj->radius, alpha);
}
break;
@@ -1093,3 +1080,14 @@ void fireball_render(object* obj, model_draw_list *scene)
Int3();
}
}
// Because fireballs are only added and removed in two places, and Unused_fireball_indices is always updated in those places to contain unused indices,
// this very simple code will give you the correct count of currently existing fireballs in use.
int fireball_get_count()
{
int count = static_cast<int>(Fireballs.size()) - static_cast<int>(Unused_fireball_indices.size());
Assert (count >= 0);
return count;
}
+4 -2
View File
@@ -95,8 +95,7 @@ typedef struct fireball {
} fireball;
// end move
extern fireball Fireballs[MAX_FIREBALLS];
extern int Num_fireballs;
extern SCP_vector<fireball> Fireballs;
extern bool fireballs_inited;
@@ -146,4 +145,7 @@ extern int Knossos_warp_ani_used;
extern bool Fireball_use_3d_warp;
// Cyborg - get a count of how many valid fireballs are in the mission.
int fireball_get_count();
#endif /* _FIREBALLS_H */
+1 -1
View File
@@ -91,7 +91,7 @@ void warpin_queue_render(model_draw_list *scene, object *obj, matrix *orient, ve
g3_transfer_vertex( &verts[4], &vecs[4] );
float alpha = (The_mission.flags[Mission::Mission_Flags::Fullneb]) ? (1.0f - neb2_get_fog_intensity(obj)) : 1.0f;
float alpha = (The_mission.flags[Mission::Mission_Flags::Fullneb]) ? neb2_get_fog_visibility(&obj->pos, 1.0f) : 1.0f;
//batch_add_bitmap(warp_glow_bitmap, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT, &verts[4], 0, r, alpha);
batching_add_bitmap(warp_glow_bitmap, &verts[4], 0, r, alpha);
+8 -16
View File
@@ -112,35 +112,27 @@ static int Current_help_page;
void gameplay_help_blit_control_line(int x, int y, int id)
{
int has_key=0, has_joy=0;
char buf[256];
config_item *ci;
ci = &Control_config[id];
auto ci = &Control_config[id];
buf[0] = 0;
if ( ci->key_id >= 0 ) {
strcpy_s(buf, textify_scancode(ci->key_id));
has_key=1;
strcpy_s(buf, ci->first.textify().c_str());
if (!ci->second.empty()) {
strcat_s(buf, XSTR( ", ", 129));
strcat_s(buf, ci->second.textify().c_str());
}
if ( ci->joy_id >= 0 ) {
if ( has_key ) {
strcat_s(buf, XSTR( ", ", 129));
}
strcat_s(buf, Joy_button_text[ci->joy_id]);
has_joy=1;
}
if ( !has_key && !has_joy ) {
if (ci->empty()) {
strcpy_s(buf, XSTR( "no binding", 130));
}
gr_string(x,y,buf,GR_RESIZE_MENU);
// gr_string(x+KEY_DESCRIPTION_OFFSET,y,ci->text,GR_RESIZE_MENU);
gr_string(x+KEY_DESCRIPTION_OFFSET, y, XSTR(ci->text, CONTROL_CONFIG_XSTR + id), GR_RESIZE_MENU);
gr_string(x+KEY_DESCRIPTION_OFFSET, y, XSTR(ci->text.c_str(), CONTROL_CONFIG_XSTR + id), GR_RESIZE_MENU);
}
void gameplay_help_blit_control_line_raw(int x, int y, const char *control_text, const char *control_description)
+4 -1
View File
@@ -239,7 +239,10 @@ bool GameState_Stack_Valid()
// returns one of the GS_STATE_ macros
int gameseq_get_state(int depth)
{
{
if (!GameState_Stack_Valid())
return GS_STATE_INVALID;
Assert(depth <= gs_current_stack);
return gs[gs_current_stack - depth].current_state;
+49 -36
View File
@@ -10,6 +10,7 @@
#include <sstream>
#include <climits>
#include "gamesnd.h"
#include "gamesnd/gamesnd.h"
#include "localization/localize.h"
#include "parse/parselo.h"
@@ -303,6 +304,10 @@ gamesnd_id gamesnd_get_by_name(const char* name)
{
Assert( Snds.size() <= INT_MAX );
// empty name is not valid!
if (name == nullptr || name[0] == '\0')
return gamesnd_id(-1);
int index = gamesnd_lookup_name(name, Snds);
if (index < 0)
@@ -421,21 +426,32 @@ bool parse_game_sound(const char* tag, gamesnd_id* idx_dest)
{
if (optional_string(tag))
{
SCP_string buf;
stuff_string(buf, F_NAME);
*idx_dest = gamesnd_get_by_name(buf.c_str());
// The special case "-1" is needed to silence warnings where sounds are intentionally removed
if (idx_dest->isValid() || buf == "-1")
return true;
error_display(0, "Could not find game sound with name '%s'!", buf.c_str());
*idx_dest = parse_game_sound_inline();
return true;
}
return false;
}
/**
* @brief Parses a game sound that should appear at the current parsing location
* @return The game sound id or invalid id when parsing fails
*/
gamesnd_id parse_game_sound_inline()
{
SCP_string buf;
stuff_string(buf, F_NAME);
auto id = gamesnd_get_by_name(buf.c_str());
// The special case "-1" is needed to silence warnings where sounds are intentionally removed
if (!id.isValid() && buf != "-1") {
error_display(0, "Could not find game sound with name '%s'!", buf.c_str());
}
return id;
}
/**
* Parse a sound. When using this function for a table entry,
* required_string and optional_string aren't needed, as this function deals with
@@ -510,7 +526,7 @@ void parse_iface_sound_list(const char* tag, SCP_vector<interface_snd_id>& desti
//if we're using the old format, double check the size)
if(!scp_list && (destination.size() != (unsigned)check))
{
mprintf(("%s in '%s' has " SIZE_T_ARG " entries. This does not match entered size of %i.", tag, object_name, destination.size(), check));
mprintf(("%s in '%s' has " SIZE_T_ARG " entries. This does not match entered size of %i.\n", tag, object_name, destination.size(), check));
}
}
}
@@ -528,12 +544,12 @@ void gamesnd_preload_common_sounds()
return;
Assert( Snds.size() <= INT_MAX );
for (SCP_vector<game_snd>::iterator gs = Snds.begin(); gs != Snds.end(); ++gs) {
if ( gs->preload ) {
for (auto& entry : gs->sound_entries) {
for (auto& gs: Snds) {
if ( gs.preload ) {
for (auto& entry : gs.sound_entries) {
if ( entry.filename[0] != 0 && strnicmp(entry.filename, NOX("none.wav"), 4) != 0 ) {
game_busy( NOX("** preloading common game sounds **") ); // Animate loading cursor... does nothing if loading screen not active.
entry.id = snd_load(&entry, gs->flags);
entry.id = snd_load(&entry, &gs.flags);
}
}
}
@@ -549,12 +565,12 @@ void gamesnd_load_gameplay_sounds()
return;
Assert( Snds.size() <= INT_MAX );
for (SCP_vector<game_snd>::iterator gs = Snds.begin(); gs != Snds.end(); ++gs) {
if ( !gs->preload ) { // don't try to load anything that's already preloaded
for (auto& entry : gs->sound_entries) {
for (auto& gs: Snds) {
if ( !gs.preload ) { // don't try to load anything that's already preloaded
for (auto& entry : gs.sound_entries) {
if (entry.filename[0] != 0 && strnicmp(entry.filename, NOX("none.wav"), 4) != 0) {
game_busy(NOX("** preloading gameplay sounds **")); // Animate loading cursor... does nothing if loading screen not active.
entry.id = snd_load(&entry, gs->flags);
entry.id = snd_load(&entry, &gs.flags);
}
}
}
@@ -567,8 +583,8 @@ void gamesnd_load_gameplay_sounds()
void gamesnd_unload_gameplay_sounds()
{
Assert( Snds.size() <= INT_MAX );
for (SCP_vector<game_snd>::iterator gs = Snds.begin(); gs != Snds.end(); ++gs) {
for (auto& entry : gs->sound_entries) {
for (auto& gs: Snds) {
for (auto& entry : gs.sound_entries) {
if (entry.id.isValid()) {
snd_unload(entry.id);
entry.id = sound_load_id::invalid();
@@ -586,10 +602,10 @@ void gamesnd_load_interface_sounds()
return;
Assert( Snds_iface.size() < INT_MAX );
for (SCP_vector<game_snd>::iterator si = Snds_iface.begin(); si != Snds_iface.end(); ++si) {
for (auto& entry : si->sound_entries) {
for (auto& gs: Snds) {
for (auto& entry : gs.sound_entries) {
if ( entry.filename[0] != 0 && strnicmp(entry.filename, NOX("none.wav"), 4) != 0 ) {
entry.id = snd_load(&entry, si->flags);
entry.id = snd_load(&entry, &gs.flags);
}
}
}
@@ -663,11 +679,9 @@ void parse_gamesnd_old(game_snd* gs)
{
int temp_min, temp_max;
ignore_gray_space();
if (stuff_int_optional(&temp_min, true) == 2)
if (stuff_int_optional(&temp_min) == 2)
{
ignore_gray_space();
if (stuff_int_optional(&temp_max, true) == 2)
if (stuff_int_optional(&temp_max) == 2)
{
mprintf(("Dutifully converting retail sound %s, '%s' to a 3D sound...\n", gs->name.c_str(), entry.filename));
is_3d = 1;
@@ -681,8 +695,7 @@ void parse_gamesnd_old(game_snd* gs)
}
// check for extra values per Mantis #2408
ignore_gray_space();
if (stuff_int_optional(&temp, true) == 2)
if (stuff_int_optional(&temp) == 2)
{
Warning(LOCATION, "Unexpected extra value %d found for sound '%s' (filename '%s')! Check the format of the sounds.tbl (or .tbm) entry.", temp, gs->name.c_str(), entry.filename);
}
@@ -738,14 +751,14 @@ bool required_string_no_create(const char* token, bool no_create)
static GameSoundCycleType parse_cycle_type() {
if (optional_string("Sequential")) {
return GameSoundCycleType::Sequential;
return GameSoundCycleType::SequentialCycle;
} else if (optional_string("Random")) {
return GameSoundCycleType::Random;
return GameSoundCycleType::RandomCycle;
} else {
error_display(0, "Failed to parse sound cycle type. Expected 'sequential' or 'random'. Got [%.32s]", next_tokens());
// Ignore everything until the end of the line. That should hopefully skip the bad token.
advance_to_eoln(nullptr);
return GameSoundCycleType::Sequential;
return GameSoundCycleType::SequentialCycle;
}
}
@@ -1336,7 +1349,7 @@ float gamesnd_get_max_duration(game_snd* gs) {
for (auto& entry : gs->sound_entries) {
if (!entry.id.isValid()) {
// Lazily load unloaded sound entries when required
entry.id = snd_load(&entry, gs->flags);
entry.id = snd_load(&entry, &gs->flags);
}
max_length = std::max(max_length, snd_get_duration(entry.id));
@@ -1349,14 +1362,14 @@ game_snd_entry* gamesnd_choose_entry(game_snd* gs) {
size_t index = 0;
switch(gs->cycle_type) {
case GameSoundCycleType::Random:
case GameSoundCycleType::RandomCycle:
if (gs->sound_entries.size() == 1) {
index = 0;
} else {
index = util::UniformRange<size_t>(0, gs->sound_entries.size() - 1).next();
}
break;
case GameSoundCycleType::Sequential:
case GameSoundCycleType::SequentialCycle:
if (gs->last_entry_index == std::numeric_limits<size_t>::max()) {
// If this is the first time we must return the first sound to keep everything consistent
index = 0;
+2
View File
@@ -372,6 +372,8 @@ interface_snd_id gamesnd_get_by_iface_tbl_index(int index);
// Does all parsing for a sound. Returns true if a sound was successfully parsed.
bool parse_game_sound(const char* tag, gamesnd_id* idx_dest);
gamesnd_id parse_game_sound_inline();
void parse_iface_sound(const char* tag, interface_snd_id* idx_dest);
void parse_iface_sound_list(const char* tag, SCP_vector<interface_snd_id>& destination, const char* object_name, bool scp_list = false);
+4 -13
View File
@@ -113,6 +113,8 @@ typedef struct vec2d {
float x, y;
} vec2d;
// Euler angles for a rotation: h=heading, b=bank and p=pitch; angles are around the y, z and x axes
// respectively and are performed in that order.
typedef struct angles {
float p, b, h;
} angles_t;
@@ -128,6 +130,7 @@ typedef struct matrix {
};
} matrix;
// You might think this is also row-major. You fool! It is actually column-major.
typedef struct matrix4 {
union {
struct {
@@ -303,8 +306,6 @@ constexpr bool LoggingEnabled = false;
const float PI2 = (PI*2.0f);
// half values
const float PI_2 = (PI/2.0f);
const int RAND_MAX_2 = (RAND_MAX/2);
const float RAND_MAX_1f = (1.0f / RAND_MAX);
extern int Fred_running; // Is Fred running, or FreeSpace?
@@ -350,14 +351,6 @@ const size_t INVALID_SIZE = static_cast<size_t>(-1);
#define TRUE 1
#define FALSE 0
int myrand();
// Returns a random number between 0 and 0x7fffffff
int rand32();
// Returns a random integer from low to high, inclusive
int rand32(int low, int high);
// lod checker for (modular) table parsing
typedef struct lod_checker {
@@ -463,7 +456,6 @@ SCP_string dump_stacktrace();
// DEBUG compile time catch for dangerous uses of memset/memcpy/memmove
// This is disabled for VS2013 and lower since that doesn't support the necessary features
#if !defined(NDEBUG) && !defined(USING_THIRD_PARTY_LIBS) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
#if SCP_COMPILER_CXX_AUTO_TYPE && SCP_COMPILER_CXX_STATIC_ASSERT && HAVE_STD_IS_TRIVIALLY_COPYABLE
// feature support seems to be: gcc clang msvc
// auto 4.4 2.9 2010
// std::is_trivial 4.5 ? 2012 (2010 only duplicates std::is_pod)
@@ -563,7 +555,6 @@ using std::memcpy_if_trivial_else_error;
using std::memmove_if_trivial_else_error;
using std::memset_if_trivial_else_error;
#endif // HAVE_CXX11
#endif // NDEBUG
#endif // PS_TYPES_H
#endif // _PSTYPES_H
Loaded 100 of 567 files, more files were not shown because too many files have changed in this diff. Show more