Compare commits

...
Author SHA1 Message Date
SirKnightly 20d86c6079 Automated build commit 2026-02-09 17:20:32 +00:00
wookieejedi 5eea353952 Fix Select All HUD Color Slider (#7202)
#6670 added support for custom gauges and also cleaned up the color setting of hud gauges when using 'Select All'. It seems two copy paste bug slipped through the cracks (ie the order of colors was copied from red to blue/green, when really the order should have been updated).

This PR restores the proper order and fixes the bug.
2026-01-26 23:05:08 -05:00
wookieejedi 319e573637 Fix #7199 (#7200) 2026-01-25 19:37:08 -05:00
BMagnu 83f08291af Rollback insignia fix (#7196)
* Revert "Move insignia post-processing to model load"

This reverts commit 665d7145b6.

* Revert "Cleanup of old insignia stuff"

This reverts commit fdf98a1f3a.

* Revert "render insignias as decals"

This reverts commit 24cdeb3c95.

* Isolate reverts to insignias

* Fix oversight

* clang tidy
2026-01-23 11:11:47 -05:00
Asteroth 2813ceb5e9 Fix for ships blowing up with live debris submodels (#7139) 2026-01-20 08:32:03 -05:00
wookieejedi a2c05009dc Fix asteroids toggling (#7188) 2026-01-15 23:15:31 -05:00
Daniel Cazares 2af0aefd91 fix(eye): guard missing model instance (#7180) 2026-01-15 23:15:17 -05:00
wookieejedi d9a6330d9e Fix regression from #7051 (#7191) 2026-01-15 23:14:23 -05:00
Mike Nelson 634eece877 make this check less stringent (#7182) 2026-01-15 23:13:43 -05:00
Daniel Cazares e5c4c58eba fix(graphics): measure ellipsis before shrinking in force_fit_string (#7165)
* fix(graphics): measure ellipsis before shrinking in force_fit_string

* chore(comments): clarify why ellipsis measurement is needed before shrinking
2026-01-03 19:33:40 -05:00
wookieejedi e567ae7c81 Fix warning of unbound keys for newer controls (#7173)
* Fix warning of unbound keys for newer controls

Under some circumstances, FSO will warn the player if a key is unbound if that they need to press to complete a training event. This logic though only works for keys with XSTRs from 507-619. Any control that was added with a different XSTR will not be picked up correctly, and thus the message displayed to the player will be incorrect, confusing, and not helpful.

To fix this `missiontraining.cpp` around line 757 needs to account for the newer controls as well.

This PR adds those needed checks, and this fixes the issue. This fix is especially needed for mods that use training missions with newer controls such as FotG. Note, we do ship with a full default controls table with all the needed keys bound, but we have found players will be players and simply change all their controls before even starting the training missions. Thus they unbind key training controls, and with this bug they are unable to correct their controls. With this fix they are properly able to update their controls as needed to complete the training missions.

Fixes #7172.

* var name update
2026-01-03 18:19:10 -05:00
Kestrellius 46d3e490bb Render reorder (#7177)
Move the rendering of ships with effects to immediately after rendering of ships. This fixes rendering in several edge cases where, currently, additively-rendered items, among others, are incorrectly obscured by ships with effects. This affects anything that doesn't render to the depth buffer -- nebula poofs, particles, lasers, beams, engine glows, etc.

Forward-rendered objects (due to transparency) still interact incorrectly with full nebula fog, but that's out of scope for this PR.
2026-01-03 15:37:28 -05:00
Daniel Cazares 4dfdddf4d2 fix(model): capture driver inputs by value (#7163) 2025-12-28 05:14:22 +09:00
Mike Nelson 1da50c738f medal display name should be distinct from name (#7148) 2025-12-26 19:14:36 +09:00
Daniel Cazares a8f4be1d34 fix(math): correct vector assignments in vm_vector_2_matrix_gen_vectors (#7162) 2025-12-26 19:14:24 +09:00
Goober5000 306f34a574 update force_fit_string and bounds in calling functions (#7161)
Rewrite `force_fit_string` to be more robust and to remove the potential for memory access violations.  Also audit all of the length parameters in the calling functions - some of them were the buffer size, not the length.  This should fix the mysterious crash in the mission log screen.
2025-12-25 08:08:46 +09:00
Taylor Richards fb52a8861e fix fullneb lightning glows (#7153) 2025-12-23 07:10:16 -05:00
Goober5000 587e6f4c21 Merge pull request #7150 from notimaginative/macos_ninja_ci_fix
fix macOS ci warning regarding ninja
2025-12-22 23:17:46 -05:00
Goober5000 06d1514a47 Merge pull request #7151 from notimaginative/cmake_path_variable_fix
replace FILEPATH with PATH where appropriate
2025-12-22 23:17:06 -05:00
Goober5000 f483a92329 Merge pull request #7152 from notimaginative/macos_debug_flag_fix
disable compiler optimizations on Mac debug builds
2025-12-22 23:15:09 -05:00
Goober5000 db69cb8395 Merge pull request #7157 from Baezon/uvec-dominant-frame-of-ref
Uvec dominant frame of ref for submodels
2025-12-22 23:14:08 -05:00
Asteroth e803d8a0e9 move some stuff 2025-12-21 23:36:25 -05:00
Asteroth 761b97529a comments and assertions 2025-12-21 22:12:28 -05:00
Asteroth 1cd45aa1b4 uvec dominant frame of ref for submodels 2025-12-20 20:53:54 -05:00
Goober5000 5f110962e3 use pm->autocenter when drawing weapon lines (#7147)
In the weapon loadout screen, the model's visual center should be taken into account when calculating coordinates for weapon banks.  Fixes #7145.
2025-12-19 17:01:16 +09:00
Taylor Richards 0dea8d2904 disable compiler optimizations on Mac debug builds
Clang treats -Og the same as -O1, which prevents the viewing of many variables
while debugging, so we need to use -O0 directly.
2025-12-19 02:46:18 -05:00
Taylor Richards 381eda672b replace FILEPATH with PATH where appropriate
Fix issue setting path cmake variables in the gui due to the incorrect
type being specified.
2025-12-19 02:44:21 -05:00
Taylor Richards cbf2693d33 fix macOS ci warning regarding ninja 2025-12-19 02:39:18 -05:00
Goober5000 6c96d12133 slight fix to "Always save display names" (#7146)
A ship which has a display name and belongs to a wing will display that name no matter what wave it appears in.  Since this should only be done when the mission designer deliberately wants it to be done, omit this case from the cases covered by the "Always save display names" feature.  (To always save a display name in this case would require saving one for the wing, but a wing display name feature does not yet exist.)

Fixes #7140.  Followup to #6810.
2025-12-15 15:50:09 -05:00
BMagnu cb3819309b Make sunglows respect detail boxes (#7141)
* Add flag to respect detailboxes in model collide checks

* Don't check detailboxes that are invisible for sunglare checks

* Use local coords

* Actualyl also run the check for cockpits & show ship

* Be less stupid
2025-12-13 11:15:23 +01:00
wookieejedi 8fb0cbe08c Fix Message Line Positioning (#7144)
Message Lines were not being rendered to the proper position for certain resolutions per issue #7143.

#6575 seemed to be the commit it broke on, and looking at that commit it showed that code added `x` and `y` to the render string positions, even though `x` and `y` did not exist there before.

This 1-line PR fixes that issue, and it is confirmed to fix the issue by KamiGaAtaeta. Also I checked and on more standard resolutions the message lines still display as expected in both mission and the HUD Config screen.
2025-12-12 23:16:57 +01:00
Kestrellius ce0fe088eb Fix 'on edge' parameter for ring volumes not doing anything (#7142) 2025-12-11 01:30:52 +00:00
wookieejedi 86ef459cf6 Fix shadow shader for Mac (#7138)
Turns out Mac shader does not support the cleaner bracket style
2025-12-07 18:30:55 -08:00
Kestrellius cb19fecc36 Fix culling for particles that have length (#7136)
* fix culling

* further check

* rotate check

* fix error

* bithackery
2025-12-05 13:29:56 +09:00
wookieejedi af7bb154d3 Fix shadow shader update oversight (#7135) 2025-12-04 21:03:46 +01:00
wookieejedi 937091ee77 Fix haloing in cockpit shadows (#7134)
* Fix haloing in cockpit shadows

Cockpits can use shadows, but the small geometry of cockpits and the current shadow pass methods result in bad looking shadow halos and poor quality. The issue is that small scale geometry, like cockpit struts/pillars, cast overlapping shadows--and these overlapping shadows need to use a different shadowing method then the default larger external shadows.

I made a small edit the to the shadows file to fix it for small scale shadows (external shadows, IE those at longer ranges, remain using their method since that method works better at longer ranges). In FotG we have been using this update for years and it works very well.

KamiGaAtaeta was having these halo issues on the FS Ship Interiors project, and this shader update fixed those issues. Thus, per suggestion from Lafiel, I'm PRing this fix so everyone can easily use it.

* remove dead code
2025-12-03 23:59:38 +01:00
BMagnu dd0add946b load single frame motion debris from the correct file name (#7133) 2025-12-03 14:03:59 +00:00
BMagnu 2e4d97ce4e Properly initialize max_life when no lifetime is available (#7131) 2025-12-02 12:15:36 +09:00
Goober5000 88c3cea753 fix scannable ship types (#7130)
The `$Scannable` field in objecttypes.tbl controls whether a ship can be targeted by the "target unscanned ship" key.  In retail, these were cargo containers and freighters.  This is independent of which ship types can actually be *scanned*, which in retail were cargo containers and *transports*.  Since the targetability is meaningless without the ability to scan, this fixes the retail inconsistency by moving the targetability from Freighter to Transport.

Note that this is only relevant for legacy scanning behavior.  Mods which use `$Unify scanning behavior:` do not experience the inconsistency.
2025-11-29 12:02:13 +01:00
wookieejedi 4d184d501a Two 1-line fixes for new particle lights (#7125)
* Two 1-line fixes for new particle lights

This PR provides 2 semi-fixes for the particle lights with 2 simple conditionals.

------------
First, it fixes #7124. In sum, it fixes the issue that new particle lights not affected by light quality slider. This fix is done by adding a conditional check so that particle lights only render when weapon lights do, at the highest lighting setting. This ensures that lower end machines can properly turn off particle lights to help with performance.

------------
Second, this PR adds an extra conditional to ensure that the system does not attempt to render particle lights if the radii is <= 0.

Light emitters logically do not cast light if the light emit radius is <= 0. In 2015, there are assertations in each of the `light_add` functions that trigger if the radii are <= 0.

With the new particle lights and addition of curves for particle lights, this can make result in light radii <= 0. This approach is useful to allow things like hit effects to only give off light if the hit effect is large enough (IE if it hits a large ship scale the radii to be larger, and if it is too far away or a small ship scale the radii to 0). So, in those situations of <= 0 light radii, the modder would expect that the light point is just not shown and simply not attempted to be rendered. Unfortunately, this is not quite the case, as any light radii <= for particle lights will trigger the assertation within the `light_add` functions.

The fix for this is simply to have early conditional checks for light radii. There is already precent for this in the code in 3 locations--1) within the weapon light code in `object.cpp` line `1365`; 2) within the beam light code in `beam.cpp` at line `2009`; and 3) within the fireball light code in `object.cpp` line `1457`. Note these sections also early out if the light intensity is <= 0 as an optimization step, which also makes sense.

This PR simply follows that precedence for particle lights by adding an early out if the light radii are 0. This allows modders to properly use a range of radii and helps ensure internal consistency with the code mentioned above that already does this early out for weapon and beam lights.

----------
Given the benefits listed above, having this PR in before stable would be fantastic (or a relatively quick point release after stable).

Tested and fixes the issue as expected. Happy to answer any questions and/or discuss!

* comment signpost
2025-11-26 22:38:26 +01:00
BMagnu 702e8f430d Fix rendertargets being incorrectly invalidated (#7126) 2025-11-26 22:38:17 +01:00
Goober5000 5941d5ed8b Merge pull request #7123 from Goober5000/ship_cleanup_tweak
tweak ship_cleanup
2025-11-21 20:05:13 -05:00
Goober5000 879b1f43ad Merge pull request #7121 from Goober5000/fix_font_crash
fix a font crash
2025-11-21 20:04:56 -05:00
Goober5000 fff480e101 tweak ship_cleanup
The object and ship indexes held by the ship registry do not actually need to be cleared until `ship_cleanup` completes.  This will allow the ship to be referenced in any functions (via script hooks or otherwise) that are called during cleanup.

Tweak related script documentation while we're at it.

Follow-up to #7120.  Fixes #7122.
2025-11-21 02:32:20 -05:00
Goober5000 406f8e58ca fix a font crash
The `setAutoScaleBehavior` function call should go after the null check to prevent crashing FSO if the font fails to load.

Also, if a font fails to load, skip over it so that subsequent fonts can be parsed.
2025-11-20 21:16:22 -05:00
Goober5000 2af19b6605 Merge pull request #7120 from Goober5000/scripting_ship_access
remove ship_name_lookup in scripting code
2025-11-16 13:10:57 -05:00
Goober5000 59e8742c84 remove ship_name_lookup in scripting code
Use the ship registry rather than `ship_name_lookup`.  Also, be sure to check that ships, targets, and subsystems exist.

Fixes #7109.
2025-11-16 02:41:24 -05:00
Goober5000 849a4f6d4e Merge pull request #7118 from Goober5000/reuse_sound_handles
reuse sexp audio handles
2025-11-15 14:42:51 -05:00
Goober5000 8c4849aaee reuse sexp audio handles
The `play-sound-from-file` sexp did not have any way to automatically recycle audio handles, instead relying on other sexps to explicitly close them.  It is more modder-friendly to implicitly free any handles of streams that have finished playing, so this PR adds that capability.  It also adds a Warning in the event that an unused audio handle cannot be found.

Fixes #7108.
2025-11-14 18:02:32 -05:00
Goober5000 a06da31c6a Merge pull request #7114 from Goober5000/quick_anchor_fix
quick fix to arrival anchor bug
2025-11-12 22:27:47 -05:00
Goober5000 77c7569f68 quick fix to arrival anchor bug
Fix the wing arrival anchor check when ships are cleaned up.  This resolves bug #7110.  A more comprehensive refactor will be done post-25.0.
2025-11-12 21:42:22 -05:00
BMagnu fb229ccbb1 Fix texture slot 0 incorrectly being considered invalid in a lot of cases (#7106) 2025-11-06 09:34:03 +09:00
Goober5000 be2045013b Merge pull request #7103 from Goober5000/fred_waypoint_names
fix FRED and QtFRED handling of waypoint names
2025-11-02 01:00:12 -04:00
Goober5000 5659243766 Merge pull request #7100 from Goober5000/message_log_add_seg_fix
fix multi-line display in mission log
2025-10-31 22:18:48 -04:00
Goober5000 6d03ce1c13 make clang happy 2025-10-31 21:12:49 -04:00
Goober5000 cb3a17a404 fix FRED and QtFRED handling of waypoint names
First, fix a crash reported in Discord: prevent FRED or QtFRED from giving a waypoint list a name that is too long.

Also fix the updating of references, including for the constituent points, if a waypoint list is renamed.
2025-10-29 00:24:09 -04:00
Goober5000 f4a3a885dd fix multi-line display in mission log
At some point the mission log became unable to display entries that wrapped around to subsequent lines.  Fix that by keeping track of how many lines each entry occupies and adjusting the display accordingly.

This will correctly display mission log segments on multiple lines, but it uses the current string splitting technique which does not split properly in all cases.  The splitting will work properly once the `split_str_once` refactor is merged.
2025-10-26 00:13:37 -04:00
BMagnu 1fe175b523 Make FSO not crash if invalid options get displayed (#7096)
* Make FSO not crash if invalid options get displayed

* Add release warning
2025-10-24 00:45:23 +02:00
Goober5000 2ceb523954 Merge pull request #7089 from z64555/issue_7088
Adds doxy and warnings to control_used() and check_control_used()
2025-10-17 12:36:04 -04:00
wookieejedi 349b353bbe Fix 'has parent' case for apparent size input curve (#7094)
Thanks for lafiel for catching this: the "has parent" case of that #7090 needs to account for the parents rotation
2025-10-17 12:30:02 +00:00
Kestrellius c0cc43f558 add input (#7090) 2025-10-17 07:43:50 -04:00
Asteroth c3980b7aa6 patch split_str_once (#7093) 2025-10-17 15:26:29 +09:00
Asteroth 5d78e3ee63 Temporary perpetual shudder (#7092)
* perpetual shudder should only last for the mission

* reset other variables too
2025-10-17 00:44:41 +02:00
wookieejedi 38acf9ae15 Allow mods to easily tune 'Target Hostile Bomb or Bomber' control (#7048)
* Allow mods to easily tune 'Target Hostile Bomb or Bomber' control

The `Target Hostile Bomb or Bomber` control targets bombs or bombers, though in mods may want a way to easily set exactly what this control does while still leveraging the utility of the 'bomb' flag for weapons and/or 'bomber' flag for ships (both of which affect AI behavior in various ways).

For example, in Solaris, Darius wants a way a way to allow players to only target bombs, while still allowing ships to be classified as as 'bomber'. In this case, the 'only bombs' option can be used. This PR adds a `$Behavior for pressing 'Target Hostile Bomb or Bomber' control:` to allow mods to set the behavior to `default` `only bombs` or `only bombers`.

For example, in FotG we want to allow players to target just bombers, as bombs themselves are hard to shoot down (critically though we still want the player to be able to manually target a bomb via 'target object in reticle'. This allows players to prioritize and easily target bombers while allowing skilled pilots to retain the option of manually shooting down bombs if they want an extra challenge. In this case the 'only bomber' option can be used.

Tested and works as expected in all cases.

* update using cleaned up function

* extra alt option for default case

* warning cleanup

* update default message

* whitespace clang

* appease clang with brackets
2025-10-15 16:47:45 -04:00
z64555 10cdf2d367 Add some more doxy, since we're here 2025-10-15 09:08:31 -05:00
z64555 d84600356a Adds doxy and warnings to control_used() and check_control_used() 2025-10-15 08:52:44 -05:00
wookieejedi a0b4ae1a2e Mouse Wheel Cleanup Option (#7079)
* Adds an optional bool to `mouse_down()` for `must_be_mousewheel` to only process mousewheel events.
* Used within `controlsconfig.cpp::check_control_used()` to handle a special case with the mousewheel to allow using it for `CC_TYPE_TRIGGER` controls.
2025-10-15 08:27:35 -05:00
Taylor Richards 6a9b6a38e9 fix several uninitialized variable warnings (#7087)
Initialize structs as appropriate in PXO and mdns code that were reported by
gcc/clang/coverity.
2025-10-14 13:51:53 -04:00
Goober5000 ea985b6455 Merge pull request #7081 from Goober5000/wing_destruction_updates
add flag to automatically cancel future waves if a mothership is destroyed
2025-10-13 22:51:04 -04:00
wookieejedi 6b5f9c9be7 Cleanup and fix hud_target_missile (#7051)
* Cleanup and fix `hud_target_missile`

The function `hud_target_missile` is what runs when the `Target Hostile Bomb or Bomber` control is pressed. Notably, it does not work if there is only 1 hostile bomber. The issue is that the loop within `hud_target_missile` is designed to search for the "next" target, but when there's only one valid target, the loop logic skips over it. But, `hud_target_missile` correctly will target if there is only 1 bomb though, so the fix is to just update the ship bomber logic similarly. This PR updates the logic of the bomber search so it matches the bombs search, that way the bomber search can target a bomber if there is only 1 present in the mission (just as how the existing bomb search will let you target 1 bomb if there is only 1 bomb in the mission currently).

This PR also takes the opportunity to cleanup the function with more clear variable and function naming, along with two new internal bools which will be very helpful for later planned PRs.

Fixes #7050. Tested and works as expected.

* rework hud_target_hostile_bomb_or_bomber

* appease clang

* additional cleanup and modernization

* more cleanup for select previous
2025-10-13 21:51:50 -04:00
Goober5000 eca894e732 add flag to automatically cancel future waves if a mothership is destroyed
Infamously, in FSO, a wing whose mothership is destroyed will not, itself, be considered destroyed if it has more waves remaining, because wing cleanup and logging typically only happen when the last ship in the last wave exits the mission.  Instead, with no mothership to arrive from, waves will simply no longer arrive.  Events that check to see if the wing is destroyed or departed will never fire.

This PR fixes that behavior, ensuring that the wing is cleaned up and logged properly, by using the same logic as the enhanced `cancel-future-waves` SEXP operator.  This is tied to a new `$cancel future waves of any wing launched from an exited ship:` AI profiles flag which is enabled by default on 25.0+.
2025-10-13 21:00:14 -04:00
Goober5000 beaeafeb97 Merge pull request #7086 from Goober5000/vectorize_subsys_hits
vectorize subsystem damage/heal list
2025-10-13 20:58:15 -04:00
Goober5000 d77f413f39 vectorize subsystem damage/heal list
When checking for subsystems within range of a hit, more than one subsystem is unusual, and 32 (the original `MAX_SUBSYS_LIST` value) is so unlikely as to be impossible.  So bumping it to 200 was not needed here -- DTP might have confused it with one of the other subsystem limits.  But rather than reducing the limit again, we can simply make the array into a vector.
2025-10-11 19:13:18 -04:00
BMagnu a243052f4c Fix Particles Hosted on Particles Which Themselves Are Parented (#7085)
* Allow particles serving as particles hosts to provide their own parent to their children

* Fix parent particles with parents
2025-10-11 23:12:46 +02:00
Goober5000 05bf43bfcc Merge pull request #7082 from Goober5000/fred_menus
rearrange FRED2 menus to match qtFRED
2025-10-11 13:05:45 -04:00
LuytenKy 1b8f920529 Implement a new ai_profiles.tbl field that allows the maximum number of allies allowed to be rearming to be modified (#7065) 2025-10-11 11:41:53 -04:00
Goober5000 ea1a3707ad Merge pull request #7084 from Goober5000/initialize_fields
initialize some fields
2025-10-10 19:18:55 -04:00
Goober5000 cd2ff46541 initialize some fields
Handle some uninitialized fields recently flagged by Coverity.
2025-10-10 18:52:49 -04:00
Goober5000 17feb28d29 rearrange FRED2 menus to match qtFRED
See issue #6985 and PR #7064.
2025-10-10 01:29:27 -04:00
Goober5000 897429e239 Merge pull request #6630 from Goober5000/form_on_wing_and_stay_still_updates
form-on-wing and stay-still updates
2025-10-09 22:48:58 -04:00
Goober5000 712491696f Merge pull request #7080 from Goober5000/patch_cancel_future_waves
patch cancel-future-waves to handle absent waves
2025-10-08 22:48:58 -04:00
Goober5000 b8fa52097c patch cancel-future-waves to handle absent waves
If the `cancel-future-waves` sexp is called when a wave is not present, it would previously not handle wing cleanup, such as setting flags and updating the mission log.
2025-10-08 21:56:42 -04:00
wookieejedi 7eff7900a6 Allow mods to tune generic debris generation (#7027)
* Allow 'No Impact Debris' to also apply to explosions

The ship/subsystem flag `no impact debris` prevents the default debris shards from spawning, which is very valuable especially now that impact effects can be completely customized via the -particles table. Now that particle effects are also an option for ship and subsystems explosions, it is also invaluable to have the ability to turn off the spawning of shards when either of those explosions happen.

This PR uses the same flag of `no impact debris` and simply does not spawn the default shards for ship explosions or subsystem explosions of that flag is enabled. I thought about adding two new flags, but that would add to the flag clutter, and most folks who use these flags to turn off impacts I would estimate also want them off for actual explosions. Also, a new flag called `no explosion debris` would likely be confusing, so if a new flag were to be added it should be called something like `no default explosion debris shards` or something. Overall I've opted with the simpler and cleaner solution to just have it all under the impact flags. If two new flags are desired though I can easily add them.

Tested and works as expected.

* throw clang a bone

* use two different flags

* update flag name

* typo and logic fix

* allow custom generic debris to always work

* update to better methods

* cleanup logic

* clang
2025-10-07 07:42:44 -04:00
BMagnu 2808606b84 Fix Cross Compilation (#7077)
* Fix a few libraries for functional cross compilation

* include timeval
2025-10-06 14:12:40 +00:00
BMagnu 84d3cdf847 Fix anim executables when animations wrap around (#7076) 2025-10-06 13:53:44 +00:00
Asteroth b1e4bd7ef8 flight cursor aiming for secondaries (#7075) 2025-10-06 15:49:16 +02:00
Goober5000 f087c8adbb Merge pull request #7072 from Goober5000/colour_followup
fix required_string_either bug
2025-10-03 13:55:11 -04:00
Goober5000 8a8d7222f2 fix required_string_either bug
`optional_string_either` advances the parser by default, but `required_string_either` does not advance the parser by default.  So be sure to advance the parser here.

Followup to #7069.
2025-10-03 12:42:38 -04:00
Goober5000 9c353e71da Merge pull request #7067 from Goober5000/fireball_parse_lookup
Updates to fireball parsing
2025-10-01 23:36:55 -04:00
Goober5000 5a3cfa5f19 add ability to parse fireball type using its ID 2025-10-01 21:47:03 -04:00
Goober5000 afcb4871a3 upgrade stuff_int_list with enum class types
For type safety, use enum class rather than int.

Also make a few miscellaneous tweaks such as adding a new casting macro and making detail level settings a bit cleaner.
2025-10-01 21:47:01 -04:00
Goober5000 2589d8c542 Merge pull request #7069 from Goober5000/color_colour
color/colour updates
2025-10-01 21:42:53 -04:00
Mike Nelson 2374da528f Fix Asteroid Table Parsing (#7068)
* Zero initialize the string

* Initialize Radius
2025-09-30 13:56:30 +09:00
Goober5000 3d8174d0d8 color/colour updates
Add Colour as a recognized parsing option in several more places where Color is used
2025-09-29 02:05:49 -04:00
BMagnu dc50aaa86b Particle Scripting API (#7061)
* Expose particle effects to scripting

* Allow creation of particle sources

* Add API for particle sources

* bugfixes
2025-09-28 10:40:46 +02:00
BMagnu 2c04313c59 Fix copy and paste error (#7066) 2025-09-27 12:55:22 +00:00
Mike Nelson e6e551121f Modernize QtFRED menus (#7064) 2025-09-26 15:22:50 +00:00
Mike Nelson aadf1c5f8b QtFRED Briefing Dialog (#7032)
* initial cleanup

* rough in more code by copying from debrief

* make buildable

* prep the briefing data model

* completed data model

* activate the ui

* unused param

* stub this

* cmake list is important

* set max cam transition higher

* static cast
2025-09-26 08:56:28 -04:00
Mike Nelson 6cb49fa245 QtFRED Campaign Editor (#7030)
* refactored campaign dialog model

* begin setting up the ui code

* work on available missions list

* available missions filter

* working state

* better available missions list

* add tooltip

* handle the retail file format

* add custom data button

* prep for mission graph

* prep for campaign graph widget

* basic functioning nodes

* Basic edges implementation

* path arrows and names

* graph view upgrades

* fixup mode handling

* clean comments

* add some styling

* drag to connect

* add and remove mission nodes

* minor cleanup

* edge emphasis based on selection

* style tweak

* cleanup some graph and tree interactions

* clean out old code

* add getters for lists

* implement the rest of the detail controls

* custom data

* cleanup

* add branch manipulation controls

* branch move buttons

* set as first command

* linux is soooo sensitive (to case)

* more case

* unused param

* fixes

* more terminator checks

* namespaces are fun for the whole family

* Clang

* Private, you're fired!
2025-09-26 08:56:10 -04:00
Mike Nelson d0737926bd QtFRED Variables Dialog (#7020)
* refactor variables/containers model and variables ui

* get containers working

* switch to tab view and add filters

* fix the filters

* fixes

* ooo

* static casts

* more static cast

* unused

* clang
2025-09-26 08:55:43 -04:00
Goober5000 cfc1088ed0 Merge pull request #7056 from Goober5000/docked_wing_fixes
for docked wings, initialize some data before the wing is created
2025-09-25 13:17:29 -04:00
Mike Nelson 0d4c222a97 QtFRED Debriefing Dialog (#7018)
* qtfred debriefing dialog

* explicit flags

* clang
2025-09-25 07:51:08 -04:00
BMagnu 22b5f8148d Fix Beam Particles (#7057) 2025-09-25 07:49:49 -04:00
Goober5000 e3ff08ea46 for docked wings, initialize some data before the wing is created
Docked wings are created with a sort of reverse-bootstrapped initialization sequence, since normally wings are created first and the wing creates the ships.  Issue #7041 found a hole in this sequence, as the mission log entry was added before the IFF was assigned.  Fix this bug, and potential future bugs, by moving the assignment of certain fields to immediately before the docked wing is brought in.  AI can't be moved that easily, so fix AI setup by calling a separate function.

Fixes #7041.
2025-09-24 23:47:14 -04:00
Goober5000 e6da70a0ff Merge pull request #7053 from Goober5000/check_for_hangar_bay
check missions for missing hangar bays
2025-09-22 22:49:51 -04:00
Goober5000 ac03c1a612 Merge pull request #7046 from Goober5000/nebula_poofs_sexps
add more advanced sexps for nebula poofs
2025-09-22 22:49:28 -04:00
BMagnu 5903dc7e9d Filter out invalid particles (#7055) 2025-09-23 04:06:09 +02:00
Mike Nelson a5dc72c56c localize language names for SCPUI (#7052) 2025-09-22 07:50:15 -04:00
Goober5000 c1444781e8 check missions for missing hangar bays
It's possible for ships to be designated as arrival or departure anchors when these ships do not actually have hangar bays.  This cannot normally happen in the interface but can happen when missions are edited in a text editor, or are imported, or the modpack is changed.  So add warnings and checks for this situation.
2025-09-22 00:37:38 -04:00
Goober5000 300cd8a1cf Merge pull request #7049 from Goober5000/fix_anchor_error_handling
fix handling of invalid arrival/departure anchors
2025-09-21 15:36:07 -04:00
Goober5000 60b8876eb0 fix handling of invalid arrival/departure anchors
Missions with invalid anchors would previously cause crashes, so update the anchor code to fail gracefully:
1. Negative anchors (especially -1) could trip up the bitwise-AND in the mission saving code, so check for negative anchors *before* checking the special-anchor flag
2. In the event a mission runs with a negative anchor (which shouldn't happen because even `<error>` is added as a recognized anchor), handle the situation with if()s as well as Asserts
2025-09-20 23:59:34 -04:00
BMagnu 3c939f5cc4 Particle Curves with access to host object & ship properties (#7047)
* Add parent / host input curves

* reorder some includes to allow particles access to ship.h

* Allow modular_curves to deal with functions returning optionals

* Add convenience function to return an optional from an objp and an instance type

* Add a bunch of particle curve inputs

* Fix internal linkage warning

* Add hitpoint and shield fraction (albeit limited to ships)
2025-09-21 02:52:22 +02:00
The Force db4f9e4370 QTFred: Ship Editor Modernization 2 Part 2 (#7026)
* Switch to using signals/slots rather than connect

* Adds coloured persona box

Adds coloured persona box/ fixes persona loading, and reduces amount of data reset on model change

* Update To new flags widget

Updates To new flags widget this reqires moving several non flag items to other dialogs, which also lead to adding a new feature that lets a custom guardian threshold be set in editor

* Switch to size_t

* Make Mission Spec work with changes to Flaglist

* Remove Duplicate member

* Clang Appeasement

* Make static

* Properly initialise guardian threshold

* Convert Player Orders dialog to Checklist box

Unfortunately also formats Shipeditordialogmodel

* Update Custom warp to modern style

* Update Initial status to modern style

* Missed a file

* Update Ship Flags Closing

* Remove QT Code from model

* Update Ship Goals Closing

* Update Special Stats Closing

* Minor Changes

* Replace Paths Dialog With generic checkbox dialog

* Update Texture Replace to modern style

* Cast to int

* Initialize then append

* Clang Fixes

* Two didn't save

* More Clang

* Tighten NOLINT's

* Remove dead code

* Change to built in buttons
2025-09-20 08:18:40 -04:00
Goober5000 fa250c5ff6 add more advanced sexps for nebula poofs
1. Add new SEXP operators for setting and fading poofs that will work with one or more arguments, or no arguments (which will affect all poofs at once).
2. Update SEXP help, including clarification that the three `nebula-change-` operators require a nebula to be active to have any effect.
2025-09-19 22:55:09 -04:00
Goober5000 0c736b7bca address feedback 2025-09-18 21:30:45 -04:00
Goober5000 440c6fda4c restore original squad-commanded priority by removing lines added in 09d116f 2025-09-18 21:30:45 -04:00
Goober5000 aad6c84684 add $purge player-issued form-on-wing after subsequent order: flag 2025-09-18 21:30:45 -04:00
Goober5000 7f8674c532 enhance goals with additional parameters 2025-09-18 21:30:43 -04:00
Goober5000 40b4294b13 add Clear_all_goals_first goal flag, allow it to be used with any goal, and adapt goal assignment accordingly 2025-09-18 21:27:43 -04:00
Goober5000 54ee4a30d6 Preliminary cleanup of goal code. 2025-09-18 21:27:41 -04:00
Goober5000 a3691ed481 Merge pull request #7043 from Goober5000/disable_form_on_wing_at_mission_start
add ai_profiles flag to disable form-on-wing at mission start
2025-09-18 21:25:14 -04:00
BMagnu 973bd5570e Small particle copy-paste fixes (#7044)
* Use correct curve

* Properly init animation data
2025-09-18 12:49:46 +00:00
BMagnu 15445408f9 Particles can now emit light (#7040)
* Add backend for paticles to emit light

* Parse new light options

* Modular Curves account for light

* Actually apply curves

* remove comment

* Add cone mode
2025-09-18 13:39:12 +02:00
Goober5000 337b0402ce add ai_profiles flag to disable form-on-wing at mission start
By default, the player's wing will be issued a form-on-wing order at the beginning of the mission if the wing has no other initial goals.  Wings in the "starting wings" list that are not the player's wing will also be issued this goal if they arrive after mission start and have no other initial goals.  This PR adds a flag to disable this behavior.
2025-09-17 23:19:36 -04:00
Mike Nelson 8ee96781b3 QtFRED Team Loadout Dialog (#7016)
* unify naming and some ui cleanup

* sidetracked by a typo

* another sidetrack

* show team list as dropdown like other dialogs

* This is a mess

* cleanup model init and apply

* tweaks

* clear string vars

* validate variables

* some cleanup

* refactor data model

* finish the model and begin the ui

* start getting the ship list working

* fix multi select issues

* activate all 4 lists

* activate teams, filters, selection stuff

* fixes

* clang

* one more cleanup
2025-09-17 16:39:55 -04:00
wookieejedi 58ff297495 Update to #7023 (#7042)
From Sept 15 onward in fast debug I hit this error when using weapon create
ASSERTION: "cur_frame < part->nframes" at particle.cpp:371

Assert: "cur_frame < part->nframes"
File: particle.cpp
Line: 371

Thanks to lafiel, `that assert should have an `|| (part->nframes == 0 && cur_frame == 0) appended beaause while we need to stay inbounds for animations, frame 0 for both is valid for static images`
2025-09-17 15:16:24 +09:00
BMagnu 7875e5f7c7 Supply normals for edge hits (#7039)
* Pass out normals for edge hits

* Remove now-obsolete comments

* Also add third instance of disabled particle normal
2025-09-16 13:45:12 +09:00
Shivansps 8057b52a04 Do not set IS_X86 when target cpu is armv7 (#7033) 2025-09-16 13:38:02 +09:00
Mike Nelson ce3597f5a6 QtFRED Relative Coordinates Dialog (#7031)
* qtfred relative coords dialog

* clang
2025-09-15 15:56:58 -04:00
wookieejedi d42789decd Add new option for 'better combat collision avoidance' (#7036)
The `$better combat collision avoidance for fightercraft:` flag is incredibly useful in preventing AI from hitting large ships while attacking, and the ability to tune the collision avoidance aggression factor further helps mods tune behavior based on their ship speeds (tuning that value is via `+combat collision avoidance aggression for fightercraft:` ). Nevertheless, the improve collision avoidance still is not performed for that actual fightercraft's actual target, so there can be situations where a fast fighter attacks a large ship with high speed, and then when that fighter break off from the attack they turn directly into the large ship's hull. Allowing the target to be incorporated into the `better_collision_avoidance_triggered` check prevents this from happening. To allow the target to be a part of the checks we just simply need to not pass an 'ignor_ship` argument to `maybe_avoid_big_ship`.

This PR adds that ability via flag. This new flag is tested and works as expected. For example, without the flag a squadron of TIEs in FotG would literally ram themselves to death attacking some of our larger capital ships because they would break off attack too late, but with this flag they no longer accidentally collide at all. In discussion with Asteroth it was decided to make this enhanced behavior behind a flag, too.

This PR also fixes a small oversight from the original #2810 where `next_check_time` was never actually used within the `else` block of `maybe_avoid_big_ship`.
2025-09-15 07:31:02 -04:00
Goober5000 a32c3187de Merge pull request #7037 from Goober5000/save_fix
fix load/save mismatch in missions
2025-09-14 23:12:11 -04:00
BMagnu 1e7431a044 Give Particles Access to Their Spawner Effect (#7034)
* Make particles aware of the effect that spawned them

* Allow modular curves to call global functions from submember inputs

* Add more in and outputs for particle lifetime curves

* Allow post-curve velocity as a curve input

* fix warning and incorrect assig

* Disable CheckTriviallyCopyableMove

* Clang Tidy
2025-09-15 04:42:06 +02:00
Goober5000 b0166dd6a6 fix null vector assertion in slash beam collisions (#7038)
Beams in general, and slash beams most likely, can collide with the edge of a model, which will cause a non-normalized vector assertion when particle effects are created.  So comment out the normal assignment until a more comprehensive fix can be coded.
2025-09-15 04:42:00 +02:00
Goober5000 a023a1a82f Merge pull request #7014 from Goober5000/cinematic_followup
modular table fix for cinematic warp
2025-09-14 19:43:53 -04:00
Goober5000 bf51f8ee91 fix load/save mismatch in missions
When `$Contrail Speed Threshold:` and `+Volumetric Nebula:` are used in a mission, builds since 23.1 would throw an error, since the fields were loaded in a different order than they were saved.  It turned out that the save order was the correct order, so this fixes the load order.
2025-09-14 16:50:11 -04:00
Goober5000 f89cd69ad9 Merge pull request #7024 from Goober5000/event_double_quotes
restore double quote conversion in event editor
2025-09-08 22:04:07 -04:00
Taylor Richards 9d3ba25606 fix various issues with DDS decompression (#7022)
Fix heap corruption with smaller mipmaps due to decompression output
always being a 4x4 block rather than actual mipmap size. It's necessary to
pad the allocated data size to allow for the overflow.

Fix heap corruption with DXT1 due to using improper data offsets for the type.

Fix data offsets not always taking the depth value into account.

Adjust code for slight increase in decode performance.

Update bcdec to 0.98
2025-09-08 21:49:48 -04:00
Goober5000 0ee8c4b01a restore double quote conversion in event editor
The double quote conversions added in #7008 were removed in #6961, probably inadvertently due to the branch merge.  This adds them back.
2025-09-07 23:23:02 -04:00
Goober5000 fc102056b8 make legacy particle spews more robust (#7023)
If a pspew specifies a single-frame image, be sure that it is loaded correctly.  Fixes a particle crash in Inferno.
2025-09-07 02:32:07 +00:00
wookieejedi 877373f257 Fix for modern Thruster Particles (#7021)
Allows `$Thruster Effect:` by fixing copy-paste bug. Tested and fix confirms to work.
2025-09-06 06:31:55 +02:00
combatlombax 31d73b7ed5 Removing block that prevented volumetric and background nebulae from rendering simultaneously (#6853)
* Removing block to allow volumetric and background nebulas to render at the same time

* Updating code from feedback on pull request #6853

* What the... did I miss a line?

* Dual fog layering should now work when both nebula types are active
2025-09-06 00:00:06 +02:00
Goober5000 e4bba0c7f2 Merge pull request #7010 from Baezon/direct-fire-lead-target
Add 'direct fire lead target' beam flag
2025-09-03 23:27:27 -04:00
Goober5000 0d0574df0f Merge pull request #7013 from vmedea/vulkan-build-fixes
Fix build issues with Vulkan SDK 1.3.301+
2025-09-03 23:27:19 -04:00
Goober5000 f2bc71c13a small optimization 2025-09-03 22:45:31 -04:00
Goober5000 f88e5c475e modular table fix for cinematic warp
Small and subtle fix: move the CINEMATIC options inside the preceding if() block in the same way that the flare style options are inside their preceding if() block.  This prevents the CINEMATIC options from being reset to their default values in subsequent modular table parsing.  Followup to #6500.
2025-09-03 22:04:48 -04:00
Mara Huldra 5848df31dd Fix build issues with Vulkan SDK 1.3.301+
This fixes two build issues:

- `vk::PFN_DebugReportCallbackEXT` takes `vk::DebugReport*EXT` not C
  type `VkDebugReport*EXT` since version 1.3.304.
  (KhronosGroup/Vulkan-Hpp@d5a18dc87e)
- `vk::ObjectDestroy` was moved to namespace `vk::detail` in version
  1.3.301.
  (KhronosGroup/Vulkan-Hpp@6e5489fcd9)

Guard them with version checks, so that compiling with older verions
of Vulkan keeps working.

Closes #6416.
2025-09-03 20:26:29 +00:00
wookieejedi 2820532ab3 Expose value of idle circling with 'attack-any' (#7011)
* Expose value of idle circling with 'attack-any'

The attack-any order is extremely useful and utilized order, and there was one aspect that was ripe for exposure to modders: the `CHASE_CIRCLE_DIST` that was used to determine how big of a circle to fly around while the AI waited for new hostiles to arrive. For ships with high speeds, like FotG and others, the default radius of 200 m was a bit on the small side, which lead to ships flying in tight circles. This was was counterproductive to goals of tuning the AI to have a more cinematic style, such as where AI would fly around in large distances while waiting for enemies to arrive. Fortunately, this value is easy to expose and allow modders to tune with just a new ai_profiles value.

Tested and works as expected.

* improve name and use correct distance
2025-09-02 22:55:03 -04:00
Goober5000 32084cfe59 Merge pull request #7012 from Shivansps/check-mod-nullptr
Fix crashing if mod uses custom hud gauge and no -mod cmdline is passed
2025-09-02 20:59:26 -04:00
Salvador Cipolla 7a75aaa172 Check cmdline_mod for nullptr before using it 2025-09-02 19:17:16 -03:00
John Fernandez c50cffae17 Merge pull request #6998 from MjnMixael/qtfred_background_dialog
QtFRED Background Editor Dialog
2025-09-01 22:50:12 -04:00
Mike Nelson b7aa53dd79 address feedback and fix some ui issues 2025-09-01 18:54:15 -05:00
Asteroth 90e1f18fa2 add recoil for secondaries (#7007) 2025-08-31 10:01:31 -04:00
John Fernandez 19a6a1aa97 Merge pull request #7000 from MjnMixael/qtfred_volumetric_nebula_dialog
QtFRED Volumetric Nebula Dialog
2025-08-31 09:09:56 -04:00
Asteroth 730b294d7b add 'direct fire lead target' 2025-08-29 20:39:06 -04:00
The Force 42eae002d2 Fix Permanent Guardian (#7009)
* Switch to using signals/slots rather than connect

* Adds coloured persona box

Adds coloured persona box/ fixes persona loading, and reduces amount of data reset on model change

* Update To new flags widget

Updates To new flags widget this reqires moving several non flag items to other dialogs, which also lead to adding a new feature that lets a custom guardian threshold be set in editor

* Switch to size_t

* Make Mission Spec work with changes to Flaglist

* Remove Duplicate member

* Clang Appeasement

* Make static

* Properly initialise guardian threshold
2025-08-29 15:01:11 +00:00
Mike Nelson b3db62cf25 QtFRED Mission Events Editor (#6961)
* Match file framework of mission events to the rest

* first pass at splitting up the model and ui layers

* fix up message side of event editor

* fix event selection

* fix lots of event bugs

* Get event annotations working

* type to search for operators

* control annotations

* Add event dragging and fix event deletion

* cleanup

* Event and Msg order manipulation

* implement some remaining message todos

* minor cleanup

* prep for browse ani dialog picker

* basic file browser for now

* cleanup

* unused var

* more unused

* clang

* static member access

* rebase error

* fix rebase error

* address feedback

* missing parenthesis
2025-08-29 09:05:11 -04:00
Mike Nelson 689d184869 commit error 2025-08-28 22:54:35 -05:00
Mike Nelson 772920b727 static method 2025-08-28 22:54:34 -05:00
Mike Nelson 2b2320ffe7 volumetric nebula dialog 2025-08-28 22:54:34 -05:00
Mike Nelson b9229cd713 more static 2025-08-28 22:54:12 -05:00
Mike Nelson 886b54c298 more clang 2025-08-28 22:54:12 -05:00
Mike Nelson ca4540a821 clang 2025-08-28 22:54:12 -05:00
Mike Nelson 0f73a59155 clean 2025-08-28 22:54:12 -05:00
Mike Nelson 1d1ed1905b minor fix 2025-08-28 22:54:11 -05:00
Mike Nelson 917b4547ec save swap with index 2025-08-28 22:54:11 -05:00
Mike Nelson 614f510d0a misc group stuff 2025-08-28 22:54:11 -05:00
Mike Nelson aa7fc6dafd skybox section 2025-08-28 22:54:11 -05:00
Mike Nelson cb4e132df2 ambient controls 2025-08-28 22:54:11 -05:00
Mike Nelson fe17997ca3 nebula groups 2025-08-28 22:54:11 -05:00
Mike Nelson f691966675 sun section 2025-08-28 22:54:10 -05:00
Mike Nelson a531fd9fed wire up the bitmap section 2025-08-28 22:54:10 -05:00
Goober5000 9c65e1c9b5 Merge pull request #7008 from Goober5000/more_fred_replace_stuff
add additional double quote conversion to FRED and qtFRED
2025-08-28 19:11:03 -04:00
Goober5000 69dfe8194d Merge pull request #7006 from Goober5000/parse_error_fixes
fix some parse error handling
2025-08-28 19:10:50 -04:00
Goober5000 2d37a24b4d fix formatting of alt names and callsigns when saving a mission 2025-08-28 01:20:51 -04:00
Goober5000 b970e22410 add additional double quote conversion to FRED and qtFRED
Any place in FRED that uses XSTR is susceptible to double quotes interfering with the standard XSTR format, not just the expected briefing, debriefing, messages, etc.  This PR adds `lcl_fred_replace_stuff()` - which handles not only quotes but semicolons and slashes as well - to the other places where XSTR is used.

(Note that quotes are handled just fine in non-XSTR fields.  Semicolons will still truncate non-XSTR fields, but addressing that is beyond the scope of this PR as it would require editing every text field in FRED.  Fortunately, errant semicolons merely modify the text as opposed to corrupting the mission file.)

Fixes #7005 in both FRED and qtFRED; tested in both.  Caveat aedificator: the briefing editor is not yet implemented in qtFRED, so the relevant fixes to briefing icon labels and closeup labels cannot yet be ported over.
2025-08-28 01:02:06 -04:00
Goober5000 289de86eb9 fix some parse error handling
Parse errors that are actually errors should use `error_display(1, ...)`, rather than merely logging them (which doesn't abort parsing) or using `Error` (which omits the line number and file name).

Fixes #7002.  Tested with a modified script file that produces the same error.
2025-08-27 22:48:39 -04:00
Kestrellius 1d561a1f1e Merge pull request #6999 from Kestrellius/cockpit-brightness-adjust
Cockpit-specific light intensity adjustment for lighting profiles
2025-08-27 17:59:56 -07:00
The Force 1310c38642 QTFRED Ship editor Modernisation 2 Part 1 (#6989)
* Switch to using signals/slots rather than connect

* Adds coloured persona box

Adds coloured persona box/ fixes persona loading, and reduces amount of data reset on model change

* Update To new flags widget

Updates To new flags widget this reqires moving several non flag items to other dialogs, which also lead to adding a new feature that lets a custom guardian threshold be set in editor

* Switch to size_t

* Make Mission Spec work with changes to Flaglist

* Remove Duplicate member

* Clang Appeasement

* Make static
2025-08-27 12:38:43 -04:00
John Fernandez b090e5d09c Merge pull request #7004 from MjnMixael/qtfred_fix_loadout_again
Fix QtFRED Weapon usage for real
2025-08-27 12:37:43 -04:00
Mike Nelson e9311ec828 use correct array throughout 2025-08-27 10:58:04 -05:00
wookieejedi eaafd8cb13 Merge branch 'master' into cockpit-brightness-adjust 2025-08-26 18:02:29 -04:00
wookieejedi 5f899634c2 Fix new deferred check (#7001) 2025-08-26 17:33:40 -04:00
Kestrellius 7918797eec more bugfixing 2025-08-26 00:37:30 -07:00
Kestrellius d279e477b3 bugfixing 2025-08-25 21:33:00 -07:00
Kestrellius d2f842494d setup 2025-08-25 21:00:05 -07:00
Kestrellius a6f9337d5a add check (#6997) 2025-08-25 22:48:13 -04:00
Asteroth 1a1d3f040c fix interpolate matrices (#6996) 2025-08-25 17:17:52 +00:00
Mike Nelson 109c2b5a81 QtFRED Music Player Dialog (#6994)
* qtfred music player dialog

* cleanup

* int comparisons

* clang wins
2025-08-25 09:59:16 -04:00
Mike Nelson c2b92410aa QtFRED Global Ship Flags Dialog (#6993)
* Global Ship Flags Dialog

* modernize old fred code
2025-08-25 07:44:07 -04:00
Mike Nelson 16926a4395 QtFRED Shield Sys Dialog (#6991)
* refactor global shield sys dialog

* I win this time, Clang

* Or not...
2025-08-25 07:43:53 -04:00
Mike Nelson 76440ddfd1 QtFRED Reinforcements Dialog (#6990)
* cleanup reinforcements dialog

* unused
2025-08-25 07:43:41 -04:00
Mike Nelson 08a73c1ed7 QtFRED Voice Acting Manager Dialog (#6962)
* revise Voice Acting Manager dialog

* fixes

* clang

* make window modal
2025-08-25 07:43:10 -04:00
Mike Nelson 9f30d3001c QtFRED Command Briefing Dialog (#6968)
* refactor the qtfred command briefing dialog

* clang
2025-08-24 10:10:52 -04:00
Mike Nelson 6c101fe52a QtFRED Fiction Viewer Dialog (#6965)
* refactory qtfred fiction viewer dialog

* do this more efficiently
2025-08-24 10:10:40 -04:00
Mike Nelson bc083dc61b QtFRED Mission Specs Dialog (#6945)
* refactor mission specs dialog

* clang

* Stub in all the sub dialog files

* custom strings dialog

* custom strings dialog cleanup

* custom data dialog

* sound environment dialog

* move custom wing names into mission specs subfolder

* custom wings dialog

* unused

* clang tidy

* clean return

* refactor mission specs dialog

* move custom wing names into mission specs subfolder

* custom wings dialog

* rebase error

* remove a couple empty functions

* select after creating
2025-08-24 10:10:22 -04:00
Taylor Richards 9755d44c5f update mdns to v1.4.3 (#3973) 2025-08-23 20:32:18 -04:00
wookieejedi 9502af6f20 Fix gr.createParticle with parent velocity (#6970) 2025-08-23 17:40:21 -04:00
wookieejedi 5eb80f55bc Fix conversion of old muzzle flash to new type (#6972)
Fixes the updated name of `Pixel Size At Emitter`
2025-08-23 16:31:39 +00:00
Mike Nelson 4b9ccfc68f QtFRED Asteroid Dialog (#6908)
* begin asteroid dialog rewrite

* activate fields

* clean up connections

* clean up and rearrange files for consistency

* use constructor to zero out the field when needed

* fix checks errors

* remove duplicate lines

* add missing z
2025-08-23 15:20:38 +00:00
Mike Nelson 9db567ea40 QtFRED Mission Goals Dialog Cleanup (#6951)
* cleanup and bugfixes of mission goals editor

* redundant specifier

* static casts
2025-08-23 10:58:14 -04:00
Mike Nelson 7d8b437847 Qtfred Wing Editor Dialog (#6939)
* wing editor dialog base functionality

* flags and path masks plus some cleanup

* squad logo selection

* custom warp params using the ship editor version

* some cleanup

* some finalization

* cleanup, fixes, and clang

* remove duplicate lines after rebase

* merge namespaces
2025-08-23 10:57:54 -04:00
Mike Nelson e9d4f322d0 QtFRED Waypoint Path Dialog (#6935)
* cleanup qtfred waypoint dialog

* range based loops

* typo
2025-08-23 10:57:35 -04:00
Mike Nelson 080aa65ec8 QtFRED Object Orientation Editor Dialog (#6932)
* refactor and cleanup the qtfred object dialog

* clang is being pedantic

* BRAAAACE FOR IMPACT

* clang

* switch statement
2025-08-23 10:57:18 -04:00
Kestrellius 1207dfcae9 Merge pull request #6946 from Kestrellius/asteroid-effects
Particle effects for asteroid death
2025-08-23 07:00:43 -07:00
Kestrellius 8db0a44d7d Merge pull request #6938 from Kestrellius/deathroll-effects
Particle effect hooks for ship deathroll explosions, pre-death explosions, and propagating explosions
2025-08-23 07:00:32 -07:00
Goober5000 9ba88ca163 fix error in error reporting routine (#6969)
The `_s` functions throw an exception if the string's length is exceeded.  But in an error reporting routine, we are far more interested in the string than the exception, and we'd rather get a partial error message than a crash.  Plus this bit of code is already set up to truncate the string anyway.
2025-08-23 09:37:47 -04:00
John Fernandez c451a82f87 Merge pull request #6966 from MjnMixael/fix_qtfred_variables_strings
QtFRED Fix variables heap corruption crash
2025-08-22 22:00:05 -04:00
Mike Nelson eb36b56f1d fix 2025-08-22 12:10:52 -05:00
Mike Nelson c39d6b290f convert tostdstring to const data 2025-08-22 11:31:19 -05:00
John Fernandez 56a3ddd5d3 Merge pull request #6150 from JohnAFernandez/qtFRED-variables-editor
qtFRED variables editor
2025-08-22 10:44:39 -04:00
John Fernandez 89219df6af And move this 2025-08-22 09:40:49 -04:00
John Fernandez fa80fc3278 APPEND THIS 2025-08-22 09:36:19 -04:00
John Fernandez 84897636cd Hopefully last of clang warnings 2025-08-22 09:14:07 -04:00
Kestrellius 9e618f6632 Particle effect hooks for subsystem destruction (#6937)
* add particle hook

* partial bugfixing

* bugfixing

* appeasement

* syntax fix
2025-08-22 07:19:15 -04:00
Taylor Richards 43e85dd8fd update libpcp to lastest main (#6963)
This should fix build issues on ARM64 Windows.

Note that the project name changed from libpcp to libpcpnatpmp and is in
active development again after a multi-year period of inactivity. There
are currently no versioned releases. This is hash b5fe5e2.
2025-08-22 07:18:48 -04:00
Taylor Richards a12c1de012 fix memory access error with active games list (#6964)
During maintenance of the active games list it's possible for the list to be
cleared before a stale item is removed leading to a access violation. So we
need to confirm the list isn't empty when attemping to erase the stale  entry.

This also fixes a bug where the list entry index is incremented even when an
entry is removed which can cause an improper selection index being used.
2025-08-22 07:18:38 -04:00
The Force b1247042ac QTFred: Alt Ships Class dialogs (#6959)
* Add Files and UI

* Initial Setup of dialog

* Finish Dialog

* Obey Rule of 3(Possibly)

Why Cant the compiler do this?

add const

* Fix use of default keyword

* bit more

* Remove unused Vars

* Missed one

* Clang work

* Remove future proofing as the compiler doesnt like it

* Make static/ Apply de morgans

* Lets try this one

* Revert to inital and supress warning

Also switch to Assertions
2025-08-22 07:18:30 -04:00
John Fernandez 3725f27a5c Match header 2025-08-21 23:54:18 -04:00
John Fernandez 55efbe73b3 Forgot that VS doesn't have auto save 2025-08-21 23:41:48 -04:00
John Fernandez 40b9924892 More clangy 2025-08-21 23:39:40 -04:00
John Fernandez c21cbb3067 Handle shadowing 2025-08-21 11:57:06 -04:00
John Fernandez 7b9b237a66 No clever commit message this time
How long must I attempt to satisfy the endless hunger of clang
2025-08-20 23:45:42 -04:00
John Fernandez f15a6bc3e8 Handle more clang warnings 2025-08-20 23:37:11 -04:00
John Fernandez 3a7269275e blarg 2025-08-20 22:58:10 -04:00
John Fernandez 81656e8dff Add back missing closing markup 2025-08-20 22:37:04 -04:00
John Fernandez 7c2deb6761 Merge branch 'qtFRED-variables-editor' of https://github.com/JohnAFernandez/fs2open into qtFRED-variables-editor 2025-08-20 22:29:56 -04:00
John Fernandez 552817b8e1 Missing Parentheses 2025-08-20 22:29:30 -04:00
John Fernandez 555a413d7e Merge branch 'master' into qtFRED-variables-editor 2025-08-20 22:26:57 -04:00
John Fernandez 0241c9f4b9 Scare off DeMorgan 2025-08-20 22:25:34 -04:00
John Fernandez c93c91013c Do what clang says or else 2025-08-20 22:17:36 -04:00
John Fernandez 81d08cd6f3 More Clang 2025-08-20 22:13:16 -04:00
John Fernandez 4efcd82940 Make sure to use empty 2025-08-20 22:05:52 -04:00
John Fernandez a4014ea985 According to the internet this should work 2025-08-20 22:02:40 -04:00
wookieejedi 83dee2eab4 Spare future modders (#6960) 2025-08-20 18:56:16 -04:00
Mike Nelson 39f80289c2 QtFRED Mission Cutscenes Dialog (#6956)
* Mission cutscenes dialog

* fixes

* getter for now private member

* missed some cleanup

* assertion
2025-08-20 22:51:53 +00:00
Mike Nelson cbd491e277 QtFRED Jump Node Editor Dialog (#6940)
* qtfred jumpnode editor dialog

* unused function

* clang

* and and use SCP_trim
2025-08-20 18:08:52 -04:00
John Fernandez 1a60eff319 And try this 2025-08-20 17:31:20 -04:00
John Fernandez 7713e1b09b Don't overzealously mark this 2025-08-20 17:17:33 -04:00
John Fernandez c85876d0d3 don't forget headers 2025-08-20 14:46:33 -04:00
John Fernandez 6cadc350b1 Start fixing myriad clang issues 2025-08-20 14:40:10 -04:00
Kestrellius 806bb77298 Particle effect hooks for subsystem debris trails and debris explosions (#6944)
* add hooks

* parsing

* add particle hooks

* appease clang

* readability
2025-08-20 09:49:00 -04:00
Taylor Richards e5fccc0b19 fix conversion of Missiontime to milliseconds (#6957)
Broken in #6948 but the original didn't work properly either due to conversion
to int32_t and the resulting size limit.

Big assist from Goober5000 for noticing the bug and helping to sort out the
wonky values.
2025-08-19 12:12:55 -04:00
Kestrellius 0bf632ceae Add apparent size input for particle source curve (#6947)
* functional method

* add input
2025-08-18 22:36:17 -04:00
Kestrellius bd9ea61e9d appease clang 2025-08-18 19:17:18 -07:00
Kestrellius 836d7d4f2d switch include 2025-08-18 19:02:07 -07:00
Taylor Richards 5ea4a4c014 remove extra config bind on joystick axes (#6955)
Fixes fail sound due to the control set not changing between calls.
2025-08-18 21:50:15 -04:00
Taylor Richards dd71838715 fix array index bug in ship_get_subsys_index (#6954) 2025-08-18 21:49:35 -04:00
Taylor Richards b16854796a fix custom mouse cursors (#6953)
UI elements with custom mouse cursors wouldn't show them due to the cursor
being reset at the start of each frame. To fix this we need to be sure that
we only reset when the element is no longer active.
2025-08-18 21:48:51 -04:00
Taylor Richards 1142e7d9bc force proper ordering of joystick options (#6952) 2025-08-18 21:48:24 -04:00
John Fernandez 212c91c72f Merge pull request #6942 from MjnMixael/qtfred_flag_list_widget
QtFRED Flag List Widget
2025-08-18 18:39:02 -04:00
Kestrellius 4e75114479 Fix parented line sprite particles appearing in the wrong place (#6950)
* account for parenting

* fix p1 position
2025-08-18 16:45:17 -04:00
Taylor Richards e1ae938553 Merge pull request #6948 from notimaginative/compile_warnings
address various build warnings
2025-08-18 16:36:08 -04:00
Taylor Richards af833f059c fix avcodec deprecation warnings 2025-08-18 12:01:00 -04:00
Taylor Richards d9b7710f92 properly set some macOS bundle properties 2025-08-18 12:00:40 -04:00
Taylor Richards fc7389f95a fix various compiler warnings 2025-08-18 11:59:22 -04:00
Taylor Richards 908f306985 fix some abs() type conversion warnings 2025-08-18 11:56:39 -04:00
Taylor Richards 21c9b65c99 add _WIN32 guard to trackir/freetrack 2025-08-18 11:56:26 -04:00
Goober5000 f92bc15fc6 Merge pull request #6941 from Goober5000/6882_followup
restore inadvertently deleted code
2025-08-17 21:53:17 -04:00
Kestrellius 6539fc9aeb clean up control flow 2025-08-17 16:06:32 -07:00
Kestrellius a0730f601c add particle hook 2025-08-17 15:36:38 -07:00
Taylor Richards 59a362ed1d fix end-mission sexp in multi (#6943)
A previous attempt to fix the end-mission sexp in multi such that it wouldn't
trigger the jump out sequence was horribly broken. Notable safety checks, state
changes and server/client syncing was skipped over. This caused, as one
particular bug, a standalone server to stop communicating with the game host.
When this happened stats couldn't be accepted/tossed and you were stuck at the
debrief screen, being forced to disconnect from the standalone in order to
continue.

A proper fix for this is to use multi sexps to tell clients to enter a debrief
state before the standard end mission request is handled. This will allow it
the bypass the actual warp out animation for players while still doing the
normal end of mission handling.

NOTE: This is a breaking, but not fatal, multi change and should be safe
considering the recent version bump in early June. As such an additional
version bump is not required with this for 25.0.
2025-08-16 22:14:51 -04:00
Mike Nelson dadb91889f clang 2025-08-16 15:10:54 -05:00
Mike Nelson 41dc187456 add flag list widget 2025-08-16 14:51:32 -05:00
wookieejedi 7780769ba5 Add 'Nebula Usage Score' curve input (#6926)
Background:
Many mods, especially Event Horizon, have highlighted how versatile and useful using nebula are for creating a wide variety of immersive effects beyond just nebula, including weather, volcanic ash, specialty debris falling from the sky, and more. In addition volumetric nebula have opened another realm of possibilities for modders to creatively utilize. Furthermore, modders can not leverage the powerful updates to the particle system to create a wide diversity of effects, which can be also utilized within nebula (or any mission) by making weapons with those effects and then spawning those weapons with scripts/sexps. Just one creative example is exploding rock debris from a volcano (like in Event Horizon). The visuals of nebula poof though are of course spawned in density according to the `Nebula Detail Level` graphics slider in the Graphics tab of the Options menu. Thus, what the player sets will affect visuals of the nebula. As a modder creating visuals, this can have the effect where particle effects spawned from weapons used in nebula may look different in less dense fields. Ideally there could be a way for the modder to incorporate this Nebula slider detail into the development of their effects, to ensure that the effect visuals properly reflect and integrate with the nebula density that the player might see. The solution to this problem is luckily not difficult, as PR #6889 shows how it can be dealt with. That PR added a particle curve input `Particle Usage Score` which is incredibly useful to allow particle effects to scale visually according to the `Particle Detail Level` that the player has set in the Graphics tab of the Options menu. As such this utility could also be extended to the Nebula detail level to allow modders to develop weapon spawned effects for nebula missions that properly visually integrate with the nebula detail level.

This PR
This PR follows and extends the relatively simple approach of #6889, in that it adds a new curve input `Nebula Usage Score` for use within the particle effects table. This new input will allow modders to develop effects that can properly mesh with varying nebula detail levels--all with the aim to further expand the visuals and range of nebula utility for environmental effects.
2025-08-16 12:03:00 +02:00
wookieejedi c535108708 Allow background option to completely work (VR useful) (#6930)
From discussion with VR players, it appears that being able to toggle off the skybox/starfield/planet background is very useful to some players to prevent motion sickness. Currently, FSO has the toggle in the Graphics Options window for `Planets/Backgrounds` but it only turns off planet bitmaps. This PR suggests it should be expanded into an accessibility role for players, and properly turn off all background elements--so planets, plus skybox rendering (especially now since most mods also use skyboxes as backgrounds, whereas in early days this was not primarily the case).

Happy to discuss and/or add as a flag if folks think that would be needed.
2025-08-16 12:02:46 +02:00
Goober5000 dde16a54c6 restore inadvertently deleted code
This has an interesting history.  In the original Volition source release, global damage was set to originate from the shockwave position, which would have avoided the fireball issue fixed by #6882.  (Technically, though, this was only done for huge ships which would have still left the problem for smaller ships.)  In c2716d4ee8, this code was changed to run in non-HTL mode in an attempt to fix a bug.  Then in 70529dbd5a, as part of the removal of non-HTL mode, the code was entirely deleted.  So, this restores the code.

The #6882 fix is still valid, both because it works for non-huge ships, and because shockwaves may not necessarily originate from the subobject being destroyed.  The #6882 fix also subsumes the effect of the restored code, but it's still good to restore the code in case future development may depend on it.
2025-08-16 01:14:45 -04:00
Kestrellius 09b6888bc6 fix normals 2025-08-15 20:45:05 -07:00
Kestrellius be4d4eb1d5 add hooks 2025-08-15 19:09:12 -07:00
John Fernandez f0ecd88da9 Merge pull request #6934 from MjnMixael/qtfred_loadout_bug
Fix QtFRED weapon quantities
2025-08-14 22:35:36 -04:00
John Fernandez 6d9e590996 Merge branch 'master' into qtFRED-variables-editor 2025-08-14 22:32:35 -04:00
John Fernandez fe3dce092a Merge pull request #6169 from TheForce172/feature/weaponsDialog
Weapons Editor Dialog and Table Viewer
2025-08-14 22:31:05 -04:00
Mike Nelson 0b6150e961 Qt fred misc bugs (#6933)
* fix main window title

* Fix loadout dialog crashing
2025-08-14 22:14:27 -04:00
John Fernandez ebe2d235da Merge pull request #6931 from MjnMixael/qtfred_utils
Merge Recent QtFRED Generalized Dialogs
2025-08-14 21:53:43 -04:00
Mike Nelson 9ecc158aee initialize used_pool 2025-08-13 16:53:54 -05:00
Mike Nelson 4d8dbcb457 Cherry pick recent qtfred generalized dialogs 2025-08-13 08:49:13 -05:00
Mike Nelson 966ac4d878 Allow testing bay paths in the lab (#6902)
* allow testing bay paths in the lab

* unused param

* static member methods
2025-08-13 08:36:53 -04:00
Mike Nelson 1ffcaafc89 Allow customizing the fs2 selection effect (#6868)
* allow customizing the fs2 selection effect

* undo color color shift
2025-08-13 07:26:55 -04:00
John Fernandez 06f576bfa6 Merge pull request #6925 from wookieejedi/fix-skybox-zbuffer
Fix/enhance skybox submodel rendering
2025-08-12 10:36:18 -04:00
Taylor Richards d0c5fe6bcc fix series of bugs due to improper multi targeting (#6916)
Multiple issues were discovered to be from improper targeting data in multi
tracing back to the addition of multi-lock. This was due to differences in
how targeting works in single/multi and de-sync of this info between server
and client. Collecting accurate targeting data on the server and making sure
that the client will use that data resolves the tracking and sync issues.
2025-08-11 11:06:46 -04:00
wookieejedi f903180411 Fix/enhance skybox submodel rendering
Background:
Currently, skybox models can be set in FRED to have `No Z-Buffer` on or off. It is off by default, which is good because it ensures ships and other game objects cannot be rendered behind a skybox. Critically though, skybox models may have sub-models, especially now that animated sub-models are a feature (thanks to BMagnu!). If a skybox is using sub-models and `No Z-Buffer` is turned off, then there will be no proper model occlusion for the skybox and there will be ugly z fighting. Overall, BMangu stated "we could clear the depth buffer after the skybox with another flag".

Update
This PR fixes that issue by adding a new FRED game settings flag, `$Skybox internal depth consistency:` which when set to yes properly ensures there is internally consistency z-sorting with the skybox and any submodels it may have while at the same time ensuring that if `No Z-Buffer` is set in FRED that the skybox still correctly is always drawn behind mission objects like ships/debris.

Many thanks to @Bmagnu for the discussion and help in identifying and creating a proper fix for this issue that is clean, surgical, and supports all existing uses of `No Z-Buffer`.

Tested and works as expected. Fixes #6913.
2025-08-11 10:56:28 -04:00
The Force 726c954e37 Fix Crash On selecting different ship types (#6920) 2025-08-10 13:25:02 -04:00
The Force 1a72471543 Last One 2025-08-09 19:31:04 +01:00
The Force a7ec8d958b And now Clang 2025-08-09 18:35:57 +01:00
The Force c1c16c2fcf GCC Apeasment 2025-08-09 17:14:14 +01:00
The Force ec3fc3477d Refresh UI on Drag Drop 2025-08-09 16:59:27 +01:00
The Force 3524679c9e Fix Copy/Paste Error 2025-08-09 16:11:15 +01:00
The Force cf4f1b2ac6 Missed this 2025-08-09 16:09:14 +01:00
The Force 95fea193ae Couple More 2025-08-09 16:05:00 +01:00
The Force 8bf7f66483 Adress Feedback and other bug fixes 2025-08-09 15:53:58 +01:00
The Force 38f7479500 Appease GCC / Clang
Appease GCC

This is a silly error

AAAAAGH

Fix Model

Sick of this

Fix Supposed Shadowing

Hopefully the last

Or maybe not

Please
2025-08-09 15:53:56 +01:00
The Force f5b2b1bbd0 Add Comments 2025-08-09 15:53:54 +01:00
The Force 1a1d4bb057 Add TBL Viewer 2025-08-09 15:53:53 +01:00
The Force 858c116b3e Add Weapons Dialog
Move Ship Files To there own folder for legibility
Set File stucture

Create Layout

Rough outline established

Ship data fully loading

limit multiselection to one type

Add in missing conditon

Add weaponlist loading and move Bank model to its own file

Delete Missed file

Fix Cmake file

Change to size_ts

Fix Arrivals

Partial Drag and drop / better multi-select

Finish drag and drop / add ammo editing

Add Saving

Feature Complete
2025-08-09 15:53:09 +01:00
Mike Nelson b8b24a67e0 pass an initialized var instead of a temp var (#6917) 2025-08-09 13:16:52 +02:00
Kestrellius df0279869c Allow shockwave orientations to be relative to their parents (#6919)
* initial setup

* various orientations
2025-08-09 13:14:22 +02:00
Taylor Richards 85b37e098e remove "safe" versions of FD_SET/FD_ISSET (#6918)
The socket is a value to be assigned, not an index to be used. As such there
is no safety check to be done here and doing so just breaks things.

Fixes networking on Windows.
2025-08-08 20:43:34 -04:00
Taylor Richards 515beef61a fix cleanup of empty model.subsystems files (#6915) 2025-08-08 20:43:11 -04:00
Taylor Richards 7281f71da4 clear screen going to PXO lobby (#6914)
Widescreen mainhalls can still be visible on the sides when going to the PXO
lobby so we need to be sure the screen is cleared.
2025-08-08 20:42:47 -04:00
Mike Nelson 7c29656c7c let the model decide if the apply was successful and should close (#6912) 2025-08-08 10:36:19 -04:00
Mike Nelson 3900faac87 Fix issue where QtFred could have multiple Ship Dialogs open (#6909) 2025-08-08 07:30:37 -04:00
Kestrellius 5cd3b3a308 Extend particle interpolation to lifetime (#6910)
* the naive method

* the correct method
2025-08-08 07:29:59 -04:00
Mike Nelson 75b61efbd1 Custom Data for Campaigns (#6906)
* campaign custom data

* Assertion instead of assert
2025-08-07 09:56:52 -04:00
LuytenKyandwookieejedi b8c3c560d2 Implementation for an AI order to chase ship types -Second Attempt- (#6828)
* Back in business hopefully

* Refactor

* Resolve CI issue in aigoals.cpp

* Updating order.cpp

* Whoops

* Updated objecttypes.tbl to incorporate attack ship type

* Fix clang-tidy issue

* Forgot one other instance

---------

Co-authored-by: wookieejedi <[email protected]>
2025-08-07 09:56:18 -04:00
John Fernandez 94744e9d69 Merge pull request #6899 from MjnMixael/lab_detail_level
Show current detail level in the lab
2025-08-05 17:14:10 -04:00
John Fernandez 04c2913f07 Merge pull request #6903 from MjnMixael/lab_misc_fixes
Misc Lab Fixes
2025-08-05 17:13:37 -04:00
John Fernandez 75a7fc5755 Merge pull request #6901 from wookieejedi/debris-cleanup-2
Debris Cleanup
2025-08-05 17:12:26 -04:00
wookieejedi 6132f8bfc1 Another particle fix for standalones (#6905)
* Another particle fix for standalones

Fix more standalone crashes from particles by "allowing the code to create particles, and just silently quit if in standalone. I.e., accept an invalid effect handle if in standalone and that'll just quit out the source on its first frame of processing." Thanks to @Bmagnu for the fix! Putting here to allow taylor and chief to more easily test.

* add needed include

* and another fix
2025-08-05 16:25:16 -04:00
wookieejedi 32183f5e71 update comments 2025-08-04 18:43:06 -04:00
Mike Nelson 3f0c33046c unused params 2025-08-04 15:17:28 -05:00
Mike Nelson 1c482d76df default case 2025-08-04 14:55:11 -05:00
Mike Nelson e5a5c30fbf misc lab fixes 2025-08-04 14:24:03 -05:00
wookieejedi 4ddcaec027 Debris Cleanup
In current master the debris creation logic was really showing some rust, which resulted in inconsistent behavior when using `Lifetime Min` and `Lifetime Max` for debris in the `ships.tbl`. Specifically, by default debris can randomly be set as `DoNotExpire` if the check `(Random::next() < (Random::MAX_VALUE / 6))` is false. This was set *before* the code that set the lifetimes from `Min` and `Max` from the table though, which could randomly result in debris with infinite lifetimes even if a min and max were specified (and the ship was small, since large ships trigger infinite debris by default always).

Also, by default, ships with a radius larger than 50 meters always had infinite debris lifetimes because of `MIN_RADIUS_FOR_PERSISTENT_DEBRIS`. Thus, this PR also allows modders to set that value in the game settings table.

Tested and works as expected.
2025-08-04 12:05:54 -04:00
John Fernandez a68b22a9d8 Merge pull request #6900 from notimaginative/fixup_client_multi_data
cleanup and fix issues with multi client data
2025-08-04 00:49:44 -04:00
Taylor Richards d7a531d449 properly this time 2025-08-04 00:18:09 -04:00
John Fernandez 8d2895d3c2 Merge pull request #6898 from MjnMixael/fix_lab_animations
Still allow subsystem animations in the lab
2025-08-03 23:52:50 -04:00
Taylor Richards 255f3773c1 address clang-tidy issues 2025-08-03 22:57:03 -04:00
Taylor Richards 8748109eaf cleanup and fix issues with multi client data
Fix use of incorrect data macro
Fix packet size safety check
Fix sequence order issue with client data packet (Cyborg)
Cleanup code to address readability and maintence issues
2025-08-03 20:08:25 -04:00
Mike Nelson 6abfddfea5 show current detail level in the lab 2025-08-02 18:59:45 -05:00
Mike Nelson 32905ac841 still allow subsystem animations in the lab 2025-08-02 16:36:27 -05:00
Taylor Richards 671a7586e7 fix nullptr crash when debris collides with ship (#6896) 2025-08-02 10:10:31 -04:00
Taylor Richards 9718e7008c fix mipmap resizing bug (#6895)
The if statement wouldn't trigger as defined which caused the incorrect data
offsets to be used. This resulted in corrupted textures and invalid memory
reads of uncompressed textures if the hardware texture detail slider was set
to anything other than max.
2025-08-01 22:36:54 -04:00
Taylor Richards ab438116d3 remove old os_sleep() hack for Mac (#6894)
SDL 1.2 had an issue using SDL_Delay() on Mac where it caused bad stuttering
and freezes. A hack was added to avoid this, but it causes 100% CPU usage when
it should be a sleep state, so it wasn't ideal.

With SDL 2 this issue was resolved with the underlying code being unified for
all *NIX variants and that problematic Mac-specific code was removed. So now
we can remove the hack to offer a little better performance on Macs.
2025-08-01 22:35:43 -04:00
wookieejedi dce9e1c031 Allow mods to cap rotational velocity from collisions (#6890)
The `+Rotation Factor:` value in the ship collide section of the ships table has been around for many years, but raising that value beyond the default of 0.2 results in ship collisions that can result in the colliding ship spinning at ludicrous rotational velocities that last for over 10 seconds (this is especially evident with long ships with high speeds).

This PR adds a field after `+Rotation Factor:` called `+Rotation Magnitude Maximum:` which serves as a maximum for the resulting rotational velocity from a ship collision in degrees per second. By default this is -1 and thus disabled.

Tested and works as expected, and now properly allows mods to have ships spin out if they have collision while not spinning out at wild speeds that look immersion breaking.
2025-08-01 22:31:40 -04:00
wookieejedi b5f3edc019 Fix engine not updating with change-ship (#6893)
* Fix engine not updating with change-ship

Yet another caveat of the change-ship function was discovered by Darius, who found that that both the 3D assigned engine sounds and the 2D throttle/cockpit engine sounds do not properly updated when a ship type is changed via sexp/script.

This PR fixes this by properly calling the respective clearing functions and it also resets the throttle sound check timestamp to ensure the 2D sounds are indeed updated on the next frame.

Tested and works as expected. Fixes #6892.

* cleanup for safety
2025-08-01 22:31:09 -04:00
BMagnu e28b3bb592 Remove unused enum in particle code (#6891)
* Remove unused enum

* remove comment as well
2025-08-01 01:33:29 +00:00
BMagnu 0a7fac447d Add particle curve input for number of existing particles (#6889)
* Add code to process global starts for modular curves

* Add particle curve input for number of existing particles
2025-08-01 02:05:08 +02:00
Taylor Richards 4c7e4feaf3 fix double dumbfire missiles for multi clients (#6888)
Dumbfire missiles are client fired for rollback purposes. However players would see
their missiles fire twice due to the server also sending a fire packet back to the player.
So we need to prevent the player that fired the missile in question from getting that
extra packet.
2025-07-31 15:09:30 -04:00
Taylor Richards bf84dbf34f fix invalid array index crash in debug message (#6887) 2025-07-31 15:08:56 -04:00
Taylor Richards b19c3e565b fix more particle issues with standalone (#6886) 2025-07-31 15:06:11 -04:00
Mike Nelson ffcf078f03 Preload asteroids and debris when config-field sexps are used (#6874) 2025-07-30 22:45:38 +02:00
Mike Nelson e52fa1edeb No need to display debris label on objects in the Debris list (#6877)
* No need to display debris label on objects in the Debris list

* remove the unnecessary loop
2025-07-30 14:04:04 -04:00
Mike Nelson 44952eb2d6 Fix some minor hud config color errors (#6881) 2025-07-30 14:03:34 -04:00
BMagnu 95d21a4085 Minor Particle Fixes (#6884)
* Fix scripted particle velocity

* Add error if no bitmaps were defined
2025-07-30 20:53:26 +09:00
Kestrellius 8f835fe34b check for global (#6882) 2025-07-30 07:52:46 -04:00
wookieejedi c26b0caeb8 Ensure Aspect_invulnerability_fix always works (#6879)
#6248 added an extra subtraction line to calculating when a bomb can be shot down `extra_buggy_time`, but that value should only be calculated if  `Aspect_invulnerability_fix` is off. Specifically, the `else if` checks on lines 79 and 97 of `collideweaponweapon.cpp` were getting hit even when `Aspect_invulnerability_fix` was enabled, so ensure that if that flag is on that it correctly keeps the `extra_buggy_time` at 0.

Fix works as expected, happy to edit or tune however.
2025-07-30 20:17:54 +09:00
Mike Nelson 85ebd316e1 Only show one warning for skipped fonts (#6880) 2025-07-30 20:17:09 +09:00
Kestrellius abc82bd323 Handle impacts for obscure code paths (#6848)
* handle healing weapons

* incorporate shield threshold features

* fix piercing sign error

* use function
2025-07-30 20:12:35 +09:00
BMagnu f374eadb45 Fix load order guarantees (#6883) 2025-07-29 18:24:51 -04:00
Kestrellius 3e77e1370d fix copy-paste error (#6878) 2025-07-29 15:24:32 -04:00
Mike Nelson 2ce4bdd55c get font scale options for lua (#6876) 2025-07-29 07:41:45 -04:00
Mike Nelson bbb838a332 custom data for personas (#6861) 2025-07-28 19:21:14 -04:00
wookieejedi 7a655e44da Fix p-spew with homing weapons (#6875)
#6808 converted all Pspews to use the new particle effects, though that in turn lead to a fun little visual bug. Overall, particles on weapons will only be rendered if they are valid, and the is valid check included a `weapon_state == m_weaponstate` check (IE states had to match between what the weapon object held and what the particle held. When a weapon switches to homing that particle state was never updated, b/c before 6808 it never needed to be. This lead to the particle effects from pspews not rendering when a missile was in homing mode.

This PR fixes that issue by also updating the particle host status whenever the weapon state changes (uses the same logic as when the particle spawns the first time).

Tested and works as expected.
2025-07-28 22:20:40 +00:00
Goober5000 2ed608b303 add smoothing field to volumetrics dialog (#6865)
FRED support for the smoothing field added in #6854.
2025-07-26 17:46:10 +02:00
John Fernandez b49dbfeaa4 Merge pull request #6832 from MjnMixael/team_colors_followup
Get, set, browse team colors with Lua
2025-07-26 07:54:21 -04:00
Asteroth bfacc16a51 Fix for single segment trails (#6864)
* fix single segmen trails when interrupted on both ends

* cleaner case handling

* remove wip stuff
2025-07-26 12:46:10 +02:00
BMagnu f96c73faac Fix unlit shader flag (#6873) 2025-07-26 01:22:44 +00:00
Taylor Richards 5fd06b40fe fix multi crash loading campaigns (#6871)
A minor bug in #6077 resulted in a crash loading campaigns on the multi ui.
The bug itself was not fatal, however it triggered a cascade of poor error
handling which led to a crash. A lot of these issues date back to retail.
This fixes the minor bug, and hopefully adds enough error handling to
preemptively squash a repeat of the crash problem.
2025-07-25 21:15:31 -04:00
Taylor Richards 21e35d0ded fix issue with turret/flak multi packets (#6870)
Normalized vectors sent over multi are never guaranteed to still be normalized
on the other side.
2025-07-25 21:14:39 -04:00
Taylor Richards 9dc6eb7248 fix particle related standalone error (#6869)
Standalone doesn't create particles so these checks will always fail.
2025-07-25 21:14:09 -04:00
BMagnu fcb257db59 Fix calculation of apparent size (#6872) 2025-07-26 03:04:44 +02:00
BMagnu fa5ee72770 Consider current RTT state for screenToBlob (#6867) 2025-07-26 02:53:44 +02:00
BMagnu 72ed863700 Prevent lua from garbage collecting possibly-stale subframes (#6866) 2025-07-25 22:06:00 +02:00
Kestrellius d6cb9892de account for add state (#6858) 2025-07-24 19:01:05 -04:00
Mike Nelson 021eb8e49a Fix two HUD Config preset issues (#6830)
* always use exact match

* custom gauges should use the type for colors with old preset files
2025-07-24 16:51:45 -04:00
Mike Nelson 487cac6456 Lua func to check if in game options are enabled (#6862) 2025-07-24 16:51:14 -04:00
wookieejedi f76bf1b8c4 Fix 'show-ship` not working (#6860)
Small one-line fix that follows up #6849. That PR removed the `!(Viewer_mode & VM_EXTERNAL))` check within a section of `ship_render_player_ship` to optimize insignia rendering, but turns out that check is needed to ensure `show-ship` properly works. This PR restores that check to fix this bug, and ideally the thoughts about slightly optimizing insignias can be considered later.

Tested and works as expected.
2025-07-24 22:33:52 +09:00
BMagnu 755c4c4e29 Fix Volumetric Nebula Bugs and Add Smoothing (#6854)
* Fix 3d tex generation

* Fix blending and emissive

* Add Smoothing

* Add smoothing parameter

* Correctly calculate fractional smoothing
2025-07-24 22:33:37 +09:00
wookieejedi 168b3310ab Cleanup of Shield Hitpoint Threshold (#6851)
* Cleanup of Shield Hitpoint Threshold

Overall cleanup that does a few things:

1) Changes `ship_is_shield_up` from integer to bool.

2) `ship_is_shield_up` was only ever called to look at one quadrant, never for all quadrants, so took the opportunity to update the 'all-quadrant' block to account for ships with a non-standard number of shield quadrants.

3) ` MAX(2.0f, Shield_percent_skips_damage * shield_get_max_quad(ship_objp)` was used many times throughout the code (and will be used at least 2 more times with #6848), so simply consolidated all of those uses into a new function called `ship_shield_hitpoint_threshold`. Happy to edit the name however.

4) Removed un-needed redundant comments from `ship_is_shield_up` definition in `ship.h` since those comments were already present in the actual function within `shield.cpp`, and all the other functions used comments in that file instead.

Again happy to tune or edit with any other choices.

* use proper default arg style

* actually commit the updated files

* appease modern clang

* clang round 2
2025-07-24 08:39:58 -04:00
Kestrellius 365d9c57e3 gate hull and subsys impacts (#6855) 2025-07-23 08:07:42 -04:00
John Fernandez 1d5fa5eef1 Merge pull request #6852 from MjnMixael/fix_rocket_ani_assertion
Remove rocket ani assertion
2025-07-22 21:15:35 -04:00
John Fernandez 05624b5092 Merge pull request #6856 from MjnMixael/fix_get_mod_root_name
Prevent string ops on nullptr char*
2025-07-22 20:50:50 -04:00
Mike Nelson 8be619db7e prevent string ops on nullptr char* 2025-07-22 19:19:31 -05:00
BMagnu 9b82daeefc Proper Ambient Light (#6849)
* Remove ambient handling from amin / decal pass

* Add ambient light to deferred pass

* Make decals work and fix double insignia rendering

* Fix decal vertex shader

* render insignias as decals

* Cleanup of old insignia stuff

* Move insignia post-processing to model load

* perform envmap lighting in deferred shader

* Remove Envmapping from main shader

* Fix MSVC warning

* exclude def_files from clang tidy
2025-07-22 00:17:22 +02:00
Mike Nelson c53daa325c Remove rocket ani assertion 2025-07-21 15:32:25 -05:00
wookieejedi 86622e1ce0 Fix Edge-case with $Preload briefing icon models: (#6850)
Found a very interesting edge case bug with `$Preload briefing icon models:.`

Situation: Mission 1 has a briefing which has a ship class A as the class (and thus loads the model), but the ship class A is not actually in that mission so no subsystems are loaded. Then in mission 2, the ship class A is actually in the mission but FSO tries to reuse the slot that was loaded, but realizes no subsystems were loaded and makes an error message. I that is because the preload code for the briefing icons on `missionparse.cpp` line 6664 just calls `model_load` but does not specify the subsystem argument.

 I've also attached a retail mod reproducible campaign (2 missions, first is 5 seconds long). When the second mission loads the incorrect subsystem warning appears.

This PR properly loads the subsystems and fixes the bug. Tested and works as expected.
2025-07-20 15:00:47 -04:00
Kestrellius 199c7b79ef Make sure global damage doesn't spawn impact effects (#6842)
* check whether damage is global

* use static

* remove extraneous arguments
2025-07-20 07:45:27 -04:00
wookieejedi 26e9ecab0e Allow mods to set threshold at which shield stops taking damage (#6847)
* Allow mods to set threshold at which shield stops taking damage

FSO logic dictates that shields collisions and any related sound or visuals related to shields are skipped if the shields are under 10%. This is rather unintuitive especially for mods, so this PR creates a game setting value that allows mods to set this value.

Also, this PR fixes a bug where the HUD shield gauge would try and incorporate this feature but did not do it correctly. Specifically, the gauge skips shield segment rendering if the raw shield strength was 0.1 not the percent. Incorrectly using the raw value results in HUD shields being rendered (albeit faintly) even if the damage was skipping the shield and directly damaging the hull. In other words, if the shield segment was 8%, the shield segment would show faintly even the shield is skipping damage.

Tested and works as expected. Also happy to discuss or answer any questions!

* update local variable name for clarity

* rename `ship_quadrant_shield_strength`

* cleanup with updated get percent function

* more cleanup

* wording and safety tuning

* use correct default number
2025-07-19 18:24:49 -04:00
Goober5000 d746ae4f1a Merge pull request #6844 from Goober5000/modular_poofs
allow modular nebula tables to not define bitmaps
2025-07-18 00:46:33 -04:00
Goober5000 5fbce060e6 Merge pull request #6845 from Goober5000/check_for_bools
make the `check_for_*` functions return bool
2025-07-18 00:46:25 -04:00
Kestrellius cb20a05967 Fix inverted piercing impact logic (#6843)
* fix piercing sign error

* more robust solution
2025-07-17 10:59:01 -04:00
Goober5000 07db2aabf5 allow modular nebula tables to not define bitmaps
This allows the first half of a modular -neb.tbm to be skipped, allowing just poofs to be specified without the need for a preceding empty bitmap section.  It's a follow-up to #5128 which allowed just bitmaps to be specified without the need for a subsequent empty poof section.
2025-07-16 22:57:23 -04:00
Goober5000 be46706081 make the check_for_* functions return bool
Small cleanup.
2025-07-16 22:56:34 -04:00
BMagnu 83282b6427 Actually take the power of 3 (#6837) 2025-07-16 03:06:54 +02:00
Goober5000 a4eeaa706e Merge pull request #6831 from Goober5000/fix_play_sound_from_file
fix play-sound-from-file with multiple variable-controlled streams
2025-07-14 20:19:12 -04:00
wookieejedi a224736543 Slots for custom post processing effects (#6827)
* Slots for custom post processing effects

Post processing effects such as tint or dithering have their uniforms hardcoded, which means the only want to create new post-process effects for a mod is to overwrite an existing effect.

Requested for DEM2, this PR creates 4 blank slots for mods to create new post processing effects--2 floats and 2 vec3. This approach to create blank slots for modders follows the successful strategy used for Custom_Controls 1-5.

Note, this PR also adds `tint` to the default `post_processing.tbl` which had it missing (even though tint is a valid effect with a valid uniform buffer already).

Happy to discuss more or edit however.

* fix ordering
2025-07-14 13:44:18 -04:00
Taylor Richards d055d03e53 get proper number of physical cpu cores on macOS (#6835)
* get proper number of physical cpu cores on macOS

macOS has an easy way to get the physical cpu count so let's use it. Also with
Apple Silicon there are performance and efficiency cores so we need to make
sure we ignore those low-perf cores when getting the count.

* improve error handling
2025-07-14 13:41:36 -04:00
BMagnu 4c80ad7243 Fix Thread Count Detection on Windows (#6834) 2025-07-14 14:01:24 +00:00
BMagnu 53394093a4 Limit the number of threads spawned (#6833)
* Limit the number of threads spawned

* Conversion on msvc
2025-07-14 22:14:40 +09:00
Mike Nelson ef631f9fe0 nested namespaces 2025-07-13 19:44:44 -05:00
Mike Nelson d58d924f3e Get, set, browse team colors 2025-07-13 19:23:02 -05:00
Goober5000 8375b33998 Merge pull request #6810 from Goober5000/display_name_update
update handling of display names
2025-07-13 20:11:09 -04:00
Goober5000 3be99b2480 fix play-sound-from-file with multiple variable-controlled streams
Since this sexp was implemented, there has been a bug that prevented multiple variable-controlled audio streams from working.  One default and one variable-controlled stream could coexist, but any subsequent variable-controlled stream would reuse a previous stream's handle if the variable's current value was a valid handle.  This adjusts the code to properly create a new handle for every invocation of the sexp with a variable.

The following cases have been tested and now all work properly:
1. The default handle by itself
2. One variable handle by itself
3. One variable handle with one default handle
4. Multiple variable handles
5. Multiple variable handles with one default handle

Follow-up to #2115.
2025-07-13 20:02:06 -04:00
Taylor Richards 7811207ca9 fix decal shader compile error (#6829)
On macOS the new decal shaders (#6813) fail to compile with the following
message: "interpolation qualifier 'flat' must precede storage qualifiers".
2025-07-13 23:19:32 +02:00
Goober5000 dd72784f28 Merge pull request #6826 from Goober5000/sexp_help_newlines
ensure SEXP help has appropriate newlines or spaces
2025-07-12 19:40:04 -04:00
Goober5000 22feeddc6f ensure SEXP help has appropriate newlines or spaces
Some SEXP help did not have newlines separating what should be separate lines of text, or spaces separating what should be separate words.  Add newlines or spaces where appropriate.  (Regexes are A-1 SUPAR.)
2025-07-12 18:43:17 -04:00
BMagnu 6c35dac535 Allow autodetection of number of cores for threading (#6824)
* Allow autodetection of threads

* cleanup
2025-07-12 22:19:23 +02:00
BMagnu 8e64845cf1 Make sure to close the g3 frame (#6825) 2025-07-12 19:02:40 +00:00
BMagnu bdde909fa6 Threading Framework & Threaded Collisions (#6814)
* Dispatch ship weapon collisions and defer handling

* multithread ship weapon collision

* properly spin

* proper memory order

* externalize generic threadpool settings

* Wait for threads to shut down

* Add result pipelining

* cleanup

* incorrect nullopt type

* randomrange todo

* parallelize ship ship collisions

* bogus debug return

* remove warnings

* clang tidy
2025-07-12 11:41:30 +02:00
Kestrellius 6921f550c1 Enhanced functionality for conditional impacts (#6785)
* add new conditions

* don't worry about it

* implement new conditions

* bugfixing

* handle pokethrough differently

* clang for the clang god

* appeasement

* to the last i grapple with thee

* from hell's heart etc

* bugfixing

* support hits while dying

* fix shield math, cleanup

* fix memory error
2025-07-12 11:40:53 +02:00
Goober5000 06eaf94cb3 Merge pull request #6804 from Goober5000/stably_sorted_map
sort map keys before saving a container
2025-07-12 00:42:05 -04:00
Goober5000 d7fdf7b079 Merge pull request #6820 from Goober5000/fix_newlines_on_comments
fix broken newline behavior when FRED saves missions
2025-07-11 23:56:43 -04:00
Mike Nelson b9b64fe49d Show team colors with Lua rendering methods (#6802)
* get set team colors with lua's rendering methods

* fix for checks

* tweak

* add includes
2025-07-11 22:01:33 -04:00
BMagnu 218d8c09be Rely on bound vai's rather than try to retrieve them (#6822) 2025-07-11 16:13:25 -04:00
BMagnu bda54ffe83 Fix some things in the particle code coverity flaggedf (#6821) 2025-07-11 14:35:07 +00:00
Goober5000 b03301897e Merge pull request #6816 from Goober5000/model_memory_safety
memory safety for model parsing
2025-07-10 22:02:07 -04:00
Goober5000 32fcb8ccba fix broken newline behavior when FRED saves missions
Ever since retail, when encountering a comment, FRED would add an extra newline and mix `\r` with `\r\n`, often causing other bugs.  The reason for this is subtle.  C and C++ library functions will automatically convert `\n` to `\r\n` and back again when reading and writing in text mode, but FRED mission files are read in binary mode and written in text mode.  Normally the parsing code can accommodate reading raw newlines of any type, but comments are copied verbatim from what is read to what is written.  A comment ending in `\r\n` will therefore be written as `\r\r\n` which will add an extra incompatible newline.

At one point (in commit a988f5eab2) a fix was made for this, but it only applied to multiline comments, not all comments.  Therefore, this PR moves the fix to a common function and uses it for all comment styles.
2025-07-10 21:57:17 -04:00
Goober5000 86851fe0d2 make other compilers happy by only using the comma trick on debug mode 2025-07-10 15:10:19 -04:00
Goober5000 a565c8e76d memory safety for model parsing
Add assertions to the model parsing macros to guard against accessing out-of-bounds memory.  Patch the model parsing routines to avoid going out of bounds, even if the out of bounds situations are temporary and benign.
2025-07-10 15:10:19 -04:00
BMagnu 44ed242aee Make pspew warning a bit less annyoing (#6819) 2025-07-10 21:26:12 +09:00
Goober5000 145a34eeac Merge pull request #6806 from Goober5000/script_eval_multiline
make script-eval-num and script-eval-bool multiline
2025-07-09 20:38:18 -04:00
Goober5000 1e27a6bed3 Merge pull request #6817 from Goober5000/fix_qtfred_crash
fix qtfred memory corruption
2025-07-09 20:37:20 -04:00
Goober5000 84905f0789 Merge pull request #6818 from Goober5000/qtfred_cleanup
qtfred cleanup
2025-07-09 20:36:48 -04:00
BMagnu a52f31f748 Instanced Decals (#6813)
* prepare instancing

* Render single decal with instance-like buffer

* encode per-instance data in the instance matrix

* Actually render decals instanced

* Update todo commit

* actually add stubs

* Gate decal preparation behind GPU capability flag

* Fix MSVC warnigns

* remove explicit layout

* Fix MSVC warnigns 2

* Clang Tidy
2025-07-10 00:51:04 +02:00
Kestrellius 62c8260d26 add flag (#6815) 2025-07-09 12:08:53 -04:00
BMagnu 82dfdbc962 Cleanup of Level 0 Particles (#6808)
* Make scripting use the full-blown particle system

* Add reverse option

* Add animation reversal as an option to modern particles

* Start implementation of beam particles

* Add multi-emission particle source API for legacy particles

* Attach beam muzzle particles to the correct host

* Reorder code in order to facilitate apparent visual size as modular curve input for particles

* fix lua api

* Typo

* Fix scripting and beam muzzle particles

* Remove muzzleflash code, prepare to replace with particles

* Begin mflash effect conversion

* Pass final parameters to mflashes and fix curve

* Remove deprecated particle function

* Add debris flame particles

* remove weapon_explosion table

* Convert shield explosion particles

* Fix modular curve full inputs and convert particle curve inputs accordingly

* Allow particle volume offset and rotation with curve-based rot around fvec

* Add Ring and Point Volumes

* Convert PSPEW to new system

* Make particle curve timing aware of interp

* Update todos

* get rid of performance waste

* Add last parameter for converted pspews

* Fix pspew bugs

* Fix direction for default pspew

* Fix order for voluem rotation

* particle vel interp

* particle vel interp 2

* Make beam muzzle stay parented to the firing ship, as it's likely the more commonly intended variamnt

* fix scripting particle lifetime

* Allow beam muzzle particle override

* Skip invalid pspews

* remove pspew dcf code

* Fix sparkler stretch

* Correct sparkler bia

* General sparkler weirdness

* Tune distribution of sparklers

* Convert Asteroid particles

* Fix conversion warning

* Fix conversion warning again

* Fix conversion warning again again

* Really clang?
2025-07-09 16:47:14 +09:00
Goober5000 220ff6752b Merge pull request #6805 from Goober5000/warn_about_bitmap_fonts
change this WarningEx to a Warning
2025-07-09 02:40:11 -04:00
Goober5000 bd767ece09 qtfred cleanup
Various cleanup associated with the `toStdString()` function in qtFRED.  Now that `lcase_equal` exists, it is no longer necessary to compare the C strings.  Additionally, QString has its own `isEmpty()` function.
2025-07-08 23:06:20 -04:00
Goober5000 f60fe6dfc2 Merge pull request #6807 from Goober5000/subsystem_enhancements
subsystem scripting enhancements
2025-07-08 23:01:30 -04:00
Goober5000 342958b99f fix qtfred memory corruption
Fix a qtFRED memory access bug when checking for autosaved files.  Passing a temporary variable to a function as a reference, then returning it, led to a dangling reference access.  Rewrite the `maybeUseAutosave` function to avoid this.
2025-07-08 22:31:56 -04:00
BMagnu 497b834766 Speed up random range creation (#6812) 2025-07-08 14:26:43 +02:00
Kestrellius 8989b6e925 make change (#6809) 2025-07-08 11:05:26 +02:00
BMagnu 650b8d4f07 Disable uneccesary raycast (#6811) 2025-07-08 11:05:11 +02:00
Goober5000 672aa7f81f apply display name updates to jump nodes too 2025-07-07 03:25:35 -04:00
Goober5000 d99c4ca172 add display name text field to ship editor 2025-07-07 03:25:35 -04:00
Goober5000 fe1540c8a4 add 'Always save display names' option 2025-07-07 00:17:24 -04:00
Goober5000 df1126923d alphabetize accelerator keys, sort editor menu, sort ship dialog 2025-07-07 00:17:24 -04:00
Goober5000 8dc03b0fff additional submodel scripting enhancements
4. `__eq` functions for models and submodels
5. Store model and submodel IDs, not pointers
2025-07-05 16:05:19 -04:00
Goober5000 1b75fea06c subsystem scripting enhancements
Add several API features to improve scripting support for subsystems.

1. A `CanonicalName` virtvar to provide a clear way to get subsystem names for indexing and referencing
2. `Submodel` and `SubmodelInstance` virtvars to get the submodel information associated with the subsystem, if it exists
3. A `getSubsystemList()` function to provide a subsystem iterator
2025-07-05 16:05:19 -04:00
Goober5000 87021dd1d2 make script-eval-num and script-eval-bool multiline
These sexp operators can be enhanced to allow multiline scripts while retaining backwards compatibility, so do so.
2025-07-02 01:39:51 -04:00
Goober5000 3a380a05c9 change this WarningEx to a Warning
I just finished helping a modder debug his mod, and it would have saved him a lot of time and trouble if this WarningEx had been a Warning.
2025-07-01 23:20:18 -04:00
Goober5000 2e7f37ef7a sort map keys before saving a container
Applies a stable sort to the keys of a map container before saving the container to the mission file.  The underlying container is not modified.  Implements #6803.

(Lists are not sorted because the mission designer might have a particular order in mind for them.)
2025-07-01 21:57:04 -04:00
wookieejedi dbe0af1a24 Allow Custom Gauges to fully use parent type colors (#6799)
* Allow Custom Gauges to fully use parent type colors

#6670 allowed for custom coloring of HUD gauges, so this PR follows that up with fixing a few edge cases where mods with custom gauges that are linked to parent gauge types accurately reflect the color of their parent gauge type in both the new saved HUD file and the in-mission HUD.

This ability to link color to parent gauges for custom gauges the player cannot edit is what the behavior has always been, so this PR does not change any expected behavior it simply ensures that the new HUD updates and various combinations of table entries for `Config` are all robustly accounted for. For example, in FotG we have multiple non-configurable custom gauges all tied to the center reticle, so if the player changes the color of the center reticle all the custom gauges set to that type also automatically change color.

Tested and works as expected.

* appease clang

* one more clang

* clang needs to calm down

* instruct clang to be calm

* fix comment typo
2025-06-30 12:53:13 -04:00
wookieejedi f95ae0b1bd Prevent Potential Memory Problems with squadmsg hook function (#6801)
* Prevent Potential Memory Problems with squadmsg hook function

Within `hud_squadmsg_run_order_issued_hook` it is possible for the calling code to pass `nullptr` as the target parameter, which would cause memory problems with the several instances of `Objects[target->objnum]`. This PR mitigates that potential while testing of  `hud_squadmsg_run_order_issued_hook` still works as expected.

* add more safety
2025-06-30 12:14:28 -04:00
wookieejedi 94dac6b3d2 Prevent Bad Preset Names by Tuning Allowed Characters (#6800)
On current master players have the ability to name their control preset `...` which creates issues with Windows. This 1-line PR allows that the only non-alphanumeric characters allowed in the control preset name are `-` or `_`.

Tested and works as expected.
2025-06-30 08:22:27 -04:00
Goober5000 11a33fe42e Merge pull request #6347 from Goober5000/electrical_arc_enhancements
Enhancements to the FSO electrical arc system
2025-06-29 23:38:24 -04:00
Goober5000 821e48d188 Merge pull request #6798 from Goober5000/message_memory_fix
fix builtin_message memory usage
2025-06-29 22:49:56 -04:00
Goober5000 c68f8d71e4 Merge pull request #6796 from Goober5000/comm_menu_update
a few refinements to the new comm node selection feature
2025-06-29 17:11:18 -04:00
Goober5000 5380d88680 small optimization to get_builtin_message_sender() 2025-06-29 16:32:03 -04:00
Goober5000 3605f66e6d fix builtin_message memory usage
Since `builtin_message` might store strdup'd names, we might need to free them when the struct is destroyed.  Add a destructor for this purpose, and add corresponding constructors per the Rule of 5.

(Use `vm_strdup` rather than `strdup` for enhanced memory tracking.)
2025-06-29 16:32:03 -04:00
Goober5000 329974b4bf a few refinements to the new comm node selection feature
1. Don't display the selection arrows until the selection is actually moved, to avoid interfering with the standard selection feature
2. Use static variables and the new `TIMESTAMP` data type
3. Minor cleanup

Follow-up to #6736.
2025-06-29 00:05:48 -04:00
BMagnu 06227bbc80 fix cmake for debugging (#6795) 2025-06-27 12:24:35 +00:00
BMagnu a918ef0b6f Allow slot 0 hud RTT (#6794) 2025-06-27 14:07:31 +02:00
wookieejedi 9e731279eb AI Cleanup and Flag Fixes (#6788)
* AI Cleanup and Flag Fixes

This PR does 3 things.

1) Cleanup `ship_subsystem_in_sight` so it properly returns a bool, which is the return type of the function. Also update `ai_big_maybe_follow_subsys_path` to be a bool instead of int.

2) Adds `+subsystem attack pathing extra distance:` for use within `"$improved subsystem attack pathing:"`. FotG found, especially with the new strafing values we use, could really use the ability to set at what distance `ai_new_maybe_reposition_attack_subsys()` determines if it wants to pick a new point or not. By default the value was 2000.

3) In FotG we started using the `standard_strafe` mode more now thanks to #6511, but we have now discovered some inherit issues with it, namely that it will prematurely trigger `ai_big_strafe_maybe_retreat` because the distance it uses for determining if a collision is going to occur is `dist_normal_to_target`, which commonly resulted in the AI flying to a point to start a strafe, then thinking it would collide with the target and then retreating from strafe mode, only to realize it was now far enough to re-engage strafe mode, which commonly resulted in AI just flying around not accomplishing much. This PR adds a flag which instead uses the `distance_to_target` and a check to see if the ship is even facing the target.

Overall all are tested and work as expected. Happy to change or tune however requested, especially regarding point 3.

* clang cleanup

* cleanup copy paste name

* remove un-needed exposed float
2025-06-26 08:05:31 -04:00
wookieejedi 742dd074ee Allow Display Name to be used in Kills Stats by Ship Class (#6791)
* Allow Display Name to be used in Kills Stats by Ship Class

Currently, the display that is shown for kills by ship type does not use display name of the ship class, which makes the ship classes look different than the other areas where they are displayed throughout the game. This PR fixes that by using display name when rendering the kill stats. Note, this PR does not edit the pilot file structure at all, it simply just tunes how the names are displayed/rendered.

Importantly though, display names do not have to be unique, while the ship class names do. Thus, this PR also consolidates the number of kills of ship classes with the same display name (For example, FotG uses classes like `X-wing#Red` and `X-wing#Blue`, but both have the same display name of `X-wing`, so this consolidation means FSO will simple display `X-wing` with the sum. Overall, mods not using display name will be unaffected, while mods that do use display name will now have it properly work in the barracks and debrief rendering.

Tested and works as expected.

* remove flag for now

* move assert

* use handy get_display_name
2025-06-26 08:01:15 -04:00
wookieejedi f1a25ab47d Per Request add flag for $Disabled or disrupted engines are silent: (#6792)
* Per Request add flag for `$Disabled engines are silent:`

Follow-up to #6781 as there are modders who use engine sound as ambient sound. Also cleans up the sound engine disabled logic.

* cleanup name

* update flag name
2025-06-25 07:50:46 -04:00
wookieejedi d3db542640 Expand collision avoidance to strafing (#6789)
Currently, the `$better combat collision avoidance for fightercraft:` flag only checks for possible big ship collisions when in normal chase mode (`AIM_CHASE`), but strafing a big ship is a separate AI mode (`AIM_STRAFE`). Per discussion with Asteroth, strafe attacking should also check for better collision avoidance, as the modder likely would expect that to fall under this AI flag. Also took the chance to make the collision avoidance check into a function to improve maintenance and usability.
2025-06-24 23:01:47 -04:00
Goober5000 74beab5177 Merge pull request #6790 from Goober5000/order_fixes
clean up Target virtvar in order.cpp
2025-06-24 21:56:06 -04:00
Goober5000 2a62f04d72 clean up Target virtvar in order.cpp
Several refinements:
1. Make the case labels consistent between the setting and getting of the virtvar, for ease of comparison and troubleshooting.
2. Consolidate CHASE_WING and GUARD_WING for setting, like they already are for getting.
3. Consolidate STAY_STILL for getting, using one simple trick.
4. Remove the KEEP_SAFE_DISTANCE goal, since it has no target. Fixes #6761
5. Fix CHASE_SHIP_CLASS which put the getting case in the setting section, and consequently did not handle setting.
2025-06-24 02:14:20 -04:00
wookieejedi acf367e66a Edge case fix for Always_show_directive_value_count (#6787) 2025-06-23 20:26:22 -04:00
Goober5000 c5d5a93766 address feedback 2025-06-22 01:37:54 -04:00
Goober5000 f0ee1e2f7c electrical arc upgrade, stage 4: add a new ModifyElectricArcPoints function with a handy valueToVec3d utility function 2025-06-22 00:46:14 -04:00
Goober5000 47025a79d7 electrical arc upgrade, stage 3: add support for customizing segment depth and using persistent arc points 2025-06-22 00:46:14 -04:00
Goober5000 305af2a1ce electrical arc upgrade, stage 2: make arrays dynamic 2025-06-22 00:46:14 -04:00
Goober5000 5732183471 rename debris spark timestamps, per Asteroth 2025-06-22 00:46:14 -04:00
Goober5000 611de4d060 fix scope of ship arc sound generation
The code for generating spark sounds was incorrectly copied from debris to ships, dating all the way back to retail.  The sound should be played once per generation event, not once for every electrical arc active on the ship.  This fixes a bug that caused overlapping electrical arc sounds.
2025-06-22 00:46:14 -04:00
Goober5000 8270a38505 electrical arc upgrade, stage 1: refactor data structures 2025-06-22 00:46:14 -04:00
Goober5000 73e82d9440 Merge pull request #6786 from Goober5000/schroedinger_goals
schroedinger AI goals
2025-06-22 00:43:33 -04:00
Mike Nelson 6a1ae81bcb Add method to get a font's font family (#6782)
* Add method to get a font's font family

* nullptr

* add more safety checks

* pass font data instead of font data members

* return an empty string instead of unknown
2025-06-20 14:27:34 -04:00
Goober5000 035334878b schroedinger AI goals
The new enum class collection of AI goal modes did not handle the superposition of states that could occur when multiple ships were selected in FRED.  Add a special FRED-only state for this purpose.
2025-06-20 03:40:30 -04:00
wookieejedi 54c00ce205 Two Sound Updates (#6781)
* Two Sound Updates

On current master, destroyed engines still make noise and the `Engine Minimum Volume` is not used by big ships. This PR makes two new game settings flags which allow mods to mitigate those issues (`$Unify minimum engine sound:` and `$Disabled engines are silent:`).

Tested and works as expected.

I also added both of these to the 25.0 default, but happy to alter however.

* disabled eng snd does not need flag fix

* ensure retail large ships still sound loud
2025-06-19 22:38:12 -04:00
John Fernandez a9664ff22f Merge pull request #6752 from Goober5000/stay_near_target_fix
fix STAY_NEAR_TARGET_ITEM
2025-06-19 19:44:55 -04:00
Mike Nelson b5f4942cd3 fix hud config sliders (#6784) 2025-06-19 16:41:45 +00:00
wookieejedi 9141f1e9ed Allow Mouse Wheel to work with trigger type controls (#6779) 2025-06-18 17:35:28 -04:00
wookieejedi 26477276da Allow modders to tune Comms Styling (#6776)
This PR adds two new localization strings, which allow modders to change how the numbers in the comms menu are displayed, and it incorporates styling for the comms selection controls from #6736, too.

Tested and works as expected.
2025-06-17 22:58:45 -04:00
Asteroth 821d16192f guard against no model viewer object (#6780) 2025-06-16 07:57:49 -04:00
Mateusz Uberna 5864ec4e7e Comms menu controls (#6736)
* Added new keybinds, keycontrol handlers,  hudsquadmsg methods for alternate communication menu control scheme

* .gitignore update. CMake in VStudio places builds in [Oo]ut/ folder by default

* Added support for menu pages

* Small formatting cleanup

* code cleanup

* removed unecessary variable

* Updates after code review

* Updated comments

* XSTR_SIZE correction
2025-06-14 13:27:01 +00:00
wookieejedi c31c258679 Allow +Debris Density: (#6775)
* Allow `+Debris Density:`

Looks like when `+Debris Density` got added it never accounted for a colon like the other fields

* comment
2025-06-13 22:14:48 +00:00
wookieejedi c307ef7600 Allow ship debris pieces hitpoints depend on radius (#6771)
* Allow ship debris pieces hitpoints depend on radius

Currently, ship debris pieces have their hitpoints set via a Max and Min range. This constricts modders by needing all debris pieces to have somewhat the same size, otherwise the uniform random hitpoints assignment will could make small pieces much stronger than big pieces.

This PR adds a new field `+Hitpoints Radius Multiplier:` which is just a value that multiplies the debris radius, this value, and the debris hitpoints to calculate a final hitpoints value for the spawned debris piece.

This multiplier thus allows modders to have debris pieces that are very large have much strong hitpoints then debris pieces that are very small.

* incorporate feedback fix

* match other logical checks
2025-06-13 12:43:08 -04:00
Kestrellius 2701544822 Improve 'laser anim state add' curve output (#6773) 2025-06-12 21:33:29 -04:00
Mike Nelson 4ad015881d Lab dock tester (#6715)
* Add docking and undocking test to the Lab

* address feedback
2025-06-12 14:46:20 -04:00
Mike Nelson 43b36fad20 fix fred view not rendering (#6772) 2025-06-12 14:32:26 -04:00
John Fernandez 3dd2c38cef Merge pull request #6764 from scp-fs2open/dependabot/pip/ci/post/requests-2.32.4
Bump requests from 2.32.0 to 2.32.4 in /ci/post
2025-06-11 21:42:50 -04:00
BMagnu cfd3b716ba Fix FastDbg builds (#6770)
* Actually pr-test fastdbg

* Set embedfile msvc runtime

* Restructure toolchain files so that embedfile is also built accordingly

* Conditional AFXDLL

* Set iterator level globally
2025-06-11 22:21:51 +09:00
BMagnu d76f38320a Properly adapt the other workflow runs as well (#6769) 2025-06-11 06:41:46 -04:00
BMagnuandTyler Blount da58556787 Library updates, Win2022 CI, CMake 4 Compat (#6766)
* Update nightly build to use windows 2022

* Update build-release.yaml

* Update build-test.yaml

* Update test-pull_request.yaml

* Upgrade to CMake 3.21+

temp commit

* remove cotire due to incompatibilities

* Update dependencies to compile on CMake 4 with no warnings

* Update ANTLR4

* Update GTest

* Fix Windows CI

* Fix case sensitive ANTLR include

* Fix naming issues and disable shared ANTLR+

* Include Mess for MSVC

* fix new windows signed/unsigned compare warning

* FRED2 linking settings

* Don't clang tidy gtest

---------

Co-authored-by: Tyler Blount <[email protected]>
2025-06-11 02:00:23 +02:00
Mike Nelson 78d6a4b881 Ensure lua context is used for image drawing and string sizes (#6765) 2025-06-10 19:04:06 -04:00
BMagnu 1180f8762a Fix them oopsies (#6768) 2025-06-11 00:23:33 +02:00
BMagnu f342925763 Always enable window_res (#6767) 2025-06-11 00:23:25 +02:00
wookieejedi 2ec1553898 Fix few small bugs (#6755)
* Fix two small visual-based bugs

PR fixes/cleans up two small visual bugs/clean-up aspects.

1) If a mod uses the autoaim frame feature of the center reticle (IE the center reticle switches to a second bitmap if a target is within the autoaim cone), then there is currently an edge case bug where if the player's target dies while in the autoaim cone, the value tracking whether or not to use the autoaim frame is never reset to false. This results in the autoaim frame remaining even when the target is dead and long destroyed. This PR properly resets the autoaim bool to false if the target is not valid, and overall works now as expected.

2) #6555 moved warp options to tables instead of via in-game options, but there was a line of code that still referenced `WarpFlash`, where that option was forced off in standalones. This PR properly removes that line and instead puts the standalone check as part of the input warp queue function.

* also cleanup techroom subsystem loading
2025-06-10 14:45:50 -04:00
dependabot[bot] bfb8c70831 Bump requests from 2.32.0 to 2.32.4 in /ci/post
Bumps [requests](https://github.com/psf/requests) from 2.32.0 to 2.32.4.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.0...v2.32.4)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-06-10 08:18:07 +00:00
BMagnu e8bb54c36d Add "Move End Position by target velocity" (#6760)
* add "Move End Position by target velocity"

* Enable the functionality for slashbeams as well
2025-06-10 03:29:36 +02:00
Mike Nelson 6115a34d13 fix hudmessage rand (#6758) 2025-06-09 17:06:33 -04:00
Mike Nelson cc7f067e0f Use RAII guard to ensure lua context is always closed (#6757) 2025-06-09 17:06:18 -04:00
Goober5000 939d6a50c2 additional fix for STAY_NEAR_TARGET_ITEM
When issuing the stay-near-target order via hud squad message, default to a distance of 300, the same as the sexp.
2025-06-07 00:09:36 -04:00
Goober5000 3783cb0d1d fix STAY_NEAR_TARGET_ITEM
1. Allow "Stay near me" and "Stay near my target" to work even if the teams do not match
2. Allow "Stay near my target" to be issued regardless of team
3. Avoid divide-by-zero errors when running `ai_stay_near()`
2025-06-07 00:06:19 -04:00
JohnAFernandez 23254c993e Allow the loadout dialog to open varaible dialog 2024-05-22 01:09:14 -04:00
JohnAFernandez 29fa8c4f9e Final Fixes
Implements container contents reading, as well
2024-05-18 23:26:21 -04:00
JohnAFernandez ab7aa63569 Whoops 2024-05-18 16:31:09 -04:00
JohnAFernandez 77062e2571 Make sure check eternal boxes are properly enabled 2024-05-18 16:17:32 -04:00
JohnAFernandez 7229fdc423 Fix variable read and write 2024-05-18 15:58:19 -04:00
John Fernandez d8c7a63af7 This should actually fix it. 2024-05-17 18:04:08 -04:00
JohnAFernandez 2cf24cb27b Trying to fix this mystifying error 2024-05-17 17:58:48 -04:00
JohnAFernandez 768f88b135 Linter Fixes 2024-05-17 17:44:38 -04:00
John Fernandez d1829a9f10 Fix Eternal flags being set with no persistence 2024-05-17 17:29:40 -04:00
John Fernandez 860e28fbdb Finish applying data from model to mission 2024-05-17 15:33:11 -04:00
John Fernandez 07a389a5e8 Typo 2024-05-16 15:45:53 -04:00
John Fernandez 87367d6b84 Make sure Keys and Items are proper length 2024-05-16 15:40:56 -04:00
John Fernandez ad7e713692 Override the virtual reject function 2024-05-16 14:40:38 -04:00
John Fernandez e5e2729157 Add non-numeric keys to warning messages 2024-05-16 11:38:01 -04:00
John Fernandez 039866d526 Linter Fixes 2024-05-16 11:24:03 -04:00
John Fernandez d432f9b1e9 Make to loop over the right constraint 2024-05-16 11:21:14 -04:00
John Fernandez c51bc08d1d linter fix 2024-05-16 11:11:07 -04:00
JohnAFernandez 3be4d0b15a Fixes based on testing and linters 2024-05-16 00:47:46 -04:00
John Fernandez 7da79c174b Add sorting to other function calls 2024-05-15 13:01:13 -04:00
John Fernandez 62a43512fd Finish safe to alter variable functions
And sorting function.
2024-05-15 12:31:13 -04:00
John Fernandez 492683db2a Add A check to safeToAlter functions
And Add more notes to the notes column in the UI
2024-05-14 13:18:11 -04:00
John Fernandez c53e6b1487 Finish Safe to Alter Options in Model Update 2024-05-14 00:14:48 -04:00
John Fernandez e7f6fa3c91 Upgrade Container Copying 2024-05-13 22:31:30 -04:00
John Fernandez 455d2c9681 Add to Apply()
And start safeguarding changes that would not be safe to make without cleaning references.
2024-05-13 20:57:39 -04:00
John Fernandez b053131e71 Connect the x with prereject instead of reject.
Because the x is not connected to the cancel button, it has to be connected to preReject first.
2024-05-13 10:18:29 -04:00
John Fernandez e502cdfe76 Finish the validation function 2024-05-13 09:48:40 -04:00
JohnAFernandez e855fc81e9 Check for conversion availability with same algo 2024-05-12 01:50:45 -04:00
JohnAFernandez df77615473 Various Fixes Based on Testing 2024-05-12 01:42:36 -04:00
John Fernandez 20f992fe54 Fix Not being able to add map items by value 2024-05-11 00:43:36 -04:00
JohnAFernandez d557a6eff4 More Bug fixes 2024-05-11 00:19:18 -04:00
JohnAFernandez e66803af09 Further Bug fixes based on testing 2024-05-10 23:17:54 -04:00
JohnAFernandez cd3464d331 Make sure string items can be deleted 2024-05-10 22:52:56 -04:00
JohnAFernandez bc5f986d76 Bug fixes from testing 2024-05-10 22:18:54 -04:00
JohnAFernandez 621030a720 Fix various issues 2024-05-05 13:30:24 -04:00
JohnAFernandez 9b29e754ce Fix overflow return values
Previously on windows it was returning 0 because std::stol has a poor windows implementation.
2024-05-05 11:13:03 -04:00
JohnAFernandez e4886db4d2 Fix shift up and down buttons 2024-05-05 00:58:24 -04:00
JohnAFernandez b9b36d2a76 Fix Shift up and down buttons 2024-05-05 00:19:01 -04:00
JohnAFernandez 87fd99f9f6 Finish implementation of type formatting option 2024-05-05 00:18:47 -04:00
JohnAFernandez 03083d1a91 Implement a few missing container item features 2024-05-04 23:25:09 -04:00
JohnAFernandez 3120d6dda1 Fix the Addition of variables by typing
And start adding container Items by typing.
2024-05-04 16:57:30 -04:00
JohnAFernandez 8dde6d47cb More spacing adjustments and text changes 2024-05-04 01:19:42 -04:00
JohnAFernandez 98e78ec086 Fix Containers Not Restoring Deleted Items
And Change Deletion Text for size savings
2024-05-04 01:03:34 -04:00
JohnAFernandez e1b9832faf Fixes Based on Testing 2024-05-04 00:51:39 -04:00
JohnAFernandez c9e11d4657 Fixes Based On Testing 2024-05-04 00:35:46 -04:00
JohnAFernandez d7c4c544e6 Bug fixes according to testing 2024-05-03 23:54:07 -04:00
JohnAFernandez 50762246cb Fixes based on linter
And size adjustments
2024-05-03 23:22:27 -04:00
John Fernandez d04ce617fe Add swap keys and values button 2024-05-01 00:53:45 -04:00
John Fernandez 8340427388 Make sure shift buttons apply the model 2024-04-30 22:15:31 -04:00
John Fernandez eda645dcfc Make sure delete button changes text
The button should now say restore when the item has already been deleted.  Also, this expands how well we warn about deletions and will dynamically decide if we need to warn about deletion.
2024-04-30 22:03:50 -04:00
John Fernandez 72b13631c8 Implement sift up and down buttons 2024-04-30 20:56:04 -04:00
John Fernandez 824b5bbb37 Write addNewVariable overload for typed items 2024-04-30 18:59:24 -04:00
John Fernandez 2a8ec9cc45 Write setContainerKeyType 2024-04-30 18:51:29 -04:00
John Fernandez 237bd53b6d Rename this variable for clarity
And add a comment
2024-04-30 17:47:55 -04:00
JohnAFernandez ba263d6889 Trying to fixup The map/list switch function 2024-04-30 00:50:55 -04:00
JohnAFernandez d333694a09 Adding Button for Key/Value Swap
And Add more width for table views
2024-04-30 00:50:26 -04:00
John Fernandez cf356ed3df Finish writing change from list to map 2024-04-29 17:26:57 -04:00
John Fernandez 4677fb8801 Start Applying List values to Keys, etc. 2024-04-29 14:02:13 -04:00
JohnAFernandez 5ee211203a Fix Containers not deleting 2024-04-29 01:06:04 -04:00
JohnAFernandez 898f8a9963 Increase size of dialog to store info better
And ... more Fixes Based On Testing
2024-04-29 00:54:02 -04:00
JohnAFernandez c5b033235a More Fixes Based On Testing 2024-04-29 00:03:02 -04:00
JohnAFernandez f1dd86477a More Fixes Based on testing 2024-04-28 23:30:23 -04:00
JohnAFernandez a8cc7d2896 Appease the linter 2024-04-28 14:44:38 -04:00
JohnAFernandez e902f6d075 More change based on testing 2024-04-28 13:38:40 -04:00
JohnAFernandez 0c2d1a7a10 Save Changes 2024-04-28 01:52:30 -04:00
JohnAFernandez c984faeda1 Add Accept and Reject Signal Reactions 2024-04-28 01:37:29 -04:00
JohnAFernandez dac5fbeb82 Fix more Container issues based on testing 2024-04-26 20:11:40 -04:00
JohnAFernandez 94dbd95530 Fix nullptr on copy 2024-04-26 19:10:24 -04:00
JohnAFernandez 6e6b4098f3 Fix add container and add item rows
They were not appearing.
2024-04-26 19:03:22 -04:00
JohnAFernandez fc11ddcd45 Fixes Based On Linter and Testing 2024-04-26 18:15:59 -04:00
John Fernandez ec4e50481b Hopefully fix Variable Persistence Not saving 2024-04-26 17:21:18 -04:00
John Fernandez d143add82d Fixes to container options updates
And other fixes and adjustments
2024-04-26 17:17:10 -04:00
John Fernandez ed3fcc8aca Switch Key Options and Data Options
The data type options are going to be used much more regularly and always enabled, and so should be higher on the layout.
2024-04-26 17:16:34 -04:00
John Fernandez c24457ff6d Add Clamping Function
And more cleanup for trimIntegerString
2024-04-26 16:41:13 -04:00
John Fernandez 4bb8456f69 Further Fix and Rename function for clarity
It turns out that an empty string is not the only possible desired value if the filtered string is "-".  "-0" should return "0", but would return "" before this.
2024-04-26 16:06:58 -04:00
John Fernandez 5503aec7b4 Fix filtering out zero by accident 2024-04-26 15:57:33 -04:00
JohnAFernandez 4324b41109 More Fixes and Tweaks based on testing 2024-04-26 01:02:22 -04:00
JohnAFernandez 6134a42ef0 Ton of fixes from testing 2024-04-26 00:01:27 -04:00
John Fernandez 2a4894d696 start implementing unified values in containers
And write map key changing function.

Revert "start implementing unified values in containers"

This reverts commit 25eeae42413373bc7f12831285f8f639c8a93904.
2024-04-26 00:01:27 -04:00
John Fernandez 55380f7e8f Start on SetContainerListOrMap 2024-04-23 13:02:48 -04:00
John Fernandez 3a5a2ab43c Linter errors from recent changes 2024-04-23 12:01:17 -04:00
John Fernandez 4f0f57e711 Clean up the model in a few places 2024-04-22 16:46:04 -04:00
John Fernandez 8801672301 Fix more linter errors 2024-04-22 13:03:22 -04:00
John Fernandez a18ac34c03 Fix Linter errors 2024-04-22 12:43:26 -04:00
John Fernandez dd44d7d13a Fixing more issues 2024-04-22 01:40:46 -04:00
John Fernandez 84cad8c188 More Fixes and polish 2024-04-22 01:09:44 -04:00
John Fernandez 9dfc6d7c68 Fixes for UI bugs 2024-04-22 01:02:17 -04:00
JohnAFernandez 3c7639a9e3 And one more apparently 2024-04-22 00:33:16 -04:00
JohnAFernandez 7f1e72ecca Some final ui adjustments 2024-04-22 00:32:51 -04:00
JohnAFernandez d147330739 Fix null item crash 2024-04-21 23:38:14 -04:00
JohnAFernandez beaadbbc16 Merge branch 'qtFRED-variables-editor' of https://github.com/JohnAFernandez/fs2open into qtFRED-variables-editor 2024-04-21 23:03:34 -04:00
John Fernandez f38dbae310 Save changes to transfer over 2024-04-21 22:50:51 -04:00
John Fernandez 7f0666cc33 More Fixes, but Requires Linter For more progress 2024-04-21 16:46:57 -04:00
John Fernandez 91723be9c5 And stub another function for now
There are at least two stubbed functions left on the model
2024-04-21 16:05:12 -04:00
John Fernandez 4e669f9ce4 don't forget to change name. 2024-04-21 16:02:00 -04:00
John Fernandez 209e905a05 Add copy Container Function 2024-04-21 16:00:43 -04:00
John Fernandez a82db02b41 Fix missing function 2024-04-21 15:56:47 -04:00
John Fernandez 22663cb297 Finish Dialog Code
Need to make more changes to the model
2024-04-21 15:54:11 -04:00
John Fernandez 3698cbe09f Hopefully Final Linter mistakes 2024-04-21 15:00:44 -04:00
John Fernandez 29754cafc2 More Linter Fixes 2024-04-21 14:39:45 -04:00
John Fernandez 474527ef83 More Linter Fixes 2024-04-21 14:15:35 -04:00
John Fernandez ce32ef3022 Linter Fixes 2024-04-21 13:41:23 -04:00
John Fernandez 792cf0a0ed More fixes 2024-04-21 01:11:57 -04:00
John Fernandez 314ba92782 Fix Compiler Errors 2024-04-21 00:41:38 -04:00
John Fernandez 6264656097 As far as I can tell populating items is done 2024-04-21 00:37:46 -04:00
John Fernandez 6bd3b43cd8 Don't forget extra line left by accident 2024-04-21 00:17:01 -04:00
John Fernandez 3bec6d8cfc No const here 2024-04-21 00:14:19 -04:00
John Fernandez 72271be1f9 Getting closer to completion of basics 2024-04-20 23:50:54 -04:00
John Fernandez bb838ea4dd Place getCurrentContainerRow in more places 2024-04-20 23:38:32 -04:00
John Fernandez ad74c21b82 Edit onVariablesTableUpdated
And Apply GetCurrentContainerRow
2024-04-20 23:31:17 -04:00
John Fernandez e7b0f61870 Start applying currentVariableRow 2024-04-20 19:04:14 -04:00
John Fernandez 7c37628e1a Add get item rows to reduce duplicate code 2024-04-20 18:52:36 -04:00
John Fernandez 2a337133cb Fixes and add a couple more UI functions 2024-04-20 18:36:30 -04:00
John Fernandez a06cb0c688 More progress on updateContainerDataOptions 2024-04-18 18:03:37 -04:00
John Fernandez e4254636dd Finish addMapItem and upgrade getcontainernames
This has an upgrade for displaying List and Map info in a configurable manner.
2024-04-18 16:52:45 -04:00
John Fernandez f61649e911 Add type text to container Table 2024-04-18 15:14:25 -04:00
John Fernandez 83fde5acfc Actually, upgrade the lambda
This handles the leading negative no matter where it is.
2024-04-18 14:45:20 -04:00
John Fernandez 7d7b6eeeb8 Prevent crash from empty source string 2024-04-18 14:25:18 -04:00
John Fernandez 94057c5ef2 Clean up container data updates
And move the trimString method.
2024-04-18 14:24:01 -04:00
John Fernandez 6fe5ab7dc7 Fix No Persistence not updating
And other fixes
2024-04-17 16:29:11 -04:00
JohnAFernandez a57949c747 getting closer to correct width 2024-04-17 00:27:22 -04:00
JohnAFernandez 24310690f6 more progress 2024-04-17 00:26:42 -04:00
JohnAFernandez bd75eff4e4 Make sure that tables have contents
And fixes and progress
2024-04-17 00:05:36 -04:00
JohnAFernandez 364e39cedf more fixes 2024-04-16 23:24:26 -04:00
JohnAFernandez e1662d8501 Fix many typos from lack of linter 2024-04-16 00:22:40 -04:00
John Fernandez 7938169730 Don't forget lookup by name functions
This helps prevent name collisions
2024-04-15 22:50:05 -04:00
John Fernandez 3d5c43e45d Fix model functions by using index
Instead of name.
2024-04-15 22:43:45 -04:00
John Fernandez 1581d9dc1f Save progress 2024-04-14 12:34:20 -04:00
John Fernandez d4f8706208 Add contents to UpdateContainerOptions 2024-04-14 12:00:32 -04:00
John Fernandez 8d40952bf5 Finish adding Variable change options 2024-04-13 00:51:20 -04:00
John Fernandez 4cac920943 Add more dialog functions 2024-04-12 23:56:00 -04:00
John Fernandez 2ad0571b22 Make sure that setVariableType signals failure 2024-04-12 23:55:22 -04:00
John Fernandez 6e10ee64f8 Add TrimNumberString function
And other things
2024-04-12 21:56:27 -04:00
John Fernandez c9f4982d08 Add apply trigger on validated data
And clean up dialog model slightly
2024-04-12 21:48:50 -04:00
John Fernandez bc059bdb86 Add Variable Editor Trigger To Loadout Dialog 2024-04-12 21:47:42 -04:00
John Fernandez e60cf65036 Write part of onVariablesTableUpdated 2024-04-12 21:04:20 -04:00
John Fernandez 2934e99ddc Progress on applyModel 2024-04-12 17:26:14 -04:00
John Fernandez 11fac90617 Add missing functions 2024-04-12 15:27:24 -04:00
John Fernandez 9ef56797fe Create the Validate Function
And correct the Apply function some.
2024-04-12 15:26:58 -04:00
JohnAFernandez e1ed5fdd78 Fixes to allow building
Edits were previously done on a machine that could not build or use a linter.
2024-04-12 00:59:29 -04:00
JohnAFernandez a125b8b3ce Fixup the Variables Dialog
Add it to the editor menu (with shortcut), and fix it showing up when started
2024-04-12 00:58:37 -04:00
John Fernandez de54dea61a save progress 2024-04-11 22:29:21 -04:00
John Fernandez d665c618c1 Starting progress on initializeData
And container functions
2024-04-11 15:12:16 -04:00
John Fernandez fa27b8d133 MOAR FUNCTIONS! 2024-04-10 01:38:48 -04:00
John Fernandez aea39664ba Add logic for addcontainer
and a few other functions
2024-04-08 14:18:14 -04:00
John Fernandez 5575ab4c95 Finish setContainerValueType
And add stubs for other model functions we'll need
2024-04-07 23:37:25 -04:00
John Fernandez 2827e93b2d Continue writing editor model functions
Also, add a few declarations for List and Map contents editing.
2024-04-07 16:36:29 -04:00
John Fernandez cad4f6980d Save progress 2024-04-07 09:51:00 -04:00
John Fernandez 286a96e2c3 save progress 2024-04-06 22:25:48 -04:00
John Fernandez 7c15adec1a Finish Loop for saving variables 2024-01-06 18:38:12 -05:00
John Fernandez 52cc1f182e Make sure flags are included 2024-01-06 18:13:19 -05:00
John Fernandez 64b54d1022 Start writing apply and reject methods 2024-04-01 17:58:07 -04:00
John Fernandez 5d0ee564ac fix support structs 2023-12-29 17:50:39 -05:00
John Fernandez e7583ddab8 start creating Model and helper structs 2023-12-22 18:41:06 -05:00
JohnAFernandez 98a5ff4a23 Initial Dialog
Not building yet.  Some sort of linking error
2024-03-31 12:43:08 -04:00
1070 changed files with 80447 additions and 50601 deletions

No files matched your search

+2
View File
@@ -48,5 +48,7 @@ CheckOptions:
value: 'std::vector;std::deque;std::list;SCP_vector;SCP_deque;SCP_list'
- key: 'readability-braces-around-statements.ShortStatementLines'
value: '4' # Avoid flagging simple if (...) return false; statements
- key: 'performance-move-const-arg.CheckTriviallyCopyableMove' # This isn't actually performance relevant, but using move on trivially copyable types can well indicate that the variable should not be used anymore, such as when passing a builder type to the finialization function
value: 'false'
...
+3 -3
View File
@@ -104,7 +104,7 @@ jobs:
arch: [Win32, x64]
simd: [SSE2]
name: Windows
runs-on: windows-2019
runs-on: windows-2022
steps:
- uses: actions/checkout@v1
name: Checkout
@@ -155,13 +155,13 @@ jobs:
-DFSO_USE_VOICEREC="ON" -DFORCED_SIMD_INSTRUCTIONS="$SIMD" \
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
-DFSO_INSTALL_DEBUG_FILES="ON" -DFSO_BUILD_WITH_VULKAN="OFF" -A "$ARCHITECTURE" \
-G "Visual Studio 16 2019" -T "v142" ..
-G "Visual Studio 17 2022" -T "v143" -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
else
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
-DFSO_USE_VOICEREC="ON" -DFORCED_SIMD_INSTRUCTIONS="$SIMD" \
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
-G "Visual Studio 16 2019" -T "v142" ..
-G "Visual Studio 17 2022" -T "v143" -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
fi
- name: Compile
working-directory: ./build
+3 -3
View File
@@ -155,7 +155,7 @@ jobs:
simd: [SSE2, AVX]
needs: create_release
name: Windows
runs-on: windows-2019
runs-on: windows-2022
steps:
- uses: actions/checkout@v1
# Checkout repo
@@ -211,13 +211,13 @@ jobs:
-DFSO_USE_VOICEREC="ON" -DFORCED_SIMD_INSTRUCTIONS="$SIMD" \
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
-DFSO_INSTALL_DEBUG_FILES="ON" -DFSO_BUILD_WITH_VULKAN="OFF" -A "$ARCHITECTURE" \
-G "Visual Studio 16 2019" -T "v142" ..
-G "Visual Studio 17 2022" -T "v143" -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
else
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
-DFSO_USE_VOICEREC="ON" -DFORCED_SIMD_INSTRUCTIONS="$SIMD" \
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
-G "Visual Studio 16 2019" -T "v142" ..
-G "Visual Studio 17 2022" -T "v143" -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
fi
- name: Compile
working-directory: ./build
+3 -3
View File
@@ -96,7 +96,7 @@ jobs:
arch: [Win32, x64]
simd: [SSE2]
name: Windows
runs-on: windows-2019
runs-on: windows-2022
steps:
- uses: actions/checkout@v1
name: Checkout
@@ -153,13 +153,13 @@ jobs:
-DFSO_USE_VOICEREC="ON" -DFORCED_SIMD_INSTRUCTIONS="$SIMD" \
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
-DFSO_INSTALL_DEBUG_FILES="ON" -DFSO_BUILD_WITH_VULKAN="OFF" -A "$ARCHITECTURE" \
-G "Visual Studio 16 2019" -T "v142" ..
-G "Visual Studio 17 2022" -T "v143" -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
else
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
-DFSO_USE_VOICEREC="ON" -DFORCED_SIMD_INSTRUCTIONS="$SIMD" \
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
-G "Visual Studio 16 2019" -T "v142" ..
-G "Visual Studio 17 2022" -T "v143" -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
fi
- name: Compile
working-directory: ./build
+6 -6
View File
@@ -76,11 +76,11 @@ jobs:
build_windows:
strategy:
matrix:
configuration: [Debug, Release]
configuration: [Debug, FastDebug, Release]
compiler: [MSVC]
arch: [Win32, x64]
name: Windows
runs-on: windows-2019
runs-on: windows-2022
steps:
- name: Prepare Environment
run: choco install ninja
@@ -139,12 +139,12 @@ jobs:
else
if [ "$ARCHITECTURE" = "Win32" ]; then
cmake -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="OFF" -DFSO_BUILD_TESTS="ON" \
-DFORCED_SIMD_INSTRUCTIONS=SSE2 -DFSO_BUILD_FRED2="ON" -DFSO_BUILD_WITH_VULKAN="OFF" -G "Visual Studio 16 2019" \
-DFSO_BUILD_QTFRED=OFF -T "v142" -A "$ARCHITECTURE" ..
-DFORCED_SIMD_INSTRUCTIONS=SSE2 -DFSO_BUILD_FRED2="ON" -DFSO_BUILD_WITH_VULKAN="OFF" -G "Visual Studio 17 2022" \
-DFSO_BUILD_QTFRED=OFF -T "v143" -A "$ARCHITECTURE" -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
else
cmake -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="OFF" -DFSO_BUILD_TESTS="ON" \
-DFORCED_SIMD_INSTRUCTIONS=SSE2 -DFSO_BUILD_FRED2="ON" -G "Visual Studio 16 2019" \
-DFSO_BUILD_QTFRED=OFF -T "v142" -A "$ARCHITECTURE" ..
-DFORCED_SIMD_INSTRUCTIONS=SSE2 -DFSO_BUILD_FRED2="ON" -G "Visual Studio 17 2022" \
-DFSO_BUILD_QTFRED=OFF -T "v143" -A "$ARCHITECTURE" -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
fi
fi
- name: Compile
+3
View File
@@ -42,6 +42,7 @@ _ReSharper*/
#Project files
[Bb]uild/
[Oo]ut/
#Subversion files
.svn
@@ -259,3 +260,5 @@ CMakePresets.json
!/code/scripting/lua/LuaConvert.h
!/code/debugconsole/
!/docker/build
CMakeFiles/cmake.check_cache
CMakeCache.txt
+8 -9
View File
@@ -1,5 +1,5 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.9)
CMAKE_MINIMUM_REQUIRED(VERSION 3.21)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# Make sure the source and binary directory are not the sample
@@ -8,12 +8,12 @@ if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
endif()
include(cmake/util.cmake)
include(cmake/embed_file.cmake)
# Set a few policies
set_policy(CMP0051 NEW)
set_policy(CMP0053 NEW)
set_policy(CMP0054 NEW)
set_policy(CMP0091 NEW) # Enable use of CMAKE_MSVC_RUNTIME_LIBRARY
SET(FSO_PROJECT_DESCRIPTION "Open source project based on the original FreeSpace 2 source code.")
@@ -57,12 +57,16 @@ LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/finder")
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/external/rpavlik-cmake-modules")
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/external/cotire")
INCLUDE(globals)
INCLUDE(toolchain)
include(embed_file)
IF(RESET_INSTALL_PREFIX)
IF(NOT $ENV{FS2PATH} STREQUAL "")
IF(NOT IS_DIRECTORY $ENV{FS2PATH})
MESSAGE(WARNING "The path '$ENV{FS2PATH}' of the environment variable FS2PATH does not point to a directory! Install prefix wasn't set.")
ELSE(NOT IS_DIRECTORY $ENV{FS2PATH})
SET(CMAKE_INSTALL_PREFIX "$ENV{FS2PATH}" CACHE FILEPATH "Install path" FORCE)
SET(CMAKE_INSTALL_PREFIX "$ENV{FS2PATH}" CACHE PATH "Install path" FORCE)
FILE(TO_CMAKE_PATH ${CMAKE_INSTALL_PREFIX} CMAKE_INSTALL_PREFIX)
ENDIF(NOT IS_DIRECTORY $ENV{FS2PATH})
ELSE(NOT $ENV{FS2PATH} STREQUAL "")
@@ -105,7 +109,7 @@ OPTION(FSO_USE_OPENALSOFT "Download and build OpenAL Soft instead of using the s
OPTION(FSO_FATAL_WARNINGS "Determines if warnings in the build are considered fatal errors, primarily used for CI" OFF)
SET(FSO_FREESPACE_PATH "${CMAKE_INSTALL_PREFIX}" CACHE FILEPATH "The path of the FreeSpace directory you want to use. Please note that you will have to change CMAKE_INSTALL_PREFIX if you change this at some point.")
SET(FSO_FREESPACE_PATH "${CMAKE_INSTALL_PREFIX}" CACHE PATH "The path of the FreeSpace directory you want to use. Please note that you will have to change CMAKE_INSTALL_PREFIX if you change this at some point.")
SET(FSO_RUN_ARGUMENTS "" CACHE STRING "Additional arguments passed to a generated executable when run with the generated build files.")
@@ -156,11 +160,6 @@ mark_as_advanced(FORCE FSO_BUILD_WITH_VULKAN)
mark_as_advanced(FORCE FSO_BUILD_WITH_OPENXR)
mark_as_advanced(FORCE FSO_BUILD_WITH_OPENXR_DEBUG)
# Include cotire file from https://github.com/sakra/cotire/
include(cotire)
INCLUDE(globals)
INCLUDE(toolchain)
INCLUDE(platforms)
include(version)
+1 -1
View File
@@ -26,5 +26,5 @@ git diff -U0 --no-color "$BASE_COMMIT..$2" | \
-extra-arg="-DWITH_VULKAN" \
-extra-arg="-DVULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1" \
-extra-arg="-DVK_NO_PROTOTYPES" \
-regex '(code(?!((\/graphics\/shaders\/compiled)|(\/globalincs\/windebug)))|freespace2|qtfred|test|build|tools)\/.*\.(cpp|h)' \
-regex '(code(?!((\/graphics\/shaders\/compiled)|(\/globalincs\/windebug)|(\/def_files\/data)))|freespace2|qtfred|test\/src|build|tools)\/.*\.(cpp|h)' \
-clang-tidy-binary /usr/bin/clang-tidy-16 -j$(nproc) -export-fixes "$(pwd)/clang-fixes.yaml"
+1 -1
View File
@@ -42,7 +42,7 @@ export CXXFLAGS
export CFLAGS
export LD_LIBRARY_PATH
if [ "$RUNNER_OS" = "macOS" ]; then
if [ "$RUNNER_OS" = "macOS" ] && [ ! -x "$(which ninja)" ]; then
brew install ninja
fi
+1 -1
View File
@@ -1,3 +1,3 @@
Mako==1.2.2
requests==2.32.0
requests==2.32.4
semantic-version==2.8.5
-4054
View File
File diff suppressed because it is too large. Load diff
+6 -1
View File
@@ -9,14 +9,19 @@ else()
endif()
set(IS_ARM64 FALSE)
set(IS_ARMV7A FALSE)
if (NOT "${CMAKE_GENERATOR_PLATFORM}" STREQUAL "") # needed to cover Visual Studio generator
if(CMAKE_GENERATOR_PLATFORM MATCHES "^(aarch64|arm64|ARM64)")
set(IS_ARM64 TRUE)
elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^(armv7)")
set(IS_ARMV7A TRUE)
endif()
else()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)")
set(IS_ARM64 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv7)")
set(IS_ARMV7A TRUE)
endif()
endif()
@@ -33,6 +38,6 @@ else()
endif()
set(IS_X86 FALSE)
if (NOT IS_ARM64 AND NOT IS_RISCV)
if (NOT IS_ARM64 AND NOT IS_RISCV AND NOT IS_ARMV7A)
set(IS_X86 TRUE)
endif()
+1 -1
View File
@@ -15,4 +15,4 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wno-unused-variable -Wno-unused-parameter")
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -Wshadow")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wshadow")
+2 -2
View File
@@ -119,14 +119,14 @@ string(REGEX MATCH "-O[a-zA-Z|0-9]+" C_OPT_FLAG ${C_BASE_FLAGS})
# If no user-set opt flag, set -O3 and -Og
if ("${CXX_OPT_FLAG}" STREQUAL "")
set(CXX_OPT_FLAG_RELEASE "-O3")
set(CXX_OPT_FLAG_DEBUG "-Og")
set(CXX_OPT_FLAG_DEBUG "-O0")
else()
set(CXX_OPT_FLAG_RELEASE "${CXX_OPT_FLAG}")
set(CXX_OPT_FLAG_DEBUG "${CXX_OPT_FLAG}")
endif()
if ("${C_OPT_FLAG}" STREQUAL "")
set(C_OPT_FLAG_RELEASE "-O3")
set(C_OPT_FLAG_DEBUG "-Og")
set(C_OPT_FLAG_DEBUG "-O0")
else()
set(C_OPT_FLAG_RELEASE "${C_OPT_FLAG}")
set(C_OPT_FLAG_DEBUG "${C_OPT_FLAG}")
+2 -2
View File
@@ -161,14 +161,14 @@ string(REGEX MATCH "-O[a-zA-Z|0-9]+" C_OPT_FLAG ${C_BASE_FLAGS})
# If no user-set opt flag, set -O3 and -Og
if ("${CXX_OPT_FLAG}" STREQUAL "")
set(CXX_OPT_FLAG_RELEASE "-O3")
set(CXX_OPT_FLAG_DEBUG "-Og")
set(CXX_OPT_FLAG_DEBUG "-O0")
else()
set(CXX_OPT_FLAG_RELEASE "${CXX_OPT_FLAG}")
set(CXX_OPT_FLAG_DEBUG "${CXX_OPT_FLAG}")
endif()
if ("${C_OPT_FLAG}" STREQUAL "")
set(C_OPT_FLAG_RELEASE "-O3")
set(C_OPT_FLAG_DEBUG "-Og")
set(C_OPT_FLAG_DEBUG "-O0")
else()
set(C_OPT_FLAG_RELEASE "${C_OPT_FLAG}")
set(C_OPT_FLAG_DEBUG "${C_OPT_FLAG}")
+5 -14
View File
@@ -72,11 +72,10 @@ if (MSVC_RELEASE_DEBUGGING)
endif()
IF(MSVC_USE_RUNTIME_DLL)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MD")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<NOT:$<CONFIG:Release>>:Debug>DLL")
add_compile_definitions(_AFXDLL)
ELSE(MSVC_USE_RUNTIME_DLL)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<NOT:$<CONFIG:Release>>:Debug>")
ENDIF(MSVC_USE_RUNTIME_DLL)
# Debug
@@ -84,14 +83,6 @@ set(CMAKE_C_FLAGS_DEBUG "/W4 /Gy /Zi /Od /RTC1 /Gd /Oy-")
set(CMAKE_CXX_FLAGS_DEBUG "/W4 /Gy /Zi /Od /RTC1 /Gd /Oy-")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /INCREMENTAL:NO /MAPINFO:EXPORTS /NODEFAULTLIB:libcmt.lib")
IF(MSVC_USE_RUNTIME_DLL)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MDd")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
ELSE(MSVC_USE_RUNTIME_DLL)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
ENDIF(MSVC_USE_RUNTIME_DLL)
INCLUDE(MSVCMultipleProcessCompile)
# Visual Studio supports compiling for multiple vector instruction sets
@@ -122,8 +113,8 @@ if(IS_X86)
endif()
target_compile_definitions(compiler INTERFACE _CRT_SECURE_NO_DEPRECATE _CRT_SECURE_NO_WARNINGS _SECURE_SCL=0 NOMINMAX
"$<$<CONFIG:FastDebug>:_ITERATOR_DEBUG_LEVEL=0>" _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
add_compile_definitions("$<$<CONFIG:FastDebug>:_ITERATOR_DEBUG_LEVEL=0>")
target_compile_definitions(compiler INTERFACE _CRT_SECURE_NO_DEPRECATE _CRT_SECURE_NO_WARNINGS _SECURE_SCL=0 NOMINMAX _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
if (FSO_FATAL_WARNINGS)
# Make warnings fatal if the right variable is set
+11 -21
View File
@@ -158,27 +158,17 @@ function(set_precompiled_header _target _headerPath)
target_compile_definitions(${_target} PRIVATE CMAKE_PCH)
target_precompile_headers(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${_headerPath}>")
else()
if (ENABLE_COTIRE)
message("${_headerPath}")
set_target_properties(${_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${_headerPath}")
# Disable unity build as it doesn't work well for us
set_target_properties(${_target} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
cotire(${_target})
else()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
target_compile_options(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-include${_headerPath}>")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
target_compile_options(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:/FI ${_headerPath}>")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
target_compile_options(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-include${_headerPath}>")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
target_compile_options(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-include${_headerPath}>")
ELSE()
MESSAGE("Unknown compiler for global includes. This will probably break the build.")
ENDIF()
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
target_compile_options(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-include${_headerPath}>")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
target_compile_options(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:/FI ${_headerPath}>")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
target_compile_options(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-include${_headerPath}>")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
target_compile_options(${_target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-include${_headerPath}>")
ELSE()
MESSAGE("Unknown compiler for global includes. This will probably break the build.")
ENDIF()
endif()
endfunction()
+1 -1
View File
@@ -59,7 +59,7 @@ endif()
target_link_libraries(code PUBLIC libRocket)
target_link_libraries(code PUBLIC pcp)
target_link_libraries(code PUBLIC pcpnatpmp)
target_link_libraries(code PUBLIC parsers)
+8 -8
View File
@@ -51,8 +51,8 @@ class ExpressionBuilderVisitor : public ActionExpressionVisitor {
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>>();
const auto leftExpr = std::any_cast<std::shared_ptr<nodes::AbstractExpression>>(visit(context->expression(0)));
const auto rightExpr = std::any_cast<std::shared_ptr<nodes::AbstractExpression>>(visit(context->expression(1)));
auto operatorExpression = std::make_shared<nodes::FunctionCallExpression>(operatorTok,
true,
@@ -88,8 +88,8 @@ class ExpressionBuilderVisitor : public ActionExpressionVisitor {
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>>());
std::any_cast<std::shared_ptr<nodes::AbstractExpression>>(visit(context->expression(0))),
std::any_cast<std::shared_ptr<nodes::AbstractExpression>>(visit(context->expression(1))));
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
}
antlrcpp::Any visitLiteral_expression(ActionExpressionParser::Literal_expressionContext* context) override
@@ -122,9 +122,9 @@ class ExpressionBuilderVisitor : public ActionExpressionVisitor {
}
antlrcpp::Any visitVec3d_constructor(ActionExpressionParser::Vec3d_constructorContext* context) override
{
const auto xExpression = visit(context->expression(0)).as<std::shared_ptr<nodes::AbstractExpression>>();
const auto yExpression = visit(context->expression(1)).as<std::shared_ptr<nodes::AbstractExpression>>();
const auto zExpression = visit(context->expression(2)).as<std::shared_ptr<nodes::AbstractExpression>>();
const auto xExpression = std::any_cast<std::shared_ptr<nodes::AbstractExpression>>(visit(context->expression(0)));
const auto yExpression = std::any_cast<std::shared_ptr<nodes::AbstractExpression>>(visit(context->expression(1)));
const auto zExpression = std::any_cast<std::shared_ptr<nodes::AbstractExpression>>(visit(context->expression(2)));
auto expression = std::make_shared<nodes::VectorConstructorExpression>(context->L_PAREN()->getSymbol(),
xExpression,
@@ -157,7 +157,7 @@ std::shared_ptr<nodes::AbstractExpression> ExpressionParser::parse(const ParseCo
if (errListener.diagnostics.empty()) {
// Only do semantic checking if syntax is valid
ExpressionBuilderVisitor builder;
exprReturn = tree->accept(&builder).as<std::shared_ptr<nodes::AbstractExpression>>();
exprReturn = std::any_cast<std::shared_ptr<nodes::AbstractExpression>>(tree->accept(&builder));
if (!exprReturn->validate(&parser, context)) {
exprReturn = nullptr;
+4 -4
View File
@@ -527,7 +527,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, int ship_info_index = -1);
extern void ai_attack_object(object *attacker, object *attacked, int ship_info_index = -1, int class_type = -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);
@@ -559,7 +559,7 @@ extern void ai_set_guard_wing(object *objp, int wingnum);
extern void ai_warp_out(object *objp, vec3d *vp);
extern void ai_attack_wing(object *attacker, int wingnum);
extern void ai_deathroll_start(object *ship_obj);
extern int set_target_objnum(ai_info *aip, int objnum);
extern int set_target_objnum(ai_info* aip, int objnum);
extern void ai_form_on_wing(object *objp, object *goal_objp);
extern void ai_do_stay_near(object *objp, object *other_obj, float dist, int additional_data);
extern ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum);
@@ -596,7 +596,7 @@ extern int ai_maybe_fire_afterburner(object *objp, ai_info *aip);
extern void set_predicted_enemy_pos(vec3d *predicted_enemy_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, ai_info *aip);
extern int is_instructor(object *objp);
extern int find_enemy(int objnum, float range, int max_attackers, int ship_info_index = -1);
extern int find_enemy(int objnum, float range, int max_attackers, int ship_info_index = -1, int class_type = -1);
float ai_get_weapon_speed(const ship_weapon *swp);
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, const vec3d *gun_pos, const object *pobjp, const vec3d *enemy_pos, const vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range);
@@ -617,7 +617,7 @@ extern float dock_orient_and_approach(object *docker_objp, int docker_index, obj
void ai_set_mode_warp_out(object *objp, ai_info *aip);
// prototyped by Goober5000
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index);
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index, int class_type = -1);
// moved to header file by Goober5000
void ai_announce_ship_dying(object *dying_objp);
+12 -1
View File
@@ -44,12 +44,15 @@ namespace AI {
Goal_on_hold, // when set, this goal cannot currently be satisfied, although it could be in the future
Subsys_needs_fixup, // when set, the subsystem index (for a destroy subsystem goal) is invalid and must be gotten from the subsys name stored in docker.name field!!
Goal_override, // paired with ai_goal_type::DYNAMIC to mean this goal overrides any other goal
Want_override, // a goal should set this flag if Goal_override should be assigned when the goal is achievable
Purge, // purge this goal next time we process
Goals_purged, // this goal has already caused other goals to get purged
Purge_when_new_goal_added, // this goal is perpetual until any other goal is added, at which time it sets its own Purge flag
Goals_purged, // this goal has already caused other goals to get purged (because it is something like ai-disarm that renders other goals invalid)
Depart_sound_played,// Goober5000 - replacement for AL's hack ;)
Target_own_team, // this attack goal is allowed to target friendlies
Afterburn_hard, // afterburn as hard as possible to the goal
Waypoints_in_reverse, // decrement instead of increment
Clear_all_goals_first, // this goal wipes all goals (including itself) before it runs (handled separately, and in a different place, from purging invalid goals)
NUM_VALUES
};
@@ -142,6 +145,7 @@ namespace AI {
Fightercraft_nonshielded_ships_can_manage_ets,
Ships_playing_dead_dont_manage_ets,
Better_combat_collision_avoidance,
Better_combat_collision_avoid_includes_target,
Better_guard_collision_avoidance,
Require_exact_los,
Improved_missile_avoidance,
@@ -170,6 +174,7 @@ namespace AI {
Debris_respects_big_damage,
Dont_limit_change_in_speed_due_to_physics_whack,
Guards_ignore_protected_attackers,
Fix_standard_strafe,
Standard_strafe_used_more,
Unify_usage_ai_shield_manage_delay,
Fix_AI_shield_management_bug,
@@ -178,6 +183,12 @@ namespace AI {
Freespace_1_missile_behavior,
ETS_uses_power_output,
ETS_energy_same_regardless_of_system_presence,
Dont_form_on_wing_at_mission_start,
Do_not_clear_goals_when_running_form_on_wing,
Do_not_clear_goals_when_running_stay_still,
Do_not_set_override_when_assigning_form_on_wing,
Purge_player_issued_form_on_wing_after_subsequent_order,
Cancel_future_waves_of_any_wing_launched_from_an_exited_ship,
NUM_VALUES
};
+39
View File
@@ -477,6 +477,9 @@ void parse_ai_profiles_tbl(const char *filename)
set_flag(profile, "$support don't add primaries:", AI::Profile_Flags::Support_dont_add_primaries);
if (optional_string("$max allies rearming threshold:"))
stuff_int(&profile->max_allies_rearming_threshold);
set_flag(profile, "$firing requires exact los:", AI::Profile_Flags::Require_exact_los);
if (optional_string("$exact los minimum detection radius:")) {
@@ -588,6 +591,8 @@ void parse_ai_profiles_tbl(const char *filename)
stuff_float(&profile->better_collision_avoid_aggression_combat);
}
set_flag(profile, "+combat collision avoidance for fightercraft includes target:", AI::Profile_Flags::Better_combat_collision_avoid_includes_target);
set_flag(profile, "$better guard collision avoidance for fightercraft:", AI::Profile_Flags::Better_guard_collision_avoidance);
if (optional_string("+guard collision avoidance aggression for fightercraft:")) {
@@ -669,6 +674,8 @@ void parse_ai_profiles_tbl(const char *filename)
set_flag(profile, "$fix avoid-shockwave bugs:", AI::Profile_Flags::Fix_avoid_shockwave_bugs);
set_flag(profile, "$fix standard strafe:", AI::Profile_Flags::Fix_standard_strafe);
set_flag(profile, "$standard strafe used more:", AI::Profile_Flags::Standard_strafe_used_more);
if (optional_string("$standard strafe triggers under this speed:")) {
@@ -697,6 +704,10 @@ void parse_ai_profiles_tbl(const char *filename)
}
}
if (optional_string("$attack-any idle circle distance:")) {
stuff_float(&profile->attack_any_idle_circle_distance);
}
set_flag(profile, "$unify usage of AI Shield Manage Delay:", AI::Profile_Flags::Unify_usage_ai_shield_manage_delay);
set_flag(profile, "$fix AI shield management bug:", AI::Profile_Flags::Fix_AI_shield_management_bug);
@@ -711,6 +722,28 @@ void parse_ai_profiles_tbl(const char *filename)
set_flag(profile, "$ETS energy same regardless of system presence:", AI::Profile_Flags::ETS_energy_same_regardless_of_system_presence);
set_flag(profile, "$don't issue form-on-wing goals at mission start:", AI::Profile_Flags::Dont_form_on_wing_at_mission_start);
if (optional_string("$default form-on-wing priority:")) {
int priority;
stuff_int(&priority);
if (priority > 0) {
profile->default_form_on_wing_priority = priority;
} else {
mprintf(("Warning: $default form-on-wing priority: should be > 0 (read %d). Value will not be used.\n", priority));
}
}
set_flag(profile, "$do not clear goals when running form-on-wing:", AI::Profile_Flags::Do_not_clear_goals_when_running_form_on_wing);
set_flag(profile, "$do not clear goals when running stay-still:", AI::Profile_Flags::Do_not_clear_goals_when_running_stay_still);
set_flag(profile, "$do not set override when assigning form-on-wing:", AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing);
set_flag(profile, "$purge player-issued form-on-wing after subsequent order:", AI::Profile_Flags::Purge_player_issued_form_on_wing_after_subsequent_order);
set_flag(profile, "$cancel future waves of any wing launched from an exited ship:", AI::Profile_Flags::Cancel_future_waves_of_any_wing_launched_from_an_exited_ship);
// end of options ----------------------------------------
@@ -803,6 +836,7 @@ void ai_profile_t::reset()
ai_range_aware_secondary_select_mode = AI_RANGE_AWARE_SEC_SEL_MODE_RETAIL;
turret_target_recheck_time = 2000.0f;
rot_fac_multiplier_ply_collisions = 0.0f;
max_allies_rearming_threshold = 2;
better_collision_avoid_aggression_combat = 3.5f;
better_collision_avoid_aggression_guard = 3.5f;
@@ -813,6 +847,9 @@ void ai_profile_t::reset()
guard_big_orbit_above_target_radius = 500.0f;
guard_big_orbit_max_speed_percent = 1.0f;
attack_any_idle_circle_distance = 100.0f;
default_form_on_wing_priority = 99; // as originally assigned in ai_add_goal_sub_sexp()
for (int i = 0; i < NUM_SKILL_LEVELS; ++i) {
max_incoming_asteroids[i] = 0;
@@ -927,5 +964,7 @@ void ai_profile_t::reset()
}
if (mod_supports_version(25, 0, 0)) {
flags.set(AI::Profile_Flags::Fix_avoid_shockwave_bugs);
flags.set(AI::Profile_Flags::Purge_player_issued_form_on_wing_after_subsequent_order);
flags.set(AI::Profile_Flags::Cancel_future_waves_of_any_wing_launched_from_an_exited_ship);
}
}
+8
View File
@@ -101,6 +101,9 @@ public:
float detail_distance_mult[MAX_DETAIL_VALUE + 1]; //MAX_DETAIL_VALUE really needs to be 4
// max number of allies allowed to be rearming at the same time
int max_allies_rearming_threshold;
// minimum radius for the line-of-sight (los) detection --wookieejedi
float los_min_detection_radius;
@@ -144,6 +147,11 @@ public:
float guard_big_orbit_above_target_radius; // Radius of guardee that triggers ai_big_guard()
float guard_big_orbit_max_speed_percent; // Max percent of forward speed that is used in ai_big_guard()
// AI attack any option --wookieejedi
float attack_any_idle_circle_distance; // Radius that AI circles around a point while waiting for new enemies in attack-any mode
int default_form_on_wing_priority; // the priority used if not specified in the sexp
void reset();
};
+40 -19
View File
@@ -62,9 +62,9 @@
void ai_big_evade_ship();
void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float dist_to_enemy, int modelnum);
void ai_big_avoid_ship();
int ai_big_maybe_follow_subsys_path(int do_dot_check=1);
bool ai_big_maybe_follow_subsys_path(bool do_dot_check=true);
void ai_big_strafe_position();
static int ai_big_strafe_maybe_retreat(const vec3d *target_pos);
static bool ai_big_strafe_maybe_retreat(const vec3d *target_pos);
extern void compute_desired_rvec(vec3d *rvec, const vec3d *goal_pos, const vec3d *cur_pos);
extern void big_ship_collide_recover_start(const object *objp, const object *big_objp, const vec3d *collision_normal);
@@ -146,6 +146,10 @@ done_1:
*attack_point = best_point;
//The following raycast tends to make up 10%(!) of total AI-frametime for basically no benefit, especially in the common case for turrets where the normal isn't even queried.
if (surface_normal == nullptr && Disable_expensive_turret_target_check)
return;
// Cast from attack_objp_pos to local_attack_pos and check for nearest collision.
// If no collision, cast to (0,0,0) [center of big ship]** [best_point initialized to 000]
@@ -347,10 +351,10 @@ void ai_big_subsys_path_cleanup(ai_info *aip)
}
// Maybe Pl_objp needs to follow a path to get in line-of-sight to a subsystem
// input: do_dot_check => default value 0, flag to indicate whether check should be done to ensure
// input: do_dot_check => default value true, flag to indicate whether check should be done to ensure
// subsystem is within certain field of view. We don't want to check fov when
// strafing, since ship is weaving to avoid turret fire
int ai_big_maybe_follow_subsys_path(int do_dot_check)
bool ai_big_maybe_follow_subsys_path(bool do_dot_check)
{
ai_info *aip;
float dot = 1.0f, min_dot;
@@ -864,7 +868,7 @@ void ai_big_chase_ct()
extern bool ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1);
extern float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip, bool burst);
extern bool ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp);
extern int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d *goal_point, float delta_time, float time_scale = 1.f);
extern bool maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d *goal_point, float delta_time, float time_scale = 1.f);
extern void maybe_cheat_fire_synaptic(object *objp);
@@ -1416,7 +1420,7 @@ void ai_big_attack_get_data(vec3d *enemy_pos, float *dist_to_enemy, float *dot_t
// check to see if Pl_objp has gotten too close to attacking point.. if so, break off by entering
// AIS_STRAFE_RETREAT
static int ai_big_strafe_maybe_retreat(const vec3d *target_pos)
static bool ai_big_strafe_maybe_retreat(const vec3d *target_pos)
{
ai_info *aip;
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
@@ -1424,25 +1428,42 @@ static int ai_big_strafe_maybe_retreat(const vec3d *target_pos)
vec3d vec_to_target;
vm_vec_sub(&vec_to_target, target_pos, &Pl_objp->pos);
float dist_to_target, dist_normal_to_target, time_to_target;
dist_to_target = vm_vec_mag_quick(&vec_to_target);
if (vm_vec_mag_quick(&aip->big_attack_surface_normal) > 0.9) {
dist_normal_to_target = -vm_vec_dot(&vec_to_target, &aip->big_attack_surface_normal);
} else {
dist_normal_to_target = 0.2f * vm_vec_mag_quick(&vec_to_target);
}
dist_normal_to_target = MAX(0.2f*dist_to_target, dist_normal_to_target);
time_to_target = dist_normal_to_target / Pl_objp->phys_info.speed;
float dist_to_target = vm_vec_mag(&vec_to_target);
// add distance penalty for going too fast
float speed_to_dist_penalty = MAX(0.0f, (Pl_objp->phys_info.speed-50));
bool collide_time;
bool collide_distance;
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_standard_strafe]) {
// check if ship facing target, as likely will only collide if facing
vec3d vec_to_tpos;
vm_vec_normalized_dir(&vec_to_tpos, target_pos, &Pl_objp->pos);
float dot = vm_vec_dot(&vec_to_tpos, &Pl_objp->orient.vec.fvec);
if (dot <= 0.0f) {
collide_time = false;
collide_distance = false;
} else {
collide_time = (dist_to_target / Pl_objp->phys_info.speed) < STRAFE_RETREAT_COLLIDE_TIME;
collide_distance = dist_to_target < (STRAFE_RETREAT_COLLIDE_DIST + speed_to_dist_penalty);
}
} else {
float dist_normal_to_target;
if (vm_vec_mag(&aip->big_attack_surface_normal) > 0.9) {
dist_normal_to_target =
MAX(0.2f * dist_to_target, -vm_vec_dot(&vec_to_target, &aip->big_attack_surface_normal));
} else {
dist_normal_to_target = 0.2f * dist_to_target;
}
collide_time = (dist_normal_to_target / Pl_objp->phys_info.speed) < STRAFE_RETREAT_COLLIDE_TIME;
collide_distance = dist_normal_to_target < (STRAFE_RETREAT_COLLIDE_DIST + speed_to_dist_penalty);
}
//if ((dot_to_enemy > 1.0f - 0.1f * En_objp->radius/(dist_to_enemy + 1.0f)) && (Pl_objp->phys_info.speed > dist_to_enemy/5.0f))
// Inside 2 sec retreat, setting goal point to box point + strafe_retreat_box_dist
// If collision, use std collision resolution.
if ( !(aip->ai_flags[AI::AI_Flags::Kamikaze]) && ((aip->ai_flags[AI::AI_Flags::Target_collision]) || (time_to_target < STRAFE_RETREAT_COLLIDE_TIME) || (dist_normal_to_target < STRAFE_RETREAT_COLLIDE_DIST + speed_to_dist_penalty)) ) {
if ( !(aip->ai_flags[AI::AI_Flags::Kamikaze]) && ((aip->ai_flags[AI::AI_Flags::Target_collision]) || (collide_time) || (collide_distance)) ) {
if (aip->ai_flags[AI::AI_Flags::Target_collision]) {
// use standard collision resolution
aip->ai_flags.remove(AI::AI_Flags::Target_collision);
@@ -1486,7 +1507,7 @@ void ai_big_strafe_attack()
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))
} else if (ai_big_maybe_follow_subsys_path(false))
return;
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
@@ -1606,7 +1627,7 @@ void ai_big_strafe_glide_attack()
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))
} else if (ai_big_maybe_follow_subsys_path(false))
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.
+125 -62
View File
@@ -542,6 +542,19 @@ void garbage_collect_path_points()
}
void reset_ai_path_points()
{
memset(Path_points, 0, sizeof(Path_points)); // optional: zero out for safety
Ppfp = Path_points;
// Also clear any AI path state
for (int i = 0; i < MAX_SHIPS; ++i) {
Ai_info[i].path_start = -1;
Ai_info[i].path_cur = -1;
Ai_info[i].path_length = 0;
}
}
/**
* Hash two values together, return result.
* Hash function: curval shifted right circular by one, newval xored in.
@@ -2216,6 +2229,7 @@ typedef struct eval_nearest_objnum {
object *trial_objp;
int enemy_team_mask;
int enemy_ship_info_index;
int enemy_class_type;
int enemy_wing;
float range;
int max_attackers;
@@ -2249,6 +2263,10 @@ void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
if ((eno->enemy_ship_info_index >= 0) && (shipp->ship_info_index != eno->enemy_ship_info_index))
return;
// If only supposed to attack ships of a certain ship type, don't attack other ships.
if ((eno->enemy_class_type >= 0) && (Ship_info[shipp->ship_info_index].class_type != eno->enemy_class_type))
return;
// Don't keep firing at a ship that is in its death throes.
if (shipp->flags[Ship::Ship_Flags::Dying])
return;
@@ -2337,8 +2355,9 @@ void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
* @param range Ship must be within range "range".
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it.
* @param ship_info_index If >=0, the enemy object must be of the specified ship class
* @param class_type If >=0, the enemy object must be of the specified ship type
*/
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index)
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index, int class_type)
{
object *danger_weapon_objp;
ai_info *aip;
@@ -2348,6 +2367,7 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra
eval_nearest_objnum eno;
eno.enemy_team_mask = enemy_team_mask;
eno.enemy_ship_info_index = ship_info_index;
eno.enemy_class_type = class_type;
eno.enemy_wing = enemy_wing;
eno.max_attackers = max_attackers;
eno.objnum = objnum;
@@ -2393,7 +2413,7 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra
// If only looking for target in certain wing and couldn't find anything in
// that wing, look for any object.
if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers, ship_info_index);
return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers, ship_info_index, class_type);
}
return eno.nearest_objnum;
@@ -2497,13 +2517,15 @@ int get_enemy_timestamp()
/**
* Return objnum if enemy found, else return -1;
*
* @param objnum Object number
* @param range Range within which to look
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it.
* @param objnum Object number
* @param range Range within which to look
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it.
* @param ship_info_index If specified, restrict the search to enemies with this ship class
* @param class_type If specified, restrict the search to enemies with this ship type
*/
int find_enemy(int objnum, float range, int max_attackers, int ship_info_index)
int find_enemy(int objnum, float range, int max_attackers, int ship_info_index, int class_type)
{
int enemy_team_mask;
int enemy_team_mask;
if (objnum < 0)
return -1;
@@ -2511,19 +2533,25 @@ int find_enemy(int objnum, float range, int max_attackers, int ship_info_index)
enemy_team_mask = iff_get_attackee_mask(obj_team(&Objects[objnum]));
// if target_objnum != -1, use that as goal.
ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
ai_info* aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
if (aip->target_objnum != -1) {
int target_objnum = aip->target_objnum;
int target_objnum = aip->target_objnum;
// DKA don't undo object as target in nebula missions.
// This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range. (BAD)
if ( Objects[target_objnum].signature == aip->target_signature ) {
if (iff_matches_mask(Ships[Objects[target_objnum].instance].team, enemy_team_mask)) {
if (ship_info_index < 0 || ship_info_index == Ships[Objects[target_objnum].instance].ship_info_index) {
if (!(Objects[target_objnum].flags[Object::Object_Flags::Protected])) {
return target_objnum;
// This could cause attack on ship on fringe on nebula to stop if attackee moves out of nebula range. (BAD)
if (Objects[target_objnum].signature == aip->target_signature) {
ship* target_shipp = (Objects[target_objnum].type == OBJ_SHIP) ? &Ships[Objects[target_objnum].instance] : nullptr;
if (target_shipp && iff_matches_mask(target_shipp->team, enemy_team_mask)) {
if (ship_info_index < 0 || ship_info_index == target_shipp->ship_info_index) {
if (class_type < 0 || (target_shipp->ship_info_index >= 0 &&
class_type == Ship_info[target_shipp->ship_info_index].class_type)) {
if (!(Objects[target_objnum].flags[Object::Object_Flags::Protected])) {
return target_objnum;
}
}
}
}
@@ -2532,9 +2560,8 @@ int find_enemy(int objnum, float range, int max_attackers, int ship_info_index)
aip->target_signature = -1;
}
}
return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers, ship_info_index);
return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers, ship_info_index, class_type);
} else {
aip->target_objnum = -1;
aip->target_signature = -1;
@@ -2575,7 +2602,7 @@ void force_avoid_player_check(object *objp, ai_info *aip)
* If attacked == NULL, then attack any enemy object.
* Attack point *rel_pos on object. This is for supporting attacking subsystems.
*/
void ai_attack_object(object* attacker, object* attacked, int ship_info_index)
void ai_attack_object(object* attacker, object* attacked, int ship_info_index, int class_type)
{
int temp;
ai_info* aip;
@@ -2608,7 +2635,7 @@ void ai_attack_object(object* attacker, object* attacked, int ship_info_index)
if (attacked == nullptr) {
aip->choose_enemy_timestamp = timestamp(0);
// nebula safe
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4, ship_info_index));
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4, ship_info_index, class_type));
} else {
// check if we can see attacked in nebula
if (aip->target_objnum != OBJ_INDEX(attacked)) {
@@ -3650,6 +3677,14 @@ void ai_form_on_wing(object *objp, object *goal_objp)
aip->ai_flags.remove(AI::AI_Flags::Formation_wing);
aip->ai_flags.set(AI::AI_Flags::Formation_object);
// if this goal is going to stick around for a while, change the mode
if (The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_form_on_wing])
{
aip->mode = AIM_NONE;
aip->submode = -1;
aip->submode_start_time = Missiontime;
}
aip->goal_objnum = OBJ_INDEX(goal_objp);
ai_formation_object_recalculate_slotnums(aip->goal_objnum);
@@ -7665,7 +7700,7 @@ void mabs_pick_goal_point(object *objp, object *big_objp, vec3d *collision_point
/**
* Return true if a large ship is being ignored.
*/
int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d *goal_point, float delta_time, float time_scale = 1.f)
bool maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d *goal_point, float delta_time, float time_scale = 1.f)
{
if (timestamp_elapsed(aip->avoid_check_timestamp)) {
float distance;
@@ -7684,7 +7719,7 @@ int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d
aip->ai_flags.remove(AI::AI_Flags::Avoiding_small_ship);
aip->avoid_ship_num = -1;
next_check_time = (int) (1500 * time_scale);
aip->avoid_check_timestamp = timestamp(1500);
aip->avoid_check_timestamp = timestamp(next_check_time);
}
}
@@ -7700,10 +7735,30 @@ int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d
if (ai_willing_to_afterburn_hard(aip) && dot > 0.99f)
ai_afterburn_hard(Pl_objp, aip);
return 1;
return true;
}
return 0;
return false;
}
/**
* Return true if small ship and it will likely collide with large ship
* developed by Asteroth
*/
bool better_collision_avoidance_triggered(bool flag_to_check, float avoidance_aggression, object* pl_objp, object* ignore_objp)
{
ship* shipp = &Ships[pl_objp->instance];
ship_info* sip = &Ship_info[shipp->ship_info_index];
if ((flag_to_check) && sip->is_small_ship()) {
vec3d collide_vec = pl_objp->phys_info.vel * (avoidance_aggression / (PI2 / sip->srotation_time));
float radius_contribution = (pl_objp->phys_info.speed + pl_objp->radius) / pl_objp->phys_info.speed;
collide_vec *= radius_contribution;
collide_vec += pl_objp->pos;
return (maybe_avoid_big_ship(pl_objp, ignore_objp, &Ai_info[shipp->ai_index], &collide_vec, 0.f, 0.1f));
}
return false;
}
/**
@@ -8928,14 +8983,12 @@ void ai_chase()
return;
}
if ((The_mission.ai_profile->flags[AI::Profile_Flags::Better_combat_collision_avoidance]) && sip->is_small_ship()) {
vec3d collide_vec = Pl_objp->phys_info.vel * (The_mission.ai_profile->better_collision_avoid_aggression_combat / (PI2 / sip->srotation_time));
float radius_contribution = (Pl_objp->phys_info.speed + Pl_objp->radius) / Pl_objp->phys_info.speed;
collide_vec *= radius_contribution;
collide_vec += Pl_objp->pos;
if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &collide_vec, 0.f, 0.1f))
return;
if (better_collision_avoidance_triggered(
The_mission.ai_profile->flags[AI::Profile_Flags::Better_combat_collision_avoidance],
The_mission.ai_profile->better_collision_avoid_aggression_combat,
Pl_objp,
The_mission.ai_profile->flags[AI::Profile_Flags::Better_combat_collision_avoid_includes_target] ? nullptr : En_objp)) {
return;
}
if (enemy_sip_flags.any_set()) {
@@ -10849,16 +10902,12 @@ void ai_guard()
return;
}
ship_info* sip = &Ship_info[shipp->ship_info_index];
if ((The_mission.ai_profile->flags[AI::Profile_Flags::Better_guard_collision_avoidance]) && sip->is_small_ship()) {
vec3d collide_vec = Pl_objp->phys_info.vel * (The_mission.ai_profile->better_collision_avoid_aggression_guard / (PI2 / sip->srotation_time));
float radius_contribution = (Pl_objp->phys_info.speed + Pl_objp->radius) / Pl_objp->phys_info.speed;
collide_vec *= radius_contribution;
collide_vec += Pl_objp->pos;
if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &collide_vec, 0.f, 0.1f))
return;
if (better_collision_avoidance_triggered(
The_mission.ai_profile->flags[AI::Profile_Flags::Better_guard_collision_avoidance],
The_mission.ai_profile->better_collision_avoid_aggression_guard,
Pl_objp,
En_objp)) {
return;
}
// handler for guard object with BIG radius
@@ -11476,7 +11525,7 @@ void ai_stay_near()
else if (aip->submode_parm0 != 0) {
vec3d rand_vec, center_to_goal, center_to_objp;
auto goal_objp = &Objects[goal_objnum];
float max_dist = aip->submode_float0;
float max_dist = std::max(aip->submode_float0, 1.0f);
// Figure out a goal point to fly to
// Make not all ships pursue same point.
@@ -11507,7 +11556,7 @@ void ai_stay_near()
else {
vec3d rand_vec, goal_pos, vec_to_goal;
auto goal_objp = &Objects[goal_objnum];
float max_dist = aip->submode_float0;
float max_dist = std::max(aip->submode_float0, 1.0f);
// Make not all ships pursue same point.
// Calculate the seed from both shipnums so the same Pl_objp doesn't always choose the same spot
@@ -12100,9 +12149,11 @@ void ai_process_subobjects(int objnum)
bool in_lab = gameseq_get_state() == GS_STATE_LAB;
// non-player ships that are playing dead do not process subsystems or turrets
if ((!(objp->flags[Object::Object_Flags::Player_ship]) || Player_use_ai) && aip->mode == AIM_PLAY_DEAD)
return;
// non-player ships that are playing dead do not process subsystems or turrets unless we're in the lab
if (gameseq_get_state() != GS_STATE_LAB) {
if ((!(objp->flags[Object::Object_Flags::Player_ship]) || Player_use_ai) && aip->mode == AIM_PLAY_DEAD)
return;
}
polymodel_instance *pmi = model_get_instance(shipp->model_instance_num);
polymodel *pm = model_get(pmi->model_num);
@@ -13203,8 +13254,6 @@ static void ai_preprocess_ignore_objnum(ai_info *aip)
aip->target_objnum = -1;
}
#define CHASE_CIRCLE_DIST 100.0f
void ai_chase_circle(object *objp)
{
float dist_to_goal;
@@ -13225,11 +13274,11 @@ void ai_chase_circle(object *objp)
if (aip->ignore_objnum == UNUSED_OBJNUM) {
dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
if (dist_to_goal > 2 * The_mission.ai_profile->attack_any_idle_circle_distance) {
vec3d vec_to_goal;
// Too far from circle goal, create a new goal point.
vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, The_mission.ai_profile->attack_any_idle_circle_distance);
}
goal_point = aip->goal_point;
@@ -14007,20 +14056,28 @@ void ai_bay_depart()
return;
}
ship* shipp;
// check if parent ship valid; if not, abort depart
auto anchor_ship_entry = ship_registry_get(Parse_names[Ships[Pl_objp->instance].departure_anchor]);
if (!anchor_ship_entry || !ship_useful_for_departure(anchor_ship_entry->shipnum, Ships[Pl_objp->instance].departure_path_mask))
{
mprintf(("Aborting bay departure!\n"));
aip->mode = AIM_NONE;
Ships[Pl_objp->instance].flags.remove(Ship::Ship_Flags::Depart_dockbay);
return;
if (gameseq_get_state() != GS_STATE_LAB) {
auto anchor_ship_entry = ship_registry_get(Parse_names[Ships[Pl_objp->instance].departure_anchor]);
if (!anchor_ship_entry ||
!ship_useful_for_departure(anchor_ship_entry->shipnum, Ships[Pl_objp->instance].departure_path_mask)) {
mprintf(("Aborting bay departure!\n"));
aip->mode = AIM_NONE;
Ships[Pl_objp->instance].flags.remove(Ship::Ship_Flags::Depart_dockbay);
return;
}
shipp = anchor_ship_entry->shipp();
} else {
shipp = &Ships[Objects[aip->goal_objnum].instance];
}
ai_manage_bay_doors(Pl_objp, aip, false);
if ( anchor_ship_entry->shipp()->bay_doors_status != MA_POS_READY )
if ( shipp->bay_doors_status != MA_POS_READY )
return;
// follow the path to the final point
@@ -14140,7 +14197,13 @@ void ai_execute_behavior(ai_info *aip)
case AIM_STRAFE:
if (En_objp) {
Assert(En_objp->type == OBJ_SHIP);
ai_big_strafe(); // strafe a big ship
if (!(better_collision_avoidance_triggered(
The_mission.ai_profile->flags[AI::Profile_Flags::Better_combat_collision_avoidance],
The_mission.ai_profile->better_collision_avoid_aggression_combat,
Pl_objp,
The_mission.ai_profile->flags[AI::Profile_Flags::Better_combat_collision_avoid_includes_target] ? nullptr : En_objp))) {
ai_big_strafe(); // strafe a big ship
}
} else {
aip->mode = AIM_NONE;
}
@@ -14322,7 +14385,7 @@ int maybe_request_support(object *objp)
try_to_rearm = true;
} else if (ai_bad_time_to_rearm(objp)) {
try_to_rearm = false;
} else if (num_allies_rearming(objp) < 2) {
} else if (num_allies_rearming(objp) < The_mission.ai_profile->max_allies_rearming_threshold) {
if (desire >= 8) { // guarantees disabled will cause repair request
try_to_rearm = true;
} else if (desire >= 3) { // >= 3 means having a single subsystem fully blown will cause repair.
+266 -109
View File
@@ -40,28 +40,43 @@ struct ship_registry_entry;
// dynamic goals are not handled here.
// defines for player issued goal priorities
#define PLAYER_PRIORITY_MIN 90
#define PLAYER_PRIORITY_SHIP 100
#define PLAYER_PRIORITY_WING 95
#define PLAYER_PRIORITY_SUPPORT_LOW 10
constexpr int PLAYER_PRIORITY_MIN = 90;
constexpr int PLAYER_PRIORITY_SHIP = 100;
constexpr int PLAYER_PRIORITY_WING = 95;
constexpr int PLAYER_PRIORITY_SUPPORT_LOW = 10;
#define MAX_GOAL_PRIORITY 200
constexpr int MAX_GOAL_PRIORITY = 200;
constexpr int MIN_GOAL_PRIORITY = 1;
// note: "purging" is not the same as "clearing" here
// purging: any goal that no longer makes sense (i.e. is "invalid") in light of a new goal is removed
// clearing: every goal is removed, including, ironically, the goal that caused the clearing
// define for which goals cause other goals to get purged
// Goober5000 - okay, this seems really stupid. If any ship in the mission is assigned a goal
// in PURGE_GOALS_ALL_SHIPS, *every* other ship will have certain goals purged. So I added
// PURGE_GOALS_ONE_SHIP for goals which should only purge other goals in the one ship.
// Goober5000 - note that the new disable and disarm goals (AI_GOAL_DISABLE_SHIP_TACTICAL and
// AI_GOAL_DISARM_SHIP_TACTICAL) do not purge ANY goals, not even the ones in the one ship
inline bool purge_goals_all_ships(ai_goal_mode ai_mode)
[[nodiscard]] bool causes_invalid_goal_purge_all_ships(ai_goal_mode ai_mode)
{
return ai_mode == AI_GOAL_IGNORE || ai_mode == AI_GOAL_DISABLE_SHIP || ai_mode == AI_GOAL_DISARM_SHIP;
}
inline bool purge_goals_one_ship(ai_goal_mode ai_mode)
[[nodiscard]] bool causes_invalid_goal_purge_one_ship(ai_goal_mode ai_mode)
{
return ai_mode == AI_GOAL_IGNORE_NEW;
}
// function for which goals cause other goals to be cleared -- see comments above on purging vs clearing
// NOTE: this function is only evaluated in ai_add_goal_sub_player() and ai_add_goal_sub_scripting(); for
// ai_add_goal_sub_sexp(), the goal clearing is integrated into the logic of the relevant sexp operators
[[nodiscard]] bool causes_goal_clearing(ai_goal_mode ai_mode)
{
return ((ai_mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_stay_still])
|| (ai_mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_form_on_wing])
|| (ai_mode == AI_GOAL_PLAY_DEAD));
}
// goals given from the player to other ships in the game are also handled in this
// code
@@ -100,6 +115,7 @@ ai_goal_list Ai_goal_names[] =
{ "Attack weapon", AI_GOAL_CHASE_WEAPON, 0 },
{ "Fly to ship", AI_GOAL_FLY_TO_SHIP, 0 },
{ "Attack ship class", AI_GOAL_CHASE_SHIP_CLASS, 0 },
{ "Attack ship type", AI_GOAL_CHASE_SHIP_TYPE, 0 },
};
int Num_ai_goals = sizeof(Ai_goal_names) / sizeof(ai_goal_list);
@@ -114,6 +130,7 @@ const char *Ai_goal_text(ai_goal_mode goal, int submode)
case AI_GOAL_CHASE:
case AI_GOAL_CHASE_WING:
case AI_GOAL_CHASE_SHIP_CLASS:
case AI_GOAL_CHASE_SHIP_TYPE:
return XSTR( "attack ", 474);
case AI_GOAL_DOCK:
return XSTR( "dock ", 475);
@@ -195,12 +212,15 @@ void ai_maybe_add_form_goal(wing* wingp)
return;
}
int j;
// we may simply not want the mission to do this
if (The_mission.ai_profile->flags[AI::Profile_Flags::Dont_form_on_wing_at_mission_start]) {
return;
}
// iterate through the ship_index list of this wing and check for orders. We will do
// this for all ships in the wing instead of on a wing only basis in case some ships
// in the wing actually have different orders than others
for (j = 0; j < wingp->current_count; j++) {
for (int j = 0; j < wingp->current_count; j++) {
ai_info* aip;
Assert(wingp->ship_index[j] != -1); // get Allender
@@ -212,7 +232,8 @@ void ai_maybe_add_form_goal(wing* wingp)
}
// need to add a form on my wing goal here. Ships are always forming on the player's wing.
// it is sufficient enough to check the first goal entry to see if it has a valid goal
// it is sufficient enough to check the first goal entry to see if it has a valid goal.
// Note: Although the player didn't explicitly order this, it is treated as a player-issued order
if (aip->goals[0].ai_mode == AI_GOAL_NONE) {
// Need to have a more specific target in multi, or they may end up trying to target standalone placeholder.
// So form on their team leader. In dogfight, all player-slot ai die, so just exclude.
@@ -513,13 +534,25 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai
if ( purge_goal->target_name == NULL )
continue;
// goals operating on ship classes are handled slightly differently
// goals operating on ship classes and ship types are handled slightly differently
if ( purge_ai_mode == AI_GOAL_CHASE_SHIP_CLASS ) {
// if the target of the purge goal is the same class of ship we are concerned about, then we have a match;
// if it is not, then we can continue (see standard ship check below)
if ( stricmp(purge_goal->target_name, Ship_info[Ships[ship_index].ship_info_index].name) != 0 )
continue;
}
else if (purge_ai_mode == AI_GOAL_CHASE_SHIP_TYPE) {
// Get the ship type of the ship we're concerned about
int ship_type = Ship_info[Ships[ship_index].ship_info_index].class_type;
// If the ship type is invalid, we can't match it, so continue
if (ship_type < 0)
continue;
// Check if the target name of the purge goal matches the ship type name
if (stricmp(purge_goal->target_name, Ship_types[ship_type].name) != 0)
continue;
}
// standard goals operating on either wings or ships
else {
// determine if the purge goal is acting either on the ship or the ship's wing.
@@ -588,8 +621,8 @@ void ai_goal_purge_all_invalid_goals(ai_goal *aigp)
int i;
ship_obj *sop;
// only purge goals if a new goal is one of the types in next statement
if (!purge_goals_all_ships(aigp->ai_mode))
// only purge goals if a new goal is one of the types that require purging
if (!causes_invalid_goal_purge_all_ships(aigp->ai_mode))
return;
for (sop = GET_FIRST(&Ship_obj_list); sop != END_OF_LIST(&Ship_obj_list); sop = GET_NEXT(sop))
@@ -732,12 +765,14 @@ void ai_goal_fixup_dockpoints(ai_info *aip, ai_goal *aigp)
// from the mission goals (i.e. those goals which come from events) in that we don't
// use sexpressions for goals from the player...so we enumerate all the parameters
void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *target_name, ai_goal *aigp, const ai_lua_parameters& lua_target )
void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *target_name, ai_goal *aigp, int int_data, float float_data, const ai_lua_parameters& lua_target )
{
Assert ( (type == ai_goal_type::PLAYER_WING) || (type == ai_goal_type::PLAYER_SHIP) );
ai_goal_reset(aigp, true, mode, submode, type);
aigp->int_data = int_data;
aigp->float_data = float_data;
aigp->lua_ai_target = lua_target;
if ( mode == AI_GOAL_WARP ) {
@@ -755,6 +790,18 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
if ( target_name != NULL )
aigp->target_name = ai_get_goal_target_name( target_name, &aigp->target_name_index );
// set up the clear-goals flag for certain goals
if (causes_goal_clearing(mode))
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
// also set up the override, since it's no longer done automatically in ai_mission_goal_achievable
if (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing])
aigp->flags.set(AI::Goal_Flags::Want_override);
// generally, players want ships to form-on-wing only until they need to do something else
if (mode == AI_GOAL_FORM_ON_WING && The_mission.ai_profile->flags[AI::Profile_Flags::Purge_player_issued_form_on_wing_after_subsequent_order])
aigp->flags.set(AI::Goal_Flags::Purge_when_new_goal_added);
if (The_mission.ai_profile->flags[AI::Profile_Flags::Player_orders_afterburn_hard])
aigp->flags.set(AI::Goal_Flags::Afterburn_hard);
@@ -766,10 +813,6 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
if ( (mode == AI_GOAL_STAY_NEAR_SHIP) || (mode == AI_GOAL_KEEP_SAFE_DISTANCE) )
aigp->priority = PLAYER_PRIORITY_SUPPORT_LOW;
// Goober5000 - same with form-on-wing, since it's a type of staying near
else if ( mode == AI_GOAL_FORM_ON_WING )
aigp->priority = PLAYER_PRIORITY_SUPPORT_LOW;
else if ( aigp->type == ai_goal_type::PLAYER_WING ) // NOLINT(readability-braces-around-statements)
aigp->priority = PLAYER_PRIORITY_WING; // player wing goals not as high as ship goals
else
@@ -784,17 +827,22 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
// my new docking code. :)
int ai_goal_find_empty_slot( ai_goal *goals, int active_goal )
{
int gindex, oldest_index;
int oldest_index = -1, first_empty_index = -1;
oldest_index = -1;
for ( gindex = 0; gindex < MAX_AI_GOALS; gindex++ )
for ( int gindex = 0; gindex < MAX_AI_GOALS; gindex++ )
{
// get the index for the first unused goal
if (goals[gindex].ai_mode == AI_GOAL_NONE)
return gindex;
{
if (first_empty_index < 0)
first_empty_index = gindex;
}
// if any goal needs to be purged when we add a goal, set the flag
else if (goals[gindex].flags[AI::Goal_Flags::Purge_when_new_goal_added])
goals[gindex].flags.set(AI::Goal_Flags::Purge);
// if this is the active goal, don't consider it for pre-emption!!
if (gindex == active_goal)
// if this is the active goal, and a real goal, don't consider it for pre-emption!!
if (gindex == active_goal && goals[gindex].ai_mode != AI_GOAL_NONE)
continue;
// store the index of the oldest goal
@@ -804,6 +852,10 @@ int ai_goal_find_empty_slot( ai_goal *goals, int active_goal )
oldest_index = gindex;
}
// try to use the first empty slot
if (first_empty_index >= 0)
return first_empty_index;
// if we didn't find an empty slot, use the oldest goal's slot
return oldest_index;
}
@@ -836,9 +888,26 @@ void ai_add_goal_sub_scripting(ai_goal_type type, ai_goal_mode mode, int submode
if ( target_name != NULL )
aigp->target_name = ai_get_goal_target_name( target_name, &aigp->target_name_index );
// set up the clear-goals flag for certain goals
if (causes_goal_clearing(mode))
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
// also set up the override, since it's no longer done automatically in ai_mission_goal_achievable
if (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing])
aigp->flags.set(AI::Goal_Flags::Want_override);
aigp->priority = priority;
aigp->int_data = int_data;
aigp->float_data = float_data;
// range check
if ( aigp->priority > MAX_GOAL_PRIORITY ) {
nprintf (("AI", "bashing scripting priority of goal %d from %d to %d.\n", mode, aigp->priority, MAX_GOAL_PRIORITY));
aigp->priority = MAX_GOAL_PRIORITY;
} else if ( aigp->priority < MIN_GOAL_PRIORITY ) {
nprintf (("AI", "bashing scripting priority of goal %d from %d to %d.\n", mode, aigp->priority, MIN_GOAL_PRIORITY));
aigp->priority = MIN_GOAL_PRIORITY;
}
}
void ai_add_ship_goal_scripting(ai_goal_mode mode, int submode, int priority, const char *shipname, ai_info *aip, int int_data, float float_data)
@@ -865,14 +934,14 @@ void ai_add_ship_goal_scripting(ai_goal_mode mode, int submode, int priority, co
// is issued to ship or wing (from player), mode is AI_GOAL_*. submode is the submode the
// ship should go into. shipname is the object of the action. aip is the ai_info pointer
// of the ship receiving the order
void ai_add_ship_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *shipname, ai_info *aip, const ai_lua_parameters& lua_target)
void ai_add_ship_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *shipname, ai_info *aip, int int_data, float float_data, const ai_lua_parameters& lua_target)
{
int empty_index;
ai_goal *aigp;
empty_index = ai_goal_find_empty_slot( aip->goals, aip->active_goal );
aigp = &aip->goals[empty_index];
ai_add_goal_sub_player( type, mode, submode, shipname, aigp, lua_target );
ai_add_goal_sub_player( type, mode, submode, shipname, aigp, int_data, float_data, lua_target );
// if the goal is to dock, then we must determine which dock points on the two ships to use.
// If the target of the dock is a cargo type container, then we should use DOCK_TYPE_CARGO
@@ -886,7 +955,7 @@ void ai_add_ship_goal_player(ai_goal_type type, ai_goal_mode mode, int submode,
// adds a goal from the player to the given wing (which in turn will add it to the proper
// ships in the wing
void ai_add_wing_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *shipname, int wingnum, const ai_lua_parameters& lua_target)
void ai_add_wing_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *shipname, int wingnum, int int_data, float float_data, const ai_lua_parameters& lua_target)
{
int i, empty_index;
wing *wingp = &Wings[wingnum];
@@ -897,7 +966,7 @@ void ai_add_wing_goal_player(ai_goal_type type, ai_goal_mode mode, int submode,
int num = wingp->ship_index[i];
if ( num == -1 ) // ship must have been destroyed or departed
continue;
ai_add_ship_goal_player( type, mode, submode, shipname, &Ai_info[Ships[num].ai_index], lua_target );
ai_add_ship_goal_player( type, mode, submode, shipname, &Ai_info[Ships[num].ai_index], int_data, float_data, lua_target );
}
}
@@ -905,7 +974,7 @@ void ai_add_wing_goal_player(ai_goal_type type, ai_goal_mode mode, int submode,
// there are more waves to come. We use the same method here as when adding a goal to
// a ship -- find the first empty entry. If none exists, take the oldest entry and replace it.
empty_index = ai_goal_find_empty_slot( wingp->ai_goals, -1 );
ai_add_goal_sub_player( type, mode, submode, shipname, &wingp->ai_goals[empty_index], lua_target );
ai_add_goal_sub_player( type, mode, submode, shipname, &wingp->ai_goals[empty_index], int_data, float_data, lua_target );
}
@@ -938,11 +1007,9 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
// for achievability.
aigp->target_name = ai_get_goal_target_name(CTEXT(CDR(node)), &aigp->target_name_index); // waypoint path name;
aigp->priority = eval_num(CDDR(node), priority_is_nan, priority_is_nan_forever);
aigp->ai_mode = AI_GOAL_WAYPOINTS;
if ( op == OP_AI_WAYPOINTS_ONCE )
aigp->ai_mode = AI_GOAL_WAYPOINTS_ONCE;
aigp->ai_mode = (op == OP_AI_WAYPOINTS_ONCE) ? AI_GOAL_WAYPOINTS_ONCE : AI_GOAL_WAYPOINTS;
if (CDDDDR(node) < 0)
aigp->int_data = 0; // handle optional node separately because we don't subtract 1 here
else
@@ -1021,10 +1088,27 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
}
case OP_AI_STAY_STILL:
{
int n = CDR(node);
aigp->target_name = ai_get_goal_target_name(CTEXT(n), &aigp->target_name_index); // waypoint path name;
n = CDR(n);
aigp->priority = eval_num(n, priority_is_nan, priority_is_nan_forever);
n = CDR(n);
bool clear_goals = !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_stay_still];
if (n >= 0)
{
clear_goals = is_sexp_true(n);
n = CDR(n);
}
if (clear_goals)
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
aigp->ai_mode = AI_GOAL_STAY_STILL;
aigp->target_name = ai_get_goal_target_name(CTEXT(CDR(node)), &aigp->target_name_index); // waypoint path name;
aigp->priority = eval_num(CDDR(node), priority_is_nan, priority_is_nan_forever);
break;
}
case OP_AI_DOCK:
aigp->target_name = ai_get_goal_target_name( CTEXT(CDR(node)), &aigp->target_name_index );
@@ -1042,13 +1126,12 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
break;
case OP_AI_PLAY_DEAD:
aigp->priority = eval_num(CDR(node), priority_is_nan, priority_is_nan_forever);
aigp->ai_mode = AI_GOAL_PLAY_DEAD;
break;
// we don't clear out ship goals for the "persistent" goal variant
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
FALLTHROUGH;
case OP_AI_PLAY_DEAD_PERSISTENT:
aigp->priority = eval_num(CDR(node), priority_is_nan, priority_is_nan_forever);
aigp->ai_mode = AI_GOAL_PLAY_DEAD_PERSISTENT;
aigp->ai_mode = (op == OP_AI_PLAY_DEAD) ? AI_GOAL_PLAY_DEAD : AI_GOAL_PLAY_DEAD_PERSISTENT;
break;
case OP_AI_KEEP_SAFE_DISTANCE:
@@ -1084,14 +1167,53 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
}
case OP_AI_FORM_ON_WING:
aigp->priority = 99;
aigp->target_name = ai_get_goal_target_name(CTEXT(CDR(node)), &aigp->target_name_index);
{
int n = CDR(node);
aigp->target_name = ai_get_goal_target_name(CTEXT(n), &aigp->target_name_index);
n = CDR(n);
if (n >= 0)
{
aigp->priority = eval_num(n, priority_is_nan, priority_is_nan_forever);
n = CDR(n);
}
else
aigp->priority = The_mission.ai_profile->default_form_on_wing_priority;
bool clear_goals = !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_form_on_wing];
if (n >= 0)
{
clear_goals = is_sexp_true(n);
n = CDR(n);
}
if (clear_goals)
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
bool set_override = !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing];
if (n >= 0)
{
set_override = is_sexp_true(n);
n = CDR(n);
}
if (set_override)
aigp->flags.set(AI::Goal_Flags::Want_override);
if (n >= 0)
{
if (is_sexp_true(n))
aigp->flags.set(AI::Goal_Flags::Purge_when_new_goal_added);
n = CDR(n);
}
aigp->ai_mode = AI_GOAL_FORM_ON_WING;
break;
}
case OP_AI_CHASE:
case OP_AI_CHASE_WING:
case OP_AI_CHASE_SHIP_CLASS:
case OP_AI_CHASE_SHIP_TYPE:
case OP_AI_GUARD:
case OP_AI_GUARD_WING:
case OP_AI_EVADE_SHIP:
@@ -1123,6 +1245,8 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
aigp->ai_mode = AI_GOAL_CHASE_WING;
} else if (op == OP_AI_CHASE_SHIP_CLASS) {
aigp->ai_mode = AI_GOAL_CHASE_SHIP_CLASS;
} else if (op == OP_AI_CHASE_SHIP_TYPE) {
aigp->ai_mode = AI_GOAL_CHASE_SHIP_TYPE;
} else if ( op == OP_AI_IGNORE ) {
aigp->ai_mode = AI_GOAL_IGNORE;
} else if ( op == OP_AI_IGNORE_NEW ) {
@@ -1164,10 +1288,13 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
} else if ( aigp->priority > MAX_GOAL_PRIORITY ) {
nprintf (("AI", "bashing add-goal sexpression priority of goal %s from %d to %d.\n", Sexp_nodes[CAR(sexp)].text, aigp->priority, MAX_GOAL_PRIORITY));
aigp->priority = MAX_GOAL_PRIORITY;
} else if ( aigp->priority < MIN_GOAL_PRIORITY ) {
nprintf (("AI", "bashing add-goal sexpression priority of goal %s from %d to %d.\n", Sexp_nodes[CAR(sexp)].text, aigp->priority, MIN_GOAL_PRIORITY));
aigp->priority = MIN_GOAL_PRIORITY;
}
// Goober5000 - we now have an extra optional chase argument to allow chasing our own team
if ( op == OP_AI_CHASE || op == OP_AI_CHASE_WING || op == OP_AI_CHASE_SHIP_CLASS
if (op == OP_AI_CHASE || op == OP_AI_CHASE_WING || op == OP_AI_CHASE_SHIP_CLASS || op == OP_AI_CHASE_SHIP_TYPE
|| op == OP_AI_DISABLE_SHIP || op == OP_AI_DISABLE_SHIP_TACTICAL || op == OP_AI_DISARM_SHIP || op == OP_AI_DISARM_SHIP_TACTICAL ) {
if (is_sexp_true(CDDDR(node)))
aigp->flags.set(AI::Goal_Flags::Target_own_team);
@@ -1192,6 +1319,7 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
if (op == OP_AI_CHASE ||
op == OP_AI_CHASE_WING ||
op == OP_AI_CHASE_SHIP_CLASS ||
op == OP_AI_CHASE_SHIP_TYPE ||
op == OP_AI_DISABLE_SHIP ||
op == OP_AI_DISABLE_SHIP_TACTICAL ||
op == OP_AI_DISARM_SHIP ||
@@ -1275,6 +1403,11 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
nprintf(("AI", "bashing remove-goal sexpression priority of goal %s from %d to %d.\n", Sexp_nodes[CAR(sexp)].text, _priority, MAX_GOAL_PRIORITY));
_priority = MAX_GOAL_PRIORITY;
}
else if (_priority < MIN_GOAL_PRIORITY)
{
nprintf(("AI", "bashing remove-goal sexpression priority of goal %s from %d to %d.\n", Sexp_nodes[CAR(sexp)].text, _priority, MIN_GOAL_PRIORITY));
_priority = MIN_GOAL_PRIORITY;
}
if (n >= 0)
{
@@ -1295,12 +1428,9 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
/* We now need to determine what the mode and submode values are*/
switch( op )
{
case OP_AI_WAYPOINTS_ONCE:
goalmode = AI_GOAL_WAYPOINTS_ONCE;
priority = eval_priority_et_seq(CDDR(node));
break;
case OP_AI_WAYPOINTS:
goalmode = AI_GOAL_WAYPOINTS;
case OP_AI_WAYPOINTS_ONCE:
goalmode = (op == OP_AI_WAYPOINTS_ONCE) ? AI_GOAL_WAYPOINTS_ONCE : AI_GOAL_WAYPOINTS;
priority = eval_priority_et_seq(CDDR(node));
break;
case OP_AI_DESTROY_SUBSYS:
@@ -1378,6 +1508,10 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
priority = eval_priority_et_seq(CDDR(node));
goalmode = AI_GOAL_CHASE_SHIP_CLASS;
break;
case OP_AI_CHASE_SHIP_TYPE:
priority = eval_priority_et_seq(CDDR(node));
goalmode = AI_GOAL_CHASE_SHIP_TYPE;
break;
case OP_AI_EVADE_SHIP:
priority = eval_priority_et_seq(CDDR(node));
goalmode = AI_GOAL_EVADE_SHIP;
@@ -1392,7 +1526,7 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
goalmode = (op == OP_AI_IGNORE) ? AI_GOAL_IGNORE : AI_GOAL_IGNORE_NEW;
break;
case OP_AI_FORM_ON_WING:
priority = eval_priority_et_seq(-1, 99);
priority = eval_priority_et_seq(CDDR(node), The_mission.ai_profile->default_form_on_wing_priority);
goalmode = AI_GOAL_FORM_ON_WING;
break;
case OP_AI_FLY_TO_SHIP:
@@ -1655,7 +1789,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
if (!target_ship_entry || !target_ship_entry->has_shipp())
return ai_achievability::NOT_ACHIEVABLE;
aigp->flags.set(AI::Goal_Flags::Goal_override);
// the override flag is now set in the calling function, ai_mission_goal_achievable
return ai_achievability::ACHIEVABLE;
}
@@ -1687,7 +1821,20 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
}
return ai_achievability::NOT_KNOWN;
}
// and similarly for chasing all ships of a certain ship type
if (aigp->ai_mode == AI_GOAL_CHASE_SHIP_TYPE) {
for (auto so : list_range(&Ship_obj_list)) {
auto type_objp = &Objects[so->objnum];
if (type_objp->type != OBJ_SHIP || type_objp->flags[Object::Object_Flags::Should_be_dead])
continue;
int ship_info_idx = Ships[type_objp->instance].ship_info_index;
int class_type = Ship_info[ship_info_idx].class_type;
if (class_type >= 0 && !strcmp(aigp->target_name, Ship_types[class_type].name)) {
return ai_achievability::ACHIEVABLE;
}
}
return ai_achievability::NOT_KNOWN;
}
return_val = ai_achievability::SATISFIED;
@@ -1917,15 +2064,15 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// Goober5000 - see note at PURGE_GOALS_ALL_SHIPS... this is bizarre
if ((status == SHIP_STATUS_ARRIVED) && !(aigp->flags[AI::Goal_Flags::Goals_purged]))
{
if (purge_goals_all_ships(aigp->ai_mode)) {
if (causes_invalid_goal_purge_all_ships(aigp->ai_mode)) {
ai_goal_purge_all_invalid_goals(aigp);
aigp->flags.set(AI::Goal_Flags::Goals_purged);
}
else if (purge_goals_one_ship(aigp->ai_mode)) {
else if (causes_invalid_goal_purge_one_ship(aigp->ai_mode)) {
ai_goal_purge_invalid_goals(aigp, aip->goals, aip, -1);
aigp->flags.set(AI::Goal_Flags::Goals_purged);
}
}
}
// if we are docking, validate the docking indices on both ships. We might have to change names to indices.
// only enter this calculation if the ship we are docking with has arrived. If the ship is gone, then
@@ -2225,6 +2372,10 @@ void validate_mission_goals(int objnum, ai_info *aip)
continue;
}
// if the status is achievable, and we want to set the override, set it
if ( (state == ai_achievability::ACHIEVABLE) && (aigp->flags[AI::Goal_Flags::Want_override]))
aigp->flags.set(AI::Goal_Flags::Goal_override);
// if the status is achievable, and the on_hold flag is set, clear the flagb
if ( (state == ai_achievability::ACHIEVABLE) && (aigp->flags[AI::Goal_Flags::Goal_on_hold]) )
aigp->flags.remove(AI::Goal_Flags::Goal_on_hold);
@@ -2274,7 +2425,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
object *objp = &Objects[objnum];
object *other_obj;
ai_goal *current_goal;
int wingnum, shipnum;
int wingnum;
int original_signature;
/* if (!stricmp(Ships[objp->instance].ship_name, "gtt comet")) {
@@ -2349,14 +2500,34 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
}
std::unique_ptr<ai_goal> current_goal_backup;
auto current_goal_target_ship = current_goal->target_name ? ship_registry_get(current_goal->target_name) : nullptr;
if (current_goal->flags[AI::Goal_Flags::Clear_all_goals_first]) {
// save the current goal before we wipe it out by clearing everything
current_goal_backup.reset(new ai_goal(*current_goal));
current_goal = current_goal_backup.get();
// stay-still, form-on-wing, and play-dead all clear their goals here...
//
// clear out the object's goals. Seems to me that if a ship is staying still for a purpose
// then we need to clear everything out since there is not a real way to get rid of this goal
//
// for form on wing, we need to clear out all goals for this ship, and then call the form on wing AI code
//
// if a ship is playing dead, MWA says that it shouldn't try to do anything else.
//
// clearing out goals is okay here since we are now what mode to set this AI object to.
ai_clear_ship_goals(aip);
}
switch ( current_goal->ai_mode ) {
case AI_GOAL_CHASE:
if ( current_goal->target_name ) {
shipnum = ship_name_lookup( current_goal->target_name );
Assert (shipnum != -1 ); // shouldn't get here if this is false!!!!
other_obj = &Objects[Ships[shipnum].objnum];
if (current_goal->target_name) {
Assert(current_goal_target_ship && current_goal_target_ship->has_objp()); // shouldn't get here if this is false!!!!
other_obj = current_goal_target_ship->objp();
} else
other_obj = NULL; // we get this case when we tell ship to engage enemy!
@@ -2373,9 +2544,8 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
break;
case AI_GOAL_GUARD:
shipnum = ship_name_lookup( current_goal->target_name );
Assert (shipnum != -1 ); // shouldn't get here if this is false!!!!
other_obj = &Objects[Ships[shipnum].objnum];
Assert(current_goal_target_ship && current_goal_target_ship->has_objp()); // shouldn't get here if this is false!!!!
other_obj = current_goal_target_ship->objp();
// shipnum and other_obj are the shipnumber and object pointer of the object that you should
// guard.
if (objp != other_obj) {
@@ -2406,9 +2576,8 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
}
case AI_GOAL_DOCK: {
shipnum = ship_name_lookup( current_goal->target_name );
Assert (shipnum != -1 ); // shouldn't get here if this is false!!!!
other_obj = &Objects[Ships[shipnum].objnum];
Assert(current_goal_target_ship && current_goal_target_ship->has_objp()); // shouldn't get here if this is false!!!!
other_obj = current_goal_target_ship->objp();
// be sure that we have indices for docking points here! If we ever had names, they should
// get fixed up in goal_achievable so that the points can be checked there for validity
@@ -2418,24 +2587,22 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
}
case AI_GOAL_UNDOCK:
// try to find the object which which this object is docked with. Use that object as the
// try to find the object with which this object is docked. Use that object as the
// "other object" for the undocking proceedure. If "other object" isn't found, then the undock
// goal cannot continue. Spit out a warning and remove the goal.
// Goober5000 - do we have a specific ship to undock from?
if ( current_goal->target_name != NULL )
if (current_goal->target_name)
{
shipnum = ship_name_lookup( current_goal->target_name );
// hmm, perhaps he was destroyed
if (shipnum == -1)
if (!current_goal_target_ship || !current_goal_target_ship->has_objp())
{
other_obj = NULL;
}
// he exists... let's undock from him
else
{
other_obj = &Objects[Ships[shipnum].objnum];
other_obj = current_goal_target_ship->objp();
}
}
// no specific ship
@@ -2488,16 +2655,15 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
case AI_GOAL_DISABLE_SHIP_TACTICAL:
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISARM_SHIP_TACTICAL: {
shipnum = ship_name_lookup( current_goal->target_name );
Assert( shipnum >= 0 );
other_obj = &Objects[Ships[shipnum].objnum];
Assert(current_goal_target_ship && current_goal_target_ship->has_objp());
other_obj = current_goal_target_ship->objp();
ai_attack_object( objp, other_obj);
ai_set_attack_subsystem( objp, current_goal->ai_submode ); // submode stored the subsystem type
// don't protect-ship for tactical goals
if (current_goal->ai_mode != AI_GOAL_DESTROY_SUBSYSTEM && current_goal->ai_mode != AI_GOAL_DISABLE_SHIP_TACTICAL && current_goal->ai_mode != AI_GOAL_DISARM_SHIP_TACTICAL) {
if (aip->target_objnum != -1) {
int class_type = Ship_info[Ships[shipnum].ship_info_index].class_type;
int class_type = Ship_info[current_goal_target_ship->shipp()->ship_info_index].class_type;
// Only protect if _not_ a capital ship. We don't want the Lucifer accidentally getting protected.
if (class_type >= 0 && Ship_types[class_type].flags[Ship::Type_Info_Flags::AI_protected_on_cripple])
Objects[aip->target_objnum].flags.set(Object::Object_Flags::Protected);
@@ -2521,10 +2687,20 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
// 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, nullptr, shipnum );
{
int ship_info_index = ship_info_lookup(current_goal->target_name);
Assertion(ship_info_index >= 0, "The target of AI_GOAL_CHASE_SHIP_CLASS must refer to a valid ship class!");
ai_attack_object(objp, nullptr, ship_info_index);
break;
}
// similarly for chase-ship-type
case AI_GOAL_CHASE_SHIP_TYPE:
{
int class_type = ship_type_name_lookup(current_goal->target_name);
Assertion(class_type >= 0, "The target of AI_GOAL_CHASE_SHIP_TYPE must refer to a valid ship type!");
ai_attack_object(objp, nullptr, -1, class_type);
break;
}
case AI_GOAL_WARP: {
mission_do_departure( objp, true );
@@ -2532,46 +2708,29 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
}
case AI_GOAL_EVADE_SHIP:
shipnum = ship_name_lookup( current_goal->target_name );
Assert( shipnum >= 0 );
other_obj = &Objects[Ships[shipnum].objnum];
Assert(current_goal_target_ship && current_goal_target_ship->has_objp()); // shouldn't get here if this is false!!!!
other_obj = current_goal_target_ship->objp();
ai_evade_object( objp, other_obj);
break;
case AI_GOAL_STAY_STILL:
// NOTE: goal clearing moved above this enclosing switch block
// for now, ignore any other parameters!!!!
// clear out the object's goals. Seems to me that if a ship is staying still for a purpose
// then we need to clear everything out since there is not a real way to get rid of this goal
// clearing out goals is okay here since we are now what mode to set this AI object to.
ai_clear_ship_goals( aip );
ai_stay_still( objp, NULL );
break;
case AI_GOAL_PLAY_DEAD:
// if a ship is playing dead, MWA says that it shouldn't try to do anything else.
// clearing out goals is okay here since we are now what mode to set this AI object to.
ai_clear_ship_goals( aip );
aip->mode = AIM_PLAY_DEAD;
aip->submode = -1;
aip->submode_start_time = Missiontime;
break;
case AI_GOAL_PLAY_DEAD_PERSISTENT:
// same as above, but we don't clear out ship goals
// NOTE: goal clearing moved above this enclosing switch block
aip->mode = AIM_PLAY_DEAD;
aip->submode = -1;
aip->submode_start_time = Missiontime;
break;
case AI_GOAL_FORM_ON_WING:
// get the ship first, since we're going to wipe it out next
shipnum = ship_name_lookup( current_goal->target_name );
Assert( shipnum >= 0 );
other_obj = &Objects[Ships[shipnum].objnum];
// for form on wing, we need to clear out all goals for this ship, and then call the form
// on wing AI code
// clearing out goals is okay here since we are now what mode to set this AI object to.
ai_clear_ship_goals( aip );
// NOTE: goal clearing moved above this enclosing switch block
Assert(current_goal_target_ship && current_goal_target_ship->has_objp()); // shouldn't get here if this is false!!!!
other_obj = current_goal_target_ship->objp();
ai_form_on_wing( objp, other_obj );
break;
@@ -2580,9 +2739,8 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
case AI_GOAL_STAY_NEAR_SHIP:
case AI_GOAL_FLY_TO_SHIP:
{
shipnum = ship_name_lookup( current_goal->target_name );
Assert( shipnum >= 0 );
other_obj = &Objects[Ships[shipnum].objnum];
Assert(current_goal_target_ship && current_goal_target_ship->has_objp()); // shouldn't get here if this is false!!!!
other_obj = current_goal_target_ship->objp();
float dist = current_goal->float_data; // How far away to stay from ship. Should be set in SEXP?
int additional_data = current_goal->int_data; // Whether to target a particular point as if escorting
ai_do_stay_near(objp, other_obj, dist, additional_data);
@@ -2597,9 +2755,8 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
break;
case AI_GOAL_REARM_REPAIR:
shipnum = ship_name_lookup( current_goal->target_name );
Assert( shipnum >= 0 );
other_obj = &Objects[Ships[shipnum].objnum];
Assert(current_goal_target_ship && current_goal_target_ship->has_objp()); // shouldn't get here if this is false!!!!
other_obj = current_goal_target_ship->objp();
ai_rearm_repair( objp, current_goal->docker.index, other_obj, current_goal->dockee.index );
break;
+9 -6
View File
@@ -47,9 +47,9 @@ enum class ai_goal_type
enum ai_goal_mode : uint8_t
{
AI_GOAL_NONE = 0,
AI_GOAL_PLACEHOLDER_1,
AI_GOAL_SCHROEDINGER, // used for FRED when multiple ships are selected with different orders
AI_GOAL_CHASE, // per the original #define list, AI_GOAL_CHASE started at 2 (1<<1)
AI_GOAL_CHASE,
AI_GOAL_DOCK, // used for undocking as well
AI_GOAL_WAYPOINTS,
AI_GOAL_WAYPOINTS_ONCE,
@@ -80,7 +80,8 @@ enum ai_goal_mode : uint8_t
AI_GOAL_FLY_TO_SHIP,
AI_GOAL_IGNORE_NEW,
AI_GOAL_CHASE_SHIP_CLASS,
AI_GOAL_PLAY_DEAD_PERSISTENT,
AI_GOAL_CHASE_SHIP_TYPE,
AI_GOAL_PLAY_DEAD_PERSISTENT, // Disables subsystem rotation/translation among other things but there is a carveout for that in the lab in ship_move_subsystems() and ai_process_subobjects()
AI_GOAL_LUA,
AI_GOAL_DISARM_SHIP_TACTICAL,
AI_GOAL_DISABLE_SHIP_TACTICAL,
@@ -103,7 +104,7 @@ inline bool ai_goal_is_disable_or_disarm(ai_goal_mode ai_mode)
}
inline bool ai_goal_is_specific_chase(ai_goal_mode ai_mode)
{
return ai_mode == AI_GOAL_CHASE || ai_mode == AI_GOAL_CHASE_WING || ai_mode == AI_GOAL_CHASE_SHIP_CLASS;
return ai_mode == AI_GOAL_CHASE || ai_mode == AI_GOAL_CHASE_WING || ai_mode == AI_GOAL_CHASE_SHIP_CLASS || ai_mode == AI_GOAL_CHASE_SHIP_TYPE;
}
enum class ai_achievability { ACHIEVABLE, NOT_ACHIEVABLE, NOT_KNOWN, SATISFIED };
@@ -152,6 +153,8 @@ typedef struct ai_goal {
extern void ai_goal_reset(ai_goal *aigp, bool adding_goal = false, ai_goal_mode ai_mode = AI_GOAL_NONE, int ai_submode = -1, ai_goal_type type = ai_goal_type::INVALID);
// Reset all path points. Used in the ship lab. Missions clean up path points with the garbage collector in garbage_collect_path_points()
extern void reset_ai_path_points();
typedef flag_def_list_templated<ai_goal_mode> ai_goal_list;
@@ -185,8 +188,8 @@ extern int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
extern void ai_remove_wing_goal_sexp( int sexp, wing *wingp );
// adds goals to ships/sings through player orders
extern void ai_add_ship_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char* shipname, ai_info* aip, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} });
extern void ai_add_wing_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char* shipname, int wingnum, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} });
extern void ai_add_ship_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char* shipname, ai_info* aip, int int_data = 0, float float_data = 0.0f, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} });
extern void ai_add_wing_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char* shipname, int wingnum, int int_data = 0, float float_data = 0.0f, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} });
extern void ai_remove_ship_goal( ai_info *aip, int index );
extern void ai_clear_ship_goals( ai_info *aip );
+9 -11
View File
@@ -1089,9 +1089,11 @@ int find_turret_enemy(const ship_subsys *turret_subsys, int objnum, const vec3d
int target_objnum = aip->target_objnum;
if (Objects[target_objnum].signature == aip->target_signature) {
if (iff_matches_mask(Ships[Objects[target_objnum].instance].team, enemy_team_mask)) {
if ( !(Objects[target_objnum].flags[Object::Object_Flags::Protected]) ) { // check this flag as well
// nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
ship* target_shipp = &Ships[Objects[target_objnum].instance];
if (target_shipp && iff_matches_mask(target_shipp->team, enemy_team_mask)) {
if (!(Objects[target_objnum].flags[Object::Object_Flags::Protected])) { // check this flag as well
// nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum,
// target_objnum));
return target_objnum;
}
}
@@ -1559,7 +1561,9 @@ void turret_set_next_fire_timestamp(int weapon_num, const weapon_info *wip, ship
float burst_shots_mult = wip->weapon_launch_curves.get_output(weapon_info::WeaponLaunchCurveOutputs::BURST_SHOTS_MULT, launch_curve_data);
int burst_shots = MAX(fl2i(i2fl(base_burst_shots) * burst_shots_mult) - 1, 0);
if (burst_shots > turret->weapons.burst_counter[weapon_num]) {
bool burst = burst_shots > turret->weapons.burst_counter[weapon_num];
if (burst) {
wait *= wip->burst_delay;
wait *= wip->weapon_launch_curves.get_output(weapon_info::WeaponLaunchCurveOutputs::BURST_DELAY_MULT, launch_curve_data);
turret->weapons.burst_counter[weapon_num]++;
@@ -1659,7 +1663,7 @@ void turret_set_next_fire_timestamp(int weapon_num, const weapon_info *wip, ship
wait *= frand_range(0.9f, 1.1f);
}
if(turret->rof_scaler != 1.0f)
if(turret->rof_scaler != 1.0f && !(burst && turret->system_info->flags[Model::Subsystem_Flags::Burst_ignores_RoF_Mult]))
wait /= get_adjusted_turret_rof(turret);
(*fs_dest) = timestamp((int)wait);
@@ -2043,9 +2047,6 @@ bool turret_fire_weapon(int weapon_num,
particleSource->setTriggerVelocity(vm_vec_mag_quick(&objp->phys_info.vel));
particleSource->finishCreation();
}
else if (wip->muzzle_flash >= 0) {
mflash_create(firing_pos, firing_vec, &Objects[parent_ship->objnum].phys_info, wip->muzzle_flash);
}
// in multiplayer (and the master), then send a turret fired packet.
if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
@@ -2166,9 +2167,6 @@ void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
particleSource->setTriggerRadius(Objects[weapon_objnum].radius);
particleSource->finishCreation();
}
else if (Weapon_info[tsi->weapon_class].muzzle_flash >= 0) {
mflash_create(&turret_pos, &turret_fvec, &Objects[tsi->parent_objnum].phys_info, Weapon_info[tsi->weapon_class].muzzle_flash);
}
// maybe sound
if ( Weapon_info[tsi->weapon_class].launch_snd.isValid() ) {
+119 -62
View File
@@ -32,8 +32,9 @@
#include "object/objcollide.h"
#include "object/object.h"
#include "parse/parselo.h"
#include "particle/ParticleEffect.h"
#include "scripting/global_hooks.h"
#include "particle/particle.h"
#include "particle/hosts/EffectHostVector.h"
#include "render/3d.h"
#include "ship/ship.h"
#include "ship/shipfx.h"
@@ -63,8 +64,7 @@ asteroid Asteroids[MAX_ASTEROIDS];
asteroid_field Asteroid_field;
static int Asteroid_impact_explosion_ani;
static float Asteroid_impact_explosion_radius;
static particle::ParticleEffectHandle Asteroid_impact_explosion_ani;
char Asteroid_icon_closeup_model[NAME_LENGTH];
vec3d Asteroid_icon_closeup_position;
float Asteroid_icon_closeup_zoom;
@@ -824,18 +824,7 @@ void asteroid_create_debris_field(int num_asteroids, int asteroid_speed, SCP_vec
*/
void asteroid_level_init()
{
Asteroid_field.num_initial_asteroids = 0; // disable asteroid field by default.
Asteroid_field.speed = 0.0f;
vm_vec_make(&Asteroid_field.min_bound, -1000.0f, -1000.0f, -1000.0f);
vm_vec_make(&Asteroid_field.max_bound, 1000.0f, 1000.0f, 1000.0f);
vm_vec_make(&Asteroid_field.inner_min_bound, -500.0f, -500.0f, -500.0f);
vm_vec_make(&Asteroid_field.inner_max_bound, 500.0f, 500.0f, 500.0f);
Asteroid_field.has_inner_bound = false;
Asteroid_field.field_type = FT_ACTIVE;
Asteroid_field.debris_genre = DG_ASTEROID;
Asteroid_field.field_debris_type.clear();
Asteroid_field.field_asteroid_type.clear();
Asteroid_field.target_names.clear();
Asteroid_field = {};
if (!Fred_running)
{
@@ -1705,37 +1694,57 @@ static void asteroid_do_area_effect(object *asteroid_objp)
*/
void asteroid_hit( object * pasteroid_obj, object * other_obj, vec3d * hitpos, float damage, vec3d* force )
{
float explosion_life;
asteroid *asp;
asp = &Asteroids[pasteroid_obj->instance];
asteroid *asp = &Asteroids[pasteroid_obj->instance];
asteroid_info *asip = &Asteroid_info[Asteroids[pasteroid_obj->instance].asteroid_type];
if (pasteroid_obj->flags[Object::Object_Flags::Should_be_dead]){
return;
}
if ( MULTIPLAYER_MASTER ){
send_asteroid_hit( pasteroid_obj, other_obj, hitpos, damage, force );
}
if (hitpos && force && The_mission.ai_profile->flags[AI::Profile_Flags::Whackable_asteroids]) {
vec3d rel_hit_pos = *hitpos - pasteroid_obj->pos;
physics_calculate_and_apply_whack(force, &rel_hit_pos, &pasteroid_obj->phys_info, &pasteroid_obj->orient, &pasteroid_obj->phys_info.I_body_inv);
pasteroid_obj->phys_info.desired_vel = pasteroid_obj->phys_info.vel;
}
pasteroid_obj->hull_strength -= damage;
if (pasteroid_obj->hull_strength < 0.0f) {
if ( !asp->final_death_time.isValid() ) {
int play_loud_collision = 0;
float explosion_life = 1.f;
int breakup_timestamp;
Assertion(!asip->end_particles.isValid() || asip->breakup_delay.has_value(), "Asteroid %s has end particles but no breakup delay. Parsing should not have allowed this!", asip->name);
explosion_life = asteroid_create_explosion(pasteroid_obj);
if (asip->end_particles.isValid()) {
auto source = particle::ParticleManager::get()->createSource(asip->end_particles);
// Use the position since the asteroid is going to be invalid soon
auto host = std::make_unique<EffectHostVector>(pasteroid_obj->pos, pasteroid_obj->orient, pasteroid_obj->phys_info.vel);
host->setRadius(pasteroid_obj->radius);
source->setHost(std::move(host));
source->setNormal(pasteroid_obj->orient.vec.uvec);
source->finishCreation();
} else {
explosion_life = asteroid_create_explosion(pasteroid_obj);
}
if (asip->breakup_delay.has_value()) {
breakup_timestamp = fl2i(*asip->breakup_delay * MILLISECONDS_PER_SECOND);
} else {
breakup_timestamp = fl2i((explosion_life * MILLISECONDS_PER_SECOND) / 5.f);
}
asteroid_explode_sound(pasteroid_obj, asp->asteroid_type, play_loud_collision);
asteroid_do_area_effect(pasteroid_obj);
asp->final_death_time = _timestamp( fl2i(explosion_life*MILLISECONDS_PER_SECOND)/5 ); // Wait till 30% of vclip time before breaking the asteroid up.
asp->final_death_time = _timestamp( breakup_timestamp ); // Wait till 20% of vclip time before breaking the asteroid up, or use the specified delay
if ( hitpos ) {
asp->death_hit_pos = *hitpos;
} else {
@@ -1751,8 +1760,10 @@ void asteroid_hit( object * pasteroid_obj, object * other_obj, vec3d * hitpos, f
weapon_info *wip;
wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index];
// If the weapon didn't play any impact animation, play custom asteroid impact animation
if (!wip->impact_weapon_expl_effect.isValid()) {
particle::create( hitpos, &vmd_zero_vector, 0.0f, Asteroid_impact_explosion_radius, Asteroid_impact_explosion_ani );
if (!wip->impact_weapon_expl_effect.isValid() && Asteroid_impact_explosion_ani.isValid()) {
auto source = particle::ParticleManager::get()->createSource(Asteroid_impact_explosion_ani);
source->setHost(std::make_unique<EffectHostVector>(*hitpos, vmd_identity_matrix, vmd_zero_vector));
source->finishCreation();
}
}
}
@@ -2313,7 +2324,7 @@ static void asteroid_parse_section()
}
if (optional_string("$Detail distance:")) {
asteroid_p->num_detail_levels = (int)stuff_int_list(asteroid_p->detail_distance, MAX_ASTEROID_DETAIL_LEVELS, RAW_INTEGER_TYPE);
asteroid_p->num_detail_levels = sz2i(stuff_int_list(asteroid_p->detail_distance, MAX_ASTEROID_DETAIL_LEVELS, ParseLookupType::RAW_INTEGER_TYPE));
}
if (optional_string("$Max Speed:")) {
@@ -2330,13 +2341,25 @@ static void asteroid_parse_section()
asteroid_p->damage_type_idx_sav = damage_type_add(buf);
asteroid_p->damage_type_idx = asteroid_p->damage_type_idx_sav;
}
if(optional_string("$Explosion Animations:")){
stuff_fireball_index_list(asteroid_p->explosion_bitmap_anims, asteroid_p->name);
if (optional_string("$Explosion Effect:")) {
asteroid_p->end_particles = particle::util::parseEffect(asteroid_p->name);
} else {
if(optional_string("$Explosion Animations:")){
stuff_fireball_index_list(asteroid_p->explosion_bitmap_anims, asteroid_p->name);
}
if (optional_string("$Explosion Radius Mult:")) {
stuff_float(&asteroid_p->fireball_radius_multiplier);
}
}
if (optional_string("$Explosion Radius Mult:")) {
stuff_float(&asteroid_p->fireball_radius_multiplier);
if (optional_string("$Breakup Delay:")) {
stuff_float(&asteroid_p->breakup_delay.emplace());
}
if (asteroid_p->end_particles.isValid() && !asteroid_p->breakup_delay.has_value()) {
error_display(0, "Asteroid %s has an explosion effect but no breakup delay!", asteroid_p->name);
}
if (optional_string("$Expl inner rad:")){
@@ -2461,18 +2484,52 @@ static void asteroid_parse_tbl(const char* filename)
required_string("#End");
if (optional_string("$Impact Explosion:")) {
char impact_ani_file[MAX_FILENAME_LEN];
stuff_string(impact_ani_file, F_NAME, MAX_FILENAME_LEN);
if (VALID_FNAME(impact_ani_file)) {
int num_frames;
Asteroid_impact_explosion_ani = bm_load_animation(impact_ani_file, &num_frames, nullptr, nullptr, nullptr, true);
}
if (optional_string("$Impact Explosion Effect:")) {
Asteroid_impact_explosion_ani = particle::util::parseEffect();
}
else {
char impact_ani_file[MAX_FILENAME_LEN] = {0};
float Asteroid_impact_explosion_radius = 0.0f;
int num_frames;
if (optional_string("$Impact Explosion Radius:")) {
stuff_float(&Asteroid_impact_explosion_radius);
if (optional_string("$Impact Explosion:")) {
stuff_string(impact_ani_file, F_NAME, MAX_FILENAME_LEN);
}
if (optional_string("$Impact Explosion Radius:")) {
stuff_float(&Asteroid_impact_explosion_radius);
}
if(VALID_FNAME(impact_ani_file)) {
Asteroid_impact_explosion_ani = particle::ParticleManager::get()->addEffect(particle::ParticleEffect(
"", //Name
::util::UniformFloatRange(1.f), //Particle num
particle::ParticleEffect::Duration::ONETIME, //Single Particle Emission
::util::UniformFloatRange(), //No duration
::util::UniformFloatRange(-1.f), //Single particle only
particle::ParticleEffect::ShapeDirection::ALIGNED, //Particle direction
::util::UniformFloatRange(), //Velocity Inherit
false, //Velocity Inherit absolute?
nullptr, //Velocity volume
::util::UniformFloatRange(), //Velocity volume multiplier
particle::ParticleEffect::VelocityScaling::NONE, //Velocity directional scaling
std::nullopt, //Orientation-based velocity
std::nullopt, //Position-based velocity
nullptr, //Position volume
particle::ParticleEffectHandle::invalid(), //Trail
1.f, //Chance
false, //Affected by detail
-1.f, //Culling range multiplier
false, //Disregard Animation Length. Must be true for everything using particle::Anim_bitmap_X
false, //Don't reverse animation
false, //parent local
false, //ignore velocity inherit if parented
false, //position velocity inherit absolute?
std::nullopt, //Local velocity offset
std::nullopt, //Local offset
::util::UniformFloatRange(-1.f), //Lifetime
::util::UniformFloatRange(Asteroid_impact_explosion_radius), //Radius
bm_load_animation(impact_ani_file, &num_frames, nullptr, nullptr, nullptr, true))); //Bitmap
}
}
if (optional_string("$Briefing Icon Closeup Model:")) {
@@ -2507,24 +2564,21 @@ int get_asteroid_index(const char* asteroid_name)
return -1;
}
// For FRED. Gets a list of unique asteroid subtype names
SCP_vector<SCP_string> get_list_valid_asteroid_subtypes()
// Returns the list of unique asteroid subtype names.
// List is cached after the first call since Asteroid_info cannot change during an engine instance.
const SCP_vector<SCP_string>& get_list_valid_asteroid_subtypes()
{
SCP_vector<SCP_string> list;
static SCP_vector<SCP_string> list;
for (const auto& this_asteroid : Asteroid_info) {
if (this_asteroid.type != ASTEROID_TYPE_DEBRIS) {
for (const auto& subtype : this_asteroid.subtypes) {
bool exists = false;
for (const auto& entry : list) {
if (subtype.type_name == entry) {
exists = true;
if (list.empty()) {
for (const auto& this_asteroid : Asteroid_info) {
if (this_asteroid.type != ASTEROID_TYPE_DEBRIS) {
for (const auto& subtype : this_asteroid.subtypes) {
// Only add unique names
if (std::find(list.begin(), list.end(), subtype.type_name) == list.end()) {
list.push_back(subtype.type_name);
}
}
if (!exists) {
list.push_back(subtype.type_name);
}
}
}
}
@@ -2532,6 +2586,7 @@ SCP_vector<SCP_string> get_list_valid_asteroid_subtypes()
return list;
}
static void verify_asteroid_splits()
{
@@ -2622,8 +2677,7 @@ static void verify_asteroid_list()
*/
void asteroid_init()
{
Asteroid_impact_explosion_ani = -1;
Asteroid_impact_explosion_radius = 20.0; // retail value
Asteroid_impact_explosion_ani = particle::ParticleEffectHandle::invalid();
Asteroid_icon_closeup_model[0] = '\0';
vm_vec_make(&Asteroid_icon_closeup_position, 0.0f, 0.0f, -334.0f); // magic numbers from retail
Asteroid_icon_closeup_zoom = 0.5f; // magic number from retail
@@ -2644,8 +2698,11 @@ void asteroid_init()
// now that Asteroid_info is filled in we can verify the asteroid splits and set their indecies
verify_asteroid_splits();
if (Asteroid_impact_explosion_ani == -1) {
Error(LOCATION, "Missing valid asteroid impact explosion definition in asteroid.tbl!");
if (!Asteroid_impact_explosion_ani.isValid()) {
// this will always be missing on a standalone
if ( !Is_standalone ) {
Error(LOCATION, "Missing valid asteroid impact explosion definition in asteroid.tbl!");
}
}
if (Asteroid_icon_closeup_model[0] == '\0')
+27 -5
View File
@@ -16,6 +16,8 @@
#include "globalincs/pstypes.h"
#include "object/object_flags.h"
#include "io/timer.h"
#include "particle/ParticleSource.h"
#include "math/vecmat.h"
class object;
class polymodel;
@@ -79,7 +81,9 @@ public:
float initial_asteroid_strength; // starting strength of asteroid
SCP_vector< asteroid_split_info > split_info;
SCP_vector<int> explosion_bitmap_anims;
float fireball_radius_multiplier; // the model radius is multiplied by this to determine the fireball size
float fireball_radius_multiplier; // the model radius is multiplied by this to determine the fireball size
particle::ParticleEffectHandle end_particles;
std::optional<float> breakup_delay;
SCP_string display_name; // only used for hud targeting display and for debris
float spawn_weight; // debris only, relative proportion to spawn compared to other types in its asteroid field
float gravity_const; // multiplier for mission gravity
@@ -90,7 +94,7 @@ public:
rotational_vel_multiplier(1), damage_type_idx(0),
damage_type_idx_sav( -1 ), inner_rad( 0 ), outer_rad( 0 ),
damage( 0 ), blast( 0 ), initial_asteroid_strength( 0 ),
fireball_radius_multiplier( -1 ), spawn_weight( 1 ), gravity_const( 0 )
fireball_radius_multiplier( -1 ), end_particles( particle::ParticleEffectHandle::invalid() ), breakup_delay( std::nullopt ), spawn_weight( 1 ), gravity_const( 0 )
{
name[ 0 ] = 0;
display_name = "";
@@ -130,7 +134,7 @@ typedef enum {
FT_PASSIVE
} field_type_t;
typedef struct asteroid_field {
struct asteroid_field {
vec3d min_bound; // Minimum range of field.
vec3d max_bound; // Maximum range of field.
float bound_rad;
@@ -147,7 +151,24 @@ typedef struct asteroid_field {
bool enhanced_visibility_checks; // if true then range checks are overridden for spawning and wrapping asteroids in the field
SCP_vector<SCP_string> target_names; // default retail behavior is to just throw at the first big ship in the field
} asteroid_field;
asteroid_field()
{
num_initial_asteroids = 0; // disable the field by default
speed = 0.0f;
vm_vec_make(&min_bound, -1000.0f, -1000.0f, -1000.0f);
vm_vec_make(&max_bound, 1000.0f, 1000.0f, 1000.0f);
vm_vec_make(&inner_min_bound, -500.0f, -500.0f, -500.0f);
vm_vec_make(&inner_max_bound, 500.0f, 500.0f, 500.0f);
has_inner_bound = false;
field_type = FT_ACTIVE;
debris_genre = DG_ASTEROID;
enhanced_visibility_checks = false;
bound_rad = 0.0f;
vel = ZERO_VECTOR;
// the vectors default-construct to empty
}
};
extern SCP_vector< asteroid_info > Asteroid_info;
extern asteroid Asteroids[MAX_ASTEROIDS];
@@ -179,7 +200,8 @@ void asteroid_show_brackets();
void asteroid_target_closest_danger();
void asteroid_add_target(object* objp);
int get_asteroid_index(const char* asteroid_name);
SCP_vector<SCP_string> get_list_valid_asteroid_subtypes();
const SCP_vector<SCP_string>& get_list_valid_asteroid_subtypes();
int get_asteroid_subtype_index_by_name(const SCP_string& name, int asteroid_idx);
// extern for the lab
void asteroid_load(int asteroid_info_index, int asteroid_subtype);
+3
View File
@@ -1294,6 +1294,9 @@ float cam_get_bbox_dist(const object* viewer_obj, float preferred_distance, cons
return preferred_distance;
polymodel* pm = object_get_model(viewer_obj);
if (pm == nullptr)
return preferred_distance;
vec3d adjusted_bbox;
const vec3d* cam_vec = &cam_orient->vec.fvec;
+1 -1
View File
@@ -902,7 +902,7 @@ static CFILE *cf_open_fill_cfblock(const char* source, int line, const char* ori
cfp->source_file = source;
cfp->line_num = line;
int pos = ftell(fp);
auto pos = ftell(fp);
if(pos == -1L)
pos = 0;
cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)), 0 );
+1 -1
View File
@@ -143,7 +143,7 @@ void lz41_load_offsets(CFILE* cf)
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);
fso_fseek(cf, static_cast<int>( ( 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);
+6
View File
@@ -534,6 +534,7 @@ cmdline_parm luadev_arg("-luadev", "Make lua errors non-fatal", AT_NONE); // Cmd
cmdline_parm override_arg("-override_data", "Enable override directory", AT_NONE); // Cmdline_override_data
cmdline_parm imgui_debug_arg("-imgui_debug", nullptr, AT_NONE);
cmdline_parm vulkan("-vulkan", nullptr, AT_NONE);
cmdline_parm multithreading("-threads", nullptr, AT_INT);
char *Cmdline_start_mission = NULL;
int Cmdline_dis_collisions = 0;
@@ -572,6 +573,7 @@ bool Cmdline_lua_devmode = false;
bool Cmdline_override_data = false;
bool Cmdline_show_imgui_debug = false;
bool Cmdline_vulkan = false;
int Cmdline_multithreading = 1;
// Other
cmdline_parm get_flags_arg(GET_FLAGS_STRING, "Output the launcher flags file", AT_STRING);
@@ -2407,6 +2409,10 @@ bool SetCmdlineParams()
}
}
if (multithreading.found()) {
Cmdline_multithreading = abs(multithreading.get_int());
}
return true;
}
+1
View File
@@ -159,6 +159,7 @@ extern bool Cmdline_lua_devmode;
extern bool Cmdline_override_data;
extern bool Cmdline_show_imgui_debug;
extern bool Cmdline_vulkan;
extern int Cmdline_multithreading;
enum class WeaponSpewType { NONE = 0, STANDARD, ALL };
extern WeaponSpewType Cmdline_spew_weapon_stats;
+28 -5
View File
@@ -1298,7 +1298,8 @@ bool control_config_accept(bool API_Access)
cstr = popup_input(flags,
XSTR( "Confirm new custom preset name.\n\nThe name must not be empty or a default preset.\n\n Press [Enter] to accept, [Esc] to abort to config menu.", 1867),
32 - 6,
default_string.c_str());
default_string.c_str(),
"-_");
if (cstr == nullptr) {
// Abort
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
@@ -2084,7 +2085,6 @@ int control_config_bind_key_on_frame(int ctrl, selItem item, bool API_Access)
if (!done && bind) {
if (!Axis_override.empty()) {
control_config_bind(ctrl, Axis_override, item, API_Access);
control_config_bind(ctrl, Axis_override, item, API_Access);
done = true;
strcpy_s(bound_string, Axis_override.textify().c_str());
@@ -2802,8 +2802,22 @@ void control_check_indicate()
Control_check_count = 0;
}
/**
* @brief Checks if a control is being used by the player this frame by checking the bound control input bindings.
*
* @param[in] id The IoActionId of the control to check
* @param[in] key DO NOT USE. Key combination acquired from game_poll(), or -1 to check against a previous poll
*
* @returns 0 If the control was disabled by a mod, or
* @returns 0 If locked by a script, or
* @returns 0 If controls need to be ignored for multiplayer, or
* @returns 0 If the control wasn't used, or
* @returns 1 If the control was used
*/
int check_control_used(int id, int key)
{
// Make sure mouse_down() is only called once during any logic path,
// or the mousewheel may be decayed multiple times!!
int mask;
static int last_key = 0;
auto & item = Control_config[id];
@@ -2827,8 +2841,8 @@ int check_control_used(int id, int key)
if (item.type == CC_TYPE_CONTINUOUS) {
// this is awful, need to make a reverse lookup table to do button -> control instead of this control -> button
// nonsense.
// this is awful, need to make a reverse lookup table to do
// button -> control instead of this control -> button nonsense.
if ((joy_down(item.first) || joy_down_count(item.first, 1)) ||
(joy_down(item.second) || joy_down_count(item.second, 1))) {
// Joy button bound to this control was pressed, control activated
@@ -2838,7 +2852,7 @@ int check_control_used(int id, int key)
if ((mouse_down(item.first) || mouse_down_count(item.first, 1)) ||
(mouse_down(item.second) || mouse_down_count(item.second, 1))) {
// Joy button bound to this control was pressed, control activated
// Mouse button bound to this control was pressed, control activated
control_used(id);
return 1;
}
@@ -2884,6 +2898,15 @@ int check_control_used(int id, int key)
return 1;
}
// special case to allow actual mouse wheel to work with trigger controls --wookieejedi
if (item.type == CC_TYPE_TRIGGER) {
if ( mouse_down(item.first, true) || mouse_down(item.second, true) ) {
// Mouse wheel bound to this trigger control was pressed, control activated
control_used(id);
return 1;
}
}
return 0;
}
+12 -1
View File
@@ -297,6 +297,13 @@ enum IoActionId : int {
JOY_ABS_THROTTLE_AXIS = 127,
JOY_REL_THROTTLE_AXIS = 128,
//!< @n
//! Communication menu controls
//! ----------------------------
COMMS_MENU_MOVE_UP,
COMMS_MENU_MOVE_DOWN,
COMMS_MENU_SELECT,
TOGGLE_HUD_SHADOWS,
CYCLE_PRIMARY_WEAPON_PATTERN,
@@ -937,8 +944,12 @@ 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.
*
* @param[in] id The IoActionId of the control to check
* @param[in] key The key combo to check against the control. If -1, re-check the last key passed to this function
* @param[in] key DO NOT USE OUTSIDE OF THE MAIN LOOP. Key combination acquired from game_poll(), or -1 to check against a previous poll
*
* @returns 0 If the control was disabled by a mod, or
* @returns 0 If locked by a script, or
* @returns 0 If controls need to be ignored for multiplayer, or
* @returns 0 If a script is handling the control execution, or
* @returns 0 If the control wasn't used, or
* @returns 1 If the control was used
*
@@ -272,6 +272,13 @@ void control_config_common_init_bindings() {
(CUSTOM_CONTROL_3, KEY_ALTED | KEY_SHIFTED | KEY_3, -1, COMPUTER_TAB, 1786, "Custom Control 3", CC_TYPE_TRIGGER, true)
(CUSTOM_CONTROL_4, KEY_ALTED | KEY_SHIFTED | KEY_4, -1, COMPUTER_TAB, 1787, "Custom Control 4", CC_TYPE_TRIGGER, true)
(CUSTOM_CONTROL_5, KEY_ALTED | KEY_SHIFTED | KEY_5, -1, COMPUTER_TAB, 1788, "Custom Control 5", CC_TYPE_TRIGGER, true)
//Comms Controls
(COMMS_MENU_MOVE_UP, -1, -1, COMPUTER_TAB, 1883, "Communication Menu Move Up", CC_TYPE_TRIGGER)
(COMMS_MENU_MOVE_DOWN, -1, -1, COMPUTER_TAB, 1884, "Communication Menu Move Down", CC_TYPE_TRIGGER)
(COMMS_MENU_SELECT, -1, -1, COMPUTER_TAB, 1885, "Communication Menu Select", CC_TYPE_TRIGGER)
.end(); // Builder
// init default preset
@@ -438,6 +445,9 @@ SCP_unordered_map<SCP_string, IoActionId> old_text = {
{"Custom Control 3", CUSTOM_CONTROL_3},
{"Custom Control 4", CUSTOM_CONTROL_4},
{"Custom Control 5", CUSTOM_CONTROL_5},
{"Communication Menu Move Up", COMMS_MENU_MOVE_UP},
{"Communication Menu Move Down", COMMS_MENU_MOVE_DOWN},
{"Communication Menu Select", COMMS_MENU_SELECT},
};
// Localization strings for hat positions. Back[0], Forward[1], Left[2], Right[3]
@@ -1164,6 +1174,10 @@ void LoadEnumsIntoActionMap() {
ADD_ENUM_TO_ACTION_MAP(JOY_ABS_THROTTLE_AXIS)
ADD_ENUM_TO_ACTION_MAP(JOY_REL_THROTTLE_AXIS)
ADD_ENUM_TO_ACTION_MAP(COMMS_MENU_MOVE_UP)
ADD_ENUM_TO_ACTION_MAP(COMMS_MENU_MOVE_DOWN)
ADD_ENUM_TO_ACTION_MAP(COMMS_MENU_SELECT)
#undef ADD_ENUM_TO_ACTION_MAP
+6 -3
View File
@@ -12,9 +12,10 @@ DecoderStatus::~DecoderStatus() {
videoCodec = nullptr;
if (videoCodecCtx != nullptr) {
avcodec_close(videoCodecCtx);
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
avcodec_free_context(&videoCodecCtx);
#else
avcodec_close(videoCodecCtx);
#endif
videoCodecCtx = nullptr;
}
@@ -24,9 +25,10 @@ DecoderStatus::~DecoderStatus() {
audioCodec = nullptr;
if (audioCodecCtx != nullptr) {
avcodec_close(audioCodecCtx);
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
avcodec_free_context(&audioCodecCtx);
#else
avcodec_close(audioCodecCtx);
#endif
audioCodecCtx = nullptr;
}
@@ -36,9 +38,10 @@ DecoderStatus::~DecoderStatus() {
subtitleCodec = nullptr;
if (subtitleCodecCtx != nullptr) {
avcodec_close(subtitleCodecCtx);
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
avcodec_free_context(&subtitleCodecCtx);
#else
avcodec_close(subtitleCodecCtx);
#endif
subtitleCodecCtx = nullptr;
}
+191 -31
View File
@@ -1,4 +1,4 @@
/* bcdec.h - v0.96
/* bcdec.h - v0.97
provides functions to decompress blocks of BC compressed images
written by Sergii "iOrange" Kudlai in 2022
@@ -23,6 +23,10 @@
For more info, issues and suggestions please visit https://github.com/iOrange/bcdec
Configuration:
#define BCDEC_BC4BC5_PRECISE:
enables more precise but slower BC4/BC5 decoding + signed/unsigned mode
CREDITS:
Aras Pranckevicius (@aras-p) - BC1/BC3 decoders optimizations (up to 3x the speed)
- BC6H/BC7 bits pulling routines optimizations
@@ -30,6 +34,11 @@
- Split BC6H decompression function into 'half' and
'float' variants
Michael Schmidt (@RunDevelopment) - Found better "magic" coefficients for integer interpolation
of reference colors in BC1 color block, that match with
the floating point interpolation. This also made it faster
than integer division by 3!
bugfixes:
@linkmauve
@@ -39,6 +48,9 @@
#ifndef BCDEC_HEADER_INCLUDED
#define BCDEC_HEADER_INCLUDED
#define BCDEC_VERSION_MAJOR 0
#define BCDEC_VERSION_MINOR 98
/* if BCDEC_STATIC causes problems, try defining BCDECDEF to 'inline' or 'static inline' */
#ifndef BCDECDEF
#ifdef BCDEC_STATIC
@@ -90,12 +102,20 @@
BCDECDEF void bcdec_bc1(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
BCDECDEF void bcdec_bc2(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
BCDECDEF void bcdec_bc3(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
#ifndef BCDEC_BC4BC5_PRECISE
BCDECDEF void bcdec_bc4(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
BCDECDEF void bcdec_bc5(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
#else
BCDECDEF void bcdec_bc4(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned);
BCDECDEF void bcdec_bc5(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned);
BCDECDEF void bcdec_bc4_float(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned);
BCDECDEF void bcdec_bc5_float(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned);
#endif
BCDECDEF void bcdec_bc6h_float(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned);
BCDECDEF void bcdec_bc6h_half(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned);
BCDECDEF void bcdec_bc7(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
#endif /* BCDEC_HEADER_INCLUDED */
#ifdef BCDEC_IMPLEMENTATION
@@ -110,35 +130,44 @@ static void bcdec__color_block(const void* compressedBlock, void* decompressedBl
c0 = ((unsigned short*)compressedBlock)[0];
c1 = ((unsigned short*)compressedBlock)[1];
/* Expand 565 ref colors to 888 */
r0 = (((c0 >> 11) & 0x1F) * 527 + 23) >> 6;
g0 = (((c0 >> 5) & 0x3F) * 259 + 33) >> 6;
b0 = ((c0 & 0x1F) * 527 + 23) >> 6;
refColors[0] = 0xFF000000 | (b0 << 16) | (g0 << 8) | r0;
/* Unpack 565 ref colors */
r0 = (c0 >> 11) & 0x1F;
g0 = (c0 >> 5) & 0x3F;
b0 = c0 & 0x1F;
r1 = (((c1 >> 11) & 0x1F) * 527 + 23) >> 6;
g1 = (((c1 >> 5) & 0x3F) * 259 + 33) >> 6;
b1 = ((c1 & 0x1F) * 527 + 23) >> 6;
refColors[1] = 0xFF000000 | (b1 << 16) | (g1 << 8) | r1;
r1 = (c1 >> 11) & 0x1F;
g1 = (c1 >> 5) & 0x3F;
b1 = c1 & 0x1F;
/* Expand 565 ref colors to 888 */
r = (r0 * 527 + 23) >> 6;
g = (g0 * 259 + 33) >> 6;
b = (b0 * 527 + 23) >> 6;
refColors[0] = 0xFF000000 | (b << 16) | (g << 8) | r;
r = (r1 * 527 + 23) >> 6;
g = (g1 * 259 + 33) >> 6;
b = (b1 * 527 + 23) >> 6;
refColors[1] = 0xFF000000 | (b << 16) | (g << 8) | r;
if (c0 > c1 || onlyOpaqueMode) { /* Standard BC1 mode (also BC3 color block uses ONLY this mode) */
/* color_2 = 2/3*color_0 + 1/3*color_1
color_3 = 1/3*color_0 + 2/3*color_1 */
r = (2 * r0 + r1 + 1) / 3;
g = (2 * g0 + g1 + 1) / 3;
b = (2 * b0 + b1 + 1) / 3;
r = ((2 * r0 + r1) * 351 + 61) >> 7;
g = ((2 * g0 + g1) * 2763 + 1039) >> 11;
b = ((2 * b0 + b1) * 351 + 61) >> 7;
refColors[2] = 0xFF000000 | (b << 16) | (g << 8) | r;
r = (r0 + 2 * r1 + 1) / 3;
g = (g0 + 2 * g1 + 1) / 3;
b = (b0 + 2 * b1 + 1) / 3;
r = ((r0 + r1 * 2) * 351 + 61) >> 7;
g = ((g0 + g1 * 2) * 2763 + 1039) >> 11;
b = ((b0 + b1 * 2) * 351 + 61) >> 7;
refColors[3] = 0xFF000000 | (b << 16) | (g << 8) | r;
} else { /* Quite rare BC1A mode */
/* color_2 = 1/2*color_0 + 1/2*color_1;
color_3 = 0; */
r = (r0 + r1 + 1) >> 1;
g = (g0 + g1 + 1) >> 1;
b = (b0 + b1 + 1) >> 1;
r = ((r0 + r1) * 1053 + 125) >> 8;
g = ((g0 + g1) * 4145 + 1019) >> 11;
b = ((b0 + b1) * 1053 + 125) >> 8;
refColors[2] = 0xFF000000 | (b << 16) | (g << 8) | r;
refColors[3] = 0x00000000;
@@ -190,19 +219,19 @@ static void bcdec__smooth_alpha_block(const void* compressedBlock, void* decompr
if (alpha[0] > alpha[1]) {
/* 6 interpolated alpha values. */
alpha[2] = (6 * alpha[0] + alpha[1] + 1) / 7; /* 6/7*alpha_0 + 1/7*alpha_1 */
alpha[3] = (5 * alpha[0] + 2 * alpha[1] + 1) / 7; /* 5/7*alpha_0 + 2/7*alpha_1 */
alpha[4] = (4 * alpha[0] + 3 * alpha[1] + 1) / 7; /* 4/7*alpha_0 + 3/7*alpha_1 */
alpha[5] = (3 * alpha[0] + 4 * alpha[1] + 1) / 7; /* 3/7*alpha_0 + 4/7*alpha_1 */
alpha[6] = (2 * alpha[0] + 5 * alpha[1] + 1) / 7; /* 2/7*alpha_0 + 5/7*alpha_1 */
alpha[7] = ( alpha[0] + 6 * alpha[1] + 1) / 7; /* 1/7*alpha_0 + 6/7*alpha_1 */
alpha[2] = (6 * alpha[0] + alpha[1]) / 7; /* 6/7*alpha_0 + 1/7*alpha_1 */
alpha[3] = (5 * alpha[0] + 2 * alpha[1]) / 7; /* 5/7*alpha_0 + 2/7*alpha_1 */
alpha[4] = (4 * alpha[0] + 3 * alpha[1]) / 7; /* 4/7*alpha_0 + 3/7*alpha_1 */
alpha[5] = (3 * alpha[0] + 4 * alpha[1]) / 7; /* 3/7*alpha_0 + 4/7*alpha_1 */
alpha[6] = (2 * alpha[0] + 5 * alpha[1]) / 7; /* 2/7*alpha_0 + 5/7*alpha_1 */
alpha[7] = ( alpha[0] + 6 * alpha[1]) / 7; /* 1/7*alpha_0 + 6/7*alpha_1 */
}
else {
/* 4 interpolated alpha values. */
alpha[2] = (4 * alpha[0] + alpha[1] + 1) / 5; /* 4/5*alpha_0 + 1/5*alpha_1 */
alpha[3] = (3 * alpha[0] + 2 * alpha[1] + 1) / 5; /* 3/5*alpha_0 + 2/5*alpha_1 */
alpha[4] = (2 * alpha[0] + 3 * alpha[1] + 1) / 5; /* 2/5*alpha_0 + 3/5*alpha_1 */
alpha[5] = ( alpha[0] + 4 * alpha[1] + 1) / 5; /* 1/5*alpha_0 + 4/5*alpha_1 */
alpha[2] = (4 * alpha[0] + alpha[1]) / 5; /* 4/5*alpha_0 + 1/5*alpha_1 */
alpha[3] = (3 * alpha[0] + 2 * alpha[1]) / 5; /* 3/5*alpha_0 + 2/5*alpha_1 */
alpha[4] = (2 * alpha[0] + 3 * alpha[1]) / 5; /* 2/5*alpha_0 + 3/5*alpha_1 */
alpha[5] = ( alpha[0] + 4 * alpha[1]) / 5; /* 1/5*alpha_0 + 4/5*alpha_1 */
alpha[6] = 0x00;
alpha[7] = 0xFF;
}
@@ -218,6 +247,117 @@ static void bcdec__smooth_alpha_block(const void* compressedBlock, void* decompr
}
}
#ifdef BCDEC_BC4BC5_PRECISE
static void bcdec__bc4_block(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int pixelSize, int isSigned) {
signed char* sblock;
unsigned char* ublock;
int alpha[8];
int i, j;
unsigned long long block, indices;
static int aWeights4[4] = { 13107, 26215, 39321, 52429 };
static int aWeights6[6] = { 9363, 18724, 28086, 37450, 46812, 56173 };
block = *(unsigned long long*)compressedBlock;
if (isSigned) {
alpha[0] = (char)(block & 0xFF);
alpha[1] = (char)((block >> 8) & 0xFF);
if (alpha[0] < -127) alpha[0] = -127; /* -128 clamps to -127 */
if (alpha[1] < -127) alpha[1] = -127; /* -128 clamps to -127 */
} else {
alpha[0] = block & 0xFF;
alpha[1] = (block >> 8) & 0xFF;
}
if (alpha[0] > alpha[1]) {
/* 6 interpolated alpha values. */
alpha[2] = (aWeights6[5] * alpha[0] + aWeights6[0] * alpha[1] + 32768) >> 16; /* 6/7*alpha_0 + 1/7*alpha_1 */
alpha[3] = (aWeights6[4] * alpha[0] + aWeights6[1] * alpha[1] + 32768) >> 16; /* 5/7*alpha_0 + 2/7*alpha_1 */
alpha[4] = (aWeights6[3] * alpha[0] + aWeights6[2] * alpha[1] + 32768) >> 16; /* 4/7*alpha_0 + 3/7*alpha_1 */
alpha[5] = (aWeights6[2] * alpha[0] + aWeights6[3] * alpha[1] + 32768) >> 16; /* 3/7*alpha_0 + 4/7*alpha_1 */
alpha[6] = (aWeights6[1] * alpha[0] + aWeights6[4] * alpha[1] + 32768) >> 16; /* 2/7*alpha_0 + 5/7*alpha_1 */
alpha[7] = (aWeights6[0] * alpha[0] + aWeights6[5] * alpha[1] + 32768) >> 16; /* 1/7*alpha_0 + 6/7*alpha_1 */
} else {
/* 4 interpolated alpha values. */
alpha[2] = (aWeights4[3] * alpha[0] + aWeights4[0] * alpha[1] + 32768) >> 16; /* 4/5*alpha_0 + 1/5*alpha_1 */
alpha[3] = (aWeights4[2] * alpha[0] + aWeights4[1] * alpha[1] + 32768) >> 16; /* 3/5*alpha_0 + 2/5*alpha_1 */
alpha[4] = (aWeights4[1] * alpha[0] + aWeights4[2] * alpha[1] + 32768) >> 16; /* 2/5*alpha_0 + 3/5*alpha_1 */
alpha[5] = (aWeights4[0] * alpha[0] + aWeights4[3] * alpha[1] + 32768) >> 16; /* 1/5*alpha_0 + 4/5*alpha_1 */
alpha[6] = isSigned ? -127 : 0;
alpha[7] = isSigned ? 127 : 255;
}
indices = block >> 16;
if (isSigned) {
sblock = (char*)decompressedBlock;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
sblock[j * pixelSize] = (char)alpha[indices & 0x07];
indices >>= 3;
}
sblock += destinationPitch;
}
} else {
ublock = (unsigned char*)decompressedBlock;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
ublock[j * pixelSize] = (unsigned char)alpha[indices & 0x07];
indices >>= 3;
}
ublock += destinationPitch;
}
}
}
static void bcdec__bc4_block_float(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int pixelSize, int isSigned) {
float* decompressed;
float alpha[8];
int i, j;
unsigned long long block, indices;
block = *(unsigned long long*)compressedBlock;
decompressed = (float*)decompressedBlock;
if (isSigned) {
alpha[0] = (float)((char)(block & 0xFF)) / 127.0f;
alpha[1] = (float)((char)((block >> 8) & 0xFF)) / 127.0f;
if (alpha[0] < -1.0f) alpha[0] = -1.0f; /* -128 clamps to -127 */
if (alpha[1] < -1.0f) alpha[1] = -1.0f; /* -128 clamps to -127 */
} else {
alpha[0] = (float)(block & 0xFF) / 255.0f;
alpha[1] = (float)((block >> 8) & 0xFF) / 255.0f;
}
if (alpha[0] > alpha[1]) {
/* 6 interpolated alpha values. */
alpha[2] = (6.0f * alpha[0] + alpha[1]) / 7.0f; /* 6/7*alpha_0 + 1/7*alpha_1 */
alpha[3] = (5.0f * alpha[0] + 2.0f * alpha[1]) / 7.0f; /* 5/7*alpha_0 + 2/7*alpha_1 */
alpha[4] = (4.0f * alpha[0] + 3.0f * alpha[1]) / 7.0f; /* 4/7*alpha_0 + 3/7*alpha_1 */
alpha[5] = (3.0f * alpha[0] + 4.0f * alpha[1]) / 7.0f; /* 3/7*alpha_0 + 4/7*alpha_1 */
alpha[6] = (2.0f * alpha[0] + 5.0f * alpha[1]) / 7.0f; /* 2/7*alpha_0 + 5/7*alpha_1 */
alpha[7] = ( alpha[0] + 6.0f * alpha[1]) / 7.0f; /* 1/7*alpha_0 + 6/7*alpha_1 */
} else {
/* 4 interpolated alpha values. */
alpha[2] = (4.0f * alpha[0] + alpha[1]) / 5.0f; /* 4/5*alpha_0 + 1/5*alpha_1 */
alpha[3] = (3.0f * alpha[0] + 2.0f * alpha[1]) / 5.0f; /* 3/5*alpha_0 + 2/5*alpha_1 */
alpha[4] = (2.0f * alpha[0] + 3.0f * alpha[1]) / 5.0f; /* 2/5*alpha_0 + 3/5*alpha_1 */
alpha[5] = ( alpha[0] + 4.0f * alpha[1]) / 5.0f; /* 1/5*alpha_0 + 4/5*alpha_1 */
alpha[6] = isSigned ? -1.0f : 0.0f;
alpha[7] = 1.0f;
}
indices = block >> 16;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
decompressed[j * pixelSize] = alpha[indices & 0x07];
indices >>= 3;
}
decompressed += destinationPitch;
}
}
#endif /* BCDEC_BC4BC5_PRECISE */
typedef struct bcdec__bitstream {
unsigned long long low;
unsigned long long high;
@@ -270,15 +410,37 @@ BCDECDEF void bcdec_bc3(const void* compressedBlock, void* decompressedBlock, in
bcdec__smooth_alpha_block(compressedBlock, ((char*)decompressedBlock) + 3, destinationPitch, 4);
}
#ifndef BCDEC_BC4BC5_PRECISE
BCDECDEF void bcdec_bc4(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
bcdec__smooth_alpha_block(compressedBlock, decompressedBlock, destinationPitch, 1);
#else
BCDECDEF void bcdec_bc4(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned) {
bcdec__bc4_block(compressedBlock, decompressedBlock, destinationPitch, 1, isSigned);
#endif
}
#ifndef BCDEC_BC4BC5_PRECISE
BCDECDEF void bcdec_bc5(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
bcdec__smooth_alpha_block(compressedBlock, decompressedBlock, destinationPitch, 2);
bcdec__smooth_alpha_block(((char*)compressedBlock) + 8, ((char*)decompressedBlock) + 1, destinationPitch, 2);
#else
BCDECDEF void bcdec_bc5(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned) {
bcdec__bc4_block(compressedBlock, decompressedBlock, destinationPitch, 2, isSigned);
bcdec__bc4_block(((char*)compressedBlock) + 8, ((char*)decompressedBlock) + 1, destinationPitch, 2, isSigned);
#endif
}
#ifdef BCDEC_BC4BC5_PRECISE
BCDECDEF void bcdec_bc4_float(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned) {
bcdec__bc4_block_float(compressedBlock, decompressedBlock, destinationPitch, 1, isSigned);
}
BCDECDEF void bcdec_bc5_float(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned) {
bcdec__bc4_block_float(compressedBlock, decompressedBlock, destinationPitch, 2, isSigned);
bcdec__bc4_block_float(((char*)compressedBlock) + 8, ((float*)decompressedBlock) + 1, destinationPitch, 2, isSigned);
}
#endif /* BCDEC_BC4BC5_PRECISE */
/* http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend */
static int bcdec__extend_sign(int val, int bits) {
return (val << (32 - bits)) >> (32 - bits);
@@ -1269,8 +1431,6 @@ BCDECDEF void bcdec_bc7(const void* compressedBlock, void* decompressedBlock, in
#endif /* BCDEC_IMPLEMENTATION */
#endif /* BCDEC_HEADER_INCLUDED */
/* LICENSE:
This software is available under 2 licenses -- choose whichever you prefer.
+56 -20
View File
@@ -96,6 +96,11 @@ static uint conversion_resize(DDS_HEADER &dds_header)
// drop levels until we get to an appropriate size, but make sure we have
// at least 1 mipmap level remaining at the end (in case there's not a full chain)
while (((width > MAX_SIZE) || (height > MAX_SIZE)) && (offset < dds_header.dwMipMapCount-1)) {
// this shouldn't happen, but catch the obscure case (like 8192x4)
if ((width <= 4) || (height <= 4)) {
break;
}
width >>= 1;
height >>= 1;
depth >>= 1;
@@ -194,9 +199,10 @@ static int _dds_read_header(CFILE *ddsfile, DDS_HEADER &dds_header, DDS_HEADER_D
return DDS_ERROR_NONE;
}
static size_t compute_dds_size(const DDS_HEADER &dds_header)
static size_t compute_dds_size(const DDS_HEADER &dds_header, bool converting = false)
{
uint d_width, d_height, d_depth;
const uint block_sz = 4;
uint d_width = 0, d_height = 0, d_depth = 0;
size_t d_size = 0;
for (uint i = 0; i < dds_header.dwMipMapCount; i++) {
@@ -204,6 +210,17 @@ static size_t compute_dds_size(const DDS_HEADER &dds_header)
d_height = std::max(1U, dds_header.dwHeight >> i);
d_depth = std::max(1U, dds_header.dwDepth >> i);
// When converting we need to pad a bit to compensate for the decompression
// size on smaller mipmap levels. We need to ensure there is always enough
// room to decode an entire 4x4 block in rgba space
if (converting) {
auto sz = std::min(d_width, d_height);
if (sz < block_sz) {
d_size += ((block_sz * block_sz) - (sz * sz)) * d_depth * 4;
}
}
if (dds_header.ddspf.dwFlags & DDPF_FOURCC) {
// size of data block (4x4)
d_size += ((d_width + 3) / 4) * ((d_height + 3) / 4) * d_depth * ((dds_header.ddspf.dwFourCC == FOURCC_DXT1) ? 8 : 16);
@@ -247,6 +264,7 @@ int dds_read_header(const char *filename, CFILE *img_cfp, int *width, int *heigh
int retval = DDS_ERROR_NONE;
int ct = DDS_UNCOMPRESSED;
int is_cubemap = 0;
bool convert = false;
if (img_cfp == NULL) {
@@ -337,7 +355,9 @@ int dds_read_header(const char *filename, CFILE *img_cfp, int *width, int *heigh
}
// maybe do conversion if format not supported
if (conversion_needed(dds_header)) {
convert = conversion_needed(dds_header);
if (convert) {
// switch to uncompressed format and reset vars
dds_header.ddspf.dwFlags &= ~DDPF_FOURCC;
dds_header.ddspf.dwFlags |= DDPF_RGB;
@@ -359,7 +379,7 @@ int dds_read_header(const char *filename, CFILE *img_cfp, int *width, int *heigh
// stuff important info
if (size)
*size = compute_dds_size(dds_header);
*size = compute_dds_size(dds_header, convert);
if (bpp)
*bpp = get_bit_count(dds_header);
@@ -386,6 +406,9 @@ Done:
return retval;
}
static void (*decompress_dds)(const void *in, void *out, int pitch) = nullptr;
static uint32_t BLOCK_SIZE = 0;
//reads pixel info from a dds file
int dds_read_bitmap(const char *filename, ubyte *data, ubyte *bpp, int cf_type)
{
@@ -423,7 +446,7 @@ int dds_read_bitmap(const char *filename, ubyte *data, ubyte *bpp, int cf_type)
cfseek(cfp, (dds_header.ddspf.dwFourCC == FOURCC_DX10) ? DX10_OFFSET : DDS_OFFSET, CF_SEEK_SET);
size = compute_dds_size(dds_header);
size = compute_dds_size(dds_header); // don't add padding on this one!!
// read in the data
if ( !conversion_needed(dds_header) ) {
@@ -447,6 +470,28 @@ int dds_read_bitmap(const char *filename, ubyte *data, ubyte *bpp, int cf_type)
const int num_faces = (dds_header.dwCaps2 & DDSCAPS2_CUBEMAP) ? 6 : 1;
const bool has_depth = (dds_header.dwFlags & DDSD_DEPTH) == DDSD_DEPTH;
switch (dds_header.ddspf.dwFourCC) {
case FOURCC_DX10:
decompress_dds = bcdec_bc7;
BLOCK_SIZE = BCDEC_BC7_BLOCK_SIZE;
break;
case FOURCC_DXT5:
decompress_dds = bcdec_bc3;
BLOCK_SIZE = BCDEC_BC3_BLOCK_SIZE;
break;
case FOURCC_DXT1:
decompress_dds = bcdec_bc1;
BLOCK_SIZE = BCDEC_BC1_BLOCK_SIZE;
break;
case FOURCC_DXT3:
decompress_dds = bcdec_bc2;
BLOCK_SIZE = BCDEC_BC2_BLOCK_SIZE;
break;
default:
Error(LOCATION, "Invalid FourCC (%d) for DDS decompression!", dds_header.ddspf.dwFourCC);
break;
}
for (int f = 0; f < num_faces; ++f) {
// if we resized then skip over all of that data (altering values to match pre-resize)
for (uint x = 0; x < mipmap_offset; ++x) {
@@ -454,7 +499,7 @@ int dds_read_bitmap(const char *filename, ubyte *data, ubyte *bpp, int cf_type)
d_height = std::max(1U, dds_header.dwHeight << (mipmap_offset - x));
d_depth = has_depth ? std::max(1U, dds_header.dwDepth << (mipmap_offset - x)) : 1U;
src += (d_width * d_height * d_depth);
src += ((d_width + 3) / 4) * ((d_height + 3) / 4) * d_depth * BLOCK_SIZE;
}
for (uint m = mipmap_offset; m < dds_header.dwMipMapCount; ++m) {
@@ -464,23 +509,14 @@ int dds_read_bitmap(const char *filename, ubyte *data, ubyte *bpp, int cf_type)
d_depth = std::max(1U, dds_header.dwDepth >> (m - mipmap_offset));
for (uint d = 0; d < d_depth; ++d) {
auto depth_offset = d * d_width * d_height * 4;
for (uint i = 0; i < d_height; i += 4) {
for (uint j = 0; j < d_width; j += 4) {
dst = data + data_offset + ((i * d_width + j) * 4);
dst = data + data_offset + depth_offset + ((i * d_width + j) * 4);
if (dds_header.ddspf.dwFourCC == FOURCC_DX10) {
bcdec_bc7(src, dst, d_width * 4);
src += BCDEC_BC7_BLOCK_SIZE;
} else if (dds_header.ddspf.dwFourCC == FOURCC_DXT5) {
bcdec_bc3(src, dst, d_width * 4);
src += BCDEC_BC3_BLOCK_SIZE;
} else if (dds_header.ddspf.dwFourCC == FOURCC_DXT1) {
bcdec_bc1(src, dst, d_width * 4);
src += BCDEC_BC1_BLOCK_SIZE;
} else if (dds_header.ddspf.dwFourCC == FOURCC_DXT3) {
bcdec_bc2(src, dst, d_width * 4);
src += BCDEC_BC2_BLOCK_SIZE;
}
decompress_dds(src, dst, d_width * 4);
src += BLOCK_SIZE;
}
}
}
+163 -71
View File
@@ -37,7 +37,6 @@
#include "utils/Random.h"
#include "weapon/weapon.h"
constexpr int MIN_RADIUS_FOR_PERSISTENT_DEBRIS = 50; // ship radius at which debris from it becomes persistant
constexpr int DEBRIS_SOUND_DELAY = 2000; // time to start debris sound after created
int Num_hull_pieces; // number of hull pieces in existence
@@ -59,30 +58,55 @@ particle::ParticleEffectHandle Debris_hit_particle;
#define DEBRIS_INDEX(dp) (int)(dp-Debris.data())
// Find the first available arc slot. If none is available, and no_create is false, add one.
debris_electrical_arc *debris_find_or_create_electrical_arc_slot(debris *db, bool no_create);
/**
* Start the sequence of a piece of debris writhing in unholy agony!!!
*/
static void debris_start_death_roll(object *debris_obj, debris *debris_p, vec3d *hitpos = nullptr)
{
auto sip = &Ship_info[debris_p->ship_info_index];
if (debris_p->is_hull) {
// tell everyone else to blow up the piece of debris
if( MULTIPLAYER_MASTER )
send_debris_update_packet(debris_obj,DEBRIS_UPDATE_NUKE);
int fireball_type = fireball_ship_explosion_type(&Ship_info[debris_p->ship_info_index]);
if(fireball_type < 0) {
fireball_type = FIREBALL_EXPLOSION_LARGE1 + Random::next(FIREBALL_NUM_LARGE_EXPLOSIONS);
if (sip->debris_end_particles.isValid()) {
auto source = particle::ParticleManager::get()->createSource(sip->debris_end_particles);
// Use the position since the object is going to be invalid soon
auto host = std::make_unique<EffectHostVector>(debris_obj->pos, debris_obj->orient, debris_obj->phys_info.vel);
host->setRadius(debris_obj->radius);
source->setHost(std::move(host));
source->setNormal(debris_obj->orient.vec.uvec);
source->finishCreation();
} else {
int fireball_type = fireball_ship_explosion_type(sip);
if(fireball_type < 0) {
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);
}
fireball_create( &debris_obj->pos, fireball_type, FIREBALL_LARGE_EXPLOSION, OBJ_INDEX(debris_obj), debris_obj->radius*1.75f);
// only play debris destroy sound if hull piece and it has been around for at least 2 seconds
if ( Missiontime > debris_p->time_started + 2*F1_0 ) {
auto snd_id = Ship_info[debris_p->ship_info_index].debris_explosion_sound;
auto snd_id = sip->debris_explosion_sound;
if (snd_id.isValid()) {
snd_play_3d( gamesnd_get_game_sound(snd_id), &debris_obj->pos, &View_position, debris_obj->radius );
}
}
} else {
if (sip->shrapnel_end_particles.isValid()) {
auto source = particle::ParticleManager::get()->createSource(sip->shrapnel_end_particles);
// Use the position since the object is going to be invalid soon
auto host = std::make_unique<EffectHostVector>(debris_obj->pos, debris_obj->orient, debris_obj->phys_info.vel);
host->setRadius(debris_obj->radius);
source->setHost(std::move(host));
source->setNormal(debris_obj->orient.vec.uvec);
source->finishCreation();
}
}
if (scripting::hooks::OnDebrisDeath->isActive()) {
@@ -119,6 +143,9 @@ void debris_init()
Debris_hit_particle = particle::ParticleManager::get()->addEffect(particle::ParticleEffect(
"", //Name
::util::UniformFloatRange(10.f), //Particle num
particle::ParticleEffect::Duration::ONETIME, //Single Particle Emission
::util::UniformFloatRange(), //No duration
::util::UniformFloatRange (-1.f), //Single particle only
particle::ParticleEffect::ShapeDirection::ALIGNED, //Particle direction
::util::UniformFloatRange(1.f), //Velocity Inherit
false, //Velocity Inherit absolute?
@@ -133,6 +160,12 @@ void debris_init()
true, //Affected by detail
1.f, //Culling range multiplier
true, //Disregard Animation Length. Must be true for everything using particle::Anim_bitmap_X
false, //Don't reverse animation
false, //parent local
false, //ignore velocity inherit if parented
false, //position velocity inherit absolute?
std::nullopt, //Local velocity offset
std::nullopt, //Local offset
::util::UniformFloatRange(0.25f, 0.75f), //Lifetime
::util::UniformFloatRange(0.2f, 0.4f), //Radius
particle::Anim_bitmap_id_fire)); //Bitmap
@@ -266,56 +299,48 @@ void debris_process_post(object * obj, float frame_time)
return; // If arc_frequency <= 0, this piece has no arcs on it
}
if ( !timestamp_elapsed(db->fire_timeout) && timestamp_elapsed(db->next_fireball)) {
if ( !timestamp_elapsed(db->arc_timeout) && timestamp_elapsed(db->arc_next_time)) {
db->next_fireball = _timestamp_rand(db->arc_frequency,db->arc_frequency*2 );
db->arc_next_time = _timestamp_rand(db->arc_frequency,db->arc_frequency*2 );
db->arc_frequency += 100;
if (db->is_hull) {
int n, n_arcs = Random::next(1, 3); // Create 1-3 sparks
int n_arcs = Random::next(1, 3); // Create 1-3 sparks
vec3d v1 = submodel_get_random_point(db->model_num, db->submodel_num);
vec3d v2 = submodel_get_random_point(db->model_num, db->submodel_num);
vec3d v3 = submodel_get_random_point(db->model_num, db->submodel_num);
vec3d v4 = submodel_get_random_point(db->model_num, db->submodel_num);
n = 0;
int lifetime = Random::next(100, 1000);
// Create the spark effects
for (int i=0; i<MAX_DEBRIS_ARCS; ++i) {
if ( !db->arc_timestamp[i].isValid() ) {
db->arc_timestamp[i] = _timestamp(lifetime); // live up to a second
for (int n = 0; n < n_arcs; n++) {
auto arc = debris_find_or_create_electrical_arc_slot(db, db->electrical_arcs.size() >= MAX_DEBRIS_ARCS);
if (arc) {
arc->timestamp = _timestamp(lifetime); // live up to a second
switch( n ) {
case 0:
db->arc_pts[i][0] = v1;
db->arc_pts[i][1] = v2;
arc->endpoint_1 = v1;
arc->endpoint_2 = v2;
break;
case 1:
db->arc_pts[i][0] = v2;
db->arc_pts[i][1] = v3;
arc->endpoint_1 = v2;
arc->endpoint_2 = v3;
break;
case 2:
db->arc_pts[i][0] = v2;
db->arc_pts[i][1] = v4;
arc->endpoint_1 = v2;
arc->endpoint_2 = v4;
break;
default:
Int3();
UNREACHABLE("Unhandled case %d for electrical arc creation in debris_process_post()!", n);
}
n++;
if ( n == n_arcs )
break; // Don't need to create anymore
}
}
// rotate v2 out of local coordinates into world.
// Use v2 since it is used in every bolt. See above switch().
vec3d snd_pos;
@@ -342,16 +367,20 @@ void debris_process_post(object * obj, float frame_time)
}
}
for (int i=0; i<MAX_DEBRIS_ARCS; ++i) {
if ( db->arc_timestamp[i].isValid() ) {
if ( timestamp_elapsed( db->arc_timestamp[i] ) ) {
for (auto &arc: db->electrical_arcs) {
if (arc.timestamp.isValid()) {
if (timestamp_elapsed(arc.timestamp)) {
// Kill off the spark
db->arc_timestamp[i] = TIMESTAMP::invalid();
arc.timestamp = TIMESTAMP::invalid();
} else {
// Maybe move a vertex.... 20% of the time maybe?
int mr = Random::next();
if ( mr < Random::MAX_VALUE/5 ) {
db->arc_pts[i][mr % 2] = submodel_get_random_point(db->model_num, db->submodel_num);
auto pt = submodel_get_random_point(db->model_num, db->submodel_num);
if (mr % 2 == 0)
arc.endpoint_1 = pt;
else
arc.endpoint_2 = pt;
}
}
}
@@ -420,6 +449,32 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, const
{
debris_create_set_velocity(&Debris[obj->instance], shipp, exp_center, exp_force, source_subsys);
debris_create_fire_hook(obj, source_obj);
const auto& sip = Ship_info[Ships[source_obj->instance].ship_info_index];
particle::ParticleEffectHandle flame_effect;
if (source_subsys != nullptr) {
if (hull_flag) {
if (source_subsys->system_info->debris_flame_particles.isValid()) {
flame_effect = source_subsys->system_info->debris_flame_particles;
} else {
flame_effect = sip.default_subsys_debris_flame_particles;
}
} else {
if (source_subsys->system_info->shrapnel_flame_particles.isValid()) {
flame_effect = source_subsys->system_info->shrapnel_flame_particles;
} else {
flame_effect = sip.default_subsys_shrapnel_flame_particles;
}
}
} else {
flame_effect = hull_flag ? sip.debris_flame_particles : sip.shrapnel_flame_particles;
}
if (flame_effect.isValid()) {
auto source = particle::ParticleManager::get()->createSource(flame_effect);
source->setHost(std::make_unique<EffectHostObject>(obj, vmd_zero_vector));
source->setTriggerRadius(source_obj->radius);
source->setTriggerVelocity(vm_vec_mag_quick(&source_obj->phys_info.vel));
source->finishCreation();
}
}
return obj;
@@ -552,22 +607,10 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
}
// Create Debris piece n!
if ( hull_flag ) {
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 {
// 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);
}
//WMC - Oh noes, we may need to change lifeleft
if(hull_flag)
{
// set lifeleft based on tabled entries if they are not negative
// coded originally by WMC then clean-up added by wookieejedi
if(sip->debris_min_lifetime >= 0.0f && sip->debris_max_lifetime >= 0.0f)
{
db->lifeleft = (( sip->debris_max_lifetime - sip->debris_min_lifetime ) * frand()) + sip->debris_min_lifetime;
@@ -582,6 +625,22 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
if(db->lifeleft > sip->debris_max_lifetime)
db->lifeleft = sip->debris_max_lifetime;
}
// By default, make some pieces blow up shortly after explosion.
else if (Random::next() < (Random::MAX_VALUE / 6))
{
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
{
// 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);
}
// increase lifetime for vaporized debris
@@ -595,15 +654,13 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
db->ship_info_index = parent_ship_class;
db->team = team;
db->ambient_sound = (sip == nullptr) ? gamesnd_id(-1) : sip->debris_ambient_sound;
db->fire_timeout = TIMESTAMP::never(); // if not changed, timestamp_elapsed() will return false
db->arc_timeout = TIMESTAMP::never(); // if not changed, timestamp_elapsed() will return false
db->time_started = Missiontime;
db->species = (sip == nullptr) ? -1 : sip->species;
db->parent_alt_name = alt_type_index;
db->damage_mult = 1.0f;
for (int i=0; i<MAX_DEBRIS_ARCS; ++i) { // NOLINT
db->arc_timestamp[i] = TIMESTAMP::invalid();
}
db->electrical_arcs.clear();
if ( db->is_hull ) {
// Percent of debris pieces with arcs controlled via table (default 50%)
@@ -616,7 +673,7 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
db->arc_frequency = 0;
}
db->next_fireball = _timestamp_rand(500,2000); //start one 1/2 - 2 secs later
db->arc_next_time = _timestamp_rand(500,2000); //start one 1/2 - 2 secs later
flagset<Object::Object_Flags> default_flags;
default_flags.set(Object::Object_Flags::Renders);
@@ -650,19 +707,25 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
obj->hull_strength = hull_strength;
if (hull_flag) {
if(sip->debris_min_hitpoints >= 0.0f && sip->debris_max_hitpoints >= 0.0f)
{
obj->hull_strength = (( sip->debris_max_hitpoints - sip->debris_min_hitpoints ) * frand()) + sip->debris_min_hitpoints;
float min_hp = sip->debris_min_hitpoints;
float max_hp = sip->debris_max_hitpoints;
if (sip->debris_hitpoints_radius_multi >= 0.0f) {
min_hp *= sip->debris_hitpoints_radius_multi * radius;
max_hp *= sip->debris_hitpoints_radius_multi * radius;
}
else if(sip->debris_min_hitpoints >= 0.0f)
if (min_hp >= 0.0f && max_hp >= 0.0f)
{
if(obj->hull_strength < sip->debris_min_hitpoints)
obj->hull_strength = sip->debris_min_hitpoints;
obj->hull_strength = ((max_hp - min_hp) * frand()) + min_hp;
}
else if(sip->debris_max_hitpoints >= 0.0f)
else if(min_hp >= 0.0f)
{
if(obj->hull_strength > sip->debris_max_hitpoints)
obj->hull_strength = sip->debris_max_hitpoints;
if (obj->hull_strength < min_hp)
obj->hull_strength = min_hp;
}
else if(max_hp >= 0.0f)
{
if (obj->hull_strength > max_hp)
obj->hull_strength = max_hp;
}
db->damage_mult = sip->debris_damage_mult;
@@ -671,21 +734,23 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
// 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.
if (spark_timeout >= 0) {
db->fire_timeout = _timestamp(spark_timeout);
db->arc_timeout = _timestamp(spark_timeout);
} else if (parent_objnum >= 0) {
float t = 1000*Objects[parent_objnum].radius/3 + (fl2i(1000*3*Objects[parent_objnum].radius) == 0 ? 0 : Random::next(fl2i(1000*3*Objects[parent_objnum].radius)));
db->fire_timeout = _timestamp(fl2i(t)); // fireballs last from 5 - 30 seconds
db->arc_timeout = _timestamp(fl2i(t)); // fireballs last from 5 - 30 seconds
} else {
db->fire_timeout = TIMESTAMP::immediate();
db->arc_timeout = TIMESTAMP::immediate();
}
if (parent_objnum >= 0 && Objects[parent_objnum].radius >= MIN_RADIUS_FOR_PERSISTENT_DEBRIS) {
if (parent_objnum >= 0 && Objects[parent_objnum].radius >= Min_radius_for_persistent_debris) {
db->flags.set(Debris_Flags::DoNotExpire);
} else {
debris_add_to_hull_list(db);
}
}
db->max_hull = obj->hull_strength;
if (hull_flag) {
MONITOR_INC(NumHullDebris,1);
} else {
@@ -1162,7 +1227,7 @@ void calc_debris_physics_properties( physics_info *pi, vec3d *mins, vec3d *maxs,
*/
void debris_render(object * obj, model_draw_list *scene)
{
int i, num, swapped;
int num, swapped;
debris *db;
swapped = -1;
@@ -1197,9 +1262,9 @@ void debris_render(object * obj, model_draw_list *scene)
// Only render electrical arcs if within 500m of the eye (for a 10m piece)
if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f ) {
for (i=0; i<MAX_DEBRIS_ARCS; i++ ) {
if ( db->arc_timestamp[i].isValid() ) {
model_instance_add_arc( pm, pmi, db->submodel_num, &db->arc_pts[i][0], &db->arc_pts[i][1], MARC_TYPE_DAMAGED );
for (auto &arc: db->electrical_arcs) {
if ( arc.timestamp.isValid() ) {
model_instance_add_arc( pm, pmi, db->submodel_num, &arc.endpoint_1, &arc.endpoint_2, nullptr, MARC_TYPE_DAMAGED );
}
}
}
@@ -1240,13 +1305,40 @@ void create_generic_debris(object* ship_objp, const vec3d* pos, float min_num_de
if (ship_objp->type != OBJ_SHIP)
return;
// determine if using custom or non-custom generic debris
int model_num = use_ship_debris ? Ship_info[Ships[ship_objp->instance].ship_info_index].generic_debris_model_num : -1;
// if using non-custom generic debris, then bail early if that kind of debris is not allowed --wookeejedi
if ( (model_num < 0) && (Disable_all_noncustom_generic_debris) ) {
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);
debris_create(ship_objp, model_num, -1, &create_pos, pos, false, speed_mult);
}
}
debris_electrical_arc *debris_find_or_create_electrical_arc_slot(debris *db, bool no_create)
{
size_t i = 0;
for (auto& ii : db->electrical_arcs)
{
if (!ii.timestamp.isValid())
break;
i++;
}
if (i == db->electrical_arcs.size())
{
if (no_create)
return nullptr;
db->electrical_arcs.emplace_back();
}
return &db->electrical_arcs[i];
}
+11 -5
View File
@@ -20,7 +20,7 @@ class object;
struct CFILE;
class model_draw_list;
#define MAX_DEBRIS_ARCS 8 // Must be less than MAX_ARC_EFFECTS in model.h
#define MAX_DEBRIS_ARCS 8
FLAG_LIST(Debris_Flags) {
Used,
@@ -30,6 +30,12 @@ FLAG_LIST(Debris_Flags) {
NUM_VALUES
};
struct debris_electrical_arc
{
vec3d endpoint_1;
vec3d endpoint_2;
TIMESTAMP timestamp; // When this times out, the spark goes away. Invalid is not used
};
typedef struct debris {
flagset<Debris_Flags> flags; // See DEBRIS_??? defines
@@ -43,15 +49,15 @@ typedef struct debris {
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
TIMESTAMP next_fireball; // When to start a fireball
TIMESTAMP arc_next_time; // When the next damage/emp arc will be created.
bool is_hull; // indicates whether this is a collideable, destructable piece of debris from the model, or just a generic debris fragment
float max_hull;
int species; // What species this is from. -1 if don't care.
TIMESTAMP fire_timeout; // timestamp that holds time for fireballs to stop appearing
TIMESTAMP arc_timeout; // timestamp that holds time for arcs to stop appearing
TIMESTAMP sound_delay; // timestamp to signal when sound should start
fix time_started; // time when debris was created
vec3d arc_pts[MAX_DEBRIS_ARCS][2]; // The endpoints of each arc
TIMESTAMP arc_timestamp[MAX_DEBRIS_ARCS]; // When this times out, the spark goes away. Invalid is not used
SCP_vector<debris_electrical_arc> electrical_arcs;
int arc_frequency; // Starts at 1000, gets bigger
int parent_alt_name;
+2 -2
View File
@@ -1083,7 +1083,7 @@ void dc_stuff_int(int *i)
value_l = dc_parse_long(token.c_str(), DCT_INT);
if ((value_l < INT_MAX) && (value_l > INT_MIN)) {
*i = value_l;
*i = static_cast<int>(value_l);
} else {
throw errParse(token.c_str(), DCT_INT);
@@ -1101,7 +1101,7 @@ void dc_stuff_uint(uint *i)
value_l = dc_parse_long(Cp, DCT_INT);
if (value_l < UINT_MAX) {
*i = value_l;
*i = static_cast<uint>(value_l);
} else {
throw errParse(token.c_str(), DCT_INT);
+110 -95
View File
@@ -181,66 +181,52 @@ void parse_decals_table(const char* filename) {
}
}
struct Decal {
int definition_handle = -1;
object_h object;
int orig_obj_type = OBJ_NONE;
int submodel = -1;
Decal::Decal() {
vm_vec_make(&scale, 1.f, 1.f, 1.f);
}
float creation_time = -1.0f; //!< The mission time at which this decal was created
float lifetime = -1.0f; //!< The time this decal is active. When negative it never expires
vec3d position = vmd_zero_vector;
vec3d scale;
matrix orientation = vmd_identity_matrix;
Decal() {
vm_vec_make(&scale, 1.f, 1.f, 1.f);
bool Decal::isValid() const {
if (!object.isValid()) {
return false;
}
if (object.objp()->flags[Object::Object_Flags::Should_be_dead]) {
return false;
}
bool isValid() const {
if (!object.isValid()) {
return false;
}
if (object.objp()->flags[Object::Object_Flags::Should_be_dead]) {
return false;
}
if (orig_obj_type != object.objp()->type) {
mprintf(("Decal object type for object %d has changed from %s to %s. Please let m!m know about this\n",
object.objnum, Object_type_names[orig_obj_type], Object_type_names[object.objp()->type]));
return false;
}
if (lifetime > 0.0f) {
if (f2fl(Missiontime) >= creation_time + lifetime) {
// Decal has expired
return false;
}
}
auto objp = object.objp();
if (objp->type == OBJ_SHIP) {
auto shipp = &Ships[objp->instance];
auto model_instance = model_get_instance(shipp->model_instance_num);
Assertion(submodel >= 0 && submodel < object_get_model(objp)->n_models,
"Invalid submodel number detected!");
auto smi = &model_instance->submodel[submodel];
if (smi->blown_off) {
return false;
}
} else {
Assertion(false, "Only ships are currently supported for decals!");
return false;
}
return true;
if (orig_obj_type != object.objp()->type) {
mprintf(("Decal object type for object %d has changed from %s to %s. Please let m!m know about this\n",
object.objnum, Object_type_names[orig_obj_type], Object_type_names[object.objp()->type]));
return false;
}
};
SCP_vector<Decal> active_decals;
if (lifetime > 0.0f) {
if (f2fl(Missiontime) >= creation_time + lifetime) {
// Decal has expired
return false;
}
}
auto objp = object.objp();
if (objp->type == OBJ_SHIP) {
auto shipp = &Ships[objp->instance];
auto model_instance = model_get_instance(shipp->model_instance_num);
Assertion(submodel >= 0 && submodel < object_get_model(objp)->n_models,
"Invalid submodel number detected!");
auto smi = &model_instance->submodel[submodel];
if (smi->blown_off) {
return false;
}
} else {
Assertion(false, "Only ships are currently supported for decals!");
return false;
}
return true;
}
SCP_vector<Decal> active_decals, active_single_frame_decals;
bool required_string_if_new(const char* token, bool new_entry) {
if (!new_entry) {
@@ -370,7 +356,11 @@ void initializeMission() {
active_decals.clear();
}
matrix4 getDecalTransform(Decal& decal) {
// Discard any fragments where the angle to the direction to greater than 45°
const float DECAL_ANGLE_CUTOFF = fl_radians(45.f);
const float DECAL_ANGLE_FADE_START = fl_radians(30.f);
static matrix4 getDecalTransform(const Decal& decal, float alpha) {
Assertion(decal.object.objp()->type == OBJ_SHIP, "Only ships are currently supported for decals!");
auto objp = decal.object.objp();
@@ -405,11 +395,62 @@ matrix4 getDecalTransform(Decal& decal) {
matrix4 mat4;
vm_matrix4_set_transform(&mat4, &worldOrient, &worldPos);
// This is currently a constant but in the future this may be configurable by the decals table
mat4.a2d[0][3] = DECAL_ANGLE_CUTOFF;
mat4.a2d[1][3] = DECAL_ANGLE_FADE_START;
mat4.a2d[2][3] = alpha;
return mat4;
}
inline static void renderDecal(graphics::decal_draw_list& draw_list, const Decal& decal) {
auto mission_time = f2fl(Missiontime);
int diffuse_bm = -1;
int glow_bm = -1;
int normal_bm = -1;
auto decal_time = mission_time - decal.creation_time;
auto progress = decal_time / decal.lifetime;
float alpha = 1.0f;
if (progress > 0.8) {
// Fade the decal out for the last 20% of its lifetime
alpha = 1.0f - smoothstep(0.8f, 1.0f, progress);
}
if (std::holds_alternative<int>(decal.definition_handle)) {
int definition_handle = std::get<int>(decal.definition_handle);
Assertion(definition_handle >= 0 && definition_handle < (int) DecalDefinitions.size(),
"Invalid decal handle detected!");
auto &decalDef = DecalDefinitions[definition_handle];
if (decalDef.getDiffuseBitmap() >= 0) {
diffuse_bm = decalDef.getDiffuseBitmap()
+
bm_get_anim_frame(decalDef.getDiffuseBitmap(), decal_time, 0.0f, decalDef.isDiffuseLooping());
}
if (decalDef.getGlowBitmap() >= 0) {
glow_bm = decalDef.getGlowBitmap()
+ bm_get_anim_frame(decalDef.getGlowBitmap(), decal_time, 0.0f, decalDef.isGlowLooping());
}
if (decalDef.getNormalBitmap() >= 0) {
normal_bm = decalDef.getNormalBitmap()
+ bm_get_anim_frame(decalDef.getNormalBitmap(), decal_time, 0.0f, decalDef.isNormalLooping());
}
}
else {
std::tie(diffuse_bm, glow_bm, normal_bm) = std::get<std::tuple<int, int, int>>(decal.definition_handle);
}
draw_list.add_decal(diffuse_bm, glow_bm, normal_bm, decal_time, getDecalTransform(decal, alpha));
}
void renderAll() {
if (!Decal_system_active || !Decal_option_active) {
if (!Decal_system_active || !Decal_option_active || !gr_is_capable(gr_capability::CAPABILITY_INSTANCED_RENDERING)) {
return;
}
@@ -432,51 +473,21 @@ void renderAll() {
++iter;
}
if (active_decals.empty()) {
if (active_decals.empty() && active_single_frame_decals.empty()) {
return;
}
auto mission_time = f2fl(Missiontime);
graphics::decal_draw_list draw_list(active_decals.size());
for (auto& decal : active_decals) {
Assertion(decal.definition_handle >= 0 && decal.definition_handle < (int)DecalDefinitions.size(),
"Invalid decal handle detected!");
auto& decalDef = DecalDefinitions[decal.definition_handle];
int diffuse_bm = -1;
int glow_bm = -1;
int normal_bm = -1;
auto decal_time = mission_time - decal.creation_time;
auto progress = decal_time / decal.lifetime;
float alpha = 1.0f;
if (progress > 0.8) {
// Fade the decal out for the last 20% of its lifetime
alpha = 1.0f - smoothstep(0.8f, 1.0f, progress);
}
if (decalDef.getDiffuseBitmap() >= 0) {
diffuse_bm = decalDef.getDiffuseBitmap()
+ bm_get_anim_frame(decalDef.getDiffuseBitmap(), decal_time, 0.0f, decalDef.isDiffuseLooping());
}
if (decalDef.getGlowBitmap() >= 0) {
glow_bm = decalDef.getGlowBitmap()
+ bm_get_anim_frame(decalDef.getGlowBitmap(), decal_time, 0.0f, decalDef.isGlowLooping());
}
if (decalDef.getNormalBitmap() >= 0) {
normal_bm = decalDef.getNormalBitmap()
+ bm_get_anim_frame(decalDef.getNormalBitmap(), decal_time, 0.0f, decalDef.isNormalLooping());
}
draw_list.add_decal(diffuse_bm, glow_bm, normal_bm, decal_time, getDecalTransform(decal), alpha);
}
graphics::decal_draw_list draw_list;
for (auto& decal : active_decals)
renderDecal(draw_list, decal);
for (auto& decal : active_single_frame_decals)
renderDecal(draw_list, decal);
draw_list.render();
active_single_frame_decals.clear();
}
void addDecal(creation_info& info, const object* host, int submodel, const vec3d& local_pos, const matrix& local_orient) {
@@ -521,4 +532,8 @@ void addDecal(creation_info& info, const object* host, int submodel, const vec3d
active_decals.push_back(newDecal);
}
void addSingleFrameDecal(Decal&& info) {
active_single_frame_decals.push_back(info);
}
}
+21
View File
@@ -50,6 +50,25 @@ class DecalDefinition {
bool isNormalLooping() const;
};
struct Decal {
//DecalDefinition idx vs immediate diffuse/glow/normal
std::variant<int, std::tuple<int, int, int>> definition_handle = -1;
object_h object;
int orig_obj_type = OBJ_NONE;
int submodel = -1;
float creation_time = -1.0f; //!< The mission time at which this decal was created
float lifetime = -1.0f; //!< The time this decal is active. When negative it never expires
vec3d position = vmd_zero_vector;
vec3d scale;
matrix orientation = vmd_identity_matrix;
Decal();
bool isValid() const;
};
extern SCP_vector<DecalDefinition> DecalDefinitions;
extern bool Decal_system_active;
extern bool Decal_option_active;
@@ -139,4 +158,6 @@ void addDecal(creation_info& info,
const vec3d& local_pos,
const matrix& local_orient);
void addSingleFrameDecal(Decal&& info);
}
+8 -5
View File
@@ -65,7 +65,7 @@ $Max Player Attackers: 2, 3, 4, 5, 99
;; maximum number of active (i.e. 'thrown') asteroids that can be
;; heading toward a friendly ship at any given time
$Max Incoming Asteroids: 3, 4, 5, 7, 10
$Max Incoming Asteroids: 3, 4, 5, 7, 10
;; factor applied to damage suffered by the player
$Player Damage Factor: 0.25, 0.5, 0.65, 0.85, 1
@@ -181,9 +181,8 @@ $Player Autoaim FOV: 0, 0, 0, 0, 0
$Detail Distance Multiplier: 0.125, 0.25, 1.0, 4.0, 8.0
;; General AI-related flags. These were previously all lumped together
;; under the New AI mission flag.
;; under the New AI mission flag.
;; if set, big ships can attack a beam turret that's firing on them
;; from a ship that they don't currently have targeted.
$big ships can attack beam turrets on untargeted ships: NO
@@ -354,6 +353,10 @@ $no warp camera: NO
;; If set, this flag overrides the retail behavior whereby a ship
;; assigned to guard a ship in a wing will instead guard the entire wing
$ai guards specific ship in wing: NO
$ai guards specific ship in wing: NO
;; maximum number of allies allowed to be rearming at the same time
;; before the AI will hold off on requesting support
$max allies rearming threshold: 2
#End
+11 -23
View File
@@ -10,6 +10,12 @@ out vec4 fragOut0; // Diffuse buffer
out vec4 fragOut1; // Normal buffer
out vec4 fragOut2; // Emissive buffer
flat in mat4 invModelMatrix;
flat in vec3 decalDirection;
flat in float normal_angle_cutoff;
flat in float angle_fade_start;
flat in float alpha_scale;
uniform sampler2D gDepthBuffer;
uniform sampler2D gNormalBuffer;
@@ -23,24 +29,15 @@ layout (std140) uniform decalGlobalData {
mat4 invViewMatrix;
mat4 invProjMatrix;
vec3 ambientLight;
vec2 viewportSize;
};
layout (std140) uniform decalInfoData {
mat4 model_matrix;
mat4 inv_model_matrix;
vec3 decal_direction;
float normal_angle_cutoff;
int diffuse_index;
int glow_index;
int normal_index;
float angle_fade_start;
float alpha_scale;
int diffuse_blend_mode;
int glow_blend_mode;
};
@@ -77,7 +74,7 @@ vec3 getPixelNormal(vec3 frag_position, vec2 tex_coord, inout float alpha, out v
#endif
//Calculate angle between surface normal and decal direction
float angle = acos(dot(normal, decal_direction));
float angle = acos(dot(normal, decalDirection));
if (angle > normal_angle_cutoff) {
// The angle between surface normal and decal direction is too big
@@ -91,7 +88,7 @@ vec3 getPixelNormal(vec3 frag_position, vec2 tex_coord, inout float alpha, out v
}
vec2 getDecalTexCoord(vec3 view_pos, inout float alpha) {
vec4 object_pos = inv_model_matrix * invViewMatrix * vec4(view_pos, 1.0);
vec4 object_pos = invModelMatrix * invViewMatrix * vec4(view_pos, 1.0);
bvec3 invalidComponents = greaterThan(abs(object_pos.xyz), vec3(0.5));
bvec4 nanComponents = isnan(object_pos); // nan can happen some times if we have an infinite depth value
@@ -111,6 +108,7 @@ void main() {
vec3 frag_position = computeViewPosition(gl_FragCoord.xy);
float alpha = alpha_scale;
vec2 tex_coord = getDecalTexCoord(frag_position, alpha);
vec3 binormal;
@@ -134,16 +132,6 @@ void main() {
// Additive blending
diffuse_out = vec4(color.rgb * alpha, 1.0);
}
// The main model shader applies ambient lighting by drawing the ambient part of the texture into the emissive
// texture. We do the same here to make sure the decal material is applied correctly
if (glow_blend_mode == 0) {
// Normal alpha blending
emissive_out = vec4(color.rgb * ambientLight, color.a * alpha);
} else {
// Additive blending
emissive_out = vec4(alpha * color.rgb * ambientLight, 1.0);
}
}
if (glow_index >= 0) {
+21 -12
View File
@@ -1,5 +1,12 @@
in vec4 vertPosition;
in mat4 vertModelMatrix;
flat out mat4 invModelMatrix;
flat out vec3 decalDirection;
flat out float normal_angle_cutoff;
flat out float angle_fade_start;
flat out float alpha_scale;
layout (std140) uniform decalGlobalData {
mat4 viewMatrix;
@@ -7,27 +14,29 @@ layout (std140) uniform decalGlobalData {
mat4 invViewMatrix;
mat4 invProjMatrix;
vec3 ambientLight;
vec2 viewportSize;
};
layout (std140) uniform decalInfoData {
mat4 model_matrix;
mat4 inv_model_matrix;
vec3 decal_direction;
float normal_angle_cutoff;
int diffuse_index;
int glow_index;
int normal_index;
float angle_fade_start;
float alpha_scale;
int diffuse_blend_mode;
int glow_blend_mode;
};
void main() {
gl_Position = projMatrix * viewMatrix * model_matrix * vertPosition;
normal_angle_cutoff = vertModelMatrix[0][3];
angle_fade_start = vertModelMatrix[1][3];
alpha_scale = vertModelMatrix[2][3];
mat4 modelMatrix = vertModelMatrix;
modelMatrix[0][3] = 0.0;
modelMatrix[1][3] = 0.0;
modelMatrix[2][3] = 0.0;
invModelMatrix = inverse(modelMatrix);
decalDirection = mat3(viewMatrix) * modelMatrix[2].xyz;
gl_Position = projMatrix * viewMatrix * modelMatrix * vertPosition;
}
@@ -7,7 +7,7 @@ out vec4 fragOut5;
void main()
{
fragOut0 = vec4(0.0, 0.0, 0.0, 1.0); // color
fragOut1 = vec4(0.0, 0.0, -1000000.0, 1.0); // position
fragOut1 = vec4(0.0, 0.0, -1000000.0, 0.0); // position
fragOut2 = vec4(0.0, 0.0, 0.0, 1.0); // normal
fragOut3 = vec4(0.0, 0.0, 0.0, 0.0); // specular
fragOut4 = vec4(0.0, 0.0, 0.0, 1.0); // emissive
+92 -25
View File
@@ -1,6 +1,6 @@
//? #version 150
#include "lighting.sdr" //! #include "lighting.sdr"
#include "gamma.sdr" //! #include "gamma.sdr"
#include "shadows.sdr" //! #include "shadows.sdr"
out vec4 fragOut0;
@@ -11,6 +11,11 @@ uniform sampler2D PositionBuffer;
uniform sampler2D SpecBuffer;
uniform sampler2DArray shadow_map;
#ifdef ENV_MAP
uniform samplerCube sEnvmap;
uniform samplerCube sIrrmap;
#endif
layout (std140) uniform globalDeferredData {
mat4 shadow_mv_matrix;
mat4 shadow_proj_matrix[4];
@@ -97,7 +102,7 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
discard;
}
attenuation = 1.0 - clamp(sqrt(dist / lightRadius), 0.0, 1.0);
}
}
else if (lightType == LT_TUBE) { // Tube light
vec3 beamVec = vec3(modelViewMatrix * vec4(0.0, 0.0, -scale.z, 0.0));
vec3 beamDir = normalize(beamVec);
@@ -138,7 +143,7 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
discard;
}
attenuation = 1.0 - clamp(sqrt(dist / lightRadius), 0.0, 1.0);
}
}
else if (lightType == LT_CONE) {
lightDirOut = lightPosition - position.xyz;
float coneDot = dot(normalize(-lightDirOut), coneDir);
@@ -165,46 +170,108 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
}
}
#ifdef ENV_MAP
void ComputeEnvLight(float alpha, float ao, vec3 light_dir, vec3 eyeDir, vec3 normal, vec4 baseColor, vec4 specColor, out vec3 envLight) {
// Bit of a hack here, pretend we're doing blinn-phong shading and use a (modified version of) the derivation from
// Plausible Blinn-Phong Reflection of Standard Cube MIP-Maps - McGuire et al.
// to determine the mip bias.
// We use the specular term listed at http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
// 1/(pi*alpha^2) * NdotM^((2/alpha^2)-2)
// so let s = (2/alpha^2 - 2)
// 1/2 log(s + 1) = 1/2 log(2/alpha^2 -1)
const float ENV_REZ = 512; // Ideally this would be #define'd and shader recompiled with envmap rez changes
const float REZ_BIAS = log2(ENV_REZ * sqrt(3));
float alphaSqr = alpha * alpha;
float rough_bias = 0.5 * log2(2/alphaSqr - 1);
float mip_bias = REZ_BIAS - rough_bias;
// Sample light, using mip bias to blur it.
vec3 env_light_dir = vec3(modelViewMatrix * vec4(light_dir, 0.0));
vec4 specEnvColour = srgb_to_linear(textureLod(sEnvmap, env_light_dir, mip_bias));
vec3 halfVec = normal;
// Lots of hacks here to get things to look right. We aren't loading a BRDF split-sum integral texture
// or properly calculating IBL levels.
// Fresnel calculation as with standard lights.
vec3 fresnel = mix(specColor.rgb, FresnelSchlick(halfVec, eyeDir, specColor.rgb), specColor.a);
// Pseudo-IBL, so use k_IBL
float k = alpha * alpha/ 2.0f;
float NdotL = max(dot(light_dir, normal),0);
float g1vNL = GeometrySchlickGGX(NdotL, k);
vec3 specEnvLighting = specEnvColour.rgb * fresnel * g1vNL;
vec3 kD = vec3(1.0)-fresnel;
kD *= (vec3(1.0) - specColor.rgb);
vec3 diffEnvColor = srgb_to_linear(texture(sIrrmap, vec3(modelViewMatrix * vec4(normal, 0.0))).rgb);
vec3 diffEnvLighting = kD * baseColor.rgb * diffEnvColor * ao;
envLight = (specEnvLighting + diffEnvLighting) * baseColor.a;
}
#endif
void main()
{
vec2 screenPos = gl_FragCoord.xy * vec2(invScreenWidth, invScreenHeight);
vec3 position = texture(PositionBuffer, screenPos).xyz;
vec4 position_buffer = texture(PositionBuffer, screenPos);
vec3 position = position_buffer.xyz;
if(abs(dot(position, position)) < nearPlane * nearPlane)
discard;
vec3 diffColor = texture(ColorBuffer, screenPos).rgb;
vec4 diffuse = texture(ColorBuffer, screenPos);
vec3 diffColor = diffuse.rgb;
vec4 normalData = texture(NormalBuffer, screenPos);
vec4 specColor = texture(SpecBuffer, screenPos);
// The vector in the normal buffer could be longer than the unit vector since decal rendering only adds to the normal buffer
vec3 normal = normalize(normalData.xyz);
float gloss = normalData.a;
float roughness = clamp(1.0f - gloss, 0.0f, 1.0f);
float alpha = roughness * roughness;
float fresnel = specColor.a;
vec3 eyeDir = normalize(-position);
vec3 lightDir;
float attenuation;
float area_normalisation;
vec3 reflectDir = reflect(-eyeDir, normal);
GetLightInfo(position, alpha, reflectDir, lightDir, attenuation, area_normalisation);
vec4 specColor = texture(SpecBuffer, screenPos);
if (enable_shadows) {
vec4 fragShadowPos = shadow_mv_matrix * inv_view_matrix * vec4(position, 1.0);
vec4 fragShadowUV[4];
fragShadowUV[0] = transformToShadowMap(shadow_proj_matrix[0], 0, fragShadowPos);
fragShadowUV[1] = transformToShadowMap(shadow_proj_matrix[1], 1, fragShadowPos);
fragShadowUV[2] = transformToShadowMap(shadow_proj_matrix[2], 2, fragShadowPos);
fragShadowUV[3] = transformToShadowMap(shadow_proj_matrix[3], 3, fragShadowPos);
vec4 fragmentColor = vec4(1.0);
attenuation *= getShadowValue(shadow_map, -position.z, fragShadowPos.z, fragShadowUV, fardist, middist,
neardist, veryneardist);
if (lightType == LT_AMBIENT) {
float ao = position_buffer.w;
fragmentColor.rgb = diffuseLightColor * diffColor * ao;
#ifdef ENV_MAP
vec3 envLight;
ComputeEnvLight(alpha, ao, reflectDir, eyeDir, normal, diffuse, specColor, envLight);
fragmentColor.rgb += envLight;
#endif
}
else {
float fresnel = specColor.a;
vec3 lightDir;
float attenuation;
float area_normalisation;
GetLightInfo(position, alpha, reflectDir, lightDir, attenuation, area_normalisation);
if (enable_shadows) {
vec4 fragShadowPos = shadow_mv_matrix * inv_view_matrix * vec4(position, 1.0);
vec4 fragShadowUV[4];
fragShadowUV[0] = transformToShadowMap(shadow_proj_matrix[0], 0, fragShadowPos);
fragShadowUV[1] = transformToShadowMap(shadow_proj_matrix[1], 1, fragShadowPos);
fragShadowUV[2] = transformToShadowMap(shadow_proj_matrix[2], 2, fragShadowPos);
fragShadowUV[3] = transformToShadowMap(shadow_proj_matrix[3], 3, fragShadowPos);
attenuation *= getShadowValue(shadow_map, -position.z, fragShadowPos.z, fragShadowUV, fardist, middist,
neardist, veryneardist);
}
vec3 halfVec = normalize(lightDir + eyeDir);
float NdotL = clamp(dot(normal, lightDir), 0.0, 1.0);
fragmentColor.rgb = computeLighting(specColor.rgb, diffColor, lightDir, normal.xyz, halfVec, eyeDir, roughness, fresnel, NdotL).rgb * diffuseLightColor * attenuation * area_normalisation;
}
vec3 halfVec = normalize(lightDir + eyeDir);
float NdotL = clamp(dot(normal, lightDir), 0.0, 1.0);
vec4 fragmentColor = vec4(1.0);
fragmentColor.rgb = computeLighting(specColor.rgb, diffColor, lightDir, normal.xyz, halfVec, eyeDir, roughness, fresnel, NdotL).rgb * diffuseLightColor * attenuation * area_normalisation;
fragOut0 = max(fragmentColor, vec4(0.0));
}
+1 -1
View File
@@ -29,7 +29,7 @@ layout (std140) uniform lightData {
void main()
{
if (lightType == LT_DIRECTIONAL) {
if (lightType == LT_DIRECTIONAL || lightType == LT_AMBIENT) {
gl_Position = vec4(vertPosition.xyz, 1.0);
} else {
gl_Position = projMatrix * modelViewMatrix * vec4(vertPosition.xyz * scale, 1.0);
+1
View File
@@ -3,6 +3,7 @@ const int LT_DIRECTIONAL = 0; // A light like a sun
const int LT_POINT = 1; // A point light, like an explosion
const int LT_TUBE = 2; // A tube light, like a fluorescent light
const int LT_CONE = 3; // A cone light, like a flood light
const int LT_AMBIENT = 4; // Directionless ambient light
const float SPEC_FACTOR_NO_SPEC_MAP = 0.1;
const float GLOW_MAP_INTENSITY = 1.5;
+17 -75
View File
@@ -32,7 +32,6 @@ layout (std140) uniform modelData {
mat4 textureMatrix;
mat4 shadow_mv_matrix;
mat4 shadow_proj_matrix[4];
mat4 envMatrix;
vec4 color;
@@ -50,6 +49,7 @@ layout (std140) uniform modelData {
int n_lights;
float defaultGloss;
//EXCLUSIVELY used for non-deferred rendering
vec3 ambientFactor;
int desaturate;
@@ -85,22 +85,16 @@ layout (std140) uniform modelData {
float fardist;
int sGlowmapIndex;
int sSpecmapIndex;
int sNormalmapIndex;
int sAmbientmapIndex;
int sMiscmapIndex;
float alphaMult;
int flags;
};
in VertexOutput {
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
vec3 envReflect;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
mat3 tangentMatrix;
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
@@ -126,10 +120,6 @@ uniform sampler2DArray sGlowmap;
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SPEC
uniform sampler2DArray sSpecmap;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SPEC
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
uniform samplerCube sEnvmap;
uniform samplerCube sIrrmap;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_NORMAL
uniform sampler2DArray sNormalmap;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_NORMAL
@@ -187,7 +177,7 @@ void GetLightInfo(int i, out vec3 lightDir, out float attenuation)
vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial, float gloss, float fresnel, float shadow, float aoFactor)
{
vec3 eyeDir = vec3(normalize(-vertIn.position).xyz);
vec3 lightAmbient = (emissionFactor + ambientFactor * ambientFactor) * aoFactor; // ambientFactor^2 due to legacy OpenGL compatibility behavior
vec3 lightAmbient = ambientFactor * aoFactor;
vec3 lightDiffuse = vec3(0.0, 0.0, 0.0);
vec3 lightSpecular = vec3(0.0, 0.0, 0.0);
#pragma optionNV unroll all
@@ -342,19 +332,7 @@ void main()
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_MISC
// Lights aren't applied when we are rendering to the G-buffers since that gets handled later
#prereplace IF_FLAG MODEL_SDR_FLAG_DEFERRED
#prereplace IF_FLAG MODEL_SDR_FLAG_LIGHT
// Ambient lighting still needs to be done since that counts as an "emissive" color
vec3 lightAmbient = (emissionFactor + ambientFactor * ambientFactor) * aoFactors.x; // ambientFactor^2 due to legacy OpenGL compatibility behavior
emissiveColor.rgb += baseColor.rgb * lightAmbient;
#prereplace ELSE_FLAG //MODEL_SDR_FLAG_LIGHT
#prereplace IF_FLAG MODEL_SDR_FLAG_SPEC
baseColor.rgb += pow(1.0 - clamp(dot(eyeDir, normal), 0.0, 1.0), 5.0 * clamp(glossData, 0.01, 1.0)) * specColor.rgb;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SPEC
// If there is no lighting then we copy the color data so far into the
emissiveColor.rgb += baseColor.rgb;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_LIGHT
#prereplace ELSE_FLAG //MODEL_SDR_FLAG_DEFERRED
#prereplace IF_NOT_FLAG MODEL_SDR_FLAG_DEFERRED
#prereplace IF_FLAG MODEL_SDR_FLAG_LIGHT
float shadow = 1.0;
#prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS
@@ -368,54 +346,6 @@ void main()
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_LIGHT
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DEFERRED
#prereplace IF_FLAG MODEL_SDR_FLAG_ENV
#prereplace IF_FLAG MODEL_SDR_FLAG_LIGHT
// Bit of a hack here, pretend we're doing blinn-phong shading and use a (modified version of) the derivation from
// Plausible Blinn-Phong Reflection of Standard Cube MIP-Maps - McGuire et al.
// to determine the mip bias.
// We use the specular term listed at http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
// 1/(pi*alpha^2) * NdotM^((2/alpha^2)-2)
// so let s = (2/alpha^2 - 2)
// 1/2 log(s + 1) = 1/2 log(2/alpha^2 -1)
const float ENV_REZ = 512; // Ideally this would be #define'd and shader recompiled with envmap rez changes
const float REZ_BIAS = log2(ENV_REZ * sqrt(3));
float roughness = clamp(1.0f - glossData, 0.0f, 1.0f);
float alpha = roughness * roughness;
float alphaSqr = alpha * alpha;
float rough_bias = 0.5 * log2(2/alphaSqr - 1);
float mip_bias = REZ_BIAS - rough_bias;
// Sample light, using mip bias to blur it.
vec3 light_dir = reflect(-eyeDir, normal);
vec3 env_light_dir = vec3(envMatrix * vec4(light_dir, 0.0));
vec4 specEnvColour = srgb_to_linear(textureLod(sEnvmap, env_light_dir, mip_bias));
vec3 halfVec = normal;
// Lots of hacks here to get things to look right. We aren't loading a BRDF split-sum integral texture
// or properly calculating IBL levels.
// Fresnel calculation as with standard lights.
vec3 fresnel = mix(specColor.rgb, FresnelSchlick(halfVec, eyeDir, specColor.rgb), specColor.a);
// Pseudo-IBL, so use k_IBL
float k = alpha * alpha/ 2.0f;
float NdotL = max(dot(light_dir, normal),0);
float g1vNL = GeometrySchlickGGX(NdotL, k);
vec3 specEnvLighting = specEnvColour.rgb * fresnel * g1vNL;
vec3 kD = vec3(1.0)-fresnel;
kD *= (vec3(1.0) - specColor.rgb);
vec3 diffEnvColor = srgb_to_linear(texture(sIrrmap, vec3(envMatrix * vec4(normal, 0.0))).rgb);
vec3 diffEnvLighting = kD * baseColor.rgb * diffEnvColor * aoFactors.x;
emissiveColor.rgb += (specEnvLighting + diffEnvLighting) * baseColor.a;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_LIGHT
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_ENV
#prereplace IF_FLAG MODEL_SDR_FLAG_GLOW
vec3 glowColor = texture(sGlowmap, vec3(texCoord, float(sGlowmapIndex))).rgb;
#prereplace IF_FLAG MODEL_SDR_FLAG_MISC
@@ -483,10 +413,22 @@ void main()
baseColor.rgb += emissiveColor.rgb;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DEFERRED
#prereplace IF_FLAG MODEL_SDR_FLAG_DEFERRED
#prereplace IF_NOT_FLAG MODEL_SDR_FLAG_LIGHT
#prereplace IF_FLAG MODEL_SDR_FLAG_SPEC
baseColor.rgb += pow(1.0 - clamp(dot(eyeDir, normal), 0.0, 1.0), 5.0 * clamp(glossData, 0.01, 1.0)) * specColor.rgb;
glossData = 0;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SPEC
// If there is no lighting then we copy the color data so far into the emissive.
emissiveColor.rgb += baseColor.rgb;
aoFactors.x = 0;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_LIGHT
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DEFERRED
fragOut0 = baseColor;
#prereplace IF_FLAG MODEL_SDR_FLAG_DEFERRED
fragOut1 = vec4(vertIn.position.xyz, 1.0);
fragOut1 = vec4(vertIn.position.xyz, aoFactors.x);
fragOut2 = vec4(normal, glossData);
fragOut3 = vec4(specColor.rgb, fresnelFactor);
fragOut4 = emissiveColor;
-17
View File
@@ -39,7 +39,6 @@ layout (std140) uniform modelData {
mat4 textureMatrix;
mat4 shadow_mv_matrix;
mat4 shadow_proj_matrix[4];
mat4 envMatrix;
vec4 color;
@@ -103,10 +102,6 @@ layout (std140) uniform modelData {
};
in VertexOutput {
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
vec3 envReflect;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
mat3 tangentMatrix;
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
@@ -131,10 +126,6 @@ in VertexOutput {
} vertIn[];
out VertexOutput {
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
vec3 envReflect;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
mat3 tangentMatrix;
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
@@ -181,10 +172,6 @@ out VertexOutput {
gl_Layer = instanceID;
#prereplace IF_FLAG MODEL_SDR_FLAG_ENV
vertOut.envReflect = vertIn[vert].envReflect;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_ENV
#prereplace IF_FLAG MODEL_SDR_FLAG_NORMAL
vertOut.tangentMatrix = vertIn[vert].tangentMatrix;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_NORMAL
@@ -239,10 +226,6 @@ out VertexOutput {
vertOut.normal = vertIn[vert].normal;
vertOut.texCoord = vertIn[vert].texCoord;
#prereplace IF_FLAG MODEL_SDR_FLAG_ENV
vertOut.envReflect = vertIn[vert].envReflect;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_ENV
#prereplace IF_FLAG MODEL_SDR_FLAG_NORMAL
vertOut.tangentMatrix = vertIn[vert].tangentMatrix;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_NORMAL
-11
View File
@@ -34,7 +34,6 @@ layout (std140) uniform modelData {
mat4 textureMatrix;
mat4 shadow_mv_matrix;
mat4 shadow_proj_matrix[4];
mat4 envMatrix;
vec4 color;
@@ -102,10 +101,6 @@ uniform samplerBuffer transform_tex;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM
out VertexOutput {
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
vec3 envReflect;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_ENV
mat3 tangentMatrix;
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
@@ -193,12 +188,6 @@ void main()
vec3 b = cross(normal, t) * vertTangent.w;
vertOut.tangentMatrix = mat3(t, b, normal);
#prereplace IF_FLAG MODEL_SDR_FLAG_ENV
// Environment mapping reflection vector.
vec3 envReflect = reflect(normalize(position.xyz), normal);
vertOut.envReflect = vec3(envMatrix * vec4(envReflect, 0.0));
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_ENV
#prereplace IF_FLAG MODEL_SDR_FLAG_FOG
vertOut.fogDist = clamp((gl_Position.z - fogStart) * 0.75 * fogScale, 0.0, 1.0);
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_FOG
@@ -14,22 +14,21 @@ SDR_FLAG(MODEL_SDR_FLAG_HDR , (1 << 2) , false)
SDR_FLAG(MODEL_SDR_FLAG_DIFFUSE , (1 << 3) , false)
SDR_FLAG(MODEL_SDR_FLAG_GLOW , (1 << 4) , false)
SDR_FLAG(MODEL_SDR_FLAG_SPEC , (1 << 5) , false)
SDR_FLAG(MODEL_SDR_FLAG_ENV , (1 << 6) , false)
SDR_FLAG(MODEL_SDR_FLAG_NORMAL , (1 << 7) , false)
SDR_FLAG(MODEL_SDR_FLAG_AMBIENT , (1 << 8) , false)
SDR_FLAG(MODEL_SDR_FLAG_MISC , (1 << 9) , false)
SDR_FLAG(MODEL_SDR_FLAG_TEAMCOLOR , (1 << 10), false)
SDR_FLAG(MODEL_SDR_FLAG_FOG , (1 << 11), false)
SDR_FLAG(MODEL_SDR_FLAG_TRANSFORM , (1 << 12), false)
SDR_FLAG(MODEL_SDR_FLAG_SHADOWS , (1 << 13), false)
SDR_FLAG(MODEL_SDR_FLAG_THRUSTER , (1 << 14), false)
SDR_FLAG(MODEL_SDR_FLAG_ALPHA_MULT , (1 << 15), false)
SDR_FLAG(MODEL_SDR_FLAG_NORMAL , (1 << 6) , false)
SDR_FLAG(MODEL_SDR_FLAG_AMBIENT , (1 << 7) , false)
SDR_FLAG(MODEL_SDR_FLAG_MISC , (1 << 8) , false)
SDR_FLAG(MODEL_SDR_FLAG_TEAMCOLOR , (1 << 9), false)
SDR_FLAG(MODEL_SDR_FLAG_FOG , (1 << 10), false)
SDR_FLAG(MODEL_SDR_FLAG_TRANSFORM , (1 << 11), false)
SDR_FLAG(MODEL_SDR_FLAG_SHADOWS , (1 << 12), false)
SDR_FLAG(MODEL_SDR_FLAG_THRUSTER , (1 << 13), false)
SDR_FLAG(MODEL_SDR_FLAG_ALPHA_MULT , (1 << 14), false)
#ifndef MODEL_SDR_FLAG_MODE_GLSL
//The following ones are used ONLY as compile-time flags, but they still need to be defined here to ensure no conflict occurs
//But since these are checked with ifdefs even for the large shader, they must never be available in GLSL mode
SDR_FLAG(MODEL_SDR_FLAG_SHADOW_MAP , (1 << 16), true)
SDR_FLAG(MODEL_SDR_FLAG_THICK_OUTLINES, (1 << 17), true)
SDR_FLAG(MODEL_SDR_FLAG_SHADOW_MAP , (1 << 15), true)
SDR_FLAG(MODEL_SDR_FLAG_THICK_OUTLINES, (1 << 16), true)
#endif
+8
View File
@@ -18,6 +18,14 @@ layout (std140) uniform genericData {
vec3 tint;
float dither;
// these are blank, valid slots for modders to create custom effects
// that can be defined in post_processing.tbl and coded below
vec3 custom_effect_vec3_a;
float custom_effect_float_a;
vec3 custom_effect_vec3_b;
float custom_effect_float_b;
};
void main()
+58 -37
View File
@@ -26,48 +26,59 @@ float sampleNoPCF(sampler2DArray shadow_map, float shadowDepth, int cascade, vec
return computeShadowFactor(shadowDepth, sampleShadowMap(shadow_map, shadowUV[cascade].xy, vec2(0.0, 0.0), cascade, 1.0/1024.0), 0.05);
}
float samplePoissonPCF(sampler2DArray shadow_map, float shadowDepth, int cascade, vec4 shadowUV[4])
float samplePoissonPCF(sampler2DArray shadow_map, float shadowDepth, int cascade, vec4 shadowUV[4], bool use_simple_pass)
{
if(cascade > 3 || cascade < 0) return 1.0;
vec2 poissonDisc[16];
poissonDisc[0] = vec2(-0.76275, -0.3432573);
poissonDisc[1] = vec2(-0.5226235, -0.8277544);
poissonDisc[2] = vec2(-0.3780261, 0.01528688);
poissonDisc[3] = vec2(-0.7742821, 0.4245702);
poissonDisc[4] = vec2(0.04196143, -0.02622231);
poissonDisc[5] = vec2(-0.2974772, -0.4722782);
poissonDisc[6] = vec2(-0.516093, 0.71495);
poissonDisc[7] = vec2(-0.3257416, 0.3910343);
poissonDisc[8] = vec2(0.2705966, 0.6670476);
poissonDisc[9] = vec2(0.4918377, 0.1853267);
poissonDisc[10] = vec2(0.4428544, -0.6251478);
poissonDisc[11] = vec2(-0.09204347, 0.9267113);
poissonDisc[12] = vec2(0.391505, -0.2558275);
poissonDisc[13] = vec2(0.05605913, -0.7570801);
poissonDisc[14] = vec2(0.81772, -0.02475523);
poissonDisc[15] = vec2(0.6890262, 0.5191521);
vec2 poissonDisc[16] = vec2[](
vec2(-0.76275, -0.3432573),
vec2(-0.5226235, -0.8277544),
vec2(-0.3780261, 0.01528688),
vec2(-0.7742821, 0.4245702),
vec2(0.04196143, -0.02622231),
vec2(-0.2974772, -0.4722782),
vec2(-0.516093, 0.71495),
vec2(-0.3257416, 0.3910343),
vec2(0.2705966, 0.6670476),
vec2(0.4918377, 0.1853267),
vec2(0.4428544, -0.6251478),
vec2(-0.09204347, 0.9267113),
vec2(0.391505, -0.2558275),
vec2(0.05605913, -0.7570801),
vec2(0.81772, -0.02475523),
vec2(0.6890262, 0.5191521)
);
float maxUVOffset[4];
maxUVOffset[0] = 1.0/300.0;
maxUVOffset[1] = 1.0/250.0;
maxUVOffset[2] = 1.0/200.0;
maxUVOffset[3] = 1.0/200.0;
vec2 sum = sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[0], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[1], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[2], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[3], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[4], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[5], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[6], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[7], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[8], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[9], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[10], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[11], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[12], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[13], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[14], cascade, maxUVOffset[cascade])*(1.0/16.0);
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[15], cascade, maxUVOffset[cascade])*(1.0/16.0);
return computeShadowFactor(shadowDepth, sum, 0.1);
if (use_simple_pass) {
// internal cockpit shadows
// bias is a bit smaller to pick up on close/sharp cockpit geometry
// favors shadow over light to avoid complex geometry halos
float visibility = 1.0f;
for (int i=0; i<16; i++) {
vec2 shadow_sample = sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[i], cascade, maxUVOffset[cascade]);
// extra bias value tuned for this distance
if( ((shadow_sample.x - 0.002f) > shadowDepth) ) {
visibility -= (1.0f/16.0f);
}
}
return visibility;
} else {
// default external shadows
// bias is a bit larger to only pick up larger geometry
// favors light over shadow to avoid flickering/spotty shadows
vec2 sum = vec2(0.0f);
for (int i=0; i<16; i++) {
sum += sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[i], cascade, maxUVOffset[cascade]);
}
return computeShadowFactor(shadowDepth, sum*(1.0f/16.0f), 0.1f);
}
}
float getShadowValue(sampler2DArray shadow_map, float depth, float shadowDepth, vec4 shadowUV[4], float fardist,
@@ -86,10 +97,20 @@ float getShadowValue(sampler2DArray shadow_map, float depth, float shadowDepth,
cascade_start_dist[3] = middist;
cascade_start_dist[4] = fardist;
if(cascade > 3 || cascade < 0) return 1.0;
bool use_simple_pass;
if (fardist < 50.0f) {
// internal cockpit shadows
use_simple_pass = true;
} else {
// default external shadows
use_simple_pass = false;
}
float dist_threshold = (cascade_start_dist[cascade+1] - cascade_start_dist[cascade])*0.2;
if(cascade_start_dist[cascade+1] - dist_threshold > depth)
return samplePoissonPCF(shadow_map, shadowDepth, cascade, shadowUV);
return mix(samplePoissonPCF(shadow_map, shadowDepth, cascade, shadowUV), samplePoissonPCF(shadow_map, shadowDepth, cascade+1, shadowUV),
return samplePoissonPCF(shadow_map, shadowDepth, cascade, shadowUV, use_simple_pass);
return mix(samplePoissonPCF(shadow_map, shadowDepth, cascade, shadowUV, use_simple_pass), samplePoissonPCF(shadow_map, shadowDepth, cascade+1, shadowUV, use_simple_pass),
smoothstep(cascade_start_dist[cascade+1] - dist_threshold, cascade_start_dist[cascade+1], depth));
}
+4 -6
View File
@@ -100,8 +100,6 @@ void main()
vec3 sampleposition = position / nebSize + 0.5;
vec4 volume_sample = textureGrad(volume_tex, sampleposition, gradX, gradY);
float stepsize_current = min(max(stepsize, volume_sample.x * udfScale), mintMax - stept);
#ifdef DO_EDGE_SMOOTHING
//Average 3D texel with texels on corner, in an attempt to reduce jagged edges.
float stepcolor_alpha = volume_sample.a;
@@ -121,6 +119,8 @@ void main()
float stepcolor_alpha = volume_sample.a;
#endif
float stepsize_current = min(max(stepsize, step(stepcolor_alpha, 0.01) * volume_sample.x * udfScale), mintMax - stept);
float stepalpha = -(pow(alphalim, 1.0 / (opacitydistance / stepsize_current)) - 1.0f) * stepcolor_alpha;
//All the following computations are just required if we have a stepcoloralpha that is non-zero.
if(stepcolor_alpha > 0.01)
@@ -151,7 +151,7 @@ void main()
//Emissive
cumnebdist += stepcolor_alpha * stepsize_current;
vec3 emissive_lod = textureLod(emissive, fragTexCoord.xy, clamp(cumnebdist * emissiveSpreadFactor, 0, float(textureQueryLevels(emissive) - 1))).rgb;
vec3 stepcolor_emissive = emissive_lod.rgb * pow(alphalim, 1.0 / (opacitydistance / ((depth - stept) * emissiveFalloff + 0.01))) * emissiveIntensity;
vec3 stepcolor_emissive = clamp(emissive_lod.rgb * pow(alphalim, 1.0 / (opacitydistance / ((depth - stept) * emissiveFalloff + 0.01))) * emissiveIntensity, 0, 1);
//Step finish
vec3 stepcolor = clamp(stepcolor_diffuse + stepcolor_emissive, 0, 1);
@@ -165,7 +165,5 @@ void main()
break;
}
cumcolor += cumOMAlpha * color_in.rgb;
fragOut0 = vec4(cumcolor, 1);
fragOut0 = vec4(cumOMAlpha * color_in.rgb + ((1.0 - cumOMAlpha) * cumcolor), 1);
}
+5 -5
View File
@@ -110,9 +110,9 @@ $Fog:
+Start dist: 10.0
+Compl dist: 500.0
$AI:
+Valid goals: ( "fly to ship" "attack ship" "waypoints" "waypoints once" "depart" "attack subsys" "attack wing" "guard ship" "disable ship" "disable ship (tactical)" "disarm ship" "disarm ship (tactical)" "attack any" "attack ship class" "ignore ship" "ignore ship (new)" "guard wing" "evade ship" "stay still" "play dead" "play dead (persistent)" "stay near ship" "keep safe dist" "form on wing")
+Valid goals: ( "fly to ship" "attack ship" "waypoints" "waypoints once" "depart" "attack subsys" "attack wing" "guard ship" "disable ship" "disable ship (tactical)" "disarm ship" "disarm ship (tactical)" "attack any" "attack ship class" "attack ship type" "ignore ship" "ignore ship (new)" "guard wing" "evade ship" "stay still" "play dead" "play dead (persistent)" "stay near ship" "keep safe dist" "form on wing")
+Accept Player Orders: YES
+Player Orders: ( "attack ship" "disable ship" "disable ship (tactical)" "disarm ship" "disarm ship (tactical)" "guard ship" "ignore ship" "ignore ship (new)" "form on wing" "cover me" "attack any" "attack ship class" "depart" "disable subsys" )
+Player Orders: ( "attack ship" "disable ship" "disable ship (tactical)" "disarm ship" "disarm ship (tactical)" "guard ship" "ignore ship" "ignore ship (new)" "form on wing" "cover me" "attack any" "attack ship class" "attack ship type" "depart" "disable subsys" )
+Auto attacks: YES
+Actively Pursues: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "fighter" "bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Guards attack this: YES
@@ -138,9 +138,9 @@ $Fog:
+Start dist: 10.0
+Compl dist: 500.0
$AI:
+Valid goals: ( "fly to ship" "attack ship" "waypoints" "waypoints once" "depart" "attack subsys" "attack wing" "guard ship" "disable ship" "disable ship (tactical)" "disarm ship" "disarm ship (tactical)" "attack any" "attack ship class" "ignore ship" "ignore ship (new)" "guard wing" "evade ship" "stay still" "play dead" "play dead (persistent)" "stay near ship" "keep safe dist" "form on wing" )
+Valid goals: ( "fly to ship" "attack ship" "waypoints" "waypoints once" "depart" "attack subsys" "attack wing" "guard ship" "disable ship" "disable ship (tactical)" "disarm ship" "disarm ship (tactical)" "attack any" "attack ship class" "attack ship type" "ignore ship" "ignore ship (new)" "guard wing" "evade ship" "stay still" "play dead" "play dead (persistent)" "stay near ship" "keep safe dist" "form on wing" )
+Accept Player Orders: YES
+Player Orders: ( "attack ship" "disable ship" "disable ship (tactical)" "disarm ship" "disarm ship (tactical)" "guard ship" "ignore ship" "ignore ship (new)" "form on wing" "cover me" "attack any" "attack ship class" "depart" "disable subsys" )
+Player Orders: ( "attack ship" "disable ship" "disable ship (tactical)" "disarm ship" "disarm ship (tactical)" "guard ship" "ignore ship" "ignore ship (new)" "form on wing" "cover me" "attack any" "attack ship class" "attack ship type" "depart" "disable subsys" )
+Auto attacks: YES
+Actively Pursues: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "fighter" "bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Guards attack this: YES
@@ -156,6 +156,7 @@ $Praise Destruction: YES
$On Hotkey List: YES
$Target as Threat: YES
$Show Attack Direction: YES
$Scannable: YES
$Max Debris Speed: 150
$FF Multiplier: 1.0
$EMP Multiplier: 2.0
@@ -186,7 +187,6 @@ $Praise Destruction: YES
$On Hotkey List: YES
$Target as Threat: YES
$Show Attack Direction: YES
$Scannable: YES
$Max Debris Speed: 150
$FF Multiplier: 1.0
$EMP Multiplier: 1.75
+10 -1
View File
@@ -63,5 +63,14 @@ $AlwaysOn: false
$Default: 0.0
$Div: 50
$Add: 0
$Name: tint
$Uniform: tint
$Define: FLAG_TINT
$AlwaysOn: false
$Default: 0.0
$Div: 1
$Add: 0
$RGB: 0.2,0.2,0.2
#End
+44 -43
View File
@@ -437,48 +437,49 @@ static void parse_fireball_tbl(const char *table_filename)
default:
error_display(0, "Invalid warp model style. Must be classic or cinematic.");
}
// Set warp_model_style options if cinematic style is chosen
if (fi->warp_model_style == warp_style::CINEMATIC) {
if (optional_string("+Warp size ratio:")) {
stuff_float(&fi->warp_size_ratio);
} else {
fi->warp_size_ratio = 1.6f;
}
// The first two values need to be implied multiples of PI
// for convenience. These shouldn't need to be faster than a full
// rotation per second, which is already ridiculous.
if (optional_string("+Rotation anim:")) {
stuff_float_list(fi->rot_anim, 3);
CLAMP(fi->rot_anim[0], 0.0f, 2.0f);
CLAMP(fi->rot_anim[1], 0.0f, 2.0f);
fi->rot_anim[2] = MAX(0.0f, fi->rot_anim[2]);
} else {
// PI / 2.75f, PI / 10.0f, 2.0f
fi->rot_anim[0] = 0.365f;
fi->rot_anim[1] = 0.083f;
fi->rot_anim[2] = 2.0f;
}
// Variable frame rate for faster propagation of ripples
if (optional_string("+Frame anim:")) {
stuff_float_list(fi->frame_anim, 3);
// A frame rate that is 4 times the normal speed is ridiculous
CLAMP(fi->frame_anim[0], 0.0f, 4.0f);
CLAMP(fi->frame_anim[1], 1.0f, 4.0f);
fi->frame_anim[2] = MAX(0.0f, fi->frame_anim[2]);
} else {
fi->frame_anim[0] = 1.0f;
fi->frame_anim[1] = 1.0f;
fi->frame_anim[2] = 3.0f;
}
}
} else if (first_time) {
fi->warp_model_style = warp_style::CLASSIC;
}
// Set warp_model_style options if cinematic style is chosen
if (fi->warp_model_style == warp_style::CINEMATIC) {
if (optional_string("+Warp size ratio:")) {
stuff_float(&fi->warp_size_ratio);
} else {
fi->warp_size_ratio = 1.6f;
}
// The first two values need to be implied multiples of PI
// for convenience. These shouldn't need to be faster than a full
// rotation per second, which is already ridiculous.
if (optional_string("+Rotation anim:")) {
stuff_float_list(fi->rot_anim, 3);
CLAMP(fi->rot_anim[0], 0.0f, 2.0f);
CLAMP(fi->rot_anim[1], 0.0f, 2.0f);
fi->rot_anim[2] = MAX(0.0f, fi->rot_anim[2]);
} else {
// PI / 2.75f, PI / 10.0f, 2.0f
fi->rot_anim[0] = 0.365f;
fi->rot_anim[1] = 0.083f;
fi->rot_anim[2] = 2.0f;
}
// Variable frame rate for faster propagation of ripples
if (optional_string("+Frame anim:")) {
stuff_float_list(fi->frame_anim, 3);
// A frame rate that is 4 times the normal speed is ridiculous
CLAMP(fi->frame_anim[0], 0.0f, 4.0f);
CLAMP(fi->frame_anim[1], 1.0f, 4.0f);
fi->frame_anim[2] = MAX(0.0f, fi->frame_anim[2]);
} else {
fi->frame_anim[0] = 1.0f;
fi->frame_anim[1] = 1.0f;
fi->frame_anim[2] = 3.0f;
}
}
}
required_string("#End");
@@ -1150,12 +1151,12 @@ static float cutscene_wormhole(float t) {
float fireball_wormhole_intensity(fireball *fb)
{
float t = fb->time_elapsed;
float rad = cutscene_wormhole(t / fb->warp_open_duration);
float rad;
fireball_info* fi = &Fireball_info[fb->fireball_info_index];
if (fi->warp_model_style == warp_style::CINEMATIC) {
rad = cutscene_wormhole(t / fb->warp_open_duration);
rad *= cutscene_wormhole((fb->total_time - t) / fb->warp_close_duration);
rad /= cutscene_wormhole(fb->total_time / (2.0f * fb->warp_open_duration));
rad /= cutscene_wormhole(fb->total_time / (2.0f * fb->warp_close_duration));
@@ -1268,7 +1269,7 @@ void fireball_render(object* obj, model_draw_list *scene)
flare_rad,
fi->flicker_magnitude,
obj->radius,
fi->use_3d_warp || (fb->flags & FBF_WARP_3D) != 0,
!Is_standalone && (fi->use_3d_warp || (fb->flags & FBF_WARP_3D) != 0),
fi->warp_glow_bitmap,
fi->warp_ball_bitmap,
fi->warp_model_id,
@@ -1295,7 +1296,7 @@ int fireball_get_count()
void stuff_fireball_index_list(SCP_vector<int> &list, const char *name)
{
stuff_int_list(list, RAW_INTEGER_TYPE);
stuff_int_list(list, ParseLookupType::FIREBALL_INFO_TYPE);
list.erase(std::remove_if(list.begin(), list.end(), [&](int index) {
if (!SCP_vector_inbounds(Fireball_info, index)) {
+7 -7
View File
@@ -324,7 +324,7 @@ void parse_colors(const char *filename)
// if so, get its rgba values and initialise it using them
mprintf(("'%s' has been redefined.\n", color_names[i]));
//if (check_for_string("(")) {
stuff_int_list(rgba, 4, RAW_INTEGER_TYPE);
stuff_int_list(rgba, 4, ParseLookupType::RAW_INTEGER_TYPE);
for (j = 0; j < 4; j++) {
if (rgba[j] < 0) {
Warning(LOCATION, "RGBA value for '%s' in %s too low (%d), capping to 0.\n", color_names[i], filename, rgba[j]);
@@ -388,9 +388,9 @@ void parse_everything_else(const char *filename)
Warning(LOCATION, "Team color in '%s' defined with a name of '%s'; this won't be usable due to 'None' being used for a lack of a team color by the engine.\n", filename, temp2);
}
if (required_string("$Team Stripe Color:")) {
if (required_string_either("$Team Stripe Color:", "$Team Stripe Colour:", true) >= 0) {
int rgb[3];
stuff_int_list(rgb, 3, RAW_INTEGER_TYPE);
stuff_int_list(rgb, 3, ParseLookupType::RAW_INTEGER_TYPE);
for (i = 0; i < 3; i++) {
CLAMP(rgb[i], 0, 255);
}
@@ -400,9 +400,9 @@ void parse_everything_else(const char *filename)
temp_color.stripe.b = rgb[2] / 255.0f;
}
if (required_string("$Team Base Color:")) {
if (required_string_either("$Team Base Color:", "$Team Base Colour:", true) >= 0) {
int rgb[3];
stuff_int_list(rgb, 3, RAW_INTEGER_TYPE);
stuff_int_list(rgb, 3, ParseLookupType::RAW_INTEGER_TYPE);
for (i = 0; i < 3; i++) {
CLAMP(rgb[i], 0, 255);
}
@@ -445,7 +445,7 @@ void parse_everything_else(const char *filename)
mprintf(("'%s' has been redefined.\n", color_names[i]));
if (check_for_string("(")) {
// If we have a list of integers, use them.
stuff_int_list(rgba, 4, RAW_INTEGER_TYPE);
stuff_int_list(rgba, 4, ParseLookupType::RAW_INTEGER_TYPE);
for (j = 0; j < 4; j++) {
if (rgba[j] < 0)
{
@@ -523,7 +523,7 @@ void parse_everything_else(const char *filename)
rgba[3] = 255; // Odds are pretty high you want it to have full alpha...
if (check_for_string("(")) {
stuff_int_list(rgba, 4, RAW_INTEGER_TYPE);
stuff_int_list(rgba, 4, ParseLookupType::RAW_INTEGER_TYPE);
for (j = 0; j < 4; j++) {
if (rgba[j] < 0)
{
+1 -1
View File
@@ -105,7 +105,7 @@ BOOL SCP_mspdbcs_ResolveSymbol( HANDLE hProcess, UINT_PTR dwAddress, SCP_mspdbcs
if ( siSymbol.dwOffset != 0 )
{
sprintf_s( szWithOffset, SCP_MSPDBCS_MAX_SYMBOL_LENGTH, "%s + %lld bytes", pszSymbol, siSymbol.dwOffset );
snprintf( szWithOffset, SCP_MSPDBCS_MAX_SYMBOL_LENGTH, "%s + %lld bytes", pszSymbol, siSymbol.dwOffset );
szWithOffset[ SCP_MSPDBCS_MAX_SYMBOL_LENGTH - 1 ] = '\0'; /* Because sprintf doesn't guarantee NULL terminating */
pszSymbol = szWithOffset;
}
+14 -9
View File
@@ -308,6 +308,20 @@ constexpr bool LoggingEnabled = false;
ASSUME( expr );\
} while (false)
#endif
template <typename T>
bool CallAssert(bool val, const char *msg, const char *filename, int linenum, T assertMsgFunc)
{
if (!val)
assertMsgFunc(msg, filename, linenum, nullptr);
ASSUME(val);
return true;
}
#if defined(NDEBUG)
# define AssertExpr(expr) (true)
#else
# define AssertExpr(expr) CallAssert(expr, #expr, __FILE__, __LINE__, os::dialogs::AssertMessage)
#endif
/*******************NEVER COMMENT Assert ************************************************/
// Goober5000 - define Verify for use in both release and debug mode
@@ -397,15 +411,6 @@ const size_t INVALID_SIZE = static_cast<size_t>(-1);
// the trailing underscores are to avoid conflicts with previously #define'd tokens
enum class TriStateBool : int { FALSE_ = 0, TRUE_ = 1, UNKNOWN_ = -1 };
// lod checker for (modular) table parsing
typedef struct lod_checker {
char filename[MAX_FILENAME_LEN];
int num_lods;
int override;
} lod_checker;
// Callback Loading function.
// If you pass a function to this, that function will get called
// around 10x per second, so you can update the screen.
+24 -18
View File
@@ -242,10 +242,11 @@ const SCP_vector<std::pair<int, std::pair<const char*, int>>> DetailLevelValues
static void parse_model_detail_func()
{
int value[static_cast<int>(DefaultDetailPreset::Num_detail_presets)];
stuff_int_list(value, static_cast<int>(DefaultDetailPreset::Num_detail_presets), RAW_INTEGER_TYPE);
constexpr int num_detail_presets = static_cast<int>(DefaultDetailPreset::Num_detail_presets);
int value[num_detail_presets];
stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE);
for (int i = 0; i < static_cast<int>(DefaultDetailPreset::Num_detail_presets); i++) {
for (int i = 0; i < num_detail_presets; i++) {
if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) {
error_display(0, "%i is an invalid detail level value!", value[i]);
@@ -272,10 +273,11 @@ const auto ModelDetailOption __UNUSED = options::OptionBuilder<int>("Graphics.De
static void parse_texture_detail_func()
{
int value[static_cast<int>(DefaultDetailPreset::Num_detail_presets)];
stuff_int_list(value, static_cast<int>(DefaultDetailPreset::Num_detail_presets), RAW_INTEGER_TYPE);
constexpr int num_detail_presets = static_cast<int>(DefaultDetailPreset::Num_detail_presets);
int value[num_detail_presets];
stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE);
for (int i = 0; i < static_cast<int>(DefaultDetailPreset::Num_detail_presets); i++) {
for (int i = 0; i < num_detail_presets; i++) {
if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) {
error_display(0, "%i is an invalid detail level value!", value[i]);
@@ -302,10 +304,11 @@ const auto TexturesOption __UNUSED = options::OptionBuilder<int>("Graphics.Textu
static void parse_particles_detail_func()
{
int value[static_cast<int>(DefaultDetailPreset::Num_detail_presets)];
stuff_int_list(value, static_cast<int>(DefaultDetailPreset::Num_detail_presets), RAW_INTEGER_TYPE);
constexpr int num_detail_presets = static_cast<int>(DefaultDetailPreset::Num_detail_presets);
int value[num_detail_presets];
stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE);
for (int i = 0; i < static_cast<int>(DefaultDetailPreset::Num_detail_presets); i++) {
for (int i = 0; i < num_detail_presets; i++) {
if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) {
error_display(0, "%i is an invalid detail level value!", value[i]);
@@ -332,10 +335,11 @@ const auto ParticlesOption __UNUSED = options::OptionBuilder<int>("Graphics.Part
static void parse_debris_detail_func()
{
int value[static_cast<int>(DefaultDetailPreset::Num_detail_presets)];
stuff_int_list(value, static_cast<int>(DefaultDetailPreset::Num_detail_presets), RAW_INTEGER_TYPE);
constexpr int num_detail_presets = static_cast<int>(DefaultDetailPreset::Num_detail_presets);
int value[num_detail_presets];
stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE);
for (int i = 0; i < static_cast<int>(DefaultDetailPreset::Num_detail_presets); i++) {
for (int i = 0; i < num_detail_presets; i++) {
if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) {
error_display(0, "%i is an invalid detail level value!", value[i]);
@@ -362,10 +366,11 @@ const auto SmallDebrisOption __UNUSED = options::OptionBuilder<int>("Graphics.Sm
static void parse_shield_detail_func()
{
int value[static_cast<int>(DefaultDetailPreset::Num_detail_presets)];
stuff_int_list(value, static_cast<int>(DefaultDetailPreset::Num_detail_presets), RAW_INTEGER_TYPE);
constexpr int num_detail_presets = static_cast<int>(DefaultDetailPreset::Num_detail_presets);
int value[num_detail_presets];
stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE);
for (int i = 0; i < static_cast<int>(DefaultDetailPreset::Num_detail_presets); i++) {
for (int i = 0; i < num_detail_presets; i++) {
if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) {
error_display(0, "%i is an invalid detail level value!", value[i]);
@@ -392,10 +397,11 @@ const auto ShieldEffectsOption __UNUSED = options::OptionBuilder<int>("Graphics.
static void parse_stars_detail_func()
{
int value[static_cast<int>(DefaultDetailPreset::Num_detail_presets)];
stuff_int_list(value, static_cast<int>(DefaultDetailPreset::Num_detail_presets), RAW_INTEGER_TYPE);
constexpr int num_detail_presets = static_cast<int>(DefaultDetailPreset::Num_detail_presets);
int value[num_detail_presets];
stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE);
for (int i = 0; i < static_cast<int>(DefaultDetailPreset::Num_detail_presets); i++) {
for (int i = 0; i < num_detail_presets; i++) {
if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) {
error_display(0, "%i is an invalid detail level value!", value[i]);
+15
View File
@@ -80,6 +80,21 @@ bool SCP_truncate(SCP_string &str, size_t len)
return false;
}
bool SCP_trim(SCP_string& str)
{
auto start = str.find_first_not_of(" \t\r\n");
auto end = str.find_last_not_of(" \t\r\n");
if (start == SCP_string::npos) {
str.clear();
return true;
}
if (start > 0 || end < str.length() - 1) {
str = str.substr(start, end - start + 1);
return true;
}
return false;
}
bool lcase_equal(const SCP_string& _Left, const SCP_string& _Right)
{
if (_Left.size() != _Right.size())
+1
View File
@@ -91,6 +91,7 @@ extern void SCP_toupper(SCP_string &str);
extern void SCP_totitle(SCP_string &str);
extern bool SCP_truncate(SCP_string &str, size_t len);
extern bool SCP_trim(SCP_string& str);
extern bool lcase_equal(const SCP_string& _Left, const SCP_string& _Right);
extern bool lcase_lessthan(const SCP_string& _Left, const SCP_string& _Right);
+15 -10
View File
@@ -79,7 +79,8 @@ gr_capability_def gr_capabilities[] = {
GR_CAPABILITY_ENTRY(SEPARATE_BLEND_FUNCTIONS),
GR_CAPABILITY_ENTRY(PERSISTENT_BUFFER_MAPPING),
gr_capability_def {gr_capability::CAPABILITY_BPTC, "BPTC Texture Compression"}, //This one had a different parse string already!
GR_CAPABILITY_ENTRY(LARGE_SHADER)
GR_CAPABILITY_ENTRY(LARGE_SHADER),
GR_CAPABILITY_ENTRY(INSTANCED_RENDERING),
};
const size_t gr_capabilities_num = sizeof(gr_capabilities) / sizeof(gr_capabilities[0]);
@@ -177,10 +178,11 @@ static auto GammaOption __UNUSED = options::OptionBuilder<float>("Graphics.Gamma
static void parse_lighting_func()
{
int value[static_cast<int>(DefaultDetailPreset::Num_detail_presets)];
stuff_int_list(value, static_cast<int>(DefaultDetailPreset::Num_detail_presets), RAW_INTEGER_TYPE);
constexpr int num_detail_presets = static_cast<int>(DefaultDetailPreset::Num_detail_presets);
int value[num_detail_presets];
stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE);
for (int i = 0; i < static_cast<int>(DefaultDetailPreset::Num_detail_presets); i++) {
for (int i = 0; i < num_detail_presets; i++) {
if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) {
error_display(0, "%i is an invalid detail level value!", value[i]);
@@ -1844,7 +1846,7 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
}
}
if (!gr_is_viewport_window() && !Cmdline_window_res.has_value()) {
if (!Fred_running && !Cmdline_window_res.has_value()) {
// For whatever reason, it seems that a combination of Win 11 and presumably NVidia GPU's causes weird artifacts.
// These artifacts do not appear in windowed mode, or with an attached renderdoc / nvidia nsight.
// Similarly using the -window_res command line parameter prevents this.
@@ -1852,6 +1854,8 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
// (be that a window, a render overlay from nsight, or an FSO-internal buffer) instead of directly rendering to the OS-provided direct screen backbuffer.
// As the cost of -window_res is one single blit of a fullscreen buffer, it's probably an acceptable compromise to get rid of render artifacts.
// As such, forcibly enable -window_res at the screen resolution here, if we're in fullscreen.
// Additionally, SDL3+ doesn't work when reading from the GL_FRONT buffers, so we need our own intermediate buffers.
Cmdline_window_res.emplace(static_cast<uint16_t>(width), static_cast<uint16_t>(height));
}
@@ -3070,7 +3074,7 @@ size_t hash<vertex_layout>::operator()(const vertex_layout& data) const {
bool vertex_layout::resident_vertex_format(vertex_format_data::vertex_format format_type) const {
return ( Vertex_mask & vertex_format_data::mask(format_type) ) ? true : false;
}
void vertex_layout::add_vertex_component(vertex_format_data::vertex_format format_type, size_t stride, size_t offset) {
void vertex_layout::add_vertex_component(vertex_format_data::vertex_format format_type, size_t stride, size_t offset, size_t divisor, size_t buffer_number ) {
// A stride value of 0 is not handled consistently by the graphics API so we must enforce that that does not happen
Assertion(stride != 0, "The stride of a vertex component may not be zero!");
@@ -3079,15 +3083,16 @@ void vertex_layout::add_vertex_component(vertex_format_data::vertex_format forma
return;
}
if (Vertex_mask == 0) {
auto stride_it = Vertex_stride.find(buffer_number);
if (stride_it == Vertex_stride.end()) {
// This is the first element so we need to initialize the global stride here
Vertex_stride = stride;
stride_it = Vertex_stride.emplace(buffer_number, stride).first;
}
Assertion(Vertex_stride == stride, "The strides of all elements must be the same in a vertex layout!");
Assertion(stride_it->second == stride, "The strides of all elements must be the same in a vertex layout!");
Vertex_mask |= (1 << format_type);
Vertex_components.push_back(vertex_format_data(format_type, stride, offset));
Vertex_components.emplace_back(format_type, stride, offset, divisor, buffer_number);
}
bool vertex_layout::operator==(const vertex_layout& other) const {
if (Vertex_mask != other.Vertex_mask) {
+15 -7
View File
@@ -239,6 +239,8 @@ enum shader_type {
#define SDR_FLAG_TONEMAPPING_LINEAR_OUT (1 << 0)
#define SDR_FLAG_ENV_MAP (1 << 0)
enum class uniform_block_type {
Lights = 0,
@@ -271,14 +273,17 @@ struct vertex_format_data
MODEL_ID,
RADIUS,
UVEC,
MATRIX4,
};
vertex_format format_type;
size_t stride;
size_t offset;
size_t divisor;
size_t buffer_number;
vertex_format_data(vertex_format i_format_type, size_t i_stride, size_t i_offset) :
format_type(i_format_type), stride(i_stride), offset(i_offset) {}
vertex_format_data(vertex_format i_format_type, size_t i_stride, size_t i_offset, size_t i_divisor, size_t i_buffer_number) :
format_type(i_format_type), stride(i_stride), offset(i_offset), divisor(i_divisor), buffer_number(i_buffer_number) {}
static inline uint mask(vertex_format v_format) { return 1 << v_format; }
@@ -291,7 +296,7 @@ class vertex_layout
SCP_vector<vertex_format_data> Vertex_components;
uint Vertex_mask = 0;
size_t Vertex_stride = 0;
SCP_unordered_map<size_t, size_t> Vertex_stride;
public:
vertex_layout() {}
@@ -301,9 +306,9 @@ public:
bool resident_vertex_format(vertex_format_data::vertex_format format_type) const;
void add_vertex_component(vertex_format_data::vertex_format format_type, size_t stride, size_t offset);
void add_vertex_component(vertex_format_data::vertex_format format_type, size_t stride, size_t offset, size_t divisor = 0, size_t buffer_number = 0);
size_t get_vertex_stride() const { return Vertex_stride; }
size_t get_vertex_stride(size_t buffer_number = 0) const { return Vertex_stride.at(buffer_number); }
bool operator==(const vertex_layout& other) const;
@@ -333,7 +338,8 @@ enum class gr_capability {
CAPABILITY_SEPARATE_BLEND_FUNCTIONS,
CAPABILITY_PERSISTENT_BUFFER_MAPPING,
CAPABILITY_BPTC,
CAPABILITY_LARGE_SHADER
CAPABILITY_LARGE_SHADER,
CAPABILITY_INSTANCED_RENDERING
};
struct gr_capability_def {
@@ -891,7 +897,9 @@ typedef struct screen {
primitive_type prim_type,
vertex_layout* layout,
int num_elements,
const indexed_vertex_source& buffers)>
const indexed_vertex_source& buffers,
const gr_buffer_handle& instance_buffer,
int num_instances)>
gf_render_decals;
void (*gf_render_rocket_primitives)(interface_material* material_info,
primitive_type prim_type,
+30 -90
View File
@@ -1,6 +1,5 @@
#include "graphics/decal_draw_list.h"
#include "graphics/util/uniform_structs.h"
#include "graphics/matrix.h"
#include "render/3d.h"
@@ -9,10 +8,6 @@
namespace {
// Discard any fragments where the angle to the direction to greater than 45°
const float DECAL_ANGLE_CUTOFF = fl_radians(45.f);
const float DECAL_ANGLE_FADE_START = fl_radians(30.f);
vec3d BOX_VERTS[] = {{{{ -0.5f, -0.5f, -0.5f }}},
{{{ -0.5f, 0.5f, -0.5f }}},
{{{ 0.5f, 0.5f, -0.5f }}},
@@ -29,6 +24,7 @@ const size_t BOX_NUM_FACES = sizeof(BOX_FACES) / sizeof(BOX_FACES[0]);
gr_buffer_handle box_vertex_buffer;
gr_buffer_handle box_index_buffer;
gr_buffer_handle decal_instance_buffer;
void init_buffers() {
box_vertex_buffer = gr_create_buffer(BufferType::Vertex, BufferUsageHint::Static);
@@ -36,6 +32,8 @@ void init_buffers() {
box_index_buffer = gr_create_buffer(BufferType::Index, BufferUsageHint::Static);
gr_update_buffer_data(box_index_buffer, sizeof(BOX_FACES), BOX_FACES);
decal_instance_buffer = gr_create_buffer(BufferType::Vertex, BufferUsageHint::Streaming);
}
bool check_box_in_view(const matrix4& transform) {
@@ -56,35 +54,6 @@ bool check_box_in_view(const matrix4& transform) {
namespace graphics {
/**
* @brief Sorts Decals so that as many decals can be batched together as possible
*
* This uses the bitmaps in the definitions to determine if two decals can be rendered at the same time. Since we use
* texture arrays we can use the base frame for batching which increases the number of draw calls that can be batched together.
*
* @param left
* @param right
* @return
*/
bool decal_draw_list::sort_draws(const decal_draw_info& left, const decal_draw_info& right) {
auto left_diffuse_base = bm_get_base_frame(left.draw_mat.get_texture_map(TM_BASE_TYPE));
auto right_diffuse_base = bm_get_base_frame(right.draw_mat.get_texture_map(TM_BASE_TYPE));
if (left_diffuse_base != right_diffuse_base) {
return left_diffuse_base < right_diffuse_base;
}
auto left_glow_base = bm_get_base_frame(left.draw_mat.get_texture_map(TM_GLOW_TYPE));
auto right_glow_base = bm_get_base_frame(right.draw_mat.get_texture_map(TM_GLOW_TYPE));
if (left_glow_base != right_glow_base) {
return left_glow_base < right_glow_base;
}
auto left_normal_base = bm_get_base_frame(left.draw_mat.get_texture_map(TM_NORMAL_TYPE));
auto right_normal_base = bm_get_base_frame(left.draw_mat.get_texture_map(TM_NORMAL_TYPE));
return left_normal_base < right_normal_base;
}
void decal_draw_list::globalInit() {
init_buffers();
@@ -96,9 +65,8 @@ void decal_draw_list::globalShutdown() {
gr_delete_buffer(box_index_buffer);
}
decal_draw_list::decal_draw_list(size_t num_decals)
{
_buffer = gr_get_uniform_buffer(uniform_block_type::DecalInfo, num_decals);
void decal_draw_list::prepare_global_data() {
_buffer = gr_get_uniform_buffer(uniform_block_type::DecalInfo, _draws.size());
auto& aligner = _buffer.aligner();
// Initialize header data
@@ -114,29 +82,34 @@ decal_draw_list::decal_draw_list(size_t num_decals)
header->viewportSize.x = (float) gr_screen.max_w;
header->viewportSize.y = (float) gr_screen.max_h;
gr_get_ambient_light(&header->ambientLight);
for (auto& [batch_info, draw_info] : _draws) {
auto info = aligner.addTypedElement<graphics::decal_info>();
info->diffuse_index = batch_info.diffuse < 0 ? -1 : bm_get_array_index(batch_info.diffuse);
info->glow_index = batch_info.glow < 0 ? -1 : bm_get_array_index(batch_info.glow);
info->normal_index = batch_info.normal < 0 ? -1 : bm_get_array_index(batch_info.normal);
// Square the ambient part of the light to match the formula used in the main model shader
header->ambientLight.xyz.x *= header->ambientLight.xyz.x;
header->ambientLight.xyz.y *= header->ambientLight.xyz.y;
header->ambientLight.xyz.z *= header->ambientLight.xyz.z;
draw_info.first.uniform_offset = _buffer.getBufferOffset(aligner.getCurrentOffset());
header->ambientLight.xyz.x += gr_light_emission[0];
header->ambientLight.xyz.y += gr_light_emission[1];
header->ambientLight.xyz.z += gr_light_emission[2];
}
decal_draw_list::~decal_draw_list() {
material_set_decal(&draw_info.first.material,
bm_get_base_frame(batch_info.diffuse),
bm_get_base_frame(batch_info.glow),
bm_get_base_frame(batch_info.normal));
info->diffuse_blend_mode = draw_info.first.material.get_blend_mode(0) == ALPHA_BLEND_ADDITIVE ? 1 : 0;
info->glow_blend_mode = draw_info.first.material.get_blend_mode(2) == ALPHA_BLEND_ADDITIVE ? 1 : 0;
}
}
void decal_draw_list::render() {
GR_DEBUG_SCOPE("Render decals");
TRACE_SCOPE(tracing::RenderDecals);
_buffer.submitData();
prepare_global_data();
std::sort(_draws.begin(), _draws.end(), decal_draw_list::sort_draws);
_buffer.submitData();
vertex_layout layout;
layout.add_vertex_component(vertex_format_data::POSITION3, sizeof(vec3d), 0);
layout.add_vertex_component(vertex_format_data::MATRIX4, sizeof(matrix4), 0, 1, 1);
indexed_vertex_source source;
source.Vbuffer_handle = box_vertex_buffer;
@@ -149,14 +122,13 @@ void decal_draw_list::render() {
_buffer.bufferHandle());
gr_screen.gf_start_decal_pass();
for (auto& draw : _draws) {
GR_DEBUG_SCOPE("Draw single decal");
for (auto& [textures, decal_list] : _draws) {
GR_DEBUG_SCOPE("Draw decal type");
TRACE_SCOPE(tracing::RenderSingleDecal);
gr_bind_uniform_buffer(uniform_block_type::DecalInfo, draw.uniform_offset, sizeof(graphics::decal_info),
_buffer.bufferHandle());
gr_screen.gf_render_decals(&draw.draw_mat, PRIM_TYPE_TRIS, &layout, BOX_NUM_FACES, source);
gr_update_buffer_data(decal_instance_buffer, sizeof(matrix4) * decal_list.second.size(), decal_list.second.data());
gr_bind_uniform_buffer(uniform_block_type::DecalInfo, decal_list.first.uniform_offset, sizeof(graphics::decal_info), _buffer.bufferHandle());
gr_screen.gf_render_decals(&decal_list.first.material, PRIM_TYPE_TRIS, &layout, BOX_NUM_FACES, source, decal_instance_buffer, static_cast<int>(decal_list.second.size()));
}
gr_screen.gf_stop_decal_pass();
@@ -165,45 +137,13 @@ void decal_draw_list::add_decal(int diffuse_bitmap,
int glow_bitmap,
int normal_bitmap,
float /*decal_timer*/,
const matrix4& transform,
float base_alpha) {
if (!check_box_in_view(transform)) {
const matrix4& instancedata) {
if (!check_box_in_view(instancedata)) {
// The decal box is not in view so we don't need to render it
return;
}
auto& aligner = _buffer.aligner();
auto info = aligner.addTypedElement<graphics::decal_info>();
info->model_matrix = transform;
// This is currently a constant but in the future this may be configurable by the decals table
info->normal_angle_cutoff = DECAL_ANGLE_CUTOFF;
info->angle_fade_start = DECAL_ANGLE_FADE_START;
info->alpha_scale = base_alpha;
matrix transform_rot;
vm_matrix4_get_orientation(&transform_rot, &transform);
// The decal shader works in view-space so the direction also has to be transformed into that space
vm_vec_transform(&info->decal_direction, &transform_rot.vec.fvec, &gr_view_matrix, false);
vm_inverse_matrix4(&info->inv_model_matrix, &info->model_matrix);
info->diffuse_index = diffuse_bitmap < 0 ? -1 : bm_get_array_index(diffuse_bitmap);
info->glow_index = glow_bitmap < 0 ? -1 : bm_get_array_index(glow_bitmap);
info->normal_index = normal_bitmap < 0 ? -1 : bm_get_array_index(normal_bitmap);
decal_draw_info current_draw;
current_draw.uniform_offset = _buffer.getBufferOffset(aligner.getCurrentOffset());
material_set_decal(&current_draw.draw_mat,
bm_get_base_frame(diffuse_bitmap),
bm_get_base_frame(glow_bitmap),
bm_get_base_frame(normal_bitmap));
info->diffuse_blend_mode = current_draw.draw_mat.get_blend_mode(0) == ALPHA_BLEND_ADDITIVE ? 1 : 0;
info->glow_blend_mode = current_draw.draw_mat.get_blend_mode(2) == ALPHA_BLEND_ADDITIVE ? 1 : 0;
_draws.push_back(current_draw);
_draws[decal_batch_info{diffuse_bitmap, glow_bitmap, normal_bitmap}].second.emplace_back(instancedata);
}
}
+16 -11
View File
@@ -3,36 +3,41 @@
#include "globalincs/pstypes.h"
#include "graphics/material.h"
#include "graphics/util/uniform_structs.h"
#include "graphics/util/UniformBuffer.h"
namespace graphics {
class decal_draw_list {
struct decal_draw_info {
decal_material draw_mat;
decal_material material;
size_t uniform_offset;
};
SCP_vector<decal_draw_info> _draws;
struct decal_batch_info {
int diffuse, glow, normal;
bool operator< (const decal_batch_info& r) const {
return std::tie(diffuse, glow, normal) < std::tie(r.diffuse, r.glow, r.normal);
}
};
SCP_map<decal_batch_info, std::pair<decal_draw_info, SCP_vector<matrix4>>> _draws;
util::UniformBuffer _buffer;
static bool sort_draws(const decal_draw_info& left, const decal_draw_info& right);
public:
explicit decal_draw_list(size_t num_decals);
~decal_draw_list();
decal_draw_list(const decal_draw_list&) = delete;
decal_draw_list& operator=(const decal_draw_list&) = delete;
void add_decal(int diffuse_bitmap, int glow_bitmap, int normal_bitmap, float decal_timer, const matrix4& transform,
float base_alpha);
void add_decal(int diffuse_bitmap, int glow_bitmap, int normal_bitmap, float decal_timer, const matrix4& instancedata);
void render();
static void globalInit();
static void globalShutdown();
decal_draw_list() = default;
private:
void prepare_global_data();
};
}
+22
View File
@@ -307,6 +307,24 @@ void gr_stub_shadow_map_end()
{
}
void gr_stub_start_decal_pass()
{
}
void gr_stub_stop_decal_pass()
{
}
void gr_stub_render_decals(decal_material* /*material_info*/,
primitive_type /*prim_type*/,
vertex_layout* /*layout*/,
int /*num_elements*/,
const indexed_vertex_source& /*buffers*/,
const gr_buffer_handle& /*instance_buffer*/,
int /*num_instances*/)
{
}
void gr_stub_render_shield_impact(shield_material* /*material_info*/,
primitive_type /*prim_type*/,
vertex_layout* /*layout*/,
@@ -569,6 +587,10 @@ void gr_stub_init_function_pointers() {
gr_screen.gf_shadow_map_start = gr_stub_shadow_map_start;
gr_screen.gf_shadow_map_end = gr_stub_shadow_map_end;
gr_screen.gf_start_decal_pass = gr_stub_start_decal_pass;
gr_screen.gf_stop_decal_pass = gr_stub_stop_decal_pass;
gr_screen.gf_render_decals = gr_stub_render_decals;
gr_screen.gf_render_shield_impact = gr_stub_render_shield_impact;
gr_screen.gf_maybe_create_shader = gr_stub_maybe_create_shader;
+10
View File
@@ -344,6 +344,16 @@ void gr_get_ambient_light(vec3d* light_vector) {
light_vector->xyz.x = over.handle(abv.handle(gr_light_ambient[0]));
light_vector->xyz.y = over.handle(abv.handle(gr_light_ambient[1]));
light_vector->xyz.z = over.handle(abv.handle(gr_light_ambient[2]));
//AmbientFactor^2 due to legacy OpenGL behaviour
*light_vector *= *light_vector;
//For some reason, emissive is in here as well...
if (Cmdline_emissive) {
light_vector->xyz.x += gr_light_emission[0];
light_vector->xyz.y += gr_light_emission[1];
light_vector->xyz.z += gr_light_emission[2];
}
}
void gr_lighting_fill_uniforms(void* data_out, size_t buffer_size) {
+7 -9
View File
@@ -768,21 +768,19 @@ int model_material::get_shader_runtime_flags() const {
flags |= MODEL_SDR_FLAG_DEFERRED;
if (is_hdr())
flags |= MODEL_SDR_FLAG_HDR;
if (get_texture_map(TM_BASE_TYPE) > 0)
if (get_texture_map(TM_BASE_TYPE) >= 0)
flags |= MODEL_SDR_FLAG_DIFFUSE;
if (get_texture_map(TM_GLOW_TYPE) > 0)
if (get_texture_map(TM_GLOW_TYPE) >= 0)
flags |= MODEL_SDR_FLAG_GLOW;
if (get_texture_map(TM_SPECULAR_TYPE) > 0 || get_texture_map(TM_SPEC_GLOSS_TYPE) > 0)
if (get_texture_map(TM_SPECULAR_TYPE) >= 0 || get_texture_map(TM_SPEC_GLOSS_TYPE) >= 0)
flags |= MODEL_SDR_FLAG_SPEC;
if (ENVMAP > 0)
flags |= MODEL_SDR_FLAG_ENV;
if (get_texture_map(TM_NORMAL_TYPE) > 0)
if (get_texture_map(TM_NORMAL_TYPE) >= 0)
flags |= MODEL_SDR_FLAG_NORMAL;
if (get_texture_map(TM_AMBIENT_TYPE) > 0)
if (get_texture_map(TM_AMBIENT_TYPE) >= 0)
flags |= MODEL_SDR_FLAG_AMBIENT;
if (get_texture_map(TM_MISC_TYPE) > 0)
if (get_texture_map(TM_MISC_TYPE) >= 0)
flags |= MODEL_SDR_FLAG_MISC;
if (get_texture_map(TM_MISC_TYPE) > 0 && is_team_color_set())
if (get_texture_map(TM_MISC_TYPE) >= 0 && is_team_color_set())
flags |= MODEL_SDR_FLAG_TEAMCOLOR;
if (is_fogged())
flags |= MODEL_SDR_FLAG_FOG;
+2 -12
View File
@@ -183,18 +183,16 @@ void opengl::ShaderProgram::linkProgram() {
freeCompiledShaders();
}
void opengl::ShaderProgram::initAttribute(const SCP_string& name, opengl_vert_attrib::attrib_id attr_id, const vec4& default_value)
void opengl::ShaderProgram::initAttribute(const SCP_string& name, const vec4& default_value)
{
auto attrib_loc = glGetAttribLocation(_program_id, name.c_str());
if (attrib_loc == -1)
{
// Not available, ignore
// Not available or optimized out, ignore
return;
}
_attribute_locations.insert(std::make_pair(attr_id, attrib_loc));
// The shader needs to be in use before glVertexAttrib can be used
use();
glVertexAttrib4f(
@@ -205,14 +203,6 @@ void opengl::ShaderProgram::initAttribute(const SCP_string& name, opengl_vert_at
default_value.xyzw.w
);
}
GLint opengl::ShaderProgram::getAttributeLocation(opengl_vert_attrib::attrib_id attribute) {
auto iter = _attribute_locations.find(attribute);
if (iter == _attribute_locations.end()) {
return -1;
} else {
return iter->second;
}
}
opengl::ShaderUniforms::ShaderUniforms(ShaderProgram* shaderProgram) : _program(shaderProgram) {
Assertion(shaderProgram != nullptr, "Shader program may not be null!");
+1 -5
View File
@@ -46,8 +46,6 @@ class ShaderProgram {
SCP_vector<GLuint> _compiled_shaders;
SCP_unordered_map<opengl_vert_attrib::attrib_id, GLint> _attribute_locations;
void freeCompiledShaders();
public:
explicit ShaderProgram(const SCP_string& program_name);
@@ -67,9 +65,7 @@ class ShaderProgram {
void linkProgram();
void initAttribute(const SCP_string& name, opengl_vert_attrib::attrib_id attr_id, const vec4& default_value);
GLint getAttributeLocation(opengl_vert_attrib::attrib_id attribute);
void initAttribute(const SCP_string& name, const vec4& default_value);
GLuint getShaderHandle();
};
+16 -2
View File
@@ -337,10 +337,21 @@ SCP_string gr_opengl_blob_screen()
GLuint pbo = 0;
GL_state.PushFramebufferState();
GL_state.BindFrameBuffer(Cmdline_window_res ? Back_framebuffer : 0, GL_FRAMEBUFFER);
GLuint source_fbo = 0;
GLuint render_target = opengl_get_rtt_framebuffer();
if (render_target != 0) {
source_fbo = render_target;
}
else if (Cmdline_window_res) {
source_fbo = Back_framebuffer;
}
GL_state.BindFrameBuffer(source_fbo, GL_FRAMEBUFFER);
//Reading from the front buffer here seems to no longer work correctly; that just reads back all zeros
glReadBuffer(Cmdline_window_res ? GL_COLOR_ATTACHMENT0 : GL_FRONT);
glReadBuffer(source_fbo != 0 ? GL_COLOR_ATTACHMENT0 : GL_FRONT);
// now for the data
if (Use_PBOs) {
@@ -1489,8 +1500,11 @@ bool gr_opengl_is_capable(gr_capability capability)
return GLAD_GL_ARB_texture_compression_bptc != 0;
case gr_capability::CAPABILITY_LARGE_SHADER:
return !Cmdline_no_large_shaders;
case gr_capability::CAPABILITY_INSTANCED_RENDERING:
return GLAD_GL_ARB_vertex_attrib_binding;
}
return false;
}
+94 -37
View File
@@ -9,6 +9,7 @@
#include "gropengltnl.h"
#include "graphics/2d.h"
#include "graphics/light.h"
#include "graphics/matrix.h"
#include "graphics/util/UniformAligner.h"
#include "graphics/util/UniformBuffer.h"
@@ -189,16 +190,19 @@ static bool override_fog = false;
graphics::deferred_light_data*
// common conversion operations to translate a game light data structure into a render-ready light uniform.
prepare_light_uniforms(light& l, graphics::util::UniformAligner& uniformAligner)
prepare_light_uniforms(light& l, graphics::util::UniformAligner& uniformAligner, const ltp::profile* lp)
{
graphics::deferred_light_data* light_data = uniformAligner.addTypedElement<graphics::deferred_light_data>();
light_data->lightType = static_cast<int>(l.type);
float intensity =
(Lighting_mode == lighting_mode::COCKPIT) ? lp->cockpit_light_intensity_modifier.handle(l.intensity) : l.intensity;
vec3d diffuse;
diffuse.xyz.x = l.r * l.intensity;
diffuse.xyz.y = l.g * l.intensity;
diffuse.xyz.z = l.b * l.intensity;
diffuse.xyz.x = l.r * intensity;
diffuse.xyz.y = l.g * intensity;
diffuse.xyz.z = l.b * intensity;
light_data->diffuseLightColor = diffuse;
@@ -223,7 +227,7 @@ void gr_opengl_deferred_lighting_finish()
// GL_state.DepthFunc(GL_GREATER);
// GL_state.DepthMask(GL_FALSE);
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_DEFERRED_LIGHTING, 0));
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_DEFERRED_LIGHTING, ENVMAP > 0 ? SDR_FLAG_ENV_MAP : 0));
// Render on top of the composite buffer texture
glDrawBuffer(GL_COLOR_ATTACHMENT5);
@@ -247,6 +251,16 @@ void gr_opengl_deferred_lighting_finish()
GL_state.Texture.Enable(4, GL_TEXTURE_2D_ARRAY, Shadow_map_texture);
}
if (ENVMAP > 0) {
Current_shader->program->Uniforms.setTextureUniform("sEnvmap", 5);
Current_shader->program->Uniforms.setTextureUniform("sIrrmap", 6);
float u_scale, v_scale;
uint32_t array_index;
gr_opengl_tcache_set(ENVMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, 5);
gr_opengl_tcache_set(IRRMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, 6);
Assertion(array_index == 0, "Cube map arrays are not supported yet!");
}
// We need to use stable sorting here to make sure that the relative ordering of the same light types is the same as
// the rest of the code. Otherwise the shadow mapping would be applied while rendering the wrong light which would
// lead to flickering lights in some circumstances
@@ -254,7 +268,7 @@ void gr_opengl_deferred_lighting_finish()
using namespace graphics;
// We need to precompute how many elements we are going to need
size_t num_data_elements = Lights.size();
size_t num_data_elements = Lights.size() + 1;
// Get a uniform buffer for our data
auto light_buffer = gr_get_uniform_buffer(uniform_block_type::Lights, num_data_elements);
@@ -286,6 +300,8 @@ void gr_opengl_deferred_lighting_finish()
case Light_Type::Tube:
cylinder_lights.push_back(l);
break;
case Light_Type::Ambient:
UNREACHABLE("Multiple ambient lights are not supported!");
}
}
{
@@ -312,38 +328,54 @@ void gr_opengl_deferred_lighting_finish()
header->invScreenHeight = 1.0f / gr_screen.max_h;
header->nearPlane = gr_near_plane;
{
//Prepare ambient light
light& l = full_frame_lights.emplace_back();
vec3d ambient;
gr_get_ambient_light(&ambient);
l.r = ambient.xyz.x;
l.g = ambient.xyz.y;
l.b = ambient.xyz.z;
l.type = Light_Type::Ambient;
l.intensity = 1.f;
l.source_radius = 0.f;
}
// Only the first directional light uses shaders so we need to know when we already saw that light
bool first_directional = true;
for (auto& l : full_frame_lights) {
auto light_data = prepare_light_uniforms(l, light_uniform_aligner);
if (Shadow_quality != ShadowQuality::Disabled) {
light_data->enable_shadows = first_directional ? 1 : 0;
auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp);
if (l.type == Light_Type::Directional ) {
if (Shadow_quality != ShadowQuality::Disabled) {
light_data->enable_shadows = first_directional ? 1 : 0;
}
// Global light direction should match shadow light direction
if (first_directional) {
global_light = &l;
global_light_diffuse = light_data->diffuseLightColor;
first_directional = false;
}
vec4 light_dir;
light_dir.xyzw.x = -l.vec.xyz.x;
light_dir.xyzw.y = -l.vec.xyz.y;
light_dir.xyzw.z = -l.vec.xyz.z;
light_dir.xyzw.w = 0.0f;
vec4 view_dir;
vm_vec_transform(&view_dir, &light_dir, &gr_view_matrix);
light_data->lightDir.xyz.x = view_dir.xyzw.x;
light_data->lightDir.xyz.y = view_dir.xyzw.y;
light_data->lightDir.xyz.z = view_dir.xyzw.z;
}
// Global light direction should match shadow light direction
if (first_directional) {
global_light = &l;
global_light_diffuse = light_data->diffuseLightColor;
}
vec4 light_dir;
light_dir.xyzw.x = -l.vec.xyz.x;
light_dir.xyzw.y = -l.vec.xyz.y;
light_dir.xyzw.z = -l.vec.xyz.z;
light_dir.xyzw.w = 0.0f;
vec4 view_dir;
vm_vec_transform(&view_dir, &light_dir, &gr_view_matrix);
light_data->lightDir.xyz.x = view_dir.xyzw.x;
light_data->lightDir.xyz.y = view_dir.xyzw.y;
light_data->lightDir.xyz.z = view_dir.xyzw.z;
first_directional = false;
}
for (auto& l : sphere_lights) {
auto light_data = prepare_light_uniforms(l, light_uniform_aligner);
auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp);
if (l.type == Light_Type::Cone) {
light_data->dualCone = (l.flags & LF_DUAL_CONE) ? 1.0f : 0.0f;
@@ -355,6 +387,7 @@ void gr_opengl_deferred_lighting_finish()
? lp->cockpit_light_radius_modifier.handle(MAX(l.rada, l.radb))
: MAX(l.rada, l.radb);
light_data->lightRadius = rad;
// A small padding factor is added to guard against potentially clipping the edges of the light with facets
// of the volume mesh.
light_data->scale.xyz.x = rad * 1.05f;
@@ -362,11 +395,12 @@ void gr_opengl_deferred_lighting_finish()
light_data->scale.xyz.z = rad * 1.05f;
}
for (auto& l : cylinder_lights) {
auto light_data = prepare_light_uniforms(l, light_uniform_aligner);
auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp);
float rad =
(Lighting_mode == lighting_mode::COCKPIT) ? lp->cockpit_light_radius_modifier.handle(l.radb) : l.radb;
light_data->lightRadius = rad;
light_data->lightType = LT_TUBE;
vec3d a;
@@ -389,7 +423,8 @@ void gr_opengl_deferred_lighting_finish()
{
for (size_t i = 0; i<full_frame_lights.size(); i++) {
// just keeping things aligned really.
matrix_uniform_aligner.addTypedElement<graphics::matrix_uniforms>();
auto matrix_data = matrix_uniform_aligner.addTypedElement<graphics::matrix_uniforms>();
matrix_data->modelViewMatrix = gr_env_texture_matrix;
}
for (auto& l : sphere_lights) {
auto matrix_data = matrix_uniform_aligner.addTypedElement<graphics::matrix_uniforms>();
@@ -494,7 +529,10 @@ void gr_opengl_deferred_lighting_finish()
// Now reset back to drawing into the color buffer
glDrawBuffer(GL_COLOR_ATTACHMENT0);
if (The_mission.flags[Mission::Mission_Flags::Fullneb] && Neb2_render_mode != NEB2_RENDER_NONE && !override_fog) {
bool bDrawFullNeb = The_mission.flags[Mission::Mission_Flags::Fullneb] && Neb2_render_mode != NEB2_RENDER_NONE && !override_fog;
bool bDrawNebVolumetrics = The_mission.volumetrics && The_mission.volumetrics->get_enabled() && !override_fog;
if (bDrawFullNeb) {
GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE);
gr_zbuffer_set(GR_ZBUFF_NONE);
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_SCENE_FOG, 0));
@@ -521,8 +559,26 @@ void gr_opengl_deferred_lighting_finish()
});
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
}
else if (The_mission.volumetrics && The_mission.volumetrics->get_enabled() && !override_fog) {
if (bDrawNebVolumetrics) {
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT5);
glBlitFramebuffer(0,
0,
gr_screen.max_w,
gr_screen.max_h,
0,
0,
gr_screen.max_w,
gr_screen.max_h,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glReadBuffer(GL_COLOR_ATTACHMENT0);
}
}
if (bDrawNebVolumetrics) {
GR_DEBUG_SCOPE("Volumetric Nebulae");
TRACE_SCOPE(tracing::Volumetrics);
@@ -602,7 +658,8 @@ void gr_opengl_deferred_lighting_finish()
gr_end_view_matrix();
gr_end_proj_matrix();
}
else {
if(!bDrawFullNeb && !bDrawNebVolumetrics) {
// Transfer the resolved lighting back to the color texture
// TODO: Maybe this could be improved so that it doesn't require the copy back operation?
glReadBuffer(GL_COLOR_ATTACHMENT5);
+4 -1
View File
@@ -4,6 +4,9 @@
#include "graphics/util/UniformAligner.h"
#include "graphics/util/uniform_structs.h"
#include "lighting/lighting.h"
#include "lighting/lighting_profiles.h"
namespace ltp = lighting_profiles;
using namespace ltp;
void gr_opengl_deferred_init();
@@ -12,7 +15,7 @@ void gr_opengl_deferred_lighting_begin(bool clearNonColorBufs = false);
void gr_opengl_deferred_lighting_msaa();
void gr_opengl_deferred_lighting_end();
void gr_opengl_deferred_lighting_finish();
graphics::deferred_light_data* prepare_light_uniforms(light& l, graphics::util::UniformAligner& uniformAligner);
graphics::deferred_light_data* prepare_light_uniforms(light& l, graphics::util::UniformAligner& uniformAligner, ltp::profile lp);
void gr_opengl_deferred_light_sphere_init(int rings, int segments);
void gr_opengl_deferred_light_cylinder_init(int segments);
+6 -4
View File
@@ -1147,14 +1147,16 @@ void gr_opengl_render_decals(decal_material* material_info,
primitive_type prim_type,
vertex_layout* layout,
int num_elements,
const indexed_vertex_source& binding) {
const indexed_vertex_source& binding,
const gr_buffer_handle& instance_buffer,
int num_instances) {
opengl_tnl_set_material_decal(material_info);
opengl_bind_vertex_layout(*layout,
opengl_buffer_get_id(GL_ARRAY_BUFFER, binding.Vbuffer_handle),
opengl_bind_vertex_layout_multiple(*layout,
SCP_vector<GLuint> { opengl_buffer_get_id(GL_ARRAY_BUFFER, binding.Vbuffer_handle), opengl_buffer_get_id(GL_ARRAY_BUFFER, instance_buffer) },
opengl_buffer_get_id(GL_ELEMENT_ARRAY_BUFFER, binding.Ibuffer_handle));
glDrawElements(opengl_primitive_type(prim_type), num_elements, GL_UNSIGNED_INT, nullptr);
glDrawElementsInstanced(opengl_primitive_type(prim_type), num_elements, GL_UNSIGNED_INT, nullptr, num_instances);
}
void gr_opengl_start_decal_pass() {
+3 -1
View File
@@ -112,7 +112,9 @@ void gr_opengl_render_decals(decal_material* material_info,
primitive_type prim_type,
vertex_layout* layout,
int num_elements,
const indexed_vertex_source& binding);
const indexed_vertex_source& binding,
const gr_buffer_handle& instance_buffer,
int num_instances);
void gr_opengl_render_rocket_primitives(interface_material* material_info,
primitive_type prim_type,
vertex_layout* layout,
@@ -597,6 +597,18 @@ void gr_opengl_post_process_end()
case graphics::PostEffectUniformType::Tint:
data->tint = postEffects[idx].rgb;
break;
case graphics::PostEffectUniformType::CustomEffectVEC3A:
data->custom_effect_vec3_a = postEffects[idx].rgb;
break;
case graphics::PostEffectUniformType::CustomEffectFloatA:
data->custom_effect_float_a = value;
break;
case graphics::PostEffectUniformType::CustomEffectVEC3B:
data->custom_effect_vec3_b = postEffects[idx].rgb;
break;
case graphics::PostEffectUniformType::CustomEffectFloatB:
data->custom_effect_float_b = value;
break;
}
}
}
+6 -17
View File
@@ -57,7 +57,7 @@ SCP_vector<opengl_vert_attrib> GL_vertex_attrib_info =
{ opengl_vert_attrib::MODEL_ID, "vertModelID", {{{ 0.0f, 0.0f, 0.0f, 0.0f }}} },
{ opengl_vert_attrib::RADIUS, "vertRadius", {{{ 1.0f, 0.0f, 0.0f, 0.0f }}} },
{ opengl_vert_attrib::UVEC, "vertUvec", {{{ 0.0f, 1.0f, 0.0f, 0.0f }}} },
{ opengl_vert_attrib::WORLD_MATRIX, "vertWorldMatrix", {{{ 1.0f, 0.0f, 0.0f, 0.0f }}} },
{ opengl_vert_attrib::MODEL_MATRIX, "vertModelMatrix", {{{ 1.0f, 0.0f, 0.0f, 0.0f }}} },
};
struct opengl_uniform_block_binding {
@@ -141,7 +141,7 @@ static opengl_shader_type_t GL_shader_types[] = {
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "NanoVG shader", false },
{ SDR_TYPE_DECAL, "decal-v.sdr", "decal-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::WORLD_MATRIX }, "Decal rendering", false },
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::MODEL_MATRIX }, "Decal rendering", false },
{ SDR_TYPE_SCENE_FOG, "post-v.sdr", "fog-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Scene fogging", false },
@@ -190,6 +190,8 @@ static opengl_shader_variant_t GL_shader_variants[] = {
{SDR_TYPE_EFFECT_PARTICLE, true, SDR_FLAG_PARTICLE_POINT_GEN, "FLAG_EFFECT_GEOMETRY", {opengl_vert_attrib::UVEC}, "Geometry shader point-based particles"},
{SDR_TYPE_DEFERRED_LIGHTING, false, SDR_FLAG_ENV_MAP, "ENV_MAP", {}, "Render ambient light with env and irrmaps"},
{SDR_TYPE_POST_PROCESS_BLUR, false, SDR_FLAG_BLUR_HORIZONTAL, "PASS_0", {}, "Horizontal blur pass"},
{SDR_TYPE_POST_PROCESS_BLUR, false, SDR_FLAG_BLUR_VERTICAL, "PASS_1", {}, "Vertical blur pass"},
@@ -884,7 +886,7 @@ void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_sha
// initialize the attributes
for (auto& attr : sdr_info->attributes) {
new_shader.program->initAttribute(GL_vertex_attrib_info[attr].name, GL_vertex_attrib_info[attr].attribute_id, GL_vertex_attrib_info[attr].default_value);
new_shader.program->initAttribute(GL_vertex_attrib_info[attr].name, GL_vertex_attrib_info[attr].default_value);
}
for (auto& uniform_block : GL_uniform_blocks) {
@@ -904,7 +906,7 @@ void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_sha
if (sdr_info->type_id == variant.type_id && variant.flag & flags) {
for (auto& attr : variant.attributes) {
auto& attr_info = GL_vertex_attrib_info[attr];
new_shader.program->initAttribute(attr_info.name, attr_info.attribute_id, attr_info.default_value);
new_shader.program->initAttribute(attr_info.name, attr_info.default_value);
}
nprintf(("shaders"," %s\n", variant.description));
@@ -1076,19 +1078,6 @@ void opengl_shader_init()
nprintf(("shaders","\n"));
}
/**
* Get the internal OpenGL location for a given attribute. Requires that the Current_shader global variable is valid
*
* @param attribute_text Name of the attribute
* @return Internal OpenGL location for the attribute
*/
GLint opengl_shader_get_attribute(opengl_vert_attrib::attrib_id attribute)
{
Assertion(Current_shader != nullptr, "Current shader may not be null!");
return Current_shader->program->getAttributeLocation(attribute);
}
void opengl_shader_set_passthrough(bool textured, bool hdr)
{
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_PASSTHROUGH_RENDER, 0));
+1 -3
View File
@@ -35,7 +35,7 @@ struct opengl_vert_attrib {
MODEL_ID,
RADIUS,
UVEC,
WORLD_MATRIX,
MODEL_MATRIX,
NUM_ATTRIBS,
};
@@ -151,8 +151,6 @@ void opengl_shader_shutdown();
int opengl_compile_shader(shader_type sdr, uint flags);
GLint opengl_shader_get_attribute(opengl_vert_attrib::attrib_id attribute);
void opengl_shader_set_passthrough(bool textured, bool hdr);
void opengl_shader_set_default_material(bool textured, bool alpha, vec4* clr, float color_scale, uint32_t array_index, const material::clip_plane& clip_plane);
+4 -2
View File
@@ -673,8 +673,10 @@ static int opengl_texture_set_level(int bitmap_handle, int bitmap_type, int bmap
auto mipmap_w = tex_w;
auto mipmap_h = tex_h;
// should never have mipmap levels if we also have to manually resize
if ((mipmap_levels > 1) && resize) {
// if we are doing mipmap resizing we need to account for adjusted tex size
// (we can end up with only one mipmap level if base_level is high enough so don't check it)
if (base_level > 0) {
Assertion(resize == false, "Cannot use manual and mipmap resizing at the same time!");
Assert(texmem == nullptr);
// If we have mipmaps then tex_w/h are already adjusted for the base level but that will cause problems with
+53 -29
View File
@@ -85,6 +85,7 @@ static opengl_vertex_bind GL_array_binding_data[] =
{ vertex_format_data::MODEL_ID, 1, GL_FLOAT, GL_FALSE, opengl_vert_attrib::MODEL_ID },
{ vertex_format_data::RADIUS, 1, GL_FLOAT, GL_FALSE, opengl_vert_attrib::RADIUS },
{ vertex_format_data::UVEC, 3, GL_FLOAT, GL_FALSE, opengl_vert_attrib::UVEC },
{ vertex_format_data::MATRIX4, 16, GL_FLOAT, GL_FALSE, opengl_vert_attrib::MODEL_MATRIX },
};
struct opengl_buffer_object {
@@ -855,10 +856,6 @@ void opengl_tnl_set_model_material(model_material *material_info)
Current_shader->program->Uniforms.setTextureUniform("sGlowmap", 1);
if (setAllUniforms || (flags & MODEL_SDR_FLAG_SPEC))
Current_shader->program->Uniforms.setTextureUniform("sSpecmap", 2);
if (setAllUniforms || (flags & MODEL_SDR_FLAG_ENV)) {
Current_shader->program->Uniforms.setTextureUniform("sEnvmap", 3);
Current_shader->program->Uniforms.setTextureUniform("sIrrmap", 11);
}
if (setAllUniforms || (flags & MODEL_SDR_FLAG_NORMAL))
Current_shader->program->Uniforms.setTextureUniform("sNormalmap", 4);
if (setAllUniforms || (flags & MODEL_SDR_FLAG_AMBIENT))
@@ -874,7 +871,7 @@ void opengl_tnl_set_model_material(model_material *material_info)
//No shader ever defines this, so don't push it.
//Current_shader->program->Uniforms.setTextureUniform("sHeightmap", 5);
if (material_info->get_texture_map(TM_BASE_TYPE) > 0) {
if (material_info->get_texture_map(TM_BASE_TYPE) >= 0) {
gr_opengl_tcache_set(material_info->get_texture_map(TM_BASE_TYPE),
TCACHE_TYPE_NORMAL,
&u_scale,
@@ -883,7 +880,7 @@ void opengl_tnl_set_model_material(model_material *material_info)
0);
}
if (material_info->get_texture_map(TM_GLOW_TYPE) > 0) {
if (material_info->get_texture_map(TM_GLOW_TYPE) >= 0) {
gr_opengl_tcache_set(material_info->get_texture_map(TM_GLOW_TYPE),
TCACHE_TYPE_NORMAL,
&u_scale,
@@ -892,9 +889,9 @@ void opengl_tnl_set_model_material(model_material *material_info)
1);
}
if (material_info->get_texture_map(TM_SPECULAR_TYPE) > 0 ||
material_info->get_texture_map(TM_SPEC_GLOSS_TYPE) > 0) {
if (material_info->get_texture_map(TM_SPEC_GLOSS_TYPE) > 0) {
if (material_info->get_texture_map(TM_SPECULAR_TYPE) >= 0 ||
material_info->get_texture_map(TM_SPEC_GLOSS_TYPE) >= 0) {
if (material_info->get_texture_map(TM_SPEC_GLOSS_TYPE) >= 0) {
gr_opengl_tcache_set(material_info->get_texture_map(TM_SPEC_GLOSS_TYPE),
TCACHE_TYPE_NORMAL,
&u_scale,
@@ -911,13 +908,7 @@ void opengl_tnl_set_model_material(model_material *material_info)
}
}
if (ENVMAP > 0) {
gr_opengl_tcache_set(ENVMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, 3);
gr_opengl_tcache_set(IRRMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, 11);
Assertion(array_index == 0, "Cube map arrays are not supported yet!");
}
if (material_info->get_texture_map(TM_NORMAL_TYPE) > 0) {
if (material_info->get_texture_map(TM_NORMAL_TYPE) >= 0) {
gr_opengl_tcache_set(material_info->get_texture_map(TM_NORMAL_TYPE),
TCACHE_TYPE_NORMAL,
&u_scale,
@@ -926,7 +917,7 @@ void opengl_tnl_set_model_material(model_material *material_info)
4);
}
if (material_info->get_texture_map(TM_HEIGHT_TYPE) > 0) {
if (material_info->get_texture_map(TM_HEIGHT_TYPE) >= 0) {
gr_opengl_tcache_set(material_info->get_texture_map(TM_HEIGHT_TYPE),
TCACHE_TYPE_NORMAL,
&u_scale,
@@ -935,7 +926,7 @@ void opengl_tnl_set_model_material(model_material *material_info)
5);
}
if (material_info->get_texture_map(TM_AMBIENT_TYPE) > 0) {
if (material_info->get_texture_map(TM_AMBIENT_TYPE) >= 0) {
gr_opengl_tcache_set(material_info->get_texture_map(TM_AMBIENT_TYPE),
TCACHE_TYPE_NORMAL,
&u_scale,
@@ -944,7 +935,7 @@ void opengl_tnl_set_model_material(model_material *material_info)
6);
}
if (material_info->get_texture_map(TM_MISC_TYPE) > 0) {
if (material_info->get_texture_map(TM_MISC_TYPE) >= 0) {
gr_opengl_tcache_set(material_info->get_texture_map(TM_MISC_TYPE),
TCACHE_TYPE_NORMAL,
&u_scale,
@@ -957,7 +948,7 @@ void opengl_tnl_set_model_material(model_material *material_info)
GL_state.Texture.Enable(8, GL_TEXTURE_2D_ARRAY, Shadow_map_texture);
}
if (material_info->get_animated_effect() > 0) {
if (material_info->get_animated_effect() >= 0) {
if (Scene_framebuffer_in_frame) {
GL_state.Texture.Enable(9, GL_TEXTURE_2D, Scene_composite_texture);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
@@ -1189,7 +1180,7 @@ void opengl_bind_vertex_component(const vertex_format_data &vert_component, size
if ( Current_shader != NULL ) {
// grabbing a vertex attribute is dependent on what current shader has been set. i hope no one calls opengl_bind_vertex_layout before opengl_set_current_shader
GLint index = opengl_shader_get_attribute(attrib_info.attribute_id);
GLint index = attrib_info.attribute_id;
if ( index >= 0 ) {
GL_state.Array.EnableVertexAttrib(index);
@@ -1233,15 +1224,23 @@ void opengl_bind_vertex_array(const vertex_layout& layout) {
auto attribIndex = attrib_info.attribute_id;
glEnableVertexAttribArray(attribIndex);
glVertexAttribFormat(attribIndex,
bind_info.size,
bind_info.data_type,
bind_info.normalized,
static_cast<GLuint>(component->offset));
GLuint add_val_index = 0;
for (GLint size = bind_info.size; size > 0; size -=4) {
glEnableVertexAttribArray(attribIndex + add_val_index);
glVertexAttribFormat(attribIndex + add_val_index,
std::min(size, 4),
bind_info.data_type,
bind_info.normalized,
static_cast<GLuint>(component->offset) + add_val_index * 16);
// Currently, all vertex data comes from one buffer.
glVertexAttribBinding(attribIndex, 0);
glVertexAttribBinding(attribIndex + add_val_index, static_cast<GLuint>(component->buffer_number));
add_val_index++;
}
if (component->divisor != 0) {
glVertexBindingDivisor(static_cast<GLuint>(component->buffer_number), static_cast<GLuint>(component->divisor));
}
}
Stored_vertex_arrays.insert(std::make_pair(layout, vao));
@@ -1267,3 +1266,28 @@ void opengl_bind_vertex_layout(vertex_layout &layout, GLuint vertexBuffer, GLuin
static_cast<GLsizei>(layout.get_vertex_stride()));
GL_state.Array.BindElementBuffer(indexBuffer);
}
void opengl_bind_vertex_layout_multiple(vertex_layout &layout, const SCP_vector<GLuint>& vertexBuffer, GLuint indexBuffer, size_t base_offset) {
GR_DEBUG_SCOPE("Bind vertex layout");
if (!GLAD_GL_ARB_vertex_attrib_binding) {
/*
* This will mean that decals don't render.
* It's possible, but way too much effort to support non-instanced fallback rendering here.
* By my estimation, every GPU you might still run FSO run supports this.
* per mesamatrix.net, even the least-extension supporting mesa drivers all support this extension
* */
return;
}
opengl_bind_vertex_array(layout);
GLuint i = 0;
for(const auto& buffer : vertexBuffer) {
GL_state.Array.BindVertexBuffer(i,
buffer,
static_cast<GLintptr>(base_offset),
static_cast<GLsizei>(layout.get_vertex_stride(i)));
i++;
}
GL_state.Array.BindElementBuffer(indexBuffer);
}
+1
View File
@@ -83,5 +83,6 @@ void opengl_tnl_set_model_material(model_material *material_info);
void gr_opengl_set_viewport(int x, int y, int width, int height);
void opengl_bind_vertex_layout(vertex_layout &layout, GLuint vertexBuffer, GLuint indexBuffer, size_t base_offset = 0);
void opengl_bind_vertex_layout_multiple(vertex_layout &layout, const SCP_vector<GLuint>& vertexBuffer, GLuint indexBuffer, size_t base_offset = 0);
#endif //_GROPENGLTNL_H
+8
View File
@@ -30,6 +30,14 @@ PostEffectUniformType mapUniformNameToType(const SCP_string& uniform_name)
return PostEffectUniformType::Dither;
} else if (!stricmp(uniform_name.c_str(), "tint")) {
return PostEffectUniformType::Tint;
} else if (!stricmp(uniform_name.c_str(), "custom_effect_vec3_a")) {
return PostEffectUniformType::CustomEffectVEC3A;
} else if (!stricmp(uniform_name.c_str(), "custom_effect_float_a")) {
return PostEffectUniformType::CustomEffectFloatA;
} else if (!stricmp(uniform_name.c_str(), "custom_effect_vec3_b")) {
return PostEffectUniformType::CustomEffectVEC3B;
} else if (!stricmp(uniform_name.c_str(), "custom_effect_float_b")) {
return PostEffectUniformType::CustomEffectFloatB;
} else {
error_display(0, "Unknown uniform name '%s'!", uniform_name.c_str());
return PostEffectUniformType::Invalid;
+4
View File
@@ -17,6 +17,10 @@ enum class PostEffectUniformType {
Cutoff,
Tint,
Dither,
CustomEffectVEC3A,
CustomEffectFloatA,
CustomEffectVEC3B,
CustomEffectFloatB,
};
struct post_effect_t {
+3 -3
View File
@@ -242,7 +242,7 @@ void gr_aabitmap(int x, int y, int resize_mode, bool mirror, float scale_factor)
resize_mode,
true,
mirror,
&gr_screen.current_color,
&GR_CURRENT_COLOR,
scale_factor);
}
void gr_aabitmap_ex(int x, int y, int w, int h, int sx, int sy, int resize_mode, bool mirror, float scale_factor) {
@@ -383,7 +383,7 @@ void gr_aabitmap_ex(int x, int y, int w, int h, int sx, int sy, int resize_mode,
resize_mode,
true,
mirror,
&gr_screen.current_color,
&GR_CURRENT_COLOR,
scale_factor);
}
//these are penguins bitmap functions
@@ -1439,7 +1439,7 @@ void gr_aabitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode, float a
render_mat.set_blend_mode(ALPHA_BLEND_ALPHA_BLEND_ALPHA);
render_mat.set_depth_mode(ZBUFFER_TYPE_NONE);
render_mat.set_texture_map(TM_BASE_TYPE, gr_screen.current_bitmap);
render_mat.set_color(gr_screen.current_color);
render_mat.set_color(GR_CURRENT_COLOR);
render_mat.set_cull_mode(false);
render_mat.set_texture_type(material::TEX_TYPE_AABITMAP);
+10
View File
@@ -70,6 +70,11 @@ namespace font
this->filename = newName;
}
void FSFont::setFamilyName(const SCP_string& newName)
{
this->familyName = newName;
}
[[nodiscard]] bool FSFont::getAutoScaleBehavior() const
{
return this->autoScale;
@@ -105,6 +110,11 @@ namespace font
return this->filename;
}
const SCP_string& FSFont::getFamilyName() const
{
return this->familyName;
}
void FSFont::computeFontMetrics() {
_height = this->getTextHeight() + this->offsetTop + this->offsetBottom;
+19
View File
@@ -39,6 +39,7 @@ namespace font
private:
SCP_string name = "<Invalid>"; //!< The name of this font
SCP_string filename; //!< The file name used to retrieve this font
SCP_string familyName; //!< The family name of the font. Will be "volition font" for bitmap fonts
protected:
bool autoScale = false; //!< If the font is allowed to auto scale. Only used for VFNT fonts as NVG fonts do the auto scale calculation during parse time
@@ -86,6 +87,15 @@ namespace font
*/
void setFilename(const SCP_string& newName);
/**
* @brief Sets the family name of this font.
*
* @date 17.6.2025
*
* @param newName The new famly name.
*/
void setFamilyName(const SCP_string& newName);
/**
* @brief Gets the name of this font.
*
@@ -104,6 +114,15 @@ namespace font
*/
const SCP_string& getFilename() const;
/**
* @brief Gets the family name of this font. Will be "Volition font" for bitmap fonts
*
* @date 17.6.2025
*
* @return The family name.
*/
virtual const SCP_string& getFamilyName() const;
/**
* @brief Gets the type of this font.
*
+150 -1
View File
@@ -53,7 +53,8 @@ namespace font
FSFont *FontManager::getCurrentFont()
{
return getFont(currentFontIndex);
int id = gr_lua_context_active() ? gr_lua_screen.current_font_index : currentFontIndex;
return getFont(id);
}
int FontManager::getCurrentFontIndex()
@@ -309,6 +310,153 @@ namespace font
}
}
// This function will extract the font family name (Name ID 1) from TrueType font data.
// It handles UCS-2 (UTF-16BE) to UTF-8 conversion.
static SCP_string extractFamilyNameFromTTF(const TrueTypeFontData& fontData)
{
try {
// TTF/OTF fonts start the table directory at byte 12.
constexpr size_t table_offset = 12;
const ubyte* data = fontData.data.get();
const size_t size = fontData.size;
if (size < table_offset) {
throw std::runtime_error("Font data too small for table offset");
}
// Offset to the start of the table directory (after SFNT header)
const uint8_t* tableDir = data + table_offset;
// Read numTables (ushort at offset 4 in SFNT header)
// The SFNT header bytes are big-endian
uint16_t numTables = (static_cast<uint16_t>(data[4]) << 8) | static_cast<uint16_t>(data[5]);
const uint8_t* nameTable = nullptr;
// Each table entry is 16 bytes
constexpr size_t tableEntrySize = 16;
const size_t tableDirSize = static_cast<size_t>(numTables) * tableEntrySize;
if (size < table_offset + tableDirSize) {
throw std::runtime_error("Table directory extends past file size");
}
// Iterate through table directory entries to find the 'name' table
for (int i = 0; i < numTables; ++i) {
const uint8_t* entry = tableDir + i * tableEntrySize;
// Ensure entry access is within range
if (entry + 12 >= data + size) {
throw std::runtime_error("Table entry access out of bounds");
}
// Read table tag (4 bytes)
uint32_t tag = (static_cast<uint32_t>(entry[0]) << 24) | (static_cast<uint32_t>(entry[1]) << 16) |
(static_cast<uint32_t>(entry[2]) << 8) | static_cast<uint32_t>(entry[3]);
// Check if it's the 'name' table (tag 0x6E616D65)
if (tag == 0x6E616D65) {
// Read offset to the 'name' table (4 bytes, big-endian)
uint32_t offset = (static_cast<uint32_t>(entry[8]) << 24) |
(static_cast<uint32_t>(entry[9]) << 16) |
(static_cast<uint32_t>(entry[10]) << 8) | static_cast<uint32_t>(entry[11]);
if (offset >= static_cast<uint32_t>(size)) {
throw std::runtime_error("Name table offset beyond file size");
}
nameTable = data + offset;
break;
}
}
if (!nameTable || nameTable + 6 > data + size) {
throw std::runtime_error("Name table header is missing or truncated");
}
// Name table header - All values are big-endian
uint16_t count = (static_cast<uint16_t>(nameTable[2]) << 8) | static_cast<uint16_t>(nameTable[3]);
uint16_t stringOffset = (static_cast<uint16_t>(nameTable[4]) << 8) | static_cast<uint16_t>(nameTable[5]);
constexpr size_t recordSize = 12;
const uint8_t* recordBase = nameTable + 6;
if (recordBase + count * recordSize > data + size) {
throw std::runtime_error("Name records extend beyond font data");
}
// Iterate through name records
for (int i = 0; i < count; ++i) {
const uint8_t* record =
recordBase + i * recordSize; // 6 bytes for name table header, 12 bytes per record
// Read metadata for the record
uint16_t platformID = (static_cast<uint16_t>(record[0]) << 8) | static_cast<uint16_t>(record[1]);
uint16_t encodingID = (static_cast<uint16_t>(record[2]) << 8) | static_cast<uint16_t>(record[3]);
uint16_t nameID = (static_cast<uint16_t>(record[6]) << 8) | static_cast<uint16_t>(record[7]);
uint16_t length = (static_cast<uint16_t>(record[8]) << 8) | static_cast<uint16_t>(record[9]);
uint16_t offset = (static_cast<uint16_t>(record[10]) << 8) | static_cast<uint16_t>(record[11]);
// We are looking for Name ID 1 (Font Family Name)
// Prefer Unicode (Platform ID 0, 3) or Apple Roman (Platform ID 1) if available
if (nameID == 1) {
// Check for Unicode (Platform ID 0, Encoding ID 3 or 4) or (Platform ID 3, Encoding ID 1)
// or Mac Roman (Platform ID 1, Encoding ID 0)
bool isUnicode = (platformID == 0 && (encodingID == 3 || encodingID == 4)) ||
(platformID == 3 && encodingID == 1);
bool isMacRoman = (platformID == 1 && encodingID == 0);
if (isUnicode || isMacRoman) {
const uint8_t* nameString = nameTable + stringOffset + offset;
SCP_string familyName;
// Bounds check to prevent reading past the end of the font metadata
if (nameString + length > data + size) {
throw std::runtime_error("Name string extends past font data");
}
if (isUnicode) {
// UCS-2 (UTF-16BE) to UTF-8 conversion
// This assumes standard UCS-2, which is UTF-16BE for basic multilingual plane.
for (int j = 0; j < length; j += 2) {
uint16_t unicodeChar = (static_cast<uint16_t>(nameString[j]) << 8) |
static_cast<uint16_t>(nameString[j + 1]);
if (unicodeChar <= 0x7F) { // 1-byte UTF-8
familyName += static_cast<char>(unicodeChar);
} else if (unicodeChar <= 0x7FF) { // 2-byte UTF-8
familyName += static_cast<char>(0xC0 | (unicodeChar >> 6));
familyName += static_cast<char>(0x80 | (unicodeChar & 0x3F));
} else { // 3-byte UTF-8
familyName += static_cast<char>(0xE0 | (unicodeChar >> 12));
familyName += static_cast<char>(0x80 | ((unicodeChar >> 6) & 0x3F));
familyName += static_cast<char>(0x80 | (unicodeChar & 0x3F));
}
// For supplementary planes (4-byte UTF-8, characters > 0xFFFF),
// this simple conversion is not sufficient and would require surrogate pair handling.
// Most common font names will be in BMP (Basic Multilingual Plane) according to my
// research - Mjn
}
} else { // Mac Roman (single byte) - this encoding is less common but supported easily enough
for (int j = 0; j < length; ++j) {
familyName += static_cast<char>(nameString[j]);
}
}
return familyName;
}
}
}
throw std::runtime_error("No suitable family name found");
} catch (const std::exception& e) {
mprintf(("Failed to extract font name: %s\n", e.what()));
return "";
} catch (...) {
mprintf(("Failed to extract font name: Unknown exception\n"));
return "";
}
}
std::pair<NVGFont*, int> FontManager::loadNVGFont(const SCP_string& fileName, float fontSize)
{
auto iter = allocatedData.find(fileName);
@@ -359,6 +507,7 @@ namespace font
std::unique_ptr<NVGFont> nvgFont(new NVGFont());
nvgFont->setHandle(handle);
nvgFont->setSize(fontSize);
nvgFont->setFamilyName(extractFamilyNameFromTTF(*data));
auto ptr = nvgFont.get();
+6
View File
@@ -33,6 +33,12 @@ namespace font
return this->fontPtr;
}
const SCP_string& VFNTFont::getFamilyName() const
{
static const SCP_string volitionFontName = "Volition Font";
return volitionFontName;
}
extern int get_char_width_old(font* fnt, ubyte c1, ubyte c2, int *width, int* spacing);
void VFNTFont::getStringSize(const char *text, size_t textSize, int /* resize_mode */, float *w1, float *h1, float scaleMultiplier) const
{
+9
View File
@@ -55,6 +55,15 @@ namespace font
*/
float getTextHeight() const override;
/**
* @brief Gets the family name of this font
*
* @see FSFont::getFamilyName()
*
* @return The family name.
*/
const SCP_string& getFamilyName() const override;
/**
* @brief Gets the size of the specified string in pixels.
*
+68 -24
View File
@@ -71,7 +71,7 @@ namespace
}
}
void parse_nvg_font(const SCP_string& fontFilename)
bool parse_nvg_font(const SCP_string& fontFilename)
{
float size = 8.0f;
SCP_string fontStr;
@@ -122,27 +122,27 @@ namespace
if (hasName)
{
error_display(0, "Font with name \"%s\" is already present! Font names have to be unique!", fontStr.c_str());
return;
return false;
}
else
{
error_display(0, "Found font with same default name (\"%s\"). This is most likely a duplicate.", fontStr.c_str());
return;
return false;
}
}
auto nvgPair = FontManager::loadNVGFont(fontFilename, size);
auto nvgFont = nvgPair.first;
// Now we can set the auto size behavior which is used for special character rendering
nvgFont->setAutoScaleBehavior(autoSize);
if (nvgFont == NULL)
{
error_display(0, "Couldn't load font \"%s\".", fontFilename.c_str());
return;
return false;
}
// Now we can set the auto size behavior which is used for special character rendering
nvgFont->setAutoScaleBehavior(autoSize);
if (optional_string("+Can Scale:")) {
bool temp;
@@ -289,9 +289,11 @@ namespace
// Make sure that the height is not invalid
nvgFont->computeFontMetrics();
return true;
}
void parse_vfnt_font(const SCP_string& fontFilename)
bool parse_vfnt_font(const SCP_string& fontFilename)
{
auto vfntPair = FontManager::loadVFNTFont(fontFilename);
auto font = vfntPair.first;
@@ -299,7 +301,7 @@ namespace
if (font == NULL)
{
error_display(0, "Couldn't load font\"%s\".", fontFilename.c_str());
return;
return false;
}
SCP_string fontName;
@@ -315,6 +317,7 @@ namespace
font->setName(fontName);
font->setFilename(fontFilename);
font->setFamilyName("Volition Font");
int font_id = vfntPair.second;
@@ -420,8 +423,11 @@ namespace
font->setBottomOffset(temp);
}
// Make sure that the height is not invalid
font->computeFontMetrics();
return true;
}
void font_parse_setup(const char *fileName)
@@ -459,25 +465,39 @@ namespace
FontType type;
SCP_string fontName;
SCP_vector<SCP_string> skipped_font_names;
while (parse_type(type, fontName))
{
bool parsed = false;
switch (type)
{
case VFNT_FONT:
if (Unicode_text_mode) {
WarningEx(LOCATION, "Bitmap fonts are not supported in Unicode text mode! Font %s will be ignored.", fontName.c_str());
skip_to_start_of_string_one_of({"$TrueType:", "$Font:", "#End"});
skipped_font_names.push_back(fontName);
} else {
parse_vfnt_font(fontName);
parsed = parse_vfnt_font(fontName);
}
break;
case NVG_FONT:
parse_nvg_font(fontName);
parsed = parse_nvg_font(fontName);
break;
default:
error_display(0, "Unknown font type %d! Get a coder!", (int)type);
break;
}
if (!parsed)
skip_to_start_of_string_one_of({ "$TrueType:", "$Font:", "#End" });
}
// check if we skipped any fonts
if (!skipped_font_names.empty()) {
Warning(LOCATION, "One or more bitmap fonts were skipped because they are not supported in Unicode text mode. The list of fonts is in the debug log.");
for (const auto& skippedFont : skipped_font_names) {
mprintf(("Warning: Skipped bitmap font: %s\n", skippedFont.c_str()));
}
}
// done parsing
@@ -539,24 +559,48 @@ namespace font
font_initialized = false;
}
int force_fit_string(char *str, int max_str, int max_width, float scale)
int force_fit_string(char *str, size_t max_str_len, int max_width, float scale)
{
int w;
if (max_width <= 0 || max_str_len == 0) {
*str = 0;
return 0;
}
size_t len = strlen(str);
if (len > max_str_len) {
len = max_str_len;
str[len] = 0;
}
int w;
gr_get_string_size(&w, nullptr, str, scale);
if (w > max_width) {
if ((int)strlen(str) > max_str - 3) {
Assert(max_str >= 3);
str[max_str - 3] = 0;
constexpr char ellipsis_char = '.';
constexpr size_t ellipsis_len = 3;
// make sure an ellipsis will fit
if (len < ellipsis_len) {
*str = 0;
return 0;
}
strcpy(str + strlen(str) - 1, "...");
gr_get_string_size(&w, nullptr, str, scale);
while (w > max_width) {
Assert(strlen(str) >= 4); // if this is hit, a bad max_width was passed in and the calling function needs fixing.
strcpy(str + strlen(str) - 4, "...");
gr_get_string_size(&w, nullptr, str, scale);
// replace the last few chars with an ellipsis
for (size_t i = 0; i < ellipsis_len; ++i) {
--len;
str[len] = ellipsis_char;
}
// measure with ellipsis before shrinking further (the ellipsis characters could be narrower than the characters they replaced)
gr_get_string_size(&w, nullptr, str, scale, len + ellipsis_len);
// move the ellipsis back until the whole string fits
while (len > 0 && w > max_width) {
--len;
str[len] = ellipsis_char;
gr_get_string_size(&w, nullptr, str, scale, len + ellipsis_len);
}
str[len + ellipsis_len] = 0;
}
return w;
+2 -2
View File
@@ -27,11 +27,11 @@ namespace font
* Does this by dropping characters at the end of the string and adding '...' to the end.
*
* @param str string to crop. Modifies this string directly
* @param max_str max characters allowed in str
* @param max_str_len max characters allowed in str (not including \0)
* @param max_width number of pixels to limit string to (less than or equal to).
* @return The width of the string
*/
int force_fit_string(char *str, int max_str, int max_width, float scale = 1.0f);
int force_fit_string(char *str, size_t max_str_len, int max_width, float scale = 1.0f);
/**
* @brief Inites the font system
+7 -17
View File
@@ -109,7 +109,7 @@ void convert_model_material(model_uniform_data* data_out,
}
data_out->defaultGloss = 0.6f;
if (material.get_texture_map(TM_BASE_TYPE) > 0) {
if (material.get_texture_map(TM_BASE_TYPE) >= 0) {
if (material.is_desaturated()) {
data_out->desaturate = 1;
} else {
@@ -132,12 +132,12 @@ void convert_model_material(model_uniform_data* data_out,
data_out->sBasemapIndex = bm_get_array_index(material.get_texture_map(TM_BASE_TYPE));
}
if (material.get_texture_map(TM_GLOW_TYPE) > 0) {
if (material.get_texture_map(TM_GLOW_TYPE) >= 0) {
data_out->sGlowmapIndex = bm_get_array_index(material.get_texture_map(TM_GLOW_TYPE));
}
if (material.get_texture_map(TM_SPEC_GLOSS_TYPE) > 0) {
if (material.get_texture_map(TM_SPEC_GLOSS_TYPE) >= 0) {
data_out->sSpecmapIndex = bm_get_array_index(material.get_texture_map(TM_SPEC_GLOSS_TYPE));
data_out->gammaSpec = 1;
@@ -145,32 +145,22 @@ void convert_model_material(model_uniform_data* data_out,
}
if (material.get_texture_map(TM_SPECULAR_TYPE) > 0) {
if (material.get_texture_map(TM_SPECULAR_TYPE) >= 0) {
data_out->sSpecmapIndex = bm_get_array_index(material.get_texture_map(TM_SPECULAR_TYPE));
data_out->gammaSpec = 0;
data_out->alphaGloss = 0;
}
if (ENVMAP > 0) {
if (material.get_texture_map(TM_SPEC_GLOSS_TYPE) > 0) {
data_out->envGloss = 1;
} else {
data_out->envGloss = 0;
}
data_out->envMatrix = gr_env_texture_matrix;
}
if (material.get_texture_map(TM_NORMAL_TYPE) > 0) {
if (material.get_texture_map(TM_NORMAL_TYPE) >= 0) {
data_out->sNormalmapIndex = bm_get_array_index(material.get_texture_map(TM_NORMAL_TYPE));
}
if (material.get_texture_map(TM_AMBIENT_TYPE) > 0) {
if (material.get_texture_map(TM_AMBIENT_TYPE) >= 0) {
data_out->sAmbientmapIndex = bm_get_array_index(material.get_texture_map(TM_AMBIENT_TYPE));
}
if (material.get_texture_map(TM_MISC_TYPE) > 0) {
if (material.get_texture_map(TM_MISC_TYPE) >= 0) {
data_out->sMiscmapIndex = bm_get_array_index(material.get_texture_map(TM_MISC_TYPE));
}
Loaded 100 of 1070 files, more files were not shown because too many files have changed in this diff. Show more