Compare commits

...
Author SHA1 Message Date
JohnAFernandez 05f2db381a Create temp_coverity_scan.yaml
Temporary coverity scan workflow until the scheduled one gets a review
2023-10-30 11:10:32 -04:00
John Fernandez 6038076bf0 Add current_cmeasure > -1 check
This is an actual edge case bug.  If no countermeasure was set, and then one was set, there might be a garbage timestamp set that would give an incorrect countermeasure delay on next use.
2023-10-30 10:56:45 -04:00
John Fernandez 40c8cbbb25 Add check for negative index
For coverity 1523515
2023-10-30 10:56:45 -04:00
John Fernandez bd7429c353 Get rid of Extraneous check
Coverity 1523341 is pointing out this check, but shipp->ship_info_index is checked earlier just before we set the shipp pointer on line 1243
2023-10-30 10:56:45 -04:00
John Fernandez faf130adb3 Add Assertion for Coverity 1093701 2023-10-30 10:56:45 -04:00
John Fernandez f7fef072df Feedback 2023-10-30 10:56:18 -04:00
John Fernandez 0da6a4a64c Coverity 1523287
Fall back to standard behavior if no allowed paths.

Missing bracket
2023-10-30 10:56:18 -04:00
John Fernandez f542fea96e coverity 1523419
Avoid dividing by zero when there is just one autoscale ai class
2023-10-30 10:56:18 -04:00
John Fernandez 742326e42e Coverity 1523548 Divide by zero
Make sure we only use autoscale delay if there is more than one autoscale ai class.
2023-10-30 10:56:18 -04:00
John Fernandez e25a6517fc Add a check for 0
To avoid division by zero (in dubious circumstances) explicitly check for zero.  For coverity 1523313
2023-10-30 10:56:18 -04:00
John Fernandez be0c78dc72 Add checks for options_multi_vox_plist_get
For coverity 1093612, this function *should* never return zero, but it is no catastrophic if it does.  Allowing it to return a negative value in some unknown edge case would allow it to clobber some other global data.

So now add a check for negative values and take no action if those negative values are reached.  Assert on debug.
2023-10-30 10:55:41 -04:00
John Fernandez ef681affbf Make sure we don't clobber other memory
If this index is negative one return early before we accidentally clobber other data.

Coverity 1523181
2023-10-30 10:55:41 -04:00
Goober5000 3a2ac5ab5f Merge pull request #5726 from Goober5000/persona_index_check
check for out-of-range persona
2023-10-30 00:01:03 -04:00
Goober5000 435676a67d Merge pull request #5730 from Goober5000/class_type_check
make sure class_type exists when used
2023-10-30 00:00:47 -04:00
Goober5000 4fbe3b7131 make sure class_type exists when used
A mission in Mantle caused an invalid vector access when checking whether to vaporize a ship whose ship type was -1.  Turns out having "no type" is actually allowed, so I audited all `class_type` accesses throughout the code for potential -1 accesses.  Most were properly checked but a few were not.  A few others needed to be tweaked.

(I suspect this is something that would be on the Coverity list if it isn't already.)
2023-10-29 15:17:22 -04:00
Goober5000 87a4ae1e41 check for out-of-range persona
For missions which use the Persona Index method, make sure the mod doesn't load a persona index that is out of range for the personas it knows about.
2023-10-28 21:23:25 -04:00
mjn-mixael c738941ccf Provide Lua a way to toggle the mainhall help overlay 2023-10-28 19:16:09 -04:00
John Fernandez 5c25bb9bcb Just use the already existing SCP_string
The destination strings in player_order are already SCP_strings, so no need to use manual allocation
2023-10-28 19:15:25 -04:00
John Fernandez e0bc6d0ed7 Fix memory leak in tga file writing
Yes.  I know, I know, we're not going to use this code anymore.  But just in case, I don't want this memory leak to have the possibility to exist in the future.
2023-10-28 19:15:25 -04:00
Mike Nelson ec706a6a87 Allow SCPUI saving current player data to file (#5718)
* Allow saving current player data to file

* return nil, dummy

* unused
2023-10-27 07:32:37 -04:00
John Fernandez 5d0127a764 Fix workflow by adding reference branch 2023-10-26 23:41:28 -04:00
Mike Nelson 91c15e6f48 Player general orders (#5638) 2023-10-26 19:22:44 -04:00
Asteroth a9fc7bab8a fix for particle culling (#5719) 2023-10-26 16:08:09 -04:00
Taylor Richards 042c85d213 revert workaround for the windows runner breakage (#5722) 2023-10-26 13:53:27 -04:00
Goober5000 bb3d59ecbe Merge pull request #5715 from Baezon/fix-hud-target-triangle
Bugfix HUD target direction triangle
2023-10-25 23:19:26 -04:00
Goober5000 d1c57391cf Merge pull request #5699 from Goober5000/handle_shuffled_parse_objects
handle parse objects that are out of order
2023-10-25 23:18:19 -04:00
Mike Nelson 5986b7143f Adds custom data for intel entries (#5660)
* adds custom data for intel entries

* update tests

* This one, too
2023-10-25 07:32:03 -04:00
wookieejedi 5d447951b4 Add Game Option to refresh selected weapon in loadout (#5713)
* Add Game Option to refresh selected weapon in loadout

This PR adds an option to always ensure the viewed weapon is one that can actually be on the selected ship when first clicking on the weapon loadout.

If you go to the loadout in the briefing and view the weapons on your ship, then go and change your ship class and go back to the weapon loadout screen, you will see that the weapon shown in the window on the right side is from your very first viewing before you changed your ship class. Displaying a weapon not allowed on a ship by default can be confusing to players, especially on mods like FotG where most ships have unique weapons.

The player can still of course click on a non-allowed weapon to view it, but with this PR the player will be initially shown a selected weapon that is allowed on the ship when opening the loadout editor.

Tested and works as expected.

* update order to be near weapon bools
2023-10-25 07:31:23 -04:00
Sébastien Noel f149c36b44 Fix a few typos (#5598) 2023-10-24 20:33:45 -04:00
Asteroth 34b6cf74ab whoops forgot the rval 2023-10-24 15:16:12 -04:00
Asteroth bc33f90e32 early return if z is 0 2023-10-24 14:32:56 -04:00
MageKing17 bb47531c55 Make lock_fov settable for aspect/javelin missiles. (#5493)
The code has always checked a per-weapon-class value called `lock_fov` when comparing the dot product of a ship to its target to see if it can lock on to it, however the weapon table hasn't ever included a way to set this value; it's always the default value of 0.85. This just adds "+Lock FOV:" entries to aspect/javelin homing missile parsing after "+Seeker Strength:", which will set `lock_fov` as long as the supplied input is between 0 and 360 degrees.
2023-10-24 07:26:54 -04:00
Asteroth aed90493ec make g3_project_vertex unconditionally project 2023-10-23 17:53:30 -04:00
John Fernandez d54c3e51e3 Apply Workaround to windows Build 2023-10-23 15:47:27 -04:00
Goober5000 a215bea0d0 unswap the net signatures 2023-10-23 15:29:06 -04:00
Goober5000 ec23f6bd0e Merge pull request #5709 from Goober5000/fix_loadout_crash
fix a crash in the apply-loadout-to-wing routine
2023-10-23 12:21:30 -04:00
John Fernandez d93d56e1c9 remove redundant setting of object_host 2023-10-23 10:24:13 -04:00
John Fernandez d2265325e9 check for nullptr to avoid nullptr dereference
For coverity 1093467
2023-10-23 10:24:13 -04:00
John Fernandez 2bb22a2153 Return to avoid nullptr dereference
For coverity 1093461.  If this is null, the function is done at this point.
2023-10-23 10:24:13 -04:00
John Fernandez 018c22eee1 Make sure Net_player is not null here
For coverity 1093460. Net_player should never be null here, but it is C++ after all.
2023-10-23 10:24:13 -04:00
John Fernandez 6ef60c67ca Make sure Multi_pxo_channels is not null
For coverity 1093458
2023-10-23 10:24:13 -04:00
John Fernandez 982e8d7f85 Add net_player nullptr check
Address coverity 1093457 by adding a nullptr check and Assertion.  This *should* never happen, but that's what asserts are for, right?
2023-10-23 10:24:13 -04:00
John Fernandez 8affd53cf6 Remove Unused Functions
These functions have not been called from what I can tell since the source code was released.  But they did happen to trigger a clang warning, so it's time to get rid of them.
2023-10-23 07:50:29 -04:00
Mike Nelson eeaf894b15 Message notes feature (#5541)
* Add stage notes feature for mission messages

* don't keep notes outside the mission editor

* use F_MULTITEXT and do lcl_replacements

* this should be multitext too
2023-10-23 07:38:33 -04:00
Goober5000 b546d897ae fix a crash in the apply-loadout-to-wing routine
If a wing has fewer than four ships, clicking the apply-to-all button will cause a vector subscript out of range error.  This fixes it.
2023-10-23 00:44:33 -04:00
John Fernandez d8aea4ad30 Add CMakePresets.json to gitignore 2023-10-22 22:54:24 -04:00
Goober5000 829692b7bd Merge pull request #5468 from Goober5000/5462_followup
additional cleanup from PR 5462
2023-10-22 17:10:42 -04:00
Goober5000 855935d4eb Merge pull request #5706 from Goober5000/fix_5705
fix change-soundtrack after <No Music>
2023-10-22 16:12:30 -04:00
Goober5000 f83efb54c0 fix change-soundtrack after <No Music>
The fix in #5578 assumed that a previously playing soundtrack would properly set the timestamp, but if no soundtrack was previously played at all, this wouldn't happen.  So if we deliberately don't play a soundtrack, be sure to set the timestamp as if `event_music_do_frame()` had been called.

Fixes #5705.
2023-10-22 15:31:02 -04:00
Mike Nelson 3ca26587eb add default player weapon flag (#5600) 2023-10-22 13:23:30 -04:00
John Fernandez 3e2bb36d4b Fix Redefined Macro Warning
Clarify
2023-10-22 00:37:18 -04:00
Goober5000 02bd62ff3c Merge pull request #5700 from Goober5000/special_to_supercap
change "special warp physics" to "supercap warp physics"
2023-10-21 17:34:20 -04:00
Goober5000 e1e0239914 also the dialog text 2023-10-21 16:40:31 -04:00
Goober5000 e621dbe997 Merge pull request #5464 from Goober5000/render_constify
add const where appropriate to the modelrender API
2023-10-21 15:57:24 -04:00
Goober5000 ea5c328c13 change "special warp physics" to "supercap warp physics"
Per discussion with @Baezon, this is a more precise term.
2023-10-21 15:51:16 -04:00
Goober5000 8c0bc25303 Merge pull request #5671 from Goober5000/subsystem_calculation_fix
add "Calculate subsystem hitpoints after parsing" option
2023-10-21 14:08:37 -04:00
Goober5000 6b0a3b7606 handle parse objects that are out of order
Occasionally, FRED can save a mission with a wing's ships out of order; this can happen if ships are deleted and FRED finds a free index in the middle of the ship list.  This can cause benign but confusing mismatches in the ship registry, the hotkey list, and other places -- and it's also possible some mismatches may not be so benign, e.g. in multiplayer.  Anyway, it's better for the ships to appear in the order the wing expects them, so this adds a routine to swap them around if necessary.
2023-10-21 13:40:09 -04:00
Mike Nelson 9afe76b5d2 force msaa in-game toggle to require restart (#5688) 2023-10-21 11:44:15 -04:00
Mike Nelson ad27664ebc Pause and Resume voices (#5636)
* pause and resume voices

* Use newer styling rather than the sound.cpp styling
2023-10-21 10:23:53 -04:00
Goober5000 2fe182a4b1 Merge pull request #5604 from Goober5000/drawString_tweak
small speedup to drawString
2023-10-21 01:28:23 -04:00
Goober5000 60c2718b91 add spacing 2023-10-20 23:26:46 -04:00
Tyler Blount 15d9b9b3db Merge pull request #5689 from MjnMixael/fix_lightshafts_optimization
Fix lightshafts and bloom in-game options
2023-10-20 15:09:03 -07:00
mjn-mixael d00003e199 Merge branch 'master' into fix_lightshafts_optimization 2023-10-20 16:11:34 -05:00
Tyler Blount 991d0ee2d6 Merge pull request #5696 from MjnMixael/deferred_lighting_ingame
Add deferred lighting as an in-game option
2023-10-20 14:09:33 -07:00
mjn-mixael 3eee74a31c remove unneeded header 2023-10-20 14:16:22 -05:00
mjn-mixael 941d5f7ec3 Move the option definition to a file that doesn't require glad.h for building and resolve xstr issue 2023-10-20 14:01:50 -05:00
Asteroth 0cde26dd6e make invalid model_subsystem model nums a hard error (#5591) 2023-10-20 14:40:48 -04:00
Mike Nelson 7804f7fff5 add feature to always display the damage gauge (#5518) 2023-10-20 14:40:27 -04:00
Asteroth 832cec27ad bugfix missile time determination (#5595) 2023-10-20 14:40:02 -04:00
Mike Nelson 31acb115c2 Add nebula-fade-poof SEXP (#5612)
* nebula-fade-poofs

* Move this up because order of operations is important.

* Add some documentation

* Respect time compression

* Fix sexp description

* Make sure faded poofs do not flicker

* optimize
2023-10-20 14:39:38 -04:00
Goober5000 f9591b3a92 Merge pull request #5664 from Goober5000/fix_cheat_status
fix incorrect setting of disabled/disarmed flags when cheating
2023-10-20 14:27:37 -04:00
mjn-mixael 21f92194f1 Add deferred lighting as an in-game option 2023-10-20 12:38:53 -05:00
wookieejedi c605451460 Fix glare shining through cockpits in freelook (#5690)
Fixes #3098. The `shipfx_eye_in_shadow` function did a collision check for cockpits, bit did not previously take into account of the viewpoint had a non-forward orientation (ie the player was using free look). This PR sets the collide check to take that into account with a 1 line fix. Tested with multiple FotG cockpits and fixes the issue.
2023-10-20 07:39:25 -04:00
Taylor Richards 2acdc65875 add workaround for broken windows workflow (#5693)
Temporary fix for broken windows workflow. Hopefully a more permanent
solution will be found soon and we can then remove this.

Reference: https://github.com/actions/runner-images/issues/8598
2023-10-19 17:39:04 -04:00
Goober5000 b7de2fd644 Merge pull request #5554 from Goober5000/set_armor_type_generic
enhance set-armor-type to work with generic subsystems
2023-10-19 14:09:50 -04:00
Tyler Blount a226a81d80 Merge pull request #5692 from JohnAFernandez/master
Revert one "Fix" and add comments
2023-10-19 09:25:36 -07:00
John Fernandez eb39532f7c Revert one "Fix" and add comments
Only one of these was an actual memory leak.

Add comments to clarify
2023-10-19 11:42:24 -04:00
JohnAFernandez 1478ace3e0 Avoid edge case divide by zero (#5683)
* Add a divide by zero check

For coverity 1107324.  Keeps framecount from dividing by zero in very rare circumstances.

* Add a clarifying comment

This marks isNaturalNumberFactor() so that we know it is the best way to get whether a number is a natural number.

Modulo division is slower and gives the same results.

* Spoiled by Rust
2023-10-19 07:57:12 -04:00
JohnAFernandez 7edc5bf842 Change underlying type to avoid endianness problem (#5680)
Before this change if the wrong endianness was used, the c style cast that was used here would lead to the argument being passed to getsockopt to lack the actual information.

Fixes coverity 1093482

(Directly cast to socklen_t)
2023-10-19 07:56:21 -04:00
JohnAFernandez b8dc7d643d Fix two small memory leaks (#5679)
Fixes 1093679 and 1093680 from coverity by freeing if an early return is reached.
2023-10-19 07:55:23 -04:00
Goober5000 883ed32796 ai-disable-ship-tactical and ai-disarm-ship-tactical (#5512)
Add "tactical" versions of disable and disarm goals which only perform the specific task without implying anything else.  The target ship is not protected and attack goals are not cleared from other ships.

There is also a new AI profiles flag to make this the default hud squad message command instead of the classic disable/disarm.
2023-10-19 07:50:49 -04:00
Mike Nelson f3dfc85c0f weapon ignores countermeasures flag (#5628) 2023-10-19 07:49:06 -04:00
mjn-mixael e32ea7b694 Merge branch 'master' into fix_lightshafts_optimization
# Conflicts:
#	code/graphics/post_processing.cpp
2023-10-18 23:09:43 -05:00
Tyler Blount 7ef70f6d4d Merge pull request #5627 from MjnMixael/new_controls_xstrs
Add built-in xstrs for SCP-added controls
2023-10-18 20:21:49 -07:00
Tyler Blount 1b1afbf8d7 Merge branch 'master' into new_controls_xstrs 2023-10-18 19:51:26 -07:00
Tyler Blount bfdf05469b Merge pull request #5622 from MjnMixael/scpui_options_localization
SCPUI Options Localization
2023-10-18 19:50:43 -07:00
mjn-mixael aee48373ab Make sure these post processing variables are not optimized out of non Debug builds 2023-10-18 15:07:21 -05:00
mjn-mixael a2f1638a99 adjust XSTR IDs 2023-10-18 13:55:46 -05:00
mjn-mixael 762cf4cb88 We'll get this right eventually 2023-10-18 13:45:55 -05:00
mjn-mixael 705287c8d4 "nothing" to see here 2023-10-18 13:26:05 -05:00
mjn-mixael e2aac31912 Merge branch 'master' into scpui_options_localization
# Conflicts:
#	code/camera/camera.cpp
2023-10-18 12:49:48 -05:00
Tyler Blount a9f5a11813 Merge pull request #5686 from MjnMixael/lua_add_scrollback
Lua add scrollback
2023-10-18 10:21:01 -07:00
mjn-mixael b8728ed828 get team rather than team index 2023-10-18 08:59:24 -05:00
mjn-mixael de1110650f Adjust XSTR IDs per feedback 2023-10-18 08:42:25 -05:00
BMagnu b3295e5a2d Fix near z planes for the cockpit rendering (#5654)
* Properly unify cockpit near planes

* Properly pass near plane to deferred shader
2023-10-18 08:08:22 -04:00
Tyler Blount a6c1b1b04a Merge pull request #5605 from naomimyselfandi/more-message-filters
More message filters
2023-10-18 00:36:24 -07:00
Goober5000 b5fdf52e8c Merge pull request #5428 from Goober5000/remove_key_save
remove hud_squadmsg_save_keys()
2023-10-18 03:25:09 -04:00
Goober5000 c43f5bdd58 Merge pull request #5516 from Goober5000/pof_version_fix
POF version fix
2023-10-18 03:24:55 -04:00
Tyler Blount dc7fc774b8 Merge pull request #5662 from MjnMixael/on_intro_hook
Intro about to play hook
2023-10-18 00:24:44 -07:00
Goober5000 dad16f76c4 Merge pull request #5574 from Goober5000/generateWeaponGroupId
add mn.generateWeaponGroupId()
2023-10-17 20:06:27 -04:00
Goober5000 d10c59f6cc Merge pull request #5629 from Goober5000/arrival_warp_distance
add support for predicting arrival warp distance
2023-10-17 20:06:17 -04:00
Goober5000 3bd2fc9a56 Merge pull request #5677 from Goober5000/fix_5529
rewrite primary weapon cycling
2023-10-17 20:06:10 -04:00
Goober5000 fabff58d09 Merge pull request #5684 from Goober5000/prevent_repeated_death
don't kill/self-destruct ship if it's already dying
2023-10-17 20:05:29 -04:00
Mike Nelson 2fd3bf091d add waytpoint as allowed lua sexp parameter type (#5631) 2023-10-17 19:28:40 -04:00
Mike Nelson b4442d3423 double the allowed cargo (#5626) 2023-10-17 19:27:43 -04:00
Mike Nelson cf4cae7264 add dkjson as a default lua file (#5534) 2023-10-17 19:26:39 -04:00
Mike Nelson ebea1b1240 remove new lines from default generated mission (#5519) 2023-10-17 19:25:06 -04:00
mjn-mixael 54341acb9b allow use of enums 2023-10-16 15:54:53 -05:00
mjn-mixael 07fd188064 lua method to add a message directly to the message log.
(cherry picked from commit 1caceeaa2b9ebe654a102e293146dc23b9f3c2ee)
2023-10-16 15:21:37 -05:00
BMagnu 0cbe06884b Allow to Capture Mouse in FSO window (#5678)
* Allow to capture mouse, and also make window present

* Fix Typo
2023-10-17 03:57:21 +09:00
Goober5000 c1d3e5a2ad Merge pull request #5597 from Goober5000/maneuver_despite_engines
add a maneuver-despite-engines flag
2023-10-16 14:40:05 -04:00
Naomi Coffman 2bcedc34cc Respond to PR feedback 2023-10-16 12:36:48 -04:00
Goober5000 16892c3e46 Merge pull request #5670 from Goober5000/fly_to_ship_upgrade
fix and upgrade ai-fly-to-ship
2023-10-16 04:26:23 -04:00
Goober5000 5c88b75b94 Merge pull request #5524 from Goober5000/splash_aspect_ratio
aspect-ratio-specific splash screens
2023-10-16 03:26:10 -04:00
Goober5000 987c705bc9 address feedback 2023-10-16 03:24:53 -04:00
Goober5000 b89cbbd530 fix and upgrade ai-fly-to-ship
The ai-fly-to-ship goal was added early in FSO's development but never worked right.  Until recently it could crash; and even aside from the crash the AI would try to reach the target's exact position, causing repeated collisions.  This folds ai-fly-to-ship into ai-stay-near-ship, since the behaviors are conceptually the same except for fly-to being satisfied as soon as the target is in range.

This also adds a new "escort" mode usable by both behaviors.  The default Volition behavior is to swoop toward the target in a long, graceful arc; the new mode generates a goal point and flies directly to that point.  These can be toggled via an optional sexp parameter.

There is also some minor cleanup and a new vec3d operator for convenience.

Needed for Scroll; depends on #5667.
2023-10-16 03:24:52 -04:00
Goober5000 d3383d9d87 Merge pull request #5667 from Goober5000/refactor_mission_goal_achievable
refactor ai_mission_goal_achievable
2023-10-16 03:23:43 -04:00
Goober5000 4f6d9c36a8 don't kill/self-destruct ship if it's already dying
If a ship is in the process of dying, it shouldn't be killed or self-destructed again.  This fixes a bug noticed by RedMageJoe during the Derelict playthrough.

(This situation is distinct from a ship being hit while in the middle of its death roll.  That shortens the death roll but does not try to kill the ship again.)
2023-10-16 00:36:10 -04:00
JohnAFernandez a36157f582 Merge pull request #5540 from MjnMixael/lua_mod_root
LUA get mod root
2023-10-15 23:16:40 -04:00
JohnAFernandez 3837455004 Merge pull request #5617 from MjnMixael/allow_no_subcategory
Allow Lua SEXPs to not specify a subcategory
2023-10-15 23:15:59 -04:00
JohnAFernandez 808f7a88c6 Merge pull request #5557 from MjnMixael/lua_launch_url
Lua method to launch arbitrary url
2023-10-15 23:15:08 -04:00
Goober5000 79702273d5 some tweaks and clarifications 2023-10-15 16:55:50 -04:00
BMagnu d06018bcad Bump version to 23.3 (#5681) 2023-10-15 19:23:34 +00:00
Goober5000 ac87cde80d supercap check should come from params 2023-10-15 14:16:51 -04:00
Goober5000 06d9e02c34 sync up tokens 2023-10-15 13:48:23 -04:00
Asteroth 40233ae05f use a constant for excess speed 2023-10-15 13:43:49 -04:00
Goober5000 57192065d6 change assert to assertion 2023-10-15 13:43:49 -04:00
Goober5000 d2031a476a some fixes 2023-10-15 13:43:49 -04:00
Asteroth c60ea2d4a7 move supercap warp time constant 2023-10-15 13:43:49 -04:00
Asteroth 6843d48358 add math for predicting warp in distance 2023-10-15 13:43:47 -04:00
Goober5000 066b251052 implement UI work for arrival warp distance 2023-10-15 13:42:27 -04:00
BMagnu 4596600627 Fix hiDPI (#5673) 2023-10-16 01:44:33 +09:00
BMagnu ea262f90c0 Make version parsing available (#5655)
* Make version parsing available

* Consolidate version parsing

* Recode version parsing and write test

* remove copy paste error
2023-10-16 01:44:25 +09:00
BMagnu 02e84f212a Fix HUD slewing (#5656)
* Fixup HUD for slewing

* Fix Training Message box

* Lead indicator slew

* Fix HUD Message Head

* Correctly render messages

* Fix HUD offset handling

* HUD offset never makes any sense

* Cleanup offset code

* Remove unused parameter

* Fix Talking Head in RTT mode
2023-10-16 01:44:17 +09:00
Goober5000 a1d92afebf Merge pull request #5659 from Goober5000/objecttypes_warp_range_factor
allow modification of warp audible range via objecttypes.tbl
2023-10-15 12:43:23 -04:00
Goober5000 a20f3ca030 Merge pull request #5561 from Goober5000/special_warp_physics
add "special warp physics" to custom warpin parameters
2023-10-15 12:42:22 -04:00
Goober5000 e132fcf74c Merge pull request #5585 from Goober5000/beam_api_stuff
additional beam-related features for Lua
2023-10-15 12:41:05 -04:00
Goober5000 7a4df25fc5 Merge pull request #5566 from Goober5000/allowed_in_campaign
add AllowedInCampaign virtvar
2023-10-14 17:01:50 -04:00
Mike Nelson 08dfcc8b74 lua access to Support Ship availability (#5570) 2023-10-15 05:40:12 +09:00
Mike Nelson 0def45b381 add death message as a dialog hv (#5542) 2023-10-15 05:40:05 +09:00
Goober5000 be6a2b9196 add gr.killAllParticles (#5481)
Some scripts under development will benefit from access to the `particle::kill_all()` function.
2023-10-15 05:39:53 +09:00
Mike Nelson f7413be1d8 preload subspace tunnel flag (#5532) 2023-10-15 05:39:42 +09:00
Mike Nelson 02233fc439 Set timer padding sexp (#5535)
* set timer padding sexp

* allow negative padding
2023-10-15 05:39:33 +09:00
BMagnu febd8cd2ad Allow an asymmetric FOV / Frustrum (#5657)
* Enable Asymmetric Projection FOV

* Properly apply asymmetric FOV to g3_rotate_ functions

* De-CPP14-ify

* cleanup

* Make GCC happy

* Fix Vertical Asymmetric FOV

* Fix printf
2023-10-15 05:39:24 +09:00
Goober5000 bf7125724d small updates 2023-10-12 18:07:11 -04:00
Goober5000 efce98db47 rewrite primary weapon cycling
Remove a whole bunch of ad-hoc code piled on top of the retail weapon cycling behavior, then replace it with a robust algorithm that takes all relevant attributes into account.

Fixes #5529.
2023-10-12 17:50:04 -04:00
Goober5000 45abdefc07 add "Calculate subsystem hitpoints after parsing" option
Subsystem hitpoints are calculated as a percentage of the ship's hull strength as they are parsed.  If a modular table modifies the ship's hull strength, the subsystems are not modified at the same time.  This can lead to unreasonably large or small subsystem hitpoint values, as recently seen in the 1.3.0 version of Cardinal Spear.

This PR adds an option to defer hitpoint calculation until after parsing so that the subsystem is a percentage of the correct hull strength.  Since this may affect the balance of existing mods, it is off by default unless the build targets 24.0 or later.

This also removes the long-unused `hull_percentage_of_hits` check and adds several log statements for significant options in game_settings.tbl.
2023-10-06 20:52:22 -04:00
Goober5000 73c612034b log the filename 2023-10-06 13:07:46 -04:00
Goober5000 f98806b034 refactor ai_mission_goal_achievable
Change `ai_mission_goal_achievable` to use the ship registry instead of looking up the target repeatedly.
2023-10-05 23:15:28 -04:00
Goober5000 dcfea6f141 modify and address feedback 2023-10-05 17:32:53 -04:00
Goober5000 5688ba8637 fix incorrect setting of disabled/disarmed flags when cheating
Normally the disabled/disarmed flags are only set if the destroyed subsystem is actually an engine or turret.  This should be done for cheats as well, otherwise, for example, destroying any subsystem on a non-engined ship will cause it to become disabled.
2023-10-04 22:18:44 -04:00
mjn-mixael 1ce9d46822 Intro about to play hook 2023-10-04 11:16:48 -05:00
Goober5000 f7b9896294 allow modification of warp audible range via objecttypes.tbl
The retail warp range multiplier can yield ridiculous results, like an Elysium warp being audible over 9000 meters away.  So allow the multiplier to be set via ship type.
2023-10-03 12:50:05 -04:00
mjn-mixael 14a98e1470 add built-in xstrs for these controls 2023-09-26 10:07:29 -05:00
mjn-mixael cb5bf05424 update comments here 2023-09-25 15:58:11 -05:00
Naomi Coffman 15db3b17bc Respond to PR feedback 2023-09-24 11:09:31 -04:00
mjn-mixael ba2c37a1fe xstr the boolean default on/off as well 2023-09-22 23:24:52 -05:00
mjn-mixael aa997dd9ed Fix some overlapping xstrs 2023-09-22 23:11:08 -05:00
mjn-mixael a0a523223c clean up options builders and use delayed xstr for localization 2023-09-22 19:15:42 -05:00
mjn-mixael 8fbf22a055 allow lua sexps to not specify a subcategory 2023-09-21 10:12:17 -05:00
Naomi Coffman 0811c7037b Allow (easily) requiring an exact mood match 2023-09-16 13:47:00 -04:00
Naomi Coffman f13a5b3736 Allow specifying tiebreakers between builtin messages 2023-09-16 13:47:00 -04:00
Naomi Coffman 04a5778235 Allow filtering builtin messages by nearby ships 2023-09-16 13:47:00 -04:00
Goober5000 5a941c1d59 small speedup to drawString
Since `gr_string` can accept a length parameter, there's no need to copy each line before it is drawn.

Related to #5495 but not one of the two options mentioned.
2023-09-16 13:43:11 -04:00
Naomi Coffman 9586f21780 Add team filters to messages 2023-09-16 10:24:40 -04:00
Goober5000 ed5a5861f7 aspect-ratio-specific splash screens
Adds the ability to specify an aspect ratio, or a range of aspect ratios, to be used by splash screens.
2023-09-16 02:18:10 -04:00
Goober5000 89b33ebbc5 add a maneuver-despite-engines flag
This flag ignores engine status when determining whether and how a ship can maneuver.  Implements #5503.
2023-09-13 22:36:58 -04:00
Goober5000 53e6edf8bb documentation 2023-09-10 17:40:02 -04:00
Goober5000 8b8daece46 additional beam-related features for Lua
1. Add a `vanish()` function for beam objects, to be consistent with other object types
2. Add a `BeamFree` virtvar, analogous to `TurretLocked`
3. Add timestamp variants of TurretLocked and BeamFree to be consistent with sexps
4. Add `BeamLife`, `BeamWarmup`, and `BeamWarmdown` virtvars so scripts can access table properties
5. Add "On Beam Death", "On Beam Warmup", and "On Beam Warmdown" hooks
6. Clarify some existing documentation and patch a couple things
2023-09-06 00:28:07 -04:00
Goober5000 344e231058 add mn.generateWeaponGroupId()
The `mn.createWeapon()` function takes an optional group ID for weapons that share lighting, but the internal `weapon_create_group_id()` function was never exposed to the API.  This adds that.
2023-08-25 02:39:40 -04:00
Goober5000 210a9a8309 add AllowedInCampaign virtvar
These can be used to set whether a ship or weapon is allowed in loadouts, without necessarily waiting for the mission to be accepted.
2023-08-21 16:32:56 -04:00
Goober5000 6b3e50dade add "special warp physics" to custom warpin parameters
Supercaps use a physics flag on warpin that makes them slow down faster than they would under the normal physics code.  This adds that flag as an option for custom warp parameters.

Since flags are not yet parsed when warp parameters are parsed, the code needs to skip ahead and peek at the flags in the same way as the weapon override.
2023-08-16 19:07:30 -04:00
mjn-mixael 9a7500de36 lua method to launch arbitrary url 2023-08-13 09:27:30 -05:00
Goober5000 e71dba4207 enhance set-armor-type to work with generic subsystems 2023-08-09 18:21:19 -04:00
Goober5000 3b42ee42ca put the hull and shields at the top to be consistent with other sexps 2023-08-09 17:23:13 -04:00
mjn-mixael bb6b010487 lua get mod root 2023-08-02 12:04:33 -05:00
Goober5000 c80f787d34 POF version fix
Modelread.cpp supports POF versions as low as 1900, but early model versions were not loaded properly because they relied on preprocessor directives rather than version checks.  This changes the POF loading to correctly check version numbers and chunk IDs.

Fixes crashes when attempting to load a FS1 model.
2023-07-26 00:00:17 -04:00
Goober5000 15daaf2633 additional cleanup from PR 5462
Some streamlining and constifying of the readyroom popup list and associated functions.

Followup to #5462.
2023-07-13 01:54:25 -04:00
Goober5000 17d36156cb add const where appropriate to the modelrender API
See title.  There were some cascade effects, but the changes are mostly contained to the model code.  The calls to `gr_render_model` and `g3_render_primitives` use `const_cast` because constifying that API would require a whole other PR.
2023-06-18 14:05:46 -04:00
Goober5000 99450fa643 remove hud_squadmsg_save_keys()
Remove this function and associated items, since they are commented out and unused.
2023-05-27 13:39:47 -04:00
258 changed files with 5441 additions and 2436 deletions

No files matched your search

+5 -1
View File
@@ -92,6 +92,7 @@ jobs:
name: Checkout
with:
submodules: true # `true` to checkout submodules, `recursive` to recursively checkout submodules
ref: '${{ github.ref }}'
- name: Prepare Vulkan SDK
uses: humbletim/[email protected]
with:
@@ -145,6 +146,7 @@ jobs:
with:
submodules: true
fetch-depth: '0' # value 0 to fetch all history and tags for all branches
ref: '${{ github.ref }}'
- name: Download Release builds
# Grab the release builds
uses: actions/download-artifact@v2
@@ -191,6 +193,7 @@ jobs:
name: Checkout
with:
submodules: true
ref: '${{ github.ref }}'
# - name: Cache Qt
# # Cache Qt
# id: cache-qt-win
@@ -285,7 +288,7 @@ jobs:
with:
submodules: true
fetch-depth: '0'
ref: '${{ github.ref }}'
- name: Download Release builds
uses: actions/download-artifact@v2
with:
@@ -357,6 +360,7 @@ jobs:
with:
submodules: true
fetch-depth: 0
ref: '${{ github.ref }}'
- name: Prepare Vulkan SDK
uses: humbletim/[email protected]
with:
+81
View File
@@ -0,0 +1,81 @@
name: Weekly Coverity Scan for Freespace Open
# adapted from code by "albert-github" on the doxygen repository
# many thanks for figuring this out first so it could be less painful for us!
# This mode is for testing
on: [push, pull_request]
# In production, push on a specific schedule
#on:
# schedule:
# - cron: "1 0 * * 5" # Run once per a week on Friday morning (midnight UTC, 4 AM EST), to avoid Coverity's submission limits
env:
QT_VERSION: 5.12.9
coverity_email: [email protected]
coverity_token: ${{ secrets.COVERITY_TOKEN }}
jobs:
build:
name: Build FSO With Coverity Wrapper
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- { name: "GCC on ubuntu", os: ubuntu-latest, build_type: "Release" }
steps:
- name: Install Dependencies
run: |
sudo apt-get install ninja-build
sudo apt install cmake libsdl2-dev g++
sudo apt-get install libopenal-dev
sudo apt-get install -y libudev-dev
sudo apt-get install doxygen
sudo apt install graphviz
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive # `true` to checkout submodules, `recursive` to recursively checkout submodules
# pipe results of wget to a log file because it's very verbose and rarely fails
- name: Install Coverity
run: |
wget -o wgetlog.txt -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=${{ env.coverity_token }}&project=scp-fs2open/fs2open.github.com" -O coverity_tool.tgz
mkdir cov-scan
tar ax -f coverity_tool.tgz --strip-components=1 -C cov-scan
- name: Setup Environment
run: |
echo "$(pwd)/cov-scan/bin" >> $GITHUB_PATH
echo "NPROC=$(getconf _NPROCESSORS_ONLN)" >> $GITHUB_ENV
- name: Prepare Vulkan SDK
uses: humbletim/[email protected]
with:
vulkan-query-version: 1.3.204.0
vulkan-components: Vulkan-Headers, Vulkan-Loader
vulkan-use-cache: true
- name: Configure CMake for FSO
env:
CONFIGURATION: Release
compiler: gcc-5
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
- name: Build FSO with Coverity Wrapper
working-directory: ./build
env:
CONFIGURATION: Release
run: cov-build --dir cov-int ninja -k 20 all
- name: Compress Results
working-directory: ./build
run: tar zcf cov-scan.tgz cov-int
#Comment this out when testing to keep from running into upload limits
- name: Upload to Coverity
run: |
cd build
curl --form token=${{ env.coverity_token }} --form email=${{ env.coverity_email }} --form [email protected] --form version="$(git rev-parse HEAD)" --form description="Automatic GHA scan" 'https://scan.coverity.com/builds?project=scp-fs2open/fs2open.github.com'
+1
View File
@@ -250,6 +250,7 @@ documentation/doxygen/*
projects/*
/.vs
CMakeSettings.json
CMakePresets.json
# Weird files created by Linux
.fuse_hidden*
+1 -1
View File
@@ -19,7 +19,7 @@
# HLP_API:
# HLP_KEY:
# LINUX_RESULT: bool, if the linux builds were sucessfully uploaded
# LINUX_RESULT: bool, if the linux builds were successfully uploaded
# WINDOWS_RESULT: bool, if the windows builds were successfully uploaded
import sys
+1 -1
View File
@@ -7,7 +7,7 @@ if (EXISTS "${PROJECT_SOURCE_DIR}/version_override.cmake")
endif()
set_if_not_defined(FSO_VERSION_MAJOR 23)
set_if_not_defined(FSO_VERSION_MINOR 1)
set_if_not_defined(FSO_VERSION_MINOR 3)
set_if_not_defined(FSO_VERSION_BUILD 0)
# A numeric revision e.g. the CI build number
+9 -18
View File
@@ -56,12 +56,6 @@ extern const int Num_ai_flag_names;
#define AIDO_DOCK_NOW 2 // Immediately move into dock position. For ships that start mission docked.
#define AIDO_UNDOCK 3 // Set goal of undocking with object.
// Submodes for seeking safety.
#define AISS_1 41 // Pick a spot to fly to.
#define AISS_2 42 // Flying to spot.
#define AISS_3 43 // Gotten near spot, fly about there.
#define AISS_1a 44 // Pick a new nearby spot because we are endangered, then go to AISS_2
// Flags to ai_turn_towards_vector().
#define AITTV_FAST (1<<0) // Turn fast, not slowed down based on skill level.
#define AITTV_VIA_SEXP (1<<1) // Goober5000 - via sexp
@@ -93,7 +87,7 @@ extern const int Num_ai_flag_names;
#define AIM_BAY_DEPART 18 // Departing to a fighter bay, following path to do so
#define AIM_SENTRYGUN 19 // AI mode for sentry guns only (floating turrets)
#define AIM_WARP_OUT 20 // Commence warp out sequence. Point in legal direction. Then call John's code.
#define AIM_FLY_TO_SHIP 21 // [Kazan] Fly to a ship, doesn't matter if it's hostile or friendly -- for Autopilot usage
#define AIM_FLY_TO_SHIP 21 // Rendezvous with a ship. Like stay-near, but non-perpetual.
#define AIM_LUA 22 // Generic Lua-based AI mode
#define MAX_AI_BEHAVIORS 23 // Number of AIM_xxxx types
@@ -186,6 +180,12 @@ typedef struct ai_class {
#define AIS_UNDOCK_3 33
#define AIS_UNDOCK_4 34
// Submodes for seeking safety.
#define AISS_1 41 // Pick a spot to fly to.
#define AISS_2 42 // Flying to spot.
#define AISS_3 43 // Gotten near spot, fly about there.
#define AISS_1a 44 // Pick a new nearby spot because we are endangered, then go to AISS_2. (Not implemented.)
// Submodes for Guard behavior
#define AIS_GUARD_PATROL 101
#define AIS_GUARD_ATTACK 102
@@ -240,7 +240,6 @@ typedef struct ai_info {
flagset<AI::AI_Flags> ai_flags; // Special flags for AI behavior.
int shipnum; // Ship using this slot, -1 means none.
int behavior; // AI behavior; vestigial field from early development of FS1
int mode;
int previous_mode;
int mode_time; // timestamp at which current mode elapses.
@@ -308,6 +307,7 @@ typedef struct ai_info {
fix submode_start_time; // time at which we entered the current submode
int submode_parm0; // parameter specific to current submode
int submode_parm1; // SUSHI: Another optional parameter
float submode_float0; // Goober5000: a float parameter
fix next_predict_pos_time; // Next time to predict position.
int next_dynamic_path_check_time; //ETP: Time of next path check in ai_new_maybe_reposition_attack_subsys
@@ -373,11 +373,6 @@ typedef struct ai_info {
float ai_turret_max_aim_update_delay;
flagset<AI::Profile_Flags> ai_profile_flags; //Holds AI_Profiles flags (possibly overriden by AI class) that actually apply to AI
union {
float stay_near_distance; // Distance to stay within for AIM_STAY_NEAR mode.
};
ship_subsys* targeted_subsys; // Targeted subobject on current target. NULL if none;
ship_subsys* last_subsys_target; // last known subsystem target
int targeted_subsys_parent; // Parent objnum of subobject, not necessarily targeted
@@ -565,7 +560,7 @@ 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 void ai_form_on_wing(object *objp, object *goal_objp);
extern void ai_do_stay_near(object *objp, object *other_obj, float dist);
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);
extern void ai_rearm_repair( object *objp, int docker_index, object *goal_objp, int dockee_index );
extern void ai_add_rearm_goal( object *requester_objp, object *support_objp );
@@ -625,10 +620,6 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra
// moved to header file by Goober5000
void ai_announce_ship_dying(object *dying_objp);
// added by kazan
void ai_start_fly_to_ship(object *objp, int shipnum);
void ai_fly_to_ship();
//Moved declaration here for player ship -WMC
void ai_process_subobjects(int objnum);
+1
View File
@@ -156,6 +156,7 @@ namespace AI {
Whackable_debris,
Whackable_asteroids,
Dynamic_goals_afterburn_hard,
Hudsquadmsg_tactical_disarm_disable,
NUM_VALUES
};
+8 -1
View File
@@ -552,7 +552,9 @@ void parse_ai_profiles_tbl(const char *filename)
profile->flags.set(AI::Profile_Flags::All_nonshielded_ships_can_manage_ets, temp);
}
set_flag(profile, "$no directional bias for missile and ship turning:", AI::Profile_Flags::No_turning_directional_bias);
// ----------
set_flag(profile, "$no directional bias for missile and ship turning:", AI::Profile_Flags::No_turning_directional_bias);
set_flag(profile, "$respect ship axial turnrate differences:", AI::Profile_Flags::Use_axial_turnrate_differences);
@@ -623,6 +625,8 @@ void parse_ai_profiles_tbl(const char *filename)
set_flag(profile, "$dynamic goals afterburn hard:", AI::Profile_Flags::Dynamic_goals_afterburn_hard);
set_flag(profile, "$hud squad messages use tactical disarm/disable:", AI::Profile_Flags::Hudsquadmsg_tactical_disarm_disable);
// if we've been through once already and are at the same place, force a move
if (saved_Mp && (saved_Mp == Mp))
{
@@ -812,4 +816,7 @@ void ai_profile_t::reset()
if (mod_supports_version(23, 2, 0)) {
flags.set(AI::Profile_Flags::Ships_playing_dead_dont_manage_ets);
}
if (mod_supports_version(23, 4, 0)) {
flags.set(AI::Profile_Flags::Hudsquadmsg_tactical_disarm_disable);
}
}
+2 -2
View File
@@ -914,13 +914,13 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
}
if (tswp->num_secondary_banks > 0) {
if (!(En_objp->flags[Object::Object_Flags::Protected]) || (aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP))) {
if (!(En_objp->flags[Object::Object_Flags::Protected]) || (aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL | AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL))) {
bool valid_secondary = ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
int current_bank = tswp->current_secondary_bank;
if (current_bank > -1 && valid_secondary) {
weapon_info *swip = &Weapon_info[tswp->secondary_bank_weapons[current_bank]];
if(!(En_objp->flags[Object::Object_Flags::Protected]) || ((aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP)) && swip->wi_flags[Weapon::Info_Flags::Puncture] )) { //override lockdown on protected ships when using anti subsystem weapons - Valathil
if(!(En_objp->flags[Object::Object_Flags::Protected]) || ((aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL | AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL)) && swip->wi_flags[Weapon::Info_Flags::Puncture] )) { //override lockdown on protected ships when using anti subsystem weapons - Valathil
// If ship is protected and very low on hits, don't fire missiles.
if (!(En_objp->flags[Object::Object_Flags::Protected]) || (En_objp->hull_strength > 10*swip->damage)) {
if (aip->ai_flags[AI::AI_Flags::Unload_secondaries]) {
+94 -153
View File
@@ -1316,7 +1316,7 @@ void ai_turn_towards_vector(vec3d* dest, object* objp, vec3d* slide_vec, vec3d*
// Don't allow a ship to turn if it has no engine strength.
// AL 3-12-98: objp may not always be a ship!
if ( (objp->type == OBJ_SHIP) && !(flags & AITTV_VIA_SEXP) ) {
if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
if (!Ships[objp->instance].flags[Ship::Ship_Flags::Maneuver_despite_engines] && ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
return;
}
@@ -3462,36 +3462,6 @@ void ai_dock_with_object(object *docker, int docker_index, object *dockee, int d
}
/*
* Cause a ship to fly toward a ship.
*/
void ai_start_fly_to_ship(object *objp, int shipnum)
{
ai_info *aip;
aip = &Ai_info[Ships[objp->instance].ai_index];
if (The_mission.flags[Mission::Mission_Flags::Use_ap_cinematics] && AutoPilotEngaged)
{
aip->ai_flags.remove(AI::AI_Flags::Formation_wing);
}
else
{
aip->ai_flags.set(AI::AI_Flags::Formation_wing);
}
aip->ai_flags.remove(AI::AI_Flags::Formation_object);
aip->mode = AIM_FLY_TO_SHIP;
aip->submode_start_time = Missiontime;
aip->target_objnum = Ships[shipnum].objnum;
// clear targeted subsystem
set_targeted_subsys(aip, nullptr, -1);
Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
}
// Cause a ship to fly its waypoints.
// flags tells:
// WPF_REPEAT Set -> repeat waypoints.
@@ -3533,7 +3503,7 @@ void ai_start_waypoints(object *objp, waypoint_list *wp_list, int wp_flags)
/**
* Make *objp stay within dist units of *other_objp
*/
void ai_do_stay_near(object *objp, object *other_objp, float dist)
void ai_do_stay_near(object *objp, object *other_objp, float dist, int additional_data)
{
ai_info *aip;
@@ -3546,7 +3516,8 @@ void ai_do_stay_near(object *objp, object *other_objp, float dist)
aip->mode = AIM_STAY_NEAR;
aip->submode = -1;
aip->submode_start_time = Missiontime;
aip->stay_near_distance = dist;
aip->submode_float0 = dist;
aip->submode_parm0 = additional_data;
aip->goal_objnum = OBJ_INDEX(other_objp);
aip->goal_signature = other_objp->signature;
}
@@ -4922,84 +4893,6 @@ void ai_waypoints()
}
}
// --------------------------------------------------------------------------
// make Pl_objp fly toward a ship
void ai_fly_to_ship()
{
ai_info *aip;
object* target_p = NULL;
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
if ( aip->mode != AIM_FLY_TO_SHIP ) {
Warning(LOCATION,
"ai_fly_to_ship called for '%s' when ai_info.mode not equal to AIM_FLY_TO_SHIP. Is actually '%d'",
Ships[Pl_objp->instance].ship_name,
aip->mode);
aip->mode = AIM_NONE;
return;
}
if ( aip->active_goal < 0 || aip->active_goal >= MAX_AI_GOALS ) {
Warning(LOCATION,
"'%s' is trying to fly-to a ship without an active AI_GOAL\n\n"
"Active ai mode is '%d'",
Ships[Pl_objp->instance].ship_name,
aip->active_goal);
aip->mode = AIM_NONE;
return;
}
Assert( aip->goals[aip->active_goal].target_name != NULL );
if ( aip->goals[aip->active_goal].target_name[0] == '\0' ) {
Warning(LOCATION, "'%s' is trying to fly-to-ship without a name for the ship", Ships[Pl_objp->instance].ship_name);
aip->mode = AIM_NONE;
ai_remove_ship_goal( aip, aip->active_goal ); // function sets aip->active_goal to NONE for me
return;
}
for (int j = 0; j < MAX_SHIPS; j++)
{
if (Ships[j].objnum != -1 && !stricmp(aip->goals[aip->active_goal].target_name, Ships[j].ship_name))
{
target_p = &Objects[Ships[j].objnum];
break;
}
}
if ( target_p == NULL ) {
#ifndef NDEBUG
for (int i = 0; i < MAX_AI_GOALS; i++)
{
if (aip->mode == AIM_FLY_TO_SHIP || aip->goals[i].ai_mode == AI_GOAL_FLY_TO_SHIP)
{
mprintf(("Ship '%s' told to fly to target ship '%s'\n",
Ships[Pl_objp->instance].ship_name,
aip->goals[i].target_name));
}
}
#endif
Warning(LOCATION, "Ship '%s' told to fly to a ship but none of the ships it was told to fly to exist.\n"
"See log before this message for list of ships set as fly-to tagets",
Ships[Pl_objp->instance].ship_name);
aip->mode = AIM_NONE;
ai_remove_ship_goal( aip, aip->active_goal ); // function sets aip->active_goal to NONE for me
return;
} else {
bool done, treat_as_ship;
ai_fly_to_target_position(&(target_p->pos), &done, &treat_as_ship);
if ( done ) {
// remove the goal from the AI goals of the ship pr wing, respectively.
// Wether or not we should treat this as a ship or a wing is determined by
// ai_fly_to_target when it marks the AI directive as complete
if ( treat_as_ship ) {
ai_mission_goal_complete( aip ); // this call should reset the AI mode
} else {
ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
}
}
}
}
// Make Pl_objp avoid En_objp
// Not like evading. This is for avoiding a collision!
// Note, use sliding if available.
@@ -5671,7 +5564,7 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag
Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < ship_info_size());
//made it so it only selects puncture weapons if the active goal is to disable something -Bobboau
if ((flags == Weapon::Info_Flags::Puncture) && (Ai_info[shipp->ai_index].goals[0].ai_mode & (AI_GOAL_DISARM_SHIP | AI_GOAL_DISABLE_SHIP)))
if ((flags == Weapon::Info_Flags::Puncture) && (Ai_info[shipp->ai_index].goals[0].ai_mode & (AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL)))
{
if (swp->current_primary_bank >= 0)
{
@@ -5896,7 +5789,7 @@ void set_primary_weapon_linkage(object *objp)
// puncture weapons
if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) )
{
if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) )
if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISABLE_SHIP_TACTICAL|AI_GOAL_DISARM_SHIP|AI_GOAL_DISARM_SHIP_TACTICAL) )
{
// only continue if both primaries are puncture weapons
if ( swp->num_primary_banks == 2 ) {
@@ -9486,9 +9379,15 @@ void ai_chase()
firing_range = MIN((swip->max_speed * swip->lifetime * 0.75f), swip->weapon_range);
else
{
float secondary_range_mult = (aip->ai_secondary_range_mult == FLT_MIN)
? (float)(Game_skill_level + 1 + (3 * ai_maybe_autoscale(aip->ai_class)/(ai_maybe_autoscale(Num_ai_classes) - 1)))/NUM_SKILL_LEVELS
: aip->ai_secondary_range_mult;
float secondary_range_mult;
// Cyborg: need to check for division by zero, coverity 1523419
if (aip->ai_secondary_range_mult == FLT_MIN && (ai_maybe_autoscale(Num_ai_classes) > 1))
secondary_range_mult = (float)(Game_skill_level + 1 + (3 * ai_maybe_autoscale(aip->ai_class)/(ai_maybe_autoscale(Num_ai_classes) - 1)))/NUM_SKILL_LEVELS;
else if (aip->ai_secondary_range_mult == FLT_MIN)
secondary_range_mult = (float)(Game_skill_level + 2)/NUM_SKILL_LEVELS; // default to using the default retail class Captain out of the seven there are total when we don't have enough autoscale classes or just one regular class. (This simplifies to 2 + Game_skill_level / Num of skill levels)
else
secondary_range_mult = aip->ai_secondary_range_mult;
firing_range = MIN((swip->max_speed * swip->lifetime * secondary_range_mult), swip->weapon_range);
}
@@ -9791,7 +9690,7 @@ float dock_orient_and_approach(object *docker_objp, int docker_index, object *do
// Goober5000 - moved out here to save calculations
if (dock_mode != DOA_DOCK_STAY)
if (ship_get_subsystem_strength(&Ships[docker_objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
if (!Ships[docker_objp->instance].flags[Ship::Ship_Flags::Maneuver_despite_engines] && ship_get_subsystem_strength(&Ships[docker_objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
return 9999.9f;
// If dockee has moved much, then path will be recreated.
@@ -11357,24 +11256,52 @@ void ai_still()
// Make *Pl_objp stay near another ship.
void ai_stay_near()
{
ai_info *aip;
int goal_objnum;
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
goal_objnum = aip->goal_objnum;
auto aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
int goal_objnum = aip->goal_objnum;
if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
aip->mode = AIM_NONE;
} else {
float dist, max_dist, scale;
vec3d rand_vec, goal_pos, vec_to_goal;
object *goal_objp;
}
// "escort" behavior
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;
goal_objp = &Objects[goal_objnum];
// Figure out a goal point to fly to
// 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
static_randvec((goal_objp->instance * MAX_SHIPS) + (Pl_objp->instance), &rand_vec);
aip->goal_point = goal_objp->pos + max_dist * rand_vec;
// Make sure point is on the near side of goal_objp (relative to Pl_objp's position).
center_to_goal = aip->goal_point - goal_objp->pos;
center_to_objp = Pl_objp->pos - goal_objp->pos;
if (vm_vec_dot(&center_to_goal, &center_to_objp) < 0.0f) {
vm_vec_negate(&rand_vec);
aip->goal_point = goal_objp->pos + max_dist * rand_vec;
}
// Fly to the point
auto dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
if (dist > max_dist) {
turn_towards_point(Pl_objp, &aip->goal_point, nullptr, 0.0f);
accelerate_ship(aip, dist / max_dist - 0.8f);
float dot = vm_vec_dot_to_point(&Pl_objp->orient.vec.fvec, &Pl_objp->pos, &aip->goal_point);
if (ai_willing_to_afterburn_hard(aip) && dot > 0.99f && dist - max_dist > 700.0f)
ai_afterburn_hard(Pl_objp, aip);
}
}
// standard behavior
else {
vec3d rand_vec, goal_pos, vec_to_goal;
auto goal_objp = &Objects[goal_objnum];
float max_dist = aip->submode_float0;
// Make not all ships pursue same point.
static_randvec(OBJ_INDEX(Pl_objp), &rand_vec);
// Calculate the seed from both shipnums so the same Pl_objp doesn't always choose the same spot
static_randvec((goal_objp->instance * MAX_SHIPS) + (Pl_objp->instance), &rand_vec);
// Make sure point is in front hemisphere (relative to Pl_objp's position.
vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
@@ -11383,28 +11310,22 @@ void ai_stay_near()
}
// Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
max_dist = aip->stay_near_distance;
scale = dist - max_dist/2;
float dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
float scale = dist - max_dist/2;
if (scale < 0.0f)
scale = 0.0f;
vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
// Fly to the point
if (dist > max_dist) {
turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
turn_towards_point(Pl_objp, &goal_pos, nullptr, 0.0f);
accelerate_ship(aip, dist / max_dist - 0.8f);
float dot = vm_vec_dot_to_point(&Pl_objp->orient.vec.fvec, &Pl_objp->pos, &goal_pos);
if (ai_willing_to_afterburn_hard(aip) && dot > 0.99f && dist - max_dist > 700.0f)
ai_afterburn_hard(Pl_objp, aip);
}
}
}
// Warn player if dock path is obstructed.
@@ -12156,7 +12077,7 @@ object *get_wing_leader(int wingnum)
// If this ship is disabled, try another ship in the wing.
int n = 0;
while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
while (!Ships[ship_num].flags[Ship::Ship_Flags::Maneuver_despite_engines] && ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
n++;
if (n >= wingp->current_count) {
@@ -12499,7 +12420,7 @@ int ai_formation()
} else { // Formation flying in waypoint mode.
Assert(aip->ai_flags[AI::AI_Flags::Formation_wing]);
if ( (aip->mode != AIM_WAYPOINTS) && (aip->mode != AIM_FLY_TO_SHIP) ) {
if ( aip->mode != AIM_WAYPOINTS ) {
aip->ai_flags.remove(AI::AI_Flags::Formation_wing);
return 1;
}
@@ -13129,10 +13050,25 @@ void ai_manage_shield(object *objp, ai_info *aip)
// Scale between 1x and 3x based on ai_class (SUSHI: only if autoscale is on)
if (aip->ai_class_autoscale)
{
if (The_mission.ai_profile->flags[AI::Profile_Flags::Adjusted_AI_class_autoscale])
delay = delay + delay * (float)(3 * (ai_get_autoscale_index(Num_ai_classes) - ai_get_autoscale_index(aip->ai_class) - 1) / (ai_get_autoscale_index(Num_ai_classes) - 1));
else
delay = delay + delay * (float)(3 * (Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
int number_of_as_classes = ai_get_autoscale_index(Num_ai_classes);
// Cyborg: Make sure that autoscale index is greater than one before dividing, coverity 1523548
if (The_mission.ai_profile->flags[AI::Profile_Flags::Adjusted_AI_class_autoscale] && number_of_as_classes > 1)
delay = delay + delay * (float)(3 * (number_of_as_classes - ai_get_autoscale_index(aip->ai_class) - 1) / (number_of_as_classes - 1));
else {
static bool autoscale_checked = false;
// warn if there's only one autoscale ai class
if (number_of_as_classes == 1 && !autoscale_checked) {
mprintf(("Warning! Having only 1 autoscale ai class will disable autoscale features that rely on division."));
autoscale_checked = true;
}
// Only adjust if we have more than one ai class to avoid div by 0
if (Num_ai_classes > 1){
delay = delay + delay * (float)(3 * (Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
}
}
}
// set timestamp
@@ -13502,8 +13438,14 @@ int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int allowed_path_
}
}
// cycle through the allowed paths
bay_path = allowed_bay_paths[Ai_last_arrive_path % num_allowed_paths];
// Cyborg: to avoid divide by zero and avoid a logic path that should not exist check for 0 allowed paths -- Coverity 1523287
if (num_allowed_paths == 0) {
bay_path = Ai_last_arrive_path % bay->num_paths;
UNREACHABLE("Parent_shipp in ai_acquire_emerge_path somehow does not have any allowed bay paths!");
} else {
// cycle through the allowed paths
bay_path = allowed_bay_paths[Ai_last_arrive_path % num_allowed_paths];
}
}
// choose from among all paths
else
@@ -13921,6 +13863,7 @@ void ai_execute_behavior(ai_info *aip)
case AIM_STILL:
ai_still();
break;
case AIM_FLY_TO_SHIP: // same code as stay-near
case AIM_STAY_NEAR:
ai_stay_near();
break;
@@ -13930,9 +13873,6 @@ void ai_execute_behavior(ai_info *aip)
case AIM_WAYPOINTS:
ai_waypoints();
break;
case AIM_FLY_TO_SHIP:
ai_fly_to_ship();
break;
case AIM_DOCK:
ai_dock();
break;
@@ -14899,7 +14839,7 @@ void ai_frame(int objnum)
aip->ai_flags.remove(AI::AI_Flags::Avoid_shockwave_started);
}
// moved call to ai_do_repair frame here from below because of the subsequent if statment returning
// moved call to ai_do_repair frame here from below because of the subsequent if statement returning
// if the ship is getting repaired
// If waiting to be repaired, just stop and sit.
ai_do_repair_frame(Pl_objp, aip, flFrametime);
@@ -15021,9 +14961,9 @@ void ai_frame(int objnum)
if ( aip->targeted_subsys->current_hits <= 0.0f ) {
int subsys_type;
if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP || aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP_TACTICAL ) {
subsys_type = SUBSYSTEM_ENGINE;
} else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
} else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP || aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP_TACTICAL ) {
subsys_type = SUBSYSTEM_TURRET;
} else {
subsys_type = -1;
@@ -15279,6 +15219,7 @@ void init_ai_object(int objnum)
aip->submode_start_time = 0;
aip->submode_parm0 = 0;
aip->submode_parm1 = 0;
aip->submode_float0 = 0.0f;
aip->active_goal = -1;
aip->goal_check_time = timestamp(0);
aip->last_predicted_enemy_pos = near_vec;
+133 -124
View File
@@ -51,6 +51,8 @@ struct ship_registry_entry;
// 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
#define PURGE_GOALS_ALL_SHIPS (AI_GOAL_IGNORE | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP)
#define PURGE_GOALS_ONE_SHIP (AI_GOAL_IGNORE_NEW)
@@ -75,7 +77,9 @@ ai_goal_list Ai_goal_names[] =
{ "Attack wing", AI_GOAL_CHASE_WING, 0 },
{ "Guard ship", AI_GOAL_GUARD, 0 },
{ "Disable ship", AI_GOAL_DISABLE_SHIP, 0 },
{ "Disable ship (tactical)",AI_GOAL_DISABLE_SHIP_TACTICAL, 0 },
{ "Disarm ship", AI_GOAL_DISARM_SHIP, 0 },
{ "Disarm ship (tactical)", AI_GOAL_DISARM_SHIP_TACTICAL, 0 },
{ "Attack any", AI_GOAL_CHASE_ANY, 0 },
{ "Ignore ship", AI_GOAL_IGNORE, 0 },
{ "Ignore ship (new)", AI_GOAL_IGNORE_NEW, 0 },
@@ -120,8 +124,10 @@ const char *Ai_goal_text(int goal, int submode)
case AI_GOAL_GUARD_WING:
return XSTR( "guard ", 480);
case AI_GOAL_DISABLE_SHIP:
case AI_GOAL_DISABLE_SHIP_TACTICAL:
return XSTR( "disable ", 481);
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISARM_SHIP_TACTICAL:
return XSTR( "disarm ", 482);
case AI_GOAL_EVADE_SHIP:
return XSTR( "evade ", 483);
@@ -528,11 +534,12 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai
// ignore goals should get rid of any kind of attack goal
case AI_GOAL_IGNORE:
case AI_GOAL_IGNORE_NEW:
if ( purge_ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_CHASE_SHIP_CLASS | AI_GOAL_DESTROY_SUBSYSTEM) )
if ( purge_ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL | AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_CHASE_SHIP_CLASS | AI_GOAL_DESTROY_SUBSYSTEM) )
purge_goal->flags.set(AI::Goal_Flags::Purge);
break;
// disarm/disable goals should remove attacks from certain ships types
// (but not tactical disarm/disable)
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISABLE_SHIP:
if ( purge_ai_mode & (AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_CHASE_SHIP_CLASS) ) {
@@ -549,12 +556,17 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai
}
// grab the ship type of the ship that is being disarmed/disabled
ship_type_info *crippled_ships_type = &Ship_types[Ship_info[Ships[ship_index].ship_info_index].class_type];
int crippled_ship_type = Ship_info[Ships[ship_index].ship_info_index].class_type;
// work through all the ship types which to see if the class matching our ai ship must ignore the ship
if (ai_ship_type < 0 || crippled_ship_type < 0)
break;
ship_type_info *crippled_ship_type_info = &Ship_types[crippled_ship_type];
// work through all the ship types to see if the class matching our ai ship must ignore the ship
// being disarmed/disabled
for ( j=0 ; j < (int)crippled_ships_type->ai_cripple_ignores.size(); j++) {
if (crippled_ships_type->ai_cripple_ignores[j] == ai_ship_type) {
for ( j=0 ; j < (int)crippled_ship_type_info->ai_cripple_ignores.size(); j++) {
if (crippled_ship_type_info->ai_cripple_ignores[j] == ai_ship_type) {
purge_goal->flags.set(AI::Goal_Flags::Purge);
}
}
@@ -931,14 +943,16 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char
break;
case OP_AI_DISABLE_SHIP:
aigp->ai_mode = AI_GOAL_DISABLE_SHIP;
case OP_AI_DISABLE_SHIP_TACTICAL:
aigp->ai_mode = (op == OP_AI_DISABLE_SHIP) ? AI_GOAL_DISABLE_SHIP : AI_GOAL_DISABLE_SHIP_TACTICAL;
aigp->target_name = ai_get_goal_target_name( CTEXT(CDR(node)), &aigp->target_name_index );
aigp->ai_submode = -SUBSYSTEM_ENGINE;
aigp->priority = atoi( CTEXT(CDR(CDR(node))) );
break;
case OP_AI_DISARM_SHIP:
aigp->ai_mode = AI_GOAL_DISARM_SHIP;
case OP_AI_DISARM_SHIP_TACTICAL:
aigp->ai_mode = (op == OP_AI_DISARM_SHIP) ? AI_GOAL_DISARM_SHIP : AI_GOAL_DISARM_SHIP_TACTICAL;
aigp->target_name = ai_get_goal_target_name( CTEXT(CDR(node)), &aigp->target_name_index );
aigp->ai_submode = -SUBSYSTEM_TURRET;
aigp->priority = atoi( CTEXT(CDR(CDR(node))) );
@@ -1022,17 +1036,30 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char
aigp->ai_mode = AI_GOAL_KEEP_SAFE_DISTANCE;
break;
case OP_AI_FLY_TO_SHIP:
case OP_AI_STAY_NEAR_SHIP:
{
bool is_nan, is_nan_forever;
aigp->target_name = ai_get_goal_target_name( CTEXT(CDR(node)), &aigp->target_name_index );
aigp->priority = atoi( CTEXT(CDDR(node)) );
// distance from ship
if ( CDDDR(node) < 0 )
aigp->float_data = 300.0f;
else
aigp->float_data = i2fl(eval_num(CDDDR(node), is_nan, is_nan_forever));
aigp->ai_mode = AI_GOAL_STAY_NEAR_SHIP;
// (the CDDDDR argument is whether to afterburn, and is handled at the end of the function, so don't handle it here)
// whether to "escort" the ship
if ( CDDDDDR(node) >= 0 )
aigp->int_data = is_sexp_true(CDDDDDR(node)) ? 1 : 0;
if (op == OP_AI_FLY_TO_SHIP)
aigp->ai_mode = AI_GOAL_FLY_TO_SHIP;
else
aigp->ai_mode = AI_GOAL_STAY_NEAR_SHIP;
break;
}
@@ -1050,7 +1077,6 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char
case OP_AI_EVADE_SHIP:
case OP_AI_IGNORE:
case OP_AI_IGNORE_NEW:
case OP_AI_FLY_TO_SHIP:
aigp->target_name = ai_get_goal_target_name( CTEXT(CDR(node)), &aigp->target_name_index );
aigp->priority = atoi( CTEXT(CDR(CDR(node))) );
@@ -1077,14 +1103,10 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char
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_STAY_NEAR_SHIP ) {
aigp->ai_mode = AI_GOAL_STAY_NEAR_SHIP;
} else if ( op == OP_AI_IGNORE ) {
aigp->ai_mode = AI_GOAL_IGNORE;
} else if ( op == OP_AI_IGNORE_NEW ) {
aigp->ai_mode = AI_GOAL_IGNORE_NEW;
} else if ( op == OP_AI_FLY_TO_SHIP ) {
aigp->ai_mode = AI_GOAL_FLY_TO_SHIP;
} else
UNREACHABLE("Coding error: unhandled AI goal in ai_add_goal_sub_sexp!");
@@ -1121,7 +1143,8 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char
}
// 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 || op == OP_AI_DISABLE_SHIP || op == OP_AI_DISARM_SHIP ) {
if ( op == OP_AI_CHASE || op == OP_AI_CHASE_WING || op == OP_AI_CHASE_SHIP_CLASS
|| 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);
}
@@ -1138,8 +1161,7 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char
if (op == OP_AI_GUARD ||
op == OP_AI_GUARD_WING ||
op == OP_AI_WAYPOINTS ||
op == OP_AI_WAYPOINTS_ONCE ||
op == OP_AI_FLY_TO_SHIP) {
op == OP_AI_WAYPOINTS_ONCE) {
if (is_sexp_true(CDDDR(node)))
aigp->flags.set(AI::Goal_Flags::Afterburn_hard);
}
@@ -1147,8 +1169,11 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char
op == OP_AI_CHASE_WING ||
op == OP_AI_CHASE_SHIP_CLASS ||
op == OP_AI_DISABLE_SHIP ||
op == OP_AI_DISABLE_SHIP_TACTICAL ||
op == OP_AI_DISARM_SHIP ||
op == OP_AI_STAY_NEAR_SHIP) {
op == OP_AI_DISARM_SHIP_TACTICAL ||
op == OP_AI_STAY_NEAR_SHIP ||
op == OP_AI_FLY_TO_SHIP) {
if (is_sexp_true(CDDDDR(node)))
aigp->flags.set(AI::Goal_Flags::Afterburn_hard);
}
@@ -1223,11 +1248,13 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
priority = ( CDR( CDR( CDR(node) ) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( CDR( node ) ) ) ) ) : -1;
break;
case OP_AI_DISABLE_SHIP:
goalmode = AI_GOAL_DISABLE_SHIP;
case OP_AI_DISABLE_SHIP_TACTICAL:
goalmode = (op == OP_AI_DISABLE_SHIP) ? AI_GOAL_DISABLE_SHIP : AI_GOAL_DISABLE_SHIP_TACTICAL;
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
break;
case OP_AI_DISARM_SHIP:
goalmode = AI_GOAL_DISABLE_SHIP;
case OP_AI_DISARM_SHIP_TACTICAL:
goalmode = (op == OP_AI_DISARM_SHIP) ? AI_GOAL_DISARM_SHIP : AI_GOAL_DISARM_SHIP_TACTICAL;
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
break;
case OP_AI_WARP_OUT:
@@ -1257,11 +1284,8 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
priority = ( CDR(node) >= 0 ) ? atoi( CTEXT( CDR( node ) ) ) : -1;
break;
case OP_AI_PLAY_DEAD:
goalmode = AI_GOAL_PLAY_DEAD;
priority = ( CDR(node) >= 0 ) ? atoi( CTEXT( CDR( node ) ) ) : -1;
break;
case OP_AI_PLAY_DEAD_PERSISTENT:
goalmode = AI_GOAL_PLAY_DEAD_PERSISTENT;
goalmode = (op == OP_AI_PLAY_DEAD) ? AI_GOAL_PLAY_DEAD : AI_GOAL_PLAY_DEAD_PERSISTENT;
priority = ( CDR(node) >= 0 ) ? atoi( CTEXT( CDR( node ) ) ) : -1;
break;
case OP_AI_KEEP_SAFE_DISTANCE:
@@ -1303,12 +1327,9 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
goalmode = AI_GOAL_STAY_NEAR_SHIP;
break;
case OP_AI_IGNORE:
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
goalmode = AI_GOAL_IGNORE;
break;
case OP_AI_IGNORE_NEW:
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
goalmode = AI_GOAL_IGNORE_NEW;
goalmode = (op == OP_AI_IGNORE) ? AI_GOAL_IGNORE : AI_GOAL_IGNORE_NEW;
break;
case OP_AI_FORM_ON_WING:
priority = 99;
@@ -1530,13 +1551,11 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
{
int status;
ai_achievability return_val;
ai_info *aip;
int index = -1, sindex = -1;
int modelnum = -1;
auto objp = &Objects[objnum];
Assert( objp->instance != -1 );
aip = &Ai_info[Ships[objp->instance].ai_index];
auto shipp = &Ships[objp->instance];
auto aip = &Ai_info[shipp->ai_index];
// these orders are always achievable.
if ( (aigp->ai_mode == AI_GOAL_KEEP_SAFE_DISTANCE)
@@ -1547,11 +1566,11 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
if (aigp->ai_mode == AI_GOAL_LUA)
return ai_lua_is_achievable(aigp, objnum);
auto target_ship_entry = aigp->target_name == nullptr ? nullptr : ship_registry_get(aigp->target_name);
// warp (depart) only achievable if there's somewhere to depart to
if (aigp->ai_mode == AI_GOAL_WARP)
{
ship *shipp = &Ships[objp->instance];
// always valid if has working subspace drive, not disabled, and not limited by subsystem strength
if ( ship_can_warp_full_check(shipp) )
return ai_achievability::ACHIEVABLE;
@@ -1570,9 +1589,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// if the wing target is valid then be sure to set the override bit so that it always
// gets executed next
if ( aigp->ai_mode == AI_GOAL_FORM_ON_WING ) {
sindex = ship_name_lookup( aigp->target_name );
if (sindex < 0)
if (!target_ship_entry || !target_ship_entry->shipp)
return ai_achievability::NOT_ACHIEVABLE;
aigp->flags.set(AI::Goal_Flags::Goal_override);
@@ -1629,11 +1646,10 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// shipnum could be -1 depending on if the ship hasn't arrived or died. only look for subsystem
// destroyed when shipnum is valid
sindex = ship_name_lookup( aigp->target_name );
// can't determine the status of this goal if ship not valid
// or we haven't found a valid subsystem index yet
if ( (sindex == -1) || (aigp->flags[AI::Goal_Flags::Subsys_needs_fixup]) ) {
if ( !target_ship_entry || !target_ship_entry->shipp || (aigp->flags[AI::Goal_Flags::Subsys_needs_fixup]) ) {
status = 0;
break;
}
@@ -1642,46 +1658,46 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// as 0 so we can continue. (The subsystem name must be turned into an index into the ship's subsystems
// for this goal to be valid).
Assert ( aigp->ai_submode >= 0 );
ssp = ship_get_indexed_subsys( &Ships[sindex], aigp->ai_submode );
ssp = ship_get_indexed_subsys( target_ship_entry->shipp, aigp->ai_submode );
if (ssp != NULL) {
// see MWA 3/20/97 comment above - instead of checking the mission log, check the current hits
status = (ssp->current_hits <= 0.0f) ? 1 : 0;
} else {
// not supposed to ever happen, but could if there is a mismatch between the table and model subsystems
nprintf(("AI", "Couldn't find subsystem %d for ship %s\n", aigp->ai_submode, Ships[sindex].ship_name));
nprintf(("AI", "Couldn't find subsystem %d for ship %s\n", aigp->ai_submode, target_ship_entry->shipp->ship_name));
status = 0;
}
break;
}
case AI_GOAL_DISABLE_SHIP:
case AI_GOAL_DISABLE_SHIP_TACTICAL:
{
// shipnum could be -1 depending on if the ship hasn't arrived or died. only look for subsystem
// destroyed when shipnum is valid
sindex = ship_name_lookup(aigp->target_name);
// can't determine the status of this goal if ship not valid
if (sindex < 0) {
if (!target_ship_entry || !target_ship_entry->shipp) {
status = 0;
} else {
// see MWA 3/20/97 comment above - instead of checking the mission log, check the current hits
status = (Ships[sindex].subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f) ? 1 : 0;
status = (target_ship_entry->shipp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f) ? 1 : 0;
}
break;
}
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISARM_SHIP_TACTICAL:
{
// shipnum could be -1 depending on if the ship hasn't arrived or died. only look for subsystem
// destroyed when shipnum is valid
sindex = ship_name_lookup(aigp->target_name);
// can't determine the status of this goal if ship not valid
if (sindex < 0) {
if (!target_ship_entry || !target_ship_entry->shipp) {
status = 0;
} else {
// see MWA 3/20/97 comment above - instead of checking the mission log, check the current hits
status = (Ships[sindex].subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f) ? 1 : 0;
status = (target_ship_entry->shipp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f) ? 1 : 0;
}
break;
}
@@ -1700,22 +1716,23 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// MWA -- 4/22/98. Check for the ship actually being in the mission before
// checking departure and destroyed. In multiplayer, since ships can respawn,
// they get log entries for being destroyed even though they have respawned.
sindex = ship_name_lookup( aigp->target_name );
if ( sindex < 0 ) {
status = mission_log_get_time( LOG_SHIP_DEPARTED, aigp->target_name, NULL, NULL);
if ( !status ) {
status = mission_log_get_time( LOG_SHIP_DESTROYED, aigp->target_name, NULL, NULL);
if ( !status ) {
status = mission_log_get_time( LOG_SELF_DESTRUCTED, aigp->target_name, NULL, NULL);
}
}
if ( status )
return_val = ai_achievability::NOT_ACHIEVABLE;
// Goober5000 - update this to use ship registry status with similar logic
if (target_ship_entry && target_ship_entry->status == ShipStatus::EXITED) {
status = 1;
return_val = ai_achievability::NOT_ACHIEVABLE;
} else {
status = 0;
}
// fly-to-ship can potentially be determined
if (aigp->ai_mode == AI_GOAL_FLY_TO_SHIP && target_ship_entry && target_ship_entry->objp) {
auto dist = vm_vec_dist(&target_ship_entry->objp->pos, &objp->pos);
if (dist < aigp->float_data) {
return_val = ai_achievability::SATISFIED;
status = 1;
}
}
break;
}
@@ -1768,12 +1785,12 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// the if statement.
if ( (aigp->ai_mode == AI_GOAL_CHASE_WING) || (aigp->ai_mode == AI_GOAL_GUARD_WING) )
{
sindex = wing_name_lookup( aigp->target_name );
int wingnum = wing_name_lookup( aigp->target_name );
if (sindex < 0)
if (wingnum < 0)
return ai_achievability::NOT_KNOWN;
wing *wingp = &Wings[sindex];
wing *wingp = &Wings[wingnum];
if ( wingp->flags[Ship::Wing_Flags::Gone] )
return ai_achievability::NOT_ACHIEVABLE;
@@ -1790,24 +1807,22 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
}
else
{
auto ship_entry = ship_registry_get(aigp->target_name);
if (!ship_entry)
if (!target_ship_entry)
{
status = SHIP_STATUS_UNKNOWN;
}
// goal ship is currently in mission
else if (ship_entry->status == ShipStatus::PRESENT || ship_entry->status == ShipStatus::DEATH_ROLL)
else if (target_ship_entry->status == ShipStatus::PRESENT || target_ship_entry->status == ShipStatus::DEATH_ROLL)
{
status = SHIP_STATUS_ARRIVED;
}
// goal ship is still on the arrival list
else if (ship_entry->status == ShipStatus::NOT_YET_PRESENT)
else if (target_ship_entry->status == ShipStatus::NOT_YET_PRESENT)
{
status = SHIP_STATUS_NOT_ARRIVED;
}
// goal ship has left the area
else if (ship_entry->status == ShipStatus::EXITED)
else if (target_ship_entry->status == ShipStatus::EXITED)
{
status = SHIP_STATUS_GONE;
}
@@ -1818,18 +1833,16 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
if (status == SHIP_STATUS_UNKNOWN)
{
mprintf(("Potentially incorrect behaviour in AI goal for ship %s: Ship %s could not be found among currently active, departed, or yet-to-arrive ships.\nPlease check the mission file.\n", Ships[objp->instance].ship_name, aigp->target_name));
mprintf(("Potentially incorrect behaviour in AI goal for ship %s: Ship %s could not be found among currently active, departed, or yet-to-arrive ships.\nPlease check the mission file.\n", shipp->ship_name, aigp->target_name));
}
}
// Goober5000 - before doing anything else, check if this is a disarm goal for an arrived ship...
if ((status == SHIP_STATUS_ARRIVED) && (aigp->ai_mode == AI_GOAL_DISARM_SHIP))
if ((status == SHIP_STATUS_ARRIVED) && (aigp->ai_mode == AI_GOAL_DISARM_SHIP || aigp->ai_mode == AI_GOAL_DISARM_SHIP_TACTICAL))
{
sindex = ship_name_lookup(aigp->target_name);
if ( sindex >= 0 ) {
if (target_ship_entry && target_ship_entry->shipp) {
// if the ship has no turrets, we can't disarm it!
if (Ships[sindex].subsys_info[SUBSYSTEM_TURRET].type_count == 0)
if (target_ship_entry->shipp->subsys_info[SUBSYSTEM_TURRET].type_count == 0)
return ai_achievability::NOT_ACHIEVABLE;
} else {
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
@@ -1856,40 +1869,36 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// this goal will get removed.
if ( (aigp->ai_mode == AI_GOAL_DOCK) && (status == SHIP_STATUS_ARRIVED) ) {
if (!(aigp->flags[AI::Goal_Flags::Docker_index_valid])) {
modelnum = Ship_info[Ships[objp->instance].ship_info_index].model_num;
int modelnum = Ship_info[shipp->ship_info_index].model_num;
Assert( modelnum >= 0 );
index = model_find_dock_name_index(modelnum, aigp->docker.name);
aigp->docker.index = index;
aigp->docker.index = model_find_dock_name_index(modelnum, aigp->docker.name);
aigp->flags.set(AI::Goal_Flags::Docker_index_valid);
}
if (!(aigp->flags[AI::Goal_Flags::Dockee_index_valid])) {
sindex = ship_name_lookup(aigp->target_name);
if ( sindex != -1 ) {
modelnum = Ship_info[Ships[sindex].ship_info_index].model_num;
index = model_find_dock_name_index(modelnum, aigp->dockee.name);
aigp->dockee.index = index;
if (target_ship_entry && target_ship_entry->shipp) {
int modelnum = Ship_info[target_ship_entry->shipp->ship_info_index].model_num;
aigp->dockee.index = model_find_dock_name_index(modelnum, aigp->dockee.name);
aigp->flags.set(AI::Goal_Flags::Dockee_index_valid);
} else
aigp->dockee.index = -1; // this will force code into if statement below making goal not achievable.
}
if ( (aigp->dockee.index == -1) || (aigp->docker.index == -1) ) {
Warning(LOCATION, "Cannot determine docking information for %s!", Ships[objp->instance].ship_name); // for now, allender wants to know about these things!!!!
Warning(LOCATION, "Cannot determine docking information for %s!", shipp->ship_name); // for now, allender wants to know about these things!!!!
return ai_achievability::NOT_ACHIEVABLE;
}
// if ship is disabled, don't know if it can dock or not
if ( Ships[objp->instance].flags[Ship::Ship_Flags::Disabled] )
if ( shipp->flags[Ship::Ship_Flags::Disabled] )
return ai_achievability::NOT_KNOWN;
// we must also determine if we're prevented from docking for any reason
sindex = ship_name_lookup(aigp->target_name);
if (sindex < 0) {
if (!target_ship_entry || !target_ship_entry->shipp) {
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid
}
object *goal_objp = &Objects[Ships[sindex].objnum];
auto goal_objp = target_ship_entry->objp;
// if the ship that I am supposed to dock with is docked with something else, then I need to put my goal on hold
// [MK, 4/23/98: With Mark, we believe this fixes the problem of Comet refusing to warp out after docking with Omega.
@@ -1938,11 +1947,9 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// might happen too, so err on the safe side. (Yay for emergent paragraph justification!)
if ((aip->mode == AIM_DOCK) && (aip->submode >= AIS_UNDOCK_0))
{
sindex = ship_name_lookup(aigp->target_name);
if ( sindex >= 0 ) {
if ( target_ship_entry && target_ship_entry->shipp ) {
// only put it on hold if it's someone other than the guy we're undocking from right now!!
if (aip->goal_objnum != Ships[sindex].objnum)
if (aip->goal_objnum != target_ship_entry->shipp->objnum)
return ai_achievability::NOT_KNOWN;
} else {
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
@@ -1955,10 +1962,8 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// have fixed up the subsystem name (of the subsystem to destroy) into an index into
// the ship's subsystem list
if ( aigp->flags[AI::Goal_Flags::Subsys_needs_fixup] ) {
sindex = ship_name_lookup( aigp->target_name );
if ( sindex != -1 ) {
aigp->ai_submode = ship_find_subsys( &Ships[sindex], aigp->docker.name );
if ( target_ship_entry && target_ship_entry->shipp ) {
aigp->ai_submode = ship_find_subsys( target_ship_entry->shipp, aigp->docker.name );
aigp->flags.remove(AI::Goal_Flags::Subsys_needs_fixup);
} else {
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
@@ -1966,15 +1971,12 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
}
}
} else if ( ((aigp->ai_mode == AI_GOAL_IGNORE) || (aigp->ai_mode == AI_GOAL_IGNORE_NEW)) && (status == SHIP_STATUS_ARRIVED) ) {
object *ignored;
// for ignoring a ship, call the ai_ignore object function, then declare the goal satisfied
sindex = ship_name_lookup( aigp->target_name );
if (sindex < 0) {
if (!target_ship_entry || !target_ship_entry->shipp) {
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid
}
ignored = &Objects[Ships[sindex].objnum];
auto ignored = target_ship_entry->objp;
ai_ignore_object(objp, ignored, (aigp->ai_mode == AI_GOAL_IGNORE_NEW));
@@ -1990,7 +1992,9 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
case AI_GOAL_GUARD:
case AI_GOAL_GUARD_WING:
case AI_GOAL_DISABLE_SHIP:
case AI_GOAL_DISABLE_SHIP_TACTICAL:
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISARM_SHIP_TACTICAL:
case AI_GOAL_DESTROY_SUBSYSTEM:
case AI_GOAL_IGNORE:
case AI_GOAL_IGNORE_NEW:
@@ -2007,7 +2011,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
else if ( status == SHIP_STATUS_UNKNOWN )
return ai_achievability::NOT_KNOWN;
UNREACHABLE("Invalid status variable %d for ship %s; get Allender or a SCP member", status, Ships[objp->instance].ship_name); // get allender -- bad logic
UNREACHABLE("Invalid status variable %d for ship %s; get Allender or a SCP member", status, shipp->ship_name); // get allender -- bad logic
break;
}
@@ -2019,30 +2023,29 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// short circuit a couple of cases. Ship not arrived shouldn't happen. Ship gone means
// we mark the goal as not achievable.
if ( status == SHIP_STATUS_NOT_ARRIVED ) {
UNREACHABLE("Ship %s cannot rearm a target %s that hasn't arrived; get Allender or a SCP member", Ships[objp->instance].ship_name, aigp->target_name); // get Allender. this shouldn't happen!!!
UNREACHABLE("Ship %s cannot rearm a target %s that hasn't arrived; get Allender or a SCP member", shipp->ship_name, aigp->target_name); // get Allender. this shouldn't happen!!!
return ai_achievability::NOT_ACHIEVABLE;
}
if ( status == SHIP_STATUS_GONE )
return ai_achievability::NOT_ACHIEVABLE;
sindex = ship_name_lookup( aigp->target_name );
if ( sindex < 0 ) {
if ( !target_ship_entry || !target_ship_entry->shipp ) {
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
return ai_achievability::NOT_ACHIEVABLE;
}
// if desitnation currently being repaired, then goal is stil active
if ( Ai_info[Ships[sindex].ai_index].ai_flags[AI::AI_Flags::Being_repaired] )
// if destination currently being repaired, then goal is still active
if ( Ai_info[target_ship_entry->shipp->ai_index].ai_flags[AI::AI_Flags::Being_repaired] )
return ai_achievability::ACHIEVABLE;
// if the destination ship is dying or departing (but not completed yet), the mark goal as
// not achievable.
if ( Ships[sindex].is_dying_or_departing())
if ( target_ship_entry->shipp->is_dying_or_departing())
return ai_achievability::NOT_ACHIEVABLE;
// if the destination object is no longer awaiting repair, then remove the item
if ( !(Ai_info[Ships[sindex].ai_index].ai_flags[AI::AI_Flags::Awaiting_repair]) )
if ( !(Ai_info[target_ship_entry->shipp->ai_index].ai_flags[AI::AI_Flags::Awaiting_repair]) )
return ai_achievability::NOT_ACHIEVABLE;
// not repairing anything means that he can do this goal!!!
@@ -2057,7 +2060,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
// if he is repairing something, he can satisfy his repair goal (his goal_objnum)
// return GOAL_NOT_KNOWN which is kind of a hack which puts the goal on hold until it can be
// satisfied.
if ( aip->goal_objnum != Ships[sindex].objnum )
if ( aip->goal_objnum != target_ship_entry->shipp->objnum )
return ai_achievability::NOT_KNOWN;
return ai_achievability::ACHIEVABLE;
@@ -2338,12 +2341,6 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
break;
}
case AI_GOAL_FLY_TO_SHIP:
shipnum = ship_name_lookup( current_goal->target_name );
Assert (shipnum != -1 ); // shouldn't get here if this is false!!!!
ai_start_fly_to_ship(objp, shipnum);
break;
case AI_GOAL_DOCK: {
shipnum = ship_name_lookup( current_goal->target_name );
Assert (shipnum != -1 ); // shouldn't get here if this is false!!!!
@@ -2418,22 +2415,27 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
break;
// when destroying a subsystem, we can destroy a specific instance of a subsystem
// or all instances of a type of subsystem (i.e. a specific engine or all engines).
// the ai_submode value is > 0 for a specific instance of subsystem and < 0 for all
// instances of a specific type
// when destroying a subsystem, we can destroy a specific instance of a subsystem
// or all instances of a type of subsystem (i.e. a specific engine or all engines).
// the ai_submode value is > 0 for a specific instance of subsystem and < 0 for all
// instances of a specific type
case AI_GOAL_DESTROY_SUBSYSTEM:
case AI_GOAL_DISABLE_SHIP:
case AI_GOAL_DISARM_SHIP: {
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];
ai_attack_object( objp, other_obj);
ai_set_attack_subsystem( objp, current_goal->ai_submode ); // submode stored the subsystem type
if (current_goal->ai_mode != AI_GOAL_DESTROY_SUBSYSTEM) {
// 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;
// Only protect if _not_ a capital ship. We don't want the Lucifer accidentally getting protected.
if (Ship_types[Ship_info[Ships[shipnum].ship_info_index].class_type].flags[Ship::Type_Info_Flags::AI_protected_on_cripple])
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);
}
} else // Just in case this ship had been protected, unprotect it.
@@ -2511,14 +2513,17 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
// labels for support ship commands
case AI_GOAL_STAY_NEAR_SHIP: {
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];
float dist = current_goal->float_data; // How far away to stay from ship. Should be set in SEXP?
ai_do_stay_near(objp, other_obj, dist);
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);
break;
}
}
case AI_GOAL_KEEP_SAFE_DISTANCE:
// todo MK: hook to keep support ship at a safe distance
@@ -2566,7 +2571,9 @@ void ai_update_goal_references(ai_goal *goals, sexp_ref_type type, const char *o
case AI_GOAL_DESTROY_SUBSYSTEM:
case AI_GOAL_GUARD:
case AI_GOAL_DISABLE_SHIP:
case AI_GOAL_DISABLE_SHIP_TACTICAL:
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISARM_SHIP_TACTICAL:
case AI_GOAL_IGNORE:
case AI_GOAL_IGNORE_NEW:
case AI_GOAL_EVADE_SHIP:
@@ -2640,7 +2647,9 @@ bool query_referenced_in_ai_goals(ai_goal *goals, sexp_ref_type type, const char
case AI_GOAL_DESTROY_SUBSYSTEM:
case AI_GOAL_GUARD:
case AI_GOAL_DISABLE_SHIP:
case AI_GOAL_DISABLE_SHIP_TACTICAL:
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISARM_SHIP_TACTICAL:
case AI_GOAL_IGNORE:
case AI_GOAL_IGNORE_NEW:
case AI_GOAL_EVADE_SHIP:
+5 -3
View File
@@ -73,15 +73,17 @@ struct ai_info;
#define AI_GOAL_FLY_TO_SHIP (1<<23) // 0x00800000
#define AI_GOAL_IGNORE_NEW (1<<24) // 0x01000000
#define AI_GOAL_CHASE_SHIP_CLASS (1<<25) // 0x02000000
#define AI_GOAL_PLAY_DEAD_PERSISTENT (1<<26) // 0x03000000
#define AI_GOAL_LUA (1<<27) // 0x04000000
#define AI_GOAL_PLAY_DEAD_PERSISTENT (1<<26) // 0x04000000
#define AI_GOAL_LUA (1<<27) // 0x08000000
#define AI_GOAL_DISARM_SHIP_TACTICAL (1<<28) // 0x10000000
#define AI_GOAL_DISABLE_SHIP_TACTICAL (1<<29) // 0x20000000
// now the masks for ship types
// Goober5000: added AI_GOAL_STAY_NEAR_SHIP and AI_GOAL_KEEP_SAFE_DISTANCE as valid for fighters
//WMC - Don't need these anymore. Whee!
/*
#define AI_GOAL_ACCEPT_FIGHTER ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_CHASE | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_WARP | AI_GOAL_DESTROY_SUBSYSTEM | AI_GOAL_CHASE_WING | AI_GOAL_GUARD | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP | AI_GOAL_CHASE_ANY | AI_GOAL_IGNORE | AI_GOAL_IGNORE_NEW | AI_GOAL_GUARD_WING | AI_GOAL_EVADE_SHIP | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD | AI_GOAL_PLAY_DEAD_PERSISTENT | AI_GOAL_STAY_NEAR_SHIP | AI_GOAL_KEEP_SAFE_DISTANCE )
#define AI_GOAL_ACCEPT_FIGHTER ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_CHASE | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_WARP | AI_GOAL_DESTROY_SUBSYSTEM | AI_GOAL_CHASE_WING | AI_GOAL_GUARD | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL | AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL | AI_GOAL_CHASE_ANY | AI_GOAL_IGNORE | AI_GOAL_IGNORE_NEW | AI_GOAL_GUARD_WING | AI_GOAL_EVADE_SHIP | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD | AI_GOAL_PLAY_DEAD_PERSISTENT | AI_GOAL_STAY_NEAR_SHIP | AI_GOAL_KEEP_SAFE_DISTANCE )
#define AI_GOAL_ACCEPT_BOMBER ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_FIGHTER | AI_GOAL_STAY_NEAR_SHIP )
#define AI_GOAL_ACCEPT_STEALTH ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_FIGHTER | AI_GOAL_STAY_NEAR_SHIP )
#define AI_GOAL_ACCEPT_TRANSPORT ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_DOCK | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_WARP | AI_GOAL_UNDOCK | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD | AI_GOAL_PLAY_DEAD_PERSISTENT | AI_GOAL_STAY_NEAR_SHIP )
+162 -5
View File
@@ -18,9 +18,9 @@ void ai_lua_add_mode(int sexp_op, const ai_mode_lua& mode) {
}
bool ai_lua_add_order(int sexp_op, player_order_lua order) {
Player_orders.emplace_back(vm_strdup(order.parseText.c_str()), vm_strdup(order.displayText.c_str()), -1, sexp_op);
Player_orders.emplace_back(order.parseText, order.displayText, -1, sexp_op);
Player_orders.back().localize();
Lua_player_orders.emplace(sexp_op, std::move(order));
return true;
@@ -48,6 +48,134 @@ const player_order_lua* ai_lua_find_player_order(int sexp_op) {
return &aiLuaOrder->second;
}
int ai_lua_get_num_general_orders() {
int count = 0;
for (const auto& order : Lua_player_orders) {
if (order.second.generalOrder) {
count++;
}
}
return count;
}
SCP_vector<SCP_string> ai_lua_get_general_order_categories(bool enabled_only)
{
SCP_vector<SCP_string> list;
for (const auto& order : Lua_player_orders) {
const SCP_string &cat = order.second.category;
// If it's not a general order then move on
if (!order.second.generalOrder) {
continue;
}
// If the order is not enabled then move on
if (enabled_only && !order.second.cur_enabled) {
continue;
}
Assertion((cat.length() > 0), "Lua general orders category name must be longer than 0 characters!");
for (size_t i = 0; i <= list.size(); i++) {
// reached the end of the list, so add it
if (i == list.size()) {
list.push_back(cat);
break;
}
// if it's already in the list then don't add it
if (cat == list[i]) {
break;
}
}
}
return list;
}
SCP_vector<SCP_string> ai_lua_get_general_orders(bool onlyEnabled, bool onlyValid, const SCP_string& category)
{
SCP_vector<SCP_string> list;
for (auto order : Lua_player_orders) {
if (order.second.generalOrder) {
bool add = false;
// Only enabled orders
if (onlyEnabled && order.second.cur_enabled) {
add = true;
// Only valid orders
} else if (onlyValid && order.second.cur_valid) {
add = true;
// Only orders that match the category requested
} else if ((!category.empty()) && (order.second.category == category)) {
add = true;
// All general orders
} else {
add = true;
}
if (add) {
list.push_back(order.second.parseText);
}
}
}
return list;
}
int ai_lua_find_general_order_id(const SCP_string& name) {
for (const auto& order : Lua_player_orders) {
if (order.second.parseText == name) {
return order.first;
}
}
return -1;
}
void ai_lua_enable_general_order(int sexp_op, bool enable) {
auto aiLuaOrder = Lua_player_orders.find(sexp_op);
if (aiLuaOrder == Lua_player_orders.end())
return;
else
aiLuaOrder->second.cur_enabled = enable;
}
void ai_lua_validate_general_order(int sexp_op, bool validity)
{
auto aiLuaOrder = Lua_player_orders.find(sexp_op);
if (aiLuaOrder == Lua_player_orders.end())
return;
else
aiLuaOrder->second.cur_valid = validity;
}
void ai_lua_reset_general_orders() {
for (auto order : Lua_player_orders) {
ai_lua_enable_general_order(order.first, false);
ai_lua_validate_general_order(order.first, false);
}
}
bool ai_lua_is_general_order(int sexp_op) {
auto aiLuaOrder = Lua_player_orders.find(sexp_op);
if (aiLuaOrder == Lua_player_orders.end())
return false;
return aiLuaOrder->second.generalOrder;
}
void run_ai_lua_action(const luacpp::LuaFunction& action, const ai_mode_lua& lua_ai, ai_info* aip) {
if (!action.isValid()) {
Error(LOCATION,
@@ -56,7 +184,11 @@ void run_ai_lua_action(const luacpp::LuaFunction& action, const ai_mode_lua& lua
}
luacpp::LuaValueList luaParameters;
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_AI_Helper.Set(object_h(&Objects[Ships[aip->shipnum].objnum]))));
if (aip->shipnum >= 0){
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_AI_Helper.Set(object_h(&Objects[Ships[aip->shipnum].objnum]))));
} else {
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_AI_Helper.Set(object_h())));
}
if (lua_ai.needsTarget) {
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_OSWPT.Set(aip->lua_ai_target.target)));
}
@@ -67,8 +199,13 @@ void run_ai_lua_action(const luacpp::LuaFunction& action, const ai_mode_lua& lua
auto retVals = action.call(Script_system.GetLuaSession(), luaParameters);
if (!retVals.empty() && retVals[0].getValueType() == luacpp::ValueType::BOOLEAN) {
if (retVals[0].getValue<bool>())
ai_mission_goal_complete(aip);
if (retVals[0].getValue<bool>()) {
// If we don't have a ship then it's a general order and we have nothing else to do
if (aip->shipnum >= 0) {
ai_mission_goal_complete(aip);
}
}
}
}
@@ -103,6 +240,26 @@ void ai_lua_start(ai_goal* aigp, object* objp){
run_ai_lua_action(action, lua_ai, aip);
}
// For sending general orders that don't run on a ship.
// Essentially just runs arbitrary lua script when the command is called
void ai_lua_start_general(int lua_sexp_id, int target_objnum)
{
//Create a dummy aip to pass the target data
ai_info aip;
aip.shipnum = -1;
if (target_objnum >= 0) {
aip.lua_ai_target = {object_ship_wing_point_team(&Ships[Objects[target_objnum].instance]), {}};
}
const auto& lua_ai = ai_lua_find_mode(lua_sexp_id);
const auto& lua_ai_sexp = lua_ai->sexp;
const auto& action = lua_ai_sexp.getActionEnter();
run_ai_lua_action(action, *lua_ai, &aip);
}
bool ai_lua_is_valid_target_intrinsic(int sexp_op, int target_objnum, ship* self) {
ship* target = &Ships[Objects[target_objnum].instance];
+13
View File
@@ -19,6 +19,10 @@ struct player_order_lua {
int ai_message = MESSAGE_YESSIR;
SCP_string parseText = "";
SCP_string displayText = "";
bool generalOrder = false;
SCP_string category = "";
bool cur_enabled = false;
bool cur_valid = false;
enum class target_restrictions : int { TARGET_ALLIES, TARGET_ALL, TARGET_OWN, TARGET_ENEMIES, TARGET_SAME_WING, TARGET_PLAYER_WING, TARGET_ALL_CAPS, TARGET_ALLIED_CAPS, TARGET_ENEMY_CAPS, TARGET_NOT_SELF } targetRestrictions = target_restrictions::TARGET_ALL;
enum class ship_restrictions : int { ANY, WING, IN_PLAYER_WING, PLAYER_WING } shipRestrictions = ship_restrictions::ANY;
};
@@ -29,8 +33,17 @@ bool ai_lua_add_order(int sexp_op, player_order_lua order);
bool ai_lua_has_mode(int sexp_op);
const ai_mode_lua* ai_lua_find_mode(int sexp_op);
const player_order_lua* ai_lua_find_player_order(int sexp_op);
SCP_vector<SCP_string> ai_lua_get_general_order_categories(bool enabled_only = true);
int ai_lua_get_num_general_orders();
SCP_vector<SCP_string> ai_lua_get_general_orders(bool onlyEnabled = false, bool onlyValid = false, const SCP_string& category = "");
int ai_lua_find_general_order_id(const SCP_string& name);
void ai_lua_enable_general_order(int sexp_op, bool enable);
void ai_lua_validate_general_order(int sexp_op, bool validity);
void ai_lua_reset_general_orders();
bool ai_lua_is_general_order(int sexp_op);
void ai_lua(ai_info* aip);
void ai_lua_start(ai_goal* aigp, object* objp);
void ai_lua_start_general(int lua_sexp_id, int target_objnum);
bool ai_lua_is_valid_target(int sexp_op, int target_objnum, ship* self, size_t order);
bool ai_lua_is_valid_ship(int sexp_op, bool isWing, ship* self);
ai_achievability ai_lua_is_achievable(const ai_goal* aigp, int objnum);
+1 -1
View File
@@ -1104,7 +1104,7 @@ int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d
ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
if ((Ship_types[sip->class_type].flags[Ship::Type_Info_Flags::Turret_tgt_ship_tgt]) && (aip->target_objnum != -1)) {
if ((sip->class_type >= 0) && (Ship_types[sip->class_type].flags[Ship::Type_Info_Flags::Turret_tgt_ship_tgt]) && (aip->target_objnum != -1)) {
int target_objnum = aip->target_objnum;
if (Objects[target_objnum].signature == aip->target_signature) {
+1 -1
View File
@@ -670,7 +670,7 @@ void anim_read_header(anim *ptr, CFILE *fp)
* @param file_mapped Whether to use memory-mapped file or not.
*
* @details Memory-mapped files will page in the animation from disk as it is needed, but performance is not as good.
* @return Pointer to anim that is loaded if sucess, NULL if failure.
* @return Pointer to anim that is loaded if success, NULL if failure.
*/
anim *anim_load(const char *real_filename, int cf_dir_type, int file_mapped)
{
+1 -1
View File
@@ -1030,7 +1030,7 @@ bool asteroid_is_within_view(vec3d *pos, float range, bool range_override)
float dot = vm_vec_dot(&Eye_matrix.vec.fvec, &vec_to_asteroid);
// if asteroid is within view or far enough away then wrap
if (dot > cos(Proj_fov)) {
if (dot > cos(g3_get_hfov(Proj_fov))) {
if (range_override) {
return true;
} else {
+1 -1
View File
@@ -598,7 +598,7 @@ int bm_create_3d(int bpp, int w, int h, int d, void* data) {
// make sure that we have valid data
if (data == nullptr) {
UNREACHABLE("No valid data recieved for 3D Bitmap creation!");
UNREACHABLE("No valid data received for 3D Bitmap creation!");
return -1;
}
+53 -21
View File
@@ -14,8 +14,8 @@
#include "ship/ship.h" //compute_slew_matrix
//*************************IMPORTANT GLOBALS*************************
float VIEWER_ZOOM_DEFAULT = 0.75f; // Default viewer zoom, 0.625 as per multi-lateral agreement on 3/24/97
float COCKPIT_ZOOM_DEFAULT = 0.75f;
fov_t VIEWER_ZOOM_DEFAULT = 0.75f; // Default viewer zoom, 0.625 as per multi-lateral agreement on 3/24/97
fov_t COCKPIT_ZOOM_DEFAULT = 0.75f;
float Sexp_fov = 0.0f;
warp_camera Warp_camera;
@@ -27,6 +27,33 @@ SCP_vector<camera*> Cameras;
camid Current_camera;
camid Main_camera;
asymmetric_fov operator* (const asymmetric_fov& left, const float& right) {
return asymmetric_fov{ left.left * right, left.right * right, left.up * right, left.down * right };
}
asymmetric_fov operator+ (const asymmetric_fov& left, const float& right) {
float ar = (left.up - left.down) / (left.right - left.left);
return asymmetric_fov{ left.left - right * 0.5f, left.right + right * 0.5f, left.up + right * 0.5f * ar, left.down - right * 0.5f * ar };
}
asymmetric_fov operator- (const asymmetric_fov& left, const float& right) {
float ar = (left.up - left.down) / (left.right - left.left);
return asymmetric_fov{ left.left + right * 0.5f, left.right - right * 0.5f, left.up - right * 0.5f * ar, left.down + right * 0.5f * ar };
}
#define APPLY_TO_FOV_T(op, name) \
struct apply_##name##_to_fov { \
float r; \
template<typename T> \
fov_t operator()(const T& l) { \
return fov_t(l op r); \
} \
}; \
fov_t operator op (const fov_t& zoom, const float& scale) { \
return mpark::visit(apply_##name##_to_fov{scale}, zoom); \
}
APPLY_TO_FOV_T(*, mul)
APPLY_TO_FOV_T(+, add)
APPLY_TO_FOV_T(-, sub)
static SCP_string fov_display(float val)
{
auto degrees = fl_degrees(val);
@@ -34,15 +61,20 @@ static SCP_string fov_display(float val)
sprintf(out, u8"%.1f\u00B0", degrees);
return out;
}
auto FovOption = options::OptionBuilder<float>("Graphics.FOV", "Field Of View", "The vertical field of view.")
.category("Graphics")
.range(0.436332f, 1.5708f)
.bind_to(&VIEWER_ZOOM_DEFAULT)
.display(fov_display)
.default_val(0.75f)
.level(options::ExpertLevel::Advanced)
.importance(60)
.finish();
auto FovOption = options::OptionBuilder<float>("Graphics.FOV",
std::pair<const char*, int>{"Field Of View", 1703},
std::pair<const char*, int>{"The vertical field of view", 1704})
.category("Graphics")
.range(0.436332f, 1.5708f)
.change_listener([](const float& val, bool) {
VIEWER_ZOOM_DEFAULT = val;
return true;
})
.display(fov_display)
.default_val(0.75f)
.level(options::ExpertLevel::Advanced)
.importance(60)
.finish();
//*************************CLASS: camera*************************
//This is where the camera class begins! :D
@@ -85,7 +117,7 @@ void camera::reset()
func_custom_orientation = NULL;
fov.clear();
fov.set(VIEWER_ZOOM_DEFAULT);
fov.set(g3_get_hfov(VIEWER_ZOOM_DEFAULT));
pos_x.clear();
pos_y.clear();
@@ -104,7 +136,7 @@ void camera::set_name(const char *in_name)
strncpy(name, in_name, NAME_LENGTH-1);
}
void camera::set_fov(float in_fov, float in_fov_time, float in_fov_acceleration_time, float in_fov_deceleration_time)
void camera::set_fov(fov_t in_fov, float in_fov_time, float in_fov_acceleration_time, float in_fov_deceleration_time)
{
if(in_fov_time == 0.0f && in_fov_acceleration_time == 0.0f && in_fov_deceleration_time == 0.0f)
{
@@ -114,26 +146,23 @@ void camera::set_fov(float in_fov, float in_fov_time, float in_fov_acceleration_
}
flags &= ~CAM_STATIONARY_FOV;
if(in_fov <= 0.0f)
if(g3_get_hfov(in_fov) <= 0.0f)
{
fov.setVD(in_fov_time, in_fov_acceleration_time, 0.0f);
}
fov.setAVD(in_fov, in_fov_time, in_fov_acceleration_time, in_fov_deceleration_time, 0.0f);
fov.setAVD(g3_get_hfov(in_fov), in_fov_time, in_fov_acceleration_time, in_fov_deceleration_time, 0.0f);
}
void camera::set_object_host(object *objp, int n_object_host_submodel)
{
if(objp == NULL)
object_host = object_h();
object_host = object_h(objp);
object_host_submodel = n_object_host_submodel;
set_custom_position_function(NULL);
set_custom_orientation_function(NULL);
if(n_object_host_submodel > 0)
{
if(objp->type == OBJ_SHIP)
if(objp != nullptr && objp->type == OBJ_SHIP)
{
ship_subsys* ssp = GET_FIRST(&Ships[objp->instance].subsys_list);
while ( ssp != END_OF_LIST( &Ships[objp->instance].subsys_list ) )
@@ -319,11 +348,14 @@ int camera::get_object_target_submodel()
return object_target_submodel;
}
float camera::get_fov()
fov_t camera::get_fov()
{
if(!(flags & CAM_STATIONARY_FOV))
{
fov.get(&c_fov, NULL);
float fov_now;
fov.get(&fov_now, nullptr);
return fov_now;
//TODO enable moving asymmetric fov
}
return c_fov;
+18 -5
View File
@@ -10,6 +10,8 @@
#include <string>
#include <mpark/variant.hpp>
#define CAM_STATIONARY_FOV (1<<0)
#define CAM_STATIONARY_ORI (1<<1)
#define CAM_STATIONARY_POS (1<<2)
@@ -18,6 +20,17 @@
#define EXTERN_CAM_BBOX_CONSTANT_PADDING 5.0f
#define EXTERN_CAM_BBOX_MULTIPLIER_PADDING 1.5f
struct asymmetric_fov {
float left, right, up, down;
friend asymmetric_fov operator* (const asymmetric_fov&, const float&);
friend asymmetric_fov operator+ (const asymmetric_fov&, const float&);
friend asymmetric_fov operator- (const asymmetric_fov&, const float&);
};
using fov_t = mpark::variant<float, asymmetric_fov>;
fov_t operator* (const fov_t&, const float&);
fov_t operator+ (const fov_t&, const float&);
fov_t operator- (const fov_t&, const float&);
class camera
{
protected:
@@ -41,7 +54,7 @@ protected:
avd_movement ori[9];
//Cache stuff
float c_fov;
fov_t c_fov;
vec3d c_pos;
matrix c_ori;
public:
@@ -59,7 +72,7 @@ public:
void set_custom_position_function(void (*n_func_custom_position)(camera *cam, vec3d *pos));
void set_custom_orientation_function(void (*n_func_custom_orientation)(camera *cam, matrix *ori));
void set_fov(float in_fov, float in_fov_time = 0.0f, float in_fov_acceleration_time = 0.0f, float in_deceleration_time = 0.0f);
void set_fov(fov_t in_fov, float in_fov_time = 0.0f, float in_fov_acceleration_time = 0.0f, float in_deceleration_time = 0.0f);
void set_position(vec3d *in_position = NULL, float in_translation_time = 0.0f, float in_translation_acceleration_time = 0.0f, float in_translation_deceleration_time = 0.0f, float in_end_velocity = 0.0f);
@@ -74,7 +87,7 @@ public:
int get_object_host_submodel();
object *get_object_target();
int get_object_target_submodel();
float get_fov();
fov_t get_fov();
void get_info(vec3d *position, matrix *orientation, bool apply_camera_orientation = true);
//Is
@@ -155,8 +168,8 @@ public:
//Some global stuff
extern SCP_vector<subtitle> Subtitles;
extern float VIEWER_ZOOM_DEFAULT;
extern float COCKPIT_ZOOM_DEFAULT;
extern fov_t VIEWER_ZOOM_DEFAULT;
extern fov_t COCKPIT_ZOOM_DEFAULT;
extern float Sexp_fov;
//Helpful functions
+1 -1
View File
@@ -438,7 +438,7 @@ int cfile_flush_dir(int dir_type)
// filename = name of filename or filepath to process
// ext = extension to add. Must start with the period
// Returns: new filename or filepath with extension.
char *cf_add_ext(const char *filename, const char *ext)
const char *cf_add_ext(const char *filename, const char *ext)
{
static char path[MAX_PATH_LEN];
+1 -1
View File
@@ -171,7 +171,7 @@ int cfile_init(const char *exe_dir, const char *cdrom_dir=NULL);
void cfile_close();
// add an extension to a filename if it doesn't already have it
char *cf_add_ext(const char *filename, const char *ext);
const char *cf_add_ext(const char *filename, const char *ext);
// return filename of a CFILE you called cfopen() successfully on.
const char *cf_get_filename(const CFILE *cfile);
+8 -1
View File
@@ -188,6 +188,7 @@ Flag exe_params[] =
//flag launcher text FSO on_flags off_flags category reference URL
{ "-window", "Run in window", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-window", },
{ "-fullscreen_window", "Run in fullscreen window", false, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fullscreen_window", },
{ "-capture_mouse", "Capture the mouse within the window", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-capture_mouse", },
{ "-stretch_menu", "Stretch interface to fill screen", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-stretch_menu", },
{ "-noscalevid", "Disable scale-to-window for movies", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noscalevid", },
{ "-nomotiondebris", "Disable motion debris", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nomotiondebris",},
@@ -406,6 +407,7 @@ cmdline_parm use_3dwarp("-3dwarp", nullptr, AT_NONE); // Is now Fireball_use_3
cmdline_parm use_warp_flash("-warp_flash", nullptr, AT_NONE); // Cmdline_warp_flash
cmdline_parm allow_autpilot_interrupt("-no_ap_interrupt", nullptr, AT_NONE);
cmdline_parm stretch_menu("-stretch_menu", nullptr, AT_NONE); // Cmdline_stretch_menu
cmdline_parm capture_mouse("-capture_mouse", nullptr, AT_NONE); // Cmdline_capture_mouse
cmdline_parm no_screenshake("-no_screenshake", nullptr, AT_NONE); // Cmdline_no_screenshake
cmdline_parm deadzone("-deadzone",
"Sets the joystick deadzone. Integer value from 0 to 100 as a percentage of the joystick's range (100% would make the stick do nothing). Disables deadzone slider in the in-game Options menu.", AT_INT); //Cmdline_deadzone
@@ -414,6 +416,7 @@ int Cmdline_ship_choice_3d = 0;
int Cmdline_weapon_choice_3d = 0;
int Cmdline_autopilot_interruptable = 1;
int Cmdline_stretch_menu = 0;
bool Cmdline_capture_mouse = false;
int Cmdline_no_screenshake = 0;
int Cmdline_deadzone = -1;
@@ -1646,7 +1649,7 @@ bool SetCmdlineParams()
// be sure that this string fits in our limits
if ( strlen(Cmdline_game_password) >= MAX_PASSWD_LEN ) {
ReleaseWarning(LOCATION, "Multi game password is longer than max of %d charaters and will be trimmed to fit!", MAX_PASSWD_LEN-1);
ReleaseWarning(LOCATION, "Multi game password is longer than max of %d characters and will be trimmed to fit!", MAX_PASSWD_LEN-1);
Cmdline_game_password[MAX_PASSWD_LEN-1] = '\0';
}
}
@@ -1871,6 +1874,10 @@ bool SetCmdlineParams()
if ( stretch_menu.found() ) {
Cmdline_stretch_menu = 1;
}
if ( capture_mouse.found() ) {
Cmdline_capture_mouse = true;
}
// new lighting lines
if ( ambient_power_arg.found() )
Cmdline_ambient_power = ambient_power_arg.get_float();
+1
View File
@@ -86,6 +86,7 @@ extern int Cmdline_ship_choice_3d;
extern int Cmdline_weapon_choice_3d;
extern int Cmdline_autopilot_interruptable;
extern int Cmdline_stretch_menu;
extern bool Cmdline_capture_mouse;
extern int Cmdline_no_screenshake;
extern int Cmdline_deadzone;
+5 -4
View File
@@ -1359,9 +1359,6 @@ bool control_config_accept(bool API_Access)
}
}
hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
if (!API_Access) {
gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
@@ -1630,7 +1627,7 @@ void control_config_close(bool API_Access)
Ui_window.destroy();
common_free_interface_palette(); // restore game palette
}
hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
game_flush();
if (Game_mode & GM_MULTIPLAYER) {
@@ -2536,6 +2533,10 @@ void control_config_do_frame(float frametime)
if (List_buttons[i].pressed()) {
// Select the pressed line
Selected_line = i + Scroll_offset;
// Cyborg - Quick assertion for Coverity 1093701. Not likely to happen, but we check for a negative Selected Line later.
Assertion(Selected_line > -1, "control_config_do_frame() has somehow managed to get a negative Selected_line index of %d. Please get an SCP member.", Selected_line);
Selected_item = selItem::None;
List_buttons[i].get_mouse_pos(&x, &y);
+28 -26
View File
@@ -45,27 +45,29 @@ int Failed_key_index;
// Joystick configuration
int Joy_dead_zone_size = 10;
auto DeadZoneOption =
options::OptionBuilder<int>("Input.JoystickDeadZone", "Deadzone", "The deadzone of the selected joystick.")
.category("Input")
.range(0, 45)
.level(options::ExpertLevel::Beginner)
.default_val(10)
.bind_to(&Joy_dead_zone_size)
.importance(1)
.finish();
auto DeadZoneOption = options::OptionBuilder<int>("Input.JoystickDeadZone",
std::pair<const char*, int>{"Deadzone", 1377},
std::pair<const char*, int>{"The deadzone used for all joysticks", 1744})
.category("Input")
.range(0, 45)
.level(options::ExpertLevel::Beginner)
.default_val(10)
.bind_to(&Joy_dead_zone_size)
.importance(1)
.finish();
int Joy_sensitivity = 9;
auto SensitivityOption =
options::OptionBuilder<int>("Input.JoystickSensitivity", "Sensitivity", "The sentitivity of the selected joystick.")
.category("Input")
.range(0, 9)
.level(options::ExpertLevel::Beginner)
.default_val(9)
.bind_to(&Joy_sensitivity)
.importance(2)
.finish();
auto SensitivityOption = options::OptionBuilder<int>("Input.JoystickSensitivity",
std::pair<const char*, int>{"Sensitivity", 1745},
std::pair<const char*, int>{"The sensitivity used for all joysticks", 1746})
.category("Input")
.range(0, 9)
.level(options::ExpertLevel::Beginner)
.default_val(9)
.bind_to(&Joy_sensitivity)
.importance(2)
.finish();
//! arrays which hold the key mappings. The array index represents a key-independent action.
//! please use SPACES for aligning the fields of this array
@@ -158,8 +160,8 @@ void control_config_common_init_bindings() {
// flight controls (flight modes)
(BANK_WHEN_PRESSED, -1, -1, SHIP_TAB, 1, "Bank When Pressed", CC_TYPE_CONTINUOUS)
(AFTERBURNER, KEY_TAB, 5, SHIP_TAB, 1, "Afterburner", CC_TYPE_CONTINUOUS)
(GLIDE_WHEN_PRESSED, -1, -1, SHIP_TAB, 0, "Glide When Pressed", CC_TYPE_CONTINUOUS)
(TOGGLE_GLIDING, KEY_ALTED | KEY_G, -1, SHIP_TAB, 0, "Toggle Gliding", CC_TYPE_TRIGGER)
(GLIDE_WHEN_PRESSED, -1, -1, SHIP_TAB, 1774, "Glide When Pressed", CC_TYPE_CONTINUOUS)
(TOGGLE_GLIDING, KEY_ALTED | KEY_G, -1, SHIP_TAB, 1775, "Toggle Gliding", CC_TYPE_TRIGGER)
// flight controls (axes)
(JOY_HEADING_AXIS, JOY_X_AXIS, MOUSE_X_AXIS, SHIP_TAB, 1016, "Turn (Yaw) Axis", CC_TYPE_AXIS_REL)
@@ -173,7 +175,7 @@ void control_config_common_init_bindings() {
(FIRE_SECONDARY, KEY_SPACEBAR, 1, WEAPON_TAB, 1, "Fire Secondary Weapon", CC_TYPE_CONTINUOUS)
(CYCLE_NEXT_PRIMARY, KEY_PERIOD, -1, WEAPON_TAB, 1, "Cycle Primary Weapon Forward", CC_TYPE_TRIGGER)
(CYCLE_PREV_PRIMARY, KEY_COMMA, -1, WEAPON_TAB, 1, "Cycle Primary Weapon Backward", CC_TYPE_TRIGGER)
(CYCLE_PRIMARY_WEAPON_SEQUENCE, KEY_O, -1, WEAPON_TAB, 0, "Cycle Primary Weapon Firing Rate", CC_TYPE_TRIGGER)
(CYCLE_PRIMARY_WEAPON_SEQUENCE, KEY_O, -1, WEAPON_TAB, 1776, "Cycle Primary Weapon Firing Rate", CC_TYPE_TRIGGER)
(CYCLE_SECONDARY, KEY_DIVIDE, -1, WEAPON_TAB, 1, "Cycle Secondary Weapon Forward", CC_TYPE_TRIGGER)
(CYCLE_NUM_MISSLES, KEY_SHIFTED | KEY_DIVIDE, -1, WEAPON_TAB, 1, "Cycle Secondary Weapon Firing Rate", CC_TYPE_TRIGGER)
(LAUNCH_COUNTERMEASURE, KEY_X, 3, WEAPON_TAB, 1, "Launch Countermeasure", CC_TYPE_TRIGGER)
@@ -209,8 +211,8 @@ void control_config_common_init_bindings() {
(PADLOCK_DOWN, -1, 32, COMPUTER_TAB, 1, "View Rear", CC_TYPE_CONTINUOUS)
(PADLOCK_LEFT, -1, 34, COMPUTER_TAB, 1, "View Left", CC_TYPE_CONTINUOUS)
(PADLOCK_RIGHT, -1, 35, COMPUTER_TAB, 1, "View Right", CC_TYPE_CONTINUOUS)
(VIEW_TOPDOWN, -1, -1, COMPUTER_TAB, 0, "Top-Down View", CC_TYPE_TRIGGER)
(VIEW_TRACK_TARGET, -1, -1, COMPUTER_TAB, 0, "Target Padlock View", CC_TYPE_TRIGGER)
(VIEW_TOPDOWN, -1, -1, COMPUTER_TAB, 1777, "Top-Down View", CC_TYPE_TRIGGER)
(VIEW_TRACK_TARGET, -1, -1, COMPUTER_TAB, 1778, "Target Padlock View", CC_TYPE_TRIGGER)
(RADAR_RANGE_CYCLE, KEY_RAPOSTRO, -1, COMPUTER_TAB, 1, "Cycle Radar Range", CC_TYPE_TRIGGER)
(SQUADMSG_MENU, KEY_C, -1, COMPUTER_TAB, 1, "Communications Menu", CC_TYPE_TRIGGER)
@@ -235,8 +237,8 @@ void control_config_common_init_bindings() {
// Navigation and Autopilot
(SHOW_NAVMAP, -1, -1, NO_TAB, 1, "Show Nav Map", CC_TYPE_TRIGGER, true)
(AUTO_PILOT_TOGGLE, KEY_ALTED | KEY_A, -1, COMPUTER_TAB, 0, "Toggle Auto Pilot", CC_TYPE_TRIGGER)
(NAV_CYCLE, KEY_ALTED | KEY_N, -1, COMPUTER_TAB, 0, "Cycle Nav Points", CC_TYPE_TRIGGER)
(AUTO_PILOT_TOGGLE, KEY_ALTED | KEY_A, -1, COMPUTER_TAB, 1779, "Toggle Auto Pilot", CC_TYPE_TRIGGER)
(NAV_CYCLE, KEY_ALTED | KEY_N, -1, COMPUTER_TAB, 1780, "Cycle Nav Points", CC_TYPE_TRIGGER)
// Escort
(ADD_REMOVE_ESCORT, KEY_ALTED | KEY_E, -1, COMPUTER_TAB, 1, "Add or Remove Escort", CC_TYPE_TRIGGER)
@@ -258,7 +260,7 @@ void control_config_common_init_bindings() {
// HUD
(TOGGLE_HUD, KEY_SHIFTED | KEY_O, -1, COMPUTER_TAB, 1, "Toggle HUD", CC_TYPE_TRIGGER)
(TOGGLE_HUD_CONTRAST, KEY_L, -1, COMPUTER_TAB, 1, "Toggle High HUD Contrast", CC_TYPE_TRIGGER)
(TOGGLE_HUD_SHADOWS, KEY_ALTED | KEY_L, -1, COMPUTER_TAB, 0, "Toggle HUD Drop Shadows", CC_TYPE_TRIGGER)
(TOGGLE_HUD_SHADOWS, KEY_ALTED | KEY_L, -1, COMPUTER_TAB, 1781, "Toggle HUD Drop Shadows", CC_TYPE_TRIGGER)
(HUD_TARGETBOX_TOGGLE_WIREFRAME, KEY_ALTED | KEY_SHIFTED | KEY_Q, -1, COMPUTER_TAB, 1, "Toggle HUD Wireframe Target View", CC_TYPE_TRIGGER)
// Custom Controls
+1 -1
View File
@@ -108,7 +108,7 @@ class Decoder {
* @note A implementation should initialize the datastructures required for decoding here.
*
* @param fileName The name of the file that should be opened
* @return @c true if the initialization was successfull, @c false otherwise
* @return @c true if the initialization was successful, @c false otherwise
*/
virtual bool initialize(const SCP_string& fileName, const PlaybackProperties& properties) = 0;
+1 -1
View File
@@ -119,7 +119,7 @@
* half-rate linear filtering on GCN.
*
* If SMAA is applied to 64-bit color buffers, switching to point filtering
* when accesing them will increase the performance. Search for
* when accessing them will increase the performance. Search for
* 'SMAASamplePoint' to see which textures may benefit from point
* filtering, and where (which is basically the color input in the edge
* detection and resolve passes).
+5 -3
View File
@@ -24,6 +24,8 @@ layout (std140) uniform globalDeferredData {
float invScreenWidth;
float invScreenHeight;
float nearPlane;
};
layout (std140) uniform matrixData {
@@ -79,7 +81,7 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
lightDirOut = lightPosition - position.xyz;
float dist = length(lightDirOut);
// this chunk is unneccessary if sourceRadius= 0.0, but let's avoid a branch.
// this chunk is unnecessary if sourceRadius= 0.0, but let's avoid a branch.
// given a sphere of radius sourceRadius centered at lightDirOut,
// move lightDirOut towards the ray defined by reflectDir
lightDirOut = ExpandLightSize(lightDirOut, reflectDir);
@@ -122,7 +124,7 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
// should figure out how to optimise these calcs - qaz
// this chunk is unneccessary if sourceRadius = 0.0, but let's avoid a branch.
// this chunk is unnecessary if sourceRadius = 0.0, but let's avoid a branch.
// Same principle as in LT_POINT, treat chosen location as a spherelight.
lightDirOut = ExpandLightSize(lightDirOut, reflectDir);
float dist = length(lightDirOut);
@@ -168,7 +170,7 @@ void main()
vec2 screenPos = gl_FragCoord.xy * vec2(invScreenWidth, invScreenHeight);
vec3 position = texture(PositionBuffer, screenPos).xyz;
if(abs(dot(position, position)) < 0.1)
if(abs(dot(position, position)) < nearPlane * nearPlane)
discard;
vec3 diffColor = texture(ColorBuffer, screenPos).rgb;
+1 -1
View File
@@ -11,7 +11,7 @@ in vec4 fragTexCoord;
out vec4 fragOut0;
// Iteratively optimised points to give low discrepancy distribution on arbritrary hemisphere.
// Iteratively optimised points to give low discrepancy distribution on arbitrary hemisphere.
const vec3 points[128] = vec3[128](
vec3(-0.2268, 0.6185, 0.7523),
vec3( 0.9958, -0.0871, -0.0277),
+2 -2
View File
@@ -33,7 +33,7 @@ vec3 ComputeGGX(vec3 specColor, vec3 diffColor, vec3 light, vec3 normal, vec3 ha
// NOTE - we're intentionally using UE4's reflectance model as modellers are using tools designed for common engines.
// Cook-Torrance BRDF Model (Specular Reflection) = Distibution * Fresnel * Geometry / (4*dotNV*dotNL)
// Cook-Torrance BRDF Model (Specular Reflection) = Distribution * Fresnel * Geometry / (4*dotNV*dotNL)
// Distribution Term - Trowbridge-Reitz GGX
@@ -49,7 +49,7 @@ vec3 ComputeGGX(vec3 specColor, vec3 diffColor, vec3 light, vec3 normal, vec3 ha
// Smith's method:
// Microsurfaces block light rays coming in from the light
// AND block light rays reflecting to the camera.
// Hence, model both seperately and multiply.
// Hence, model both separately and multiply.
float g1vNL = GeometrySchlickGGX(dotNL, k);
float g1vNV = GeometrySchlickGGX(dotNV, k);
float geometry = g1vNL * g1vNV;
+714
View File
@@ -0,0 +1,714 @@
-- Module options:
local always_try_using_lpeg = true
local register_global_module_table = false
local global_module_name = 'json'
--[==[
David Kolf's JSON module for Lua 5.1/5.2
Version 2.5
For the documentation see the corresponding readme.txt or visit
<http://dkolf.de/src/dkjson-lua.fsl/>.
You can contact the author by sending an e-mail to 'david' at the
domain 'dkolf.de'.
Copyright (C) 2010-2014 David Heiko Kolf
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--]==]
-- global dependencies:
local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset =
pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset
local error, require, pcall, select = error, require, pcall, select
local floor, huge = math.floor, math.huge
local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
string.rep, string.gsub, string.sub, string.byte, string.char,
string.find, string.len, string.format
local strmatch = string.match
local concat = table.concat
local json = { version = "dkjson 2.5" }
if register_global_module_table then
_G[global_module_name] = json
end
local _ENV = nil -- blocking globals in Lua 5.2
pcall (function()
-- Enable access to blocked metatables.
-- Don't worry, this module doesn't change anything in them.
local debmeta = require "debug".getmetatable
if debmeta then getmetatable = debmeta end
end)
json.null = setmetatable ({}, {
__tojson = function () return "null" end
})
local function isarray (tbl)
local max, n, arraylen = 0, 0, 0
for k,v in pairs (tbl) do
if k == 'n' and type(v) == 'number' then
arraylen = v
if v > max then
max = v
end
else
if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
return false
end
if k > max then
max = k
end
n = n + 1
end
end
if max > 10 and max > arraylen and max > n * 2 then
return false -- don't create an array with too many holes
end
return true, max
end
local escapecodes = {
["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"
}
local function escapeutf8 (uchar)
local value = escapecodes[uchar]
if value then
return value
end
local a, b, c, d = strbyte (uchar, 1, 4)
a, b, c, d = a or 0, b or 0, c or 0, d or 0
if a <= 0x7f then
value = a
elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
value = (a - 0xc0) * 0x40 + b - 0x80
elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
else
return ""
end
if value <= 0xffff then
return strformat ("\\u%.4x", value)
elseif value <= 0x10ffff then
-- encode as UTF-16 surrogate pair
value = value - 0x10000
local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400)
return strformat ("\\u%.4x\\u%.4x", highsur, lowsur)
else
return ""
end
end
local function fsub (str, pattern, repl)
-- gsub always builds a new string in a buffer, even when no match
-- exists. First using find should be more efficient when most strings
-- don't contain the pattern.
if strfind (str, pattern) then
return gsub (str, pattern, repl)
else
return str
end
end
local function quotestring (value)
-- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8)
if strfind (value, "[\194\216\220\225\226\239]") then
value = fsub (value, "\194[\128-\159\173]", escapeutf8)
value = fsub (value, "\216[\128-\132]", escapeutf8)
value = fsub (value, "\220\143", escapeutf8)
value = fsub (value, "\225\158[\180\181]", escapeutf8)
value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8)
value = fsub (value, "\226\129[\160-\175]", escapeutf8)
value = fsub (value, "\239\187\191", escapeutf8)
value = fsub (value, "\239\191[\176-\191]", escapeutf8)
end
return "\"" .. value .. "\""
end
json.quotestring = quotestring
local function replace(str, o, n)
local i, j = strfind (str, o, 1, true)
if i then
return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1)
else
return str
end
end
-- locale independent num2str and str2num functions
local decpoint, numfilter
local function updatedecpoint ()
decpoint = strmatch(tostring(0.5), "([^05+])")
-- build a filter that can be used to remove group separators
numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+"
end
updatedecpoint()
local function num2str (num)
return replace(fsub(tostring(num), numfilter, ""), decpoint, ".")
end
local function str2num (str)
local num = tonumber(replace(str, ".", decpoint))
if not num then
updatedecpoint()
num = tonumber(replace(str, ".", decpoint))
end
return num
end
local function addnewline2 (level, buffer, buflen)
buffer[buflen+1] = "\n"
buffer[buflen+2] = strrep (" ", level)
buflen = buflen + 2
return buflen
end
function json.addnewline (state)
if state.indent then
state.bufferlen = addnewline2 (state.level or 0,
state.buffer, state.bufferlen or #(state.buffer))
end
end
local encode2 -- forward declaration
local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state)
local kt = type (key)
if kt ~= 'string' and kt ~= 'number' then
return nil, "type '" .. kt .. "' is not supported as a key by JSON."
end
if prev then
buflen = buflen + 1
buffer[buflen] = ","
end
if indent then
buflen = addnewline2 (level, buffer, buflen)
end
buffer[buflen+1] = quotestring (key)
buffer[buflen+2] = ":"
return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state)
end
local function appendcustom(res, buffer, state)
local buflen = state.bufferlen
if type (res) == 'string' then
buflen = buflen + 1
buffer[buflen] = res
end
return buflen
end
local function exception(reason, value, state, buffer, buflen, defaultmessage)
defaultmessage = defaultmessage or reason
local handler = state.exception
if not handler then
return nil, defaultmessage
else
state.bufferlen = buflen
local ret, msg = handler (reason, value, state, defaultmessage)
if not ret then return nil, msg or defaultmessage end
return appendcustom(ret, buffer, state)
end
end
function json.encodeexception(reason, value, state, defaultmessage)
return quotestring("<" .. defaultmessage .. ">")
end
encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state)
local valtype = type (value)
local valmeta = getmetatable (value)
valmeta = type (valmeta) == 'table' and valmeta -- only tables
local valtojson = valmeta and valmeta.__tojson
if valtojson then
if tables[value] then
return exception('reference cycle', value, state, buffer, buflen)
end
tables[value] = true
state.bufferlen = buflen
local ret, msg = valtojson (value, state)
if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end
tables[value] = nil
buflen = appendcustom(ret, buffer, state)
elseif value == nil then
buflen = buflen + 1
buffer[buflen] = "null"
elseif valtype == 'number' then
local s
if value ~= value or value >= huge or -value >= huge then
-- This is the behaviour of the original JSON implementation.
s = "null"
else
s = num2str (value)
end
buflen = buflen + 1
buffer[buflen] = s
elseif valtype == 'boolean' then
buflen = buflen + 1
buffer[buflen] = value and "true" or "false"
elseif valtype == 'string' then
buflen = buflen + 1
buffer[buflen] = quotestring (value)
elseif valtype == 'table' then
if tables[value] then
return exception('reference cycle', value, state, buffer, buflen)
end
tables[value] = true
level = level + 1
local isa, n = isarray (value)
if n == 0 and valmeta and valmeta.__jsontype == 'object' then
isa = false
end
local msg
if isa then -- JSON array
buflen = buflen + 1
buffer[buflen] = "["
for i = 1, n do
buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state)
if not buflen then return nil, msg end
if i < n then
buflen = buflen + 1
buffer[buflen] = ","
end
end
buflen = buflen + 1
buffer[buflen] = "]"
else -- JSON object
local prev = false
buflen = buflen + 1
buffer[buflen] = "{"
local order = valmeta and valmeta.__jsonorder or globalorder
if order then
local used = {}
n = #order
for i = 1, n do
local k = order[i]
local v = value[k]
if v then
used[k] = true
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
prev = true -- add a seperator before the next element
end
end
for k,v in pairs (value) do
if not used[k] then
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
if not buflen then return nil, msg end
prev = true -- add a seperator before the next element
end
end
else -- unordered
for k,v in pairs (value) do
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
if not buflen then return nil, msg end
prev = true -- add a seperator before the next element
end
end
if indent then
buflen = addnewline2 (level - 1, buffer, buflen)
end
buflen = buflen + 1
buffer[buflen] = "}"
end
tables[value] = nil
else
return exception ('unsupported type', value, state, buffer, buflen,
"type '" .. valtype .. "' is not supported by JSON.")
end
return buflen
end
function json.encode (value, state)
state = state or {}
local oldbuffer = state.buffer
local buffer = oldbuffer or {}
state.buffer = buffer
updatedecpoint()
local ret, msg = encode2 (value, state.indent, state.level or 0,
buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state)
if not ret then
error (msg, 2)
elseif oldbuffer == buffer then
state.bufferlen = ret
return true
else
state.bufferlen = nil
state.buffer = nil
return concat (buffer)
end
end
local function loc (str, where)
local line, pos, linepos = 1, 1, 0
while true do
pos = strfind (str, "\n", pos, true)
if pos and pos < where then
line = line + 1
linepos = pos
pos = pos + 1
else
break
end
end
return "line " .. line .. ", column " .. (where - linepos)
end
local function unterminated (str, what, where)
return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where)
end
local function scanwhite (str, pos)
while true do
pos = strfind (str, "%S", pos)
if not pos then return nil end
local sub2 = strsub (str, pos, pos + 1)
if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then
-- UTF-8 Byte Order Mark
pos = pos + 3
elseif sub2 == "//" then
pos = strfind (str, "[\n\r]", pos + 2)
if not pos then return nil end
elseif sub2 == "/*" then
pos = strfind (str, "*/", pos + 2)
if not pos then return nil end
pos = pos + 2
else
return pos
end
end
end
local escapechars = {
["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f",
["n"] = "\n", ["r"] = "\r", ["t"] = "\t"
}
local function unichar (value)
if value < 0 then
return nil
elseif value <= 0x007f then
return strchar (value)
elseif value <= 0x07ff then
return strchar (0xc0 + floor(value/0x40),
0x80 + (floor(value) % 0x40))
elseif value <= 0xffff then
return strchar (0xe0 + floor(value/0x1000),
0x80 + (floor(value/0x40) % 0x40),
0x80 + (floor(value) % 0x40))
elseif value <= 0x10ffff then
return strchar (0xf0 + floor(value/0x40000),
0x80 + (floor(value/0x1000) % 0x40),
0x80 + (floor(value/0x40) % 0x40),
0x80 + (floor(value) % 0x40))
else
return nil
end
end
local function scanstring (str, pos)
local lastpos = pos + 1
local buffer, n = {}, 0
while true do
local nextpos = strfind (str, "[\"\\]", lastpos)
if not nextpos then
return unterminated (str, "string", pos)
end
if nextpos > lastpos then
n = n + 1
buffer[n] = strsub (str, lastpos, nextpos - 1)
end
if strsub (str, nextpos, nextpos) == "\"" then
lastpos = nextpos + 1
break
else
local escchar = strsub (str, nextpos + 1, nextpos + 1)
local value
if escchar == "u" then
value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16)
if value then
local value2
if 0xD800 <= value and value <= 0xDBff then
-- we have the high surrogate of UTF-16. Check if there is a
-- low surrogate escaped nearby to combine them.
if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then
value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16)
if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000
else
value2 = nil -- in case it was out of range for a low surrogate
end
end
end
value = value and unichar (value)
if value then
if value2 then
lastpos = nextpos + 12
else
lastpos = nextpos + 6
end
end
end
end
if not value then
value = escapechars[escchar] or escchar
lastpos = nextpos + 2
end
n = n + 1
buffer[n] = value
end
end
if n == 1 then
return buffer[1], lastpos
elseif n > 1 then
return concat (buffer), lastpos
else
return "", lastpos
end
end
local scanvalue -- forward declaration
local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta)
local len = strlen (str)
local tbl, n = {}, 0
local pos = startpos + 1
if what == 'object' then
setmetatable (tbl, objectmeta)
else
setmetatable (tbl, arraymeta)
end
while true do
pos = scanwhite (str, pos)
if not pos then return unterminated (str, what, startpos) end
local char = strsub (str, pos, pos)
if char == closechar then
return tbl, pos + 1
end
local val1, err
val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
if err then return nil, pos, err end
pos = scanwhite (str, pos)
if not pos then return unterminated (str, what, startpos) end
char = strsub (str, pos, pos)
if char == ":" then
if val1 == nil then
return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")"
end
pos = scanwhite (str, pos + 1)
if not pos then return unterminated (str, what, startpos) end
local val2
val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
if err then return nil, pos, err end
tbl[val1] = val2
pos = scanwhite (str, pos)
if not pos then return unterminated (str, what, startpos) end
char = strsub (str, pos, pos)
else
n = n + 1
tbl[n] = val1
end
if char == "," then
pos = pos + 1
end
end
end
scanvalue = function (str, pos, nullval, objectmeta, arraymeta)
pos = pos or 1
pos = scanwhite (str, pos)
if not pos then
return nil, strlen (str) + 1, "no valid JSON value (reached the end)"
end
local char = strsub (str, pos, pos)
if char == "{" then
return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta)
elseif char == "[" then
return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta)
elseif char == "\"" then
return scanstring (str, pos)
else
local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos)
if pstart then
local number = str2num (strsub (str, pstart, pend))
if number then
return number, pend + 1
end
end
pstart, pend = strfind (str, "^%a%w*", pos)
if pstart then
local name = strsub (str, pstart, pend)
if name == "true" then
return true, pend + 1
elseif name == "false" then
return false, pend + 1
elseif name == "null" then
return nullval, pend + 1
end
end
return nil, pos, "no valid JSON value at " .. loc (str, pos)
end
end
local function optionalmetatables(...)
if select("#", ...) > 0 then
return ...
else
return {__jsontype = 'object'}, {__jsontype = 'array'}
end
end
function json.decode (str, pos, nullval, ...)
local objectmeta, arraymeta = optionalmetatables(...)
return scanvalue (str, pos, nullval, objectmeta, arraymeta)
end
function json.use_lpeg ()
local g = require ("lpeg")
if g.version() == "0.11" then
error "due to a bug in LPeg 0.11, it cannot be used for JSON matching"
end
local pegmatch = g.match
local P, S, R = g.P, g.S, g.R
local function ErrorCall (str, pos, msg, state)
if not state.msg then
state.msg = msg .. " at " .. loc (str, pos)
state.pos = pos
end
return false
end
local function Err (msg)
return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall)
end
local SingleLineComment = P"//" * (1 - S"\n\r")^0
local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/"
local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0
local PlainChar = 1 - S"\"\\\n\r"
local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars
local HexDigit = R("09", "af", "AF")
local function UTF16Surrogate (match, pos, high, low)
high, low = tonumber (high, 16), tonumber (low, 16)
if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then
return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)
else
return false
end
end
local function UTF16BMP (hex)
return unichar (tonumber (hex, 16))
end
local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit))
local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP
local Char = UnicodeEscape + EscapeSequence + PlainChar
local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string")
local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0))
local Fractal = P"." * R"09"^0
local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1
local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num
local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1)
local SimpleValue = Number + String + Constant
local ArrayContent, ObjectContent
-- The functions parsearray and parseobject parse only a single value/pair
-- at a time and store them directly to avoid hitting the LPeg limits.
local function parsearray (str, pos, nullval, state)
local obj, cont
local npos
local t, nt = {}, 0
repeat
obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state)
if not npos then break end
pos = npos
nt = nt + 1
t[nt] = obj
until cont == 'last'
return pos, setmetatable (t, state.arraymeta)
end
local function parseobject (str, pos, nullval, state)
local obj, key, cont
local npos
local t = {}
repeat
key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state)
if not npos then break end
pos = npos
t[key] = obj
until cont == 'last'
return pos, setmetatable (t, state.objectmeta)
end
local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected")
local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected")
local Value = Space * (Array + Object + SimpleValue)
local ExpectedValue = Value + Space * Err "value expected"
ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue)
ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
local DecodeValue = ExpectedValue * g.Cp ()
function json.decode (str, pos, nullval, ...)
local state = {}
state.objectmeta, state.arraymeta = optionalmetatables(...)
local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state)
if state.msg then
return nil, state.pos, state.msg
else
return obj, retpos
end
end
-- use this function only once:
json.use_lpeg = function () return json end
json.using_lpeg = true
return json -- so you can get the module using json = require "dkjson".use_lpeg()
end
if always_try_using_lpeg then
pcall (json.use_lpeg)
end
return json
+29 -4
View File
@@ -1,5 +1,5 @@
#Ship types
#Ship types
$Name: Navbuoy
$Max Debris Speed: 200
$FF Multiplier: 1.0
@@ -12,6 +12,7 @@ $AI:
+Turrets attack this: YES
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
$Vaporize Percent Chance: 0.0
$Name: Sentry Gun
$Counts for Alone: YES
$On Hotkey List: YES
@@ -32,6 +33,7 @@ $AI:
+Turrets attack this: YES
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
$Vaporize Percent Chance: 0.0
$Name: Escape Pod
$Praise Destruction: YES
$On Hotkey List: YES
@@ -50,6 +52,7 @@ $AI:
+Turrets attack this: YES
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
$Vaporize Percent Chance: 0.0
$Name: Cargo
$Scannable: YES
$Max Debris Speed: 200
@@ -63,6 +66,7 @@ $Fog:
$AI:
+Passive docks: ( "cargo" )
$Vaporize Percent Chance: 0.0
$Name: Support
$Counts for Alone: YES
$Praise Destruction: YES
@@ -89,6 +93,7 @@ $AI:
+Active docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
$Vaporize Percent Chance: 0.0
;;WMC - Stealth ships always have another type, so this isn't used
$Name: Stealth
$Counts for Alone: YES
@@ -114,6 +119,7 @@ $AI:
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
$Vaporize Percent Chance: 0.0
$Name: Fighter
$Counts for Alone: YES
$Praise Destruction: YES
@@ -141,6 +147,7 @@ $AI:
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
$Vaporize Percent Chance: 0.0
$Name: Bomber
$Counts for Alone: YES
$Praise Destruction: YES
@@ -168,7 +175,8 @@ $AI:
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
$Vaporize Percent Chance: 0.0
;;WMC - This fighter/bomber type doesn't seem to be used anywhere, because no ship is set as both fighter and bomber
;;WMC - This fighter/bomber type doesn't seem to be used anywhere, because no ship is set as both fighter and bomber
$Name: Fighter/bomber
$Counts for Alone: YES
$Praise Destruction: YES
@@ -195,6 +203,7 @@ $AI:
+Passive docks: ( "support" )
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
$Vaporize Percent Chance: 0.0
$Name: Transport
$Counts for Alone: YES
$Praise Destruction: YES
@@ -204,6 +213,7 @@ $Show Attack Direction: YES
$Max Debris Speed: 150
$FF Multiplier: 1.0
$EMP Multiplier: 2.0
$Warp Sound Range Multiplier: 6.0
$Beams Easily Hit: YES
$Protected on cripple: YES
$Fog:
@@ -223,6 +233,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Freighter
$Counts for Alone: YES
$Praise Destruction: YES
@@ -233,6 +244,7 @@ $Scannable: YES
$Max Debris Speed: 150
$FF Multiplier: 1.0
$EMP Multiplier: 1.75
$Warp Sound Range Multiplier: 6.0
$Beams Easily Hit: YES
$Protected on cripple: YES
$Fog:
@@ -253,6 +265,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: AWACS
$Counts for Alone: YES
$Praise Destruction: YES
@@ -262,6 +275,7 @@ $Show Attack Direction: YES
$Max Debris Speed: 150
$FF Multiplier: 1.0
$EMP Multiplier: 0.8
$Warp Sound Range Multiplier: 6.0
$Beams Easily Hit: YES
$Protected on cripple: YES
$Fog:
@@ -281,6 +295,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Gas Miner
$Counts for Alone: YES
$Praise Destruction: YES
@@ -290,6 +305,7 @@ $Show Attack Direction: YES
$Max Debris Speed: 150
$FF Multiplier: 1.0
$EMP Multiplier: 1.0
$Warp Sound Range Multiplier: 6.0
$Beams Easily Hit: YES
$Protected on cripple: YES
$Fog:
@@ -309,6 +325,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Cruiser
$Counts for Alone: YES
$Praise Destruction: YES
@@ -318,6 +335,7 @@ $Show Attack Direction: YES
$Max Debris Speed: 150
$FF Multiplier: 1.0
$EMP Multiplier: 0.9
$Warp Sound Range Multiplier: 6.0
$Beams Easily Hit: YES
$Protected on cripple: YES
$Fog:
@@ -337,6 +355,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Corvette
$Counts for Alone: YES
$Praise Destruction: YES
@@ -346,6 +365,7 @@ $Show Attack Direction: YES
$Max Debris Speed: 150
$FF Multiplier: 1.0
$EMP Multiplier: 0.3333
$Warp Sound Range Multiplier: 6.0
$Beams Easily Hit: YES
$Protected on cripple: YES
$Fog:
@@ -365,6 +385,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Capital
$Counts for Alone: YES
$Praise Destruction: YES
@@ -394,6 +415,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Super Cap
$Counts for Alone: YES
$Praise Destruction: YES
@@ -421,6 +443,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Drydock
$Counts for Alone: YES
$Praise Destruction: YES
@@ -446,6 +469,7 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
$Name: Knossos Device
$Counts for Alone: YES
$Praise Destruction: YES
@@ -469,4 +493,5 @@ $AI:
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
$Vaporize Percent Chance: 0.0
#End
#End
+1 -1
View File
@@ -311,7 +311,7 @@ static char* GetFilePart(char *source)
SCP_string safe_string;
// Entry point into the main exception handling routine. This routine is put into an except()
// statment at the beginning of a thread and is called anytime that there is a program exception
// statement at the beginning of a thread and is called anytime that there is a program exception
// The data is stored in a file called ErrorLog.txt in the data directory.
//
// data: pointer to the exception data
+36 -29
View File
@@ -48,44 +48,51 @@ bool fireballs_parsed = false;
bool Fireball_use_3d_warp = false;
static auto WarpOption __UNUSED = options::OptionBuilder<bool>("Graphics.3dWarp", "3D Warp", "Use a 3D model for warp effects")
.category("Graphics")
.default_val(true)
.level(options::ExpertLevel::Advanced)
.bind_to(&Fireball_use_3d_warp)
.importance(65)
.finish();
static auto WarpOption __UNUSED = options::OptionBuilder<bool>("Graphics.3dWarp",
std::pair<const char*, int>{"3D Warp", 1770},
std::pair<const char*, int>{"Use a 3D model for warp effects", 1771})
.category("Graphics")
.default_val(true)
.level(options::ExpertLevel::Advanced)
.bind_to(&Fireball_use_3d_warp)
.importance(65)
.finish();
/**
* Play warp in sound for warp effect
*/
void fireball_play_warphole_open_sound(int ship_class, fireball *fb)
{
gamesnd_id sound_index;
float range_multiplier = 1.0f;
object *fireball_objp;
Assert((fb != NULL) && (fb->objnum >= 0));
if((fb == NULL) || (fb->objnum < 0)){
return;
}
fireball_objp = &Objects[fb->objnum];
auto fireball_objp = &Objects[fb->objnum];
auto sound_index = gamesnd_id(GameSounds::WARP_IN);
sound_index = gamesnd_id(GameSounds::WARP_IN);
if(fb->warp_open_sound_index.isValid()) {
if (fb->warp_open_sound_index.isValid()) {
sound_index = fb->warp_open_sound_index;
} else if ((ship_class >= 0) && (ship_class < ship_info_size())) {
if ( Ship_info[ship_class].is_huge_ship() ) {
sound_index = gamesnd_id(GameSounds::CAPITAL_WARP_IN);
fb->flags |= FBF_WARP_CAPITAL_SIZE;
} else if ( Ship_info[ship_class].is_big_ship() ) {
range_multiplier = 6.0f;
fb->flags |= FBF_WARP_CRUISER_SIZE;
}
}
snd_play_3d(gamesnd_get_game_sound(sound_index), &fireball_objp->pos, &Eye_position, fireball_objp->radius, NULL, 0, 1.0f, SND_PRIORITY_DOUBLE_INSTANCE, NULL, range_multiplier); // play warp sound effect
if (ship_class >= 0 && ship_class < ship_info_size()) {
if (Ship_info[ship_class].is_huge_ship()) {
fb->flags |= FBF_WARP_CAPITAL_SIZE;
} else if (Ship_info[ship_class].is_big_ship()) {
fb->flags |= FBF_WARP_CRUISER_SIZE;
}
// originally 6.0 for SIF_BIG_SHIP and 1.0 for everything else
if (Ship_info[ship_class].class_type >= 0)
fb->warp_sound_range_multiplier = Ship_types[Ship_info[ship_class].class_type].warp_sound_range_multiplier;
else
fb->warp_sound_range_multiplier = 1.0f;
}
snd_play_3d(gamesnd_get_game_sound(sound_index), &fireball_objp->pos, &Eye_position, fireball_objp->radius, NULL, 0, 1.0f, SND_PRIORITY_DOUBLE_INSTANCE, NULL, fb->warp_sound_range_multiplier); // play warp sound effect
}
/**
@@ -93,15 +100,14 @@ void fireball_play_warphole_open_sound(int ship_class, fireball *fb)
*/
void fireball_play_warphole_close_sound(fireball *fb)
{
gamesnd_id sound_index;
Assert((fb != NULL) && (fb->objnum >= 0));
if((fb == NULL) || (fb->objnum < 0)){
return;
}
auto fireball_objp = &Objects[fb->objnum];
auto sound_index = gamesnd_id(GameSounds::WARP_OUT);
object *fireball_objp;
fireball_objp = &Objects[fb->objnum];
sound_index = gamesnd_id(GameSounds::WARP_OUT);
if ( fb->warp_close_sound_index.isValid() ) {
if (fb->warp_close_sound_index.isValid()) {
sound_index = fb->warp_close_sound_index;
} else if ( fb->flags & FBF_WARP_CAPITAL_SIZE ) {
sound_index = gamesnd_id(GameSounds::CAPITAL_WARP_OUT);
@@ -109,7 +115,7 @@ void fireball_play_warphole_close_sound(fireball *fb)
return;
}
snd_play_3d(gamesnd_get_game_sound(sound_index), &fireball_objp->pos, &Eye_position, fireball_objp->radius); // play warp sound effect
snd_play_3d(gamesnd_get_game_sound(sound_index), &fireball_objp->pos, &Eye_position, fireball_objp->radius, NULL, 0, 1.0F, SND_PRIORITY_SINGLE_INSTANCE, NULL, fb->warp_sound_range_multiplier); // play warp sound effect
}
static void fireball_generate_unique_id(char *unique_id, int buffer_len, int fireball_index)
@@ -845,6 +851,7 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
new_fireball->warp_close_sound_index = warp_close_sound;
new_fireball->warp_open_duration = (warp_open_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_open_duration;
new_fireball->warp_close_duration = (warp_close_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_close_duration;
new_fireball->warp_sound_range_multiplier = 1.0f;
matrix orient;
if(orient_override != NULL){
+3
View File
@@ -92,6 +92,9 @@ typedef struct fireball {
gamesnd_id warp_close_sound_index;
float warp_open_duration;
float warp_close_duration;
// the sound multiplier is based on the ship class, but we lose that while the warp is closing
float warp_sound_range_multiplier;
} fireball;
// end move
+9 -7
View File
@@ -24,13 +24,15 @@
bool Fireball_warp_flash = false;
static auto WarpFlashOption __UNUSED = options::OptionBuilder<bool>("Graphics.WarpFlash", "Warp Flash", "Show flash upon warp open or close")
.category("Graphics")
.default_val(true)
.level(options::ExpertLevel::Advanced)
.bind_to_once(&Fireball_warp_flash)
.importance(65)
.finish();
static auto WarpFlashOption __UNUSED = options::OptionBuilder<bool>("Graphics.WarpFlash",
std::pair<const char*, int>{"Warp Flash", 1768},
std::pair<const char*, int>{"Show flash upon warp open or close", 1769})
.category("Graphics")
.default_val(true)
.level(options::ExpertLevel::Advanced)
.bind_to_once(&Fireball_warp_flash)
.importance(65)
.finish();
void warpin_batch_draw_face( int texture, vertex *v1, vertex *v2, vertex *v3 )
{
+3
View File
@@ -16,6 +16,7 @@
#include "gamesnd/gamesnd.h"
#include "globalincs/alphacolors.h"
#include "io/key.h"
#include "mission/missionmessage.h"
#include "missionui/missionscreencommon.h"
#include "sound/audiostr.h"
#include "ui/ui.h"
@@ -516,6 +517,7 @@ void gameplay_help_leave()
// unpause all game sounds
weapon_unpause_sounds();
audiostream_unpause_all();
message_resume_all();
gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
game_flush();
@@ -631,6 +633,7 @@ void gameplay_help_do_frame(float /*frametime*/)
// make sure game sounds are paused
weapon_pause_sounds();
audiostream_pause_all();
message_pause_all();
k = Ui_window.process() & ~KEY_DEBUGGED;
gameplay_help_process_key(k);
+12 -7
View File
@@ -45,13 +45,15 @@ static bool music_volume_change_listener(float new_val, bool /*initial*/)
return true;
}
static auto MusicVolumeOption __UNUSED = options::OptionBuilder<float>("Audio.Music", "Music", "Volume used for playing music")
.category("Audio")
.default_val(Default_music_volume)
.range(0.0f, 1.0f)
.change_listener(music_volume_change_listener)
.importance(1)
.finish();
static auto MusicVolumeOption __UNUSED = options::OptionBuilder<float>("Audio.Music",
std::pair<const char*, int>{"Music", 1371},
std::pair<const char*, int>{"Volume used for playing music", 1760})
.category("Audio")
.default_val(Default_music_volume)
.range(0.0f, 1.0f)
.change_listener(music_volume_change_listener)
.importance(1)
.finish();
typedef struct tagSNDPATTERN {
int default_next_pattern; // Needed so the next_pattern member can be reset
@@ -604,7 +606,10 @@ void event_music_level_start(int force_soundtrack)
// Check that soundtrack index is in range, except that < 0 means no soundtrack
if (Current_soundtrack_num < 0)
{
Pattern_timer_id = TIMESTAMP::never(); // update the timestamp as if a soundtrack did start
return;
}
Assert(Current_soundtrack_num < (int)Soundtracks.size());
if (Current_soundtrack_num >= (int)Soundtracks.size())
return;
+74 -42
View File
@@ -245,57 +245,89 @@ detail_levels Detail_defaults[NUM_DEFAULT_DETAIL_LEVELS] = {
// Global used to access detail levels in game and libs
detail_levels Detail = Detail_defaults[NUM_DEFAULT_DETAIL_LEVELS - 1];
const SCP_vector<std::pair<int, SCP_string>> DetailLevelValues = {{ 0, "Minimum" },
{ 1, "Low" },
{ 2, "Medium" },
{ 3, "High" },
{ 4, "Ultra" }, };
const SCP_vector<std::pair<int, std::pair<const char*, int>>> DetailLevelValues = {{ 0, {"Minimum", 1680}},
{ 1, {"Low", 1161}},
{ 2, {"Medium", 1162}},
{ 3, {"High", 1163}},
{ 4, {"Ultra", 1721}}};
const auto ModelDetailOption __UNUSED =
options::OptionBuilder<int>("Graphics.Detail", "Model Detail", "Detail level of models").importance(8).category(
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
Detail.detail_distance = val;
return true;
}).finish();
const auto ModelDetailOption __UNUSED = options::OptionBuilder<int>("Graphics.Detail",
std::pair<const char*, int>{"Model Detail", 1739},
std::pair<const char*, int>{"Detail level of models", 1740})
.importance(8)
.category("Graphics")
.values(DetailLevelValues)
.default_val(MAX_DETAIL_LEVEL)
.change_listener([](int val, bool) {
Detail.detail_distance = val;
return true;
})
.finish();
const auto TexturesOption __UNUSED = options::OptionBuilder<int>("Graphics.Texture",
"3D Hardware Textures",
"Level of detail of textures").importance(6).category("Graphics").values(
DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
Detail.hardware_textures = val;
return true;
}).finish();
std::pair<const char*, int>{"3D Hardware Textures", 1362},
std::pair<const char*, int>{"Level of detail of textures", 1720})
.importance(6)
.category("Graphics")
.values(DetailLevelValues)
.default_val(MAX_DETAIL_LEVEL)
.change_listener([](int val, bool) {
Detail.hardware_textures = val;
return true;
})
.finish();
const auto ParticlesOption __UNUSED = options::OptionBuilder<int>("Graphics.Particles",
"Particles",
"Level of detail for particles").importance(5).category(
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
Detail.num_particles = val;
return true;
}).finish();
std::pair<const char*, int>{"Particles", 1363},
std::pair<const char*, int>{"Level of detail for particles", 1717})
.importance(5)
.category("Graphics")
.values(DetailLevelValues)
.default_val(MAX_DETAIL_LEVEL)
.change_listener([](int val, bool) {
Detail.num_particles = val;
return true;
})
.finish();
const auto SmallDebrisOption __UNUSED =
options::OptionBuilder<int>("Graphics.SmallDebris", "Impact Effects", "Level of detail of impact effects").category(
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).importance(4).change_listener([](int val,
bool) {
Detail.num_small_debris = val;
return true;
}).finish();
const auto SmallDebrisOption __UNUSED = options::OptionBuilder<int>("Graphics.SmallDebris",
std::pair<const char*, int>{"Impact Effects", 1364},
std::pair<const char*, int>{"Level of detail of impact effects", 1743})
.category("Graphics")
.values(DetailLevelValues)
.default_val(MAX_DETAIL_LEVEL)
.importance(4)
.change_listener([](int val,bool) {
Detail.num_small_debris = val;
return true;
})
.finish();
const auto ShieldEffectsOption __UNUSED = options::OptionBuilder<int>("Graphics.ShieldEffects",
"Shield Hit Effects",
"Level of detail of shield impacts").importance(3).category(
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
Detail.shield_effects = val;
return true;
}).finish();
std::pair<const char*, int>{"Shield Hit Effects", 1718},
std::pair<const char*, int>{"Level of detail of shield impacts", 1719})
.importance(3)
.category("Graphics")
.values(DetailLevelValues)
.default_val(MAX_DETAIL_LEVEL)
.change_listener([](int val, bool) {
Detail.shield_effects = val;
return true;
})
.finish();
const auto StarsOption __UNUSED =
options::OptionBuilder<int>("Graphics.Stars", "Stars", "Number of stars in the mission").importance(2).category(
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
Detail.num_stars = val;
return true;
}).finish();
const auto StarsOption __UNUSED = options::OptionBuilder<int>("Graphics.Stars",
std::pair<const char*, int>{"Stars", 1366},
std::pair<const char*, int>{"Number of stars in the mission", 1698})
.importance(2)
.category("Graphics")
.values(DetailLevelValues)
.default_val(MAX_DETAIL_LEVEL)
.change_listener([](int val, bool) {
Detail.num_stars = val;
return true;
})
.finish();
// Call this with:
// 0 - lowest
+48 -32
View File
@@ -53,41 +53,10 @@ version parse_version() {
return v;
}
version parse_version_inline() {
version v;
SCP_string input;
stuff_string(input, F_RAW);
size_t start_pos = 0;
size_t end_pos = input.find('.');
v.major = std::atoi(input.substr(start_pos, end_pos - start_pos).c_str());
if (end_pos == SCP_string::npos)
return v;
start_pos = end_pos + 1;
end_pos = input.find('.', start_pos);
v.minor = std::atoi(input.substr(start_pos, end_pos - start_pos).c_str());
if (end_pos == SCP_string::npos)
return v;
start_pos = end_pos + 1;
end_pos = input.find('.', start_pos);
v.build = std::atoi(input.substr(start_pos, end_pos - start_pos).c_str());
if (end_pos == SCP_string::npos)
return v;
start_pos = end_pos + 1;
end_pos = input.find('.', start_pos);
v.revision = std::atoi(input.substr(start_pos, end_pos - start_pos).c_str());
return v;
return version(std::move(input), 0);
}
version get_executable_version() {
version v;
@@ -102,6 +71,53 @@ version get_executable_version() {
version::version(int major_in, int minor_in, int build_in, int revision_in) :
major(major_in), minor(minor_in), build(build_in), revision(revision_in) {
}
version::version(const SCP_string& semver, int missing) : major(missing), minor(missing), build(missing), revision(missing) {
int i = 0;
auto str = semver;
int has_fields = 0;
while (i <= 4) {
size_t pos = str.find_first_of('.');
SCP_string ver;
if (pos != SCP_string::npos) {
ver = str.substr(0, pos);
str.erase(0, pos + 1);
}
else {
ver = str;
str.clear();
}
if (!ver.empty() && std::find_if(ver.begin(), ver.end(), [](char c) { return !std::isdigit(c, SCP_default_locale); }) == ver.end()) {
switch (has_fields) {
case 0:
major = std::stoi(ver.c_str());
++has_fields;
break;
case 1:
minor = std::stoi(ver.c_str());
++has_fields;
break;
case 2:
build = std::stoi(ver.c_str());
++has_fields;
break;
case 3:
revision = std::stoi(ver.c_str());
++has_fields;
break;
default:
UNREACHABLE("Version parsing broken, get a coder!");
break;
}
}
else
break; //Break out of the loop if the first string is not a digit.
i++;
}
}
bool version::isValid() const { return major != 0 || minor != 0 || build != 0 || revision != 0; }
bool version::operator<(const version& v) const {
+2
View File
@@ -57,6 +57,8 @@ struct version {
version(int major, int minor = 0, int build = 0, int revision = 0);
version(const SCP_string& semver, int missing = -1);
bool isValid() const;
bool operator<(const version& other) const;
+138 -128
View File
@@ -112,35 +112,38 @@ static bool gamma_change_listener(float new_val, bool initial)
return true;
}
static auto GammaOption __UNUSED =
options::OptionBuilder<float>("Graphics.Gamma", "Brightness", "The brighness value used for the game window")
.category("Graphics")
.default_val(1.0f)
.enumerator(gamma_value_enumerator)
.display(gamma_display)
.change_listener(gamma_change_listener)
.finish();
static auto GammaOption __UNUSED = options::OptionBuilder<float>("Graphics.Gamma",
std::pair<const char*, int>{"Brightness", 1375},
std::pair<const char*, int>{"The brightness value used for the game window", 1738})
.category("Graphics")
.default_val(1.0f)
.enumerator(gamma_value_enumerator)
.display(gamma_display)
.change_listener(gamma_change_listener)
.finish();
const SCP_vector<std::pair<int, SCP_string>> DetailLevelValues = {{ 0, "Minimum" },
{ 1, "Low" },
{ 2, "Medium" },
{ 3, "High" },
{ 4, "Ultra" }, };
const SCP_vector<std::pair<int, std::pair<const char*, int>>> DetailLevelValues = {{ 0, {"Minimum", 1680}},
{ 1, {"Low", 1161}},
{ 2, {"Medium", 1162}},
{ 3, {"High", 1163}},
{ 4, {"Ultra", 1721}}};
const auto LightingOption __UNUSED = options::OptionBuilder<int>("Graphics.Lighting", "Lighting", "Level of detail of the lighting")
.importance(1)
.category("Graphics")
.values(DetailLevelValues)
.default_val(MAX_DETAIL_LEVEL)
.change_listener([](int val, bool initial) {
Detail.lighting = val;
if (!initial) {
gr_recompile_all_shaders(nullptr);
}
return true;
})
.finish();
const auto LightingOption __UNUSED = options::OptionBuilder<int>("Graphics.Lighting",
std::pair<const char*, int>{"Lighting", 1367},
std::pair<const char*, int>{"Level of detail of the lighting", 1715})
.importance(1)
.category("Graphics")
.values(DetailLevelValues)
.default_val(MAX_DETAIL_LEVEL)
.change_listener([](int val, bool initial) {
Detail.lighting = val;
if (!initial) {
gr_recompile_all_shaders(nullptr);
}
return true;
})
.finish();
os::ViewportState Gr_configured_window_state = os::ViewportState::Windowed;
@@ -162,17 +165,18 @@ static bool mode_change_func(os::ViewportState state, bool initial)
return true;
}
static auto WindowModeOption __UNUSED = options::OptionBuilder<os::ViewportState>("Graphics.WindowMode", "Window Mode",
"Controls how the game window is created.")
.category("Graphics")
.level(options::ExpertLevel::Beginner)
.values({{os::ViewportState::Fullscreen, "Fullscreen"},
{os::ViewportState::Borderless, "Borderless"},
{os::ViewportState::Windowed, "Windowed"}})
.importance(98)
.default_val(os::ViewportState::Fullscreen)
.change_listener(mode_change_func)
.finish();
static auto WindowModeOption __UNUSED = options::OptionBuilder<os::ViewportState>("Graphics.WindowMode",
std::pair<const char*, int>{"Window Mode", 1772},
std::pair<const char*, int>{"Controls how the game window is created", 1773})
.category("Graphics")
.level(options::ExpertLevel::Beginner)
.values({{os::ViewportState::Fullscreen, {"Fullscreen", 1679}},
{os::ViewportState::Borderless, {"Borderless", 1675}},
{os::ViewportState::Windowed, {"Windowed", 1676}}})
.importance(98)
.default_val(os::ViewportState::Fullscreen)
.change_listener(mode_change_func)
.finish();
const std::shared_ptr<scripting::OverridableHook<>> OnFrameHook = scripting::OverridableHook<>::Factory(
"On Frame", "Called every frame as the last action before showing the frame result to the user.", {}, tl::nullopt, CHA_ONFRAME);
@@ -188,6 +192,7 @@ int gr_stencil_mode = 0;
// Default clipping distances
const float Default_min_draw_distance = 1.0f;
const float Default_max_draw_distance = 1e10;
float Min_draw_distance_cockpit = 0.02f;
float Min_draw_distance = Default_min_draw_distance;
float Max_draw_distance = Default_max_draw_distance;
@@ -251,18 +256,19 @@ static bool videodisplay_change(int display, bool initial)
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(display), SDL_WINDOWPOS_CENTERED_DISPLAY(display));
return true;
}
static auto VideoDisplayOption =
options::OptionBuilder<int>("Graphics.Display", "Primary display", "The display used for rendering.")
.category("Graphics")
.level(options::ExpertLevel::Beginner)
.deserializer(videodisplay_deserializer)
.serializer(videodisplay_serializer)
.enumerator(videodisplay_enumerator)
.display(videodisplay_display)
.default_val(0)
.change_listener(videodisplay_change)
.importance(99)
.finish();
static auto VideoDisplayOption = options::OptionBuilder<int>("Graphics.Display",
std::pair<const char*, int>{"Primary display", 1741},
std::pair<const char*, int>{"The display used for rendering", 1742})
.category("Graphics")
.level(options::ExpertLevel::Beginner)
.deserializer(videodisplay_deserializer)
.serializer(videodisplay_serializer)
.enumerator(videodisplay_enumerator)
.display(videodisplay_display)
.default_val(0)
.change_listener(videodisplay_change)
.importance(99)
.finish();
struct ResolutionInfo {
uint32_t width = 0;
@@ -359,80 +365,83 @@ static bool resolution_change(const ResolutionInfo& /*info*/, bool initial)
}
*/
}
static auto ResolutionOption =
options::OptionBuilder<ResolutionInfo>("Graphics.Resolution", "Resolution", "The rendering resolution.")
.category("Graphics")
.level(options::ExpertLevel::Beginner)
.deserializer(resolution_deserializer)
.serializer(resolution_serializer)
.enumerator(resolution_enumerator)
.display(resolution_display)
.default_func(resolution_default)
.change_listener(resolution_change)
.importance(100)
.finish();
static auto ResolutionOption = options::OptionBuilder<ResolutionInfo>("Graphics.Resolution",
std::pair<const char*, int>{"Resolution", 1748},
std::pair<const char*, int>{"The rendering resolution", 1749})
.category("Graphics")
.level(options::ExpertLevel::Beginner)
.deserializer(resolution_deserializer)
.serializer(resolution_serializer)
.enumerator(resolution_enumerator)
.display(resolution_display)
.default_func(resolution_default)
.change_listener(resolution_change)
.importance(100)
.finish();
bool Gr_enable_soft_particles = false;
static auto SoftParticlesOption __UNUSED = options::OptionBuilder<bool>("Graphics.SoftParticles", "Soft Particles",
"Enable or disable soft particle rendering.")
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.default_val(true)
.bind_to_once(&Gr_enable_soft_particles)
.importance(68)
.finish();
static auto SoftParticlesOption __UNUSED = options::OptionBuilder<bool>("Graphics.SoftParticles",
std::pair<const char*, int>{"Soft Particles", 1761},
std::pair<const char*, int>{"Enable or disable soft particle rendering", 1762})
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.default_val(true)
.bind_to_once(&Gr_enable_soft_particles)
.importance(68)
.finish();
flagset<FramebufferEffects> Gr_framebuffer_effects;
static auto FramebufferEffectsOption __UNUSED =
options::OptionBuilder<flagset<FramebufferEffects>>(
"Graphics.FramebufferEffects", "Framebuffer effects",
"Controls which framebuffer effects will be applied to the scene.")
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.values({{{}, "None"},
{{FramebufferEffects::Shockwaves}, "Shockwaves"},
{{FramebufferEffects::Thrusters}, "Thrusters"},
{{FramebufferEffects::Shockwaves, FramebufferEffects::Thrusters}, "All"}})
.default_val({FramebufferEffects::Shockwaves, FramebufferEffects::Thrusters})
.bind_to_once(&Gr_framebuffer_effects)
.importance(77)
.finish();
static auto FramebufferEffectsOption __UNUSED = options::OptionBuilder<flagset<FramebufferEffects>>("Graphics.FramebufferEffects",
std::pair<const char*, int>{"Framebuffer effects", 1732},
std::pair<const char*, int>{"Controls which framebuffer effects will be applied to the scene", 1733})
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.values({{{}, {"None", 211}},
{{FramebufferEffects::Shockwaves}, {"Shockwaves", 1688}},
{{FramebufferEffects::Thrusters}, {"Thrusters", 1689}},
{{FramebufferEffects::Shockwaves, FramebufferEffects::Thrusters}, {"All", 1690}}})
.default_val({FramebufferEffects::Shockwaves, FramebufferEffects::Thrusters})
.bind_to_once(&Gr_framebuffer_effects)
.importance(77)
.finish();
AntiAliasMode Gr_aa_mode = AntiAliasMode::None;
AntiAliasMode Gr_aa_mode_last_frame = AntiAliasMode::None;
static auto AAOption __UNUSED = options::OptionBuilder<AntiAliasMode>("Graphics.AAMode", "Anti Aliasing",
"Controls the anti aliasing mode of the engine.")
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.values({{AntiAliasMode::None, "None"},
{AntiAliasMode::FXAA_Low, "FXAA Low"},
{AntiAliasMode::FXAA_Medium, "FXAA Medium"},
{AntiAliasMode::FXAA_High, "FXAA High"},
{AntiAliasMode::SMAA_Low, "SMAA Low"},
{AntiAliasMode::SMAA_Medium, "SMAA Medium"},
{AntiAliasMode::SMAA_High, "SMAA High"},
{AntiAliasMode::SMAA_Ultra, "SMAA Ultra"}})
.default_val(AntiAliasMode::None)
.bind_to(&Gr_aa_mode)
.importance(79)
.finish();
static auto AAOption __UNUSED = options::OptionBuilder<AntiAliasMode>("Graphics.AAMode",
std::pair<const char*, int>{"Anti Aliasing", 1752},
std::pair<const char*, int>{"Controls the anti aliasing mode of the engine.", 1753})
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.values({{AntiAliasMode::None, {"None", 211}},
{AntiAliasMode::FXAA_Low, {"FXAA Low", 1681}},
{AntiAliasMode::FXAA_Medium, {"FXAA Medium", 1682}},
{AntiAliasMode::FXAA_High, {"FXAA High", 1683}},
{AntiAliasMode::SMAA_Low, {"SMAA Low", 1684}},
{AntiAliasMode::SMAA_Medium, {"SMAA Medium", 1685}},
{AntiAliasMode::SMAA_High, {"SMAA High", 1686}},
{AntiAliasMode::SMAA_Ultra, {"SMAA Ultra", 1687}}})
.default_val(AntiAliasMode::None)
.bind_to(&Gr_aa_mode)
.importance(79)
.finish();
extern int Cmdline_msaa_enabled;
static auto MSAAOption __UNUSED = options::OptionBuilder<int>("Graphics.MSAASamples", "Multisample Anti Aliasing",
"Controls whether multisample anti aliasing is enabled, and with how many samples.")
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.values({{0, "Off"},
{4, "4 Samples"},
{8, "8 Samples"},
{16, "16 Samples"}})
.default_val(0)
.bind_to(&Cmdline_msaa_enabled)
.importance(78)
.finish();
static auto MSAAOption __UNUSED = options::OptionBuilder<int>("Graphics.MSAASamples",
std::pair<const char*, int>{"Multisample Anti Aliasing", 1758},
std::pair<const char*, int>{"Controls whether multisample anti asliasing is enabled, and with how many samples", 1759})
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.values({{0, {"Off", 1693}},
{4, {"4 Samples", 1694}},
{8, {"8 Samples", 1695}},
{16, {"16 Samples", 1696}}})
.default_val(0)
.bind_to_once(&Cmdline_msaa_enabled)
.importance(78)
.finish();
bool gr_is_fxaa_mode(AntiAliasMode mode)
{
@@ -444,26 +453,27 @@ bool gr_is_smaa_mode(AntiAliasMode mode) {
bool Gr_post_processing_enabled = true;
static auto PostProcessOption __UNUSED =
options::OptionBuilder<bool>("Graphics.PostProcessing", "Post processing",
"Controls whether post processing is enabled in the engine")
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.default_val(false)
.bind_to_once(&Gr_post_processing_enabled)
.importance(69)
.finish();
static auto PostProcessOption __UNUSED = options::OptionBuilder<bool>("Graphics.PostProcessing",
std::pair<const char*, int>{"Post processing", 1726},
std::pair<const char*, int>{"Controls whether post processing is enabled in the engine.", 1727})
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.default_val(false)
.bind_to_once(&Gr_post_processing_enabled)
.importance(69)
.finish();
bool Gr_enable_vsync = true;
static auto VSyncOption __UNUSED = options::OptionBuilder<bool>("Graphics.VSync", "Vertical Sync",
"Controls how the engine does vertical synchronization")
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.default_val(true)
.bind_to_once(&Gr_enable_vsync)
.importance(70)
.finish();
static auto VSyncOption __UNUSED = options::OptionBuilder<bool>("Graphics.VSync",
std::pair<const char*, int>{"Vertical Sync", 1766},
std::pair<const char*, int>{"Controls how the engine does vertical synchronization", 1767})
.category("Graphics")
.level(options::ExpertLevel::Advanced)
.default_val(true)
.bind_to_once(&Gr_enable_vsync)
.importance(70)
.finish();
static std::unique_ptr<graphics::util::UniformBufferManager> UniformBufferManager;
+4 -3
View File
@@ -36,6 +36,7 @@ class OverridableHook;
extern const float Default_min_draw_distance;
extern const float Default_max_draw_distance;
extern float Min_draw_distance_cockpit;
extern float Min_draw_distance;
extern float Max_draw_distance;
extern int Gr_inited;
@@ -102,7 +103,7 @@ public:
Stack.push_back(Current_transform);
}
matrix4 &get_transform()
const matrix4 &get_transform() const
{
return Current_transform;
}
@@ -115,7 +116,7 @@ public:
Stack.push_back(Current_transform);
}
void push_and_replace(matrix4 new_transform)
void push_and_replace(const matrix4 &new_transform)
{
Current_transform = new_transform;
Stack.push_back(Current_transform);
@@ -300,7 +301,7 @@ public:
void add_vertex_component(vertex_format_data::vertex_format format_type, size_t stride, size_t offset);
size_t get_vertex_stride() { return Vertex_stride; }
size_t get_vertex_stride() const { return Vertex_stride; }
bool operator==(const vertex_layout& other) const;
+37
View File
@@ -734,6 +734,43 @@ void generic_anim_render(generic_anim *ga, float frametime, int x, int y, bool m
}
}
void generic_anim_render_ex(generic_anim* ga, float frametime, int x, int y, int w, int h, bool menu, const generic_extras* ge)
{
if ((ge != nullptr) && (ga->use_hud_color == true)) {
Warning(LOCATION, "Monochrome generic anims can't use extra info (yet)");
return;
}
float a = 1.0f;
if (ge != nullptr) {
a = ge->alpha;
}
if (ga->type == BM_TYPE_PNG) {
generic_anim_render_variable_frame_delay(ga, frametime, a);
}
else {
generic_anim_render_fixed_frame_delay(ga, frametime, a);
}
if (ga->num_frames > 0) {
ga->previous_frame = ga->current_frame;
if (ga->use_hud_color) {
gr_aabitmap_ex(x, y, w, h, 0, 0, (menu ? GR_RESIZE_MENU : GR_RESIZE_FULL));
}
else {
if (ge == nullptr) {
gr_bitmap_ex(x, y, w, h, 0, 0, (menu ? GR_RESIZE_MENU : GR_RESIZE_FULL));
}
else if (ge->draw == true) {
// currently only for lua streaminganim objects
// and don't draw them unless requested...
gr_bitmap_uv(x, y, w, h, ge->u0, ge->v0, ge->u1, ge->v1, ge->resize_mode);
}
}
}
}
/*
* @brief reset an animation back to the start
*
+1
View File
@@ -91,5 +91,6 @@ int generic_anim_stream(generic_anim *ga, const bool cache = true);
int generic_bitmap_load(generic_bitmap *gb);
void generic_anim_unload(generic_anim *ga);
void generic_anim_render(generic_anim *ga, float frametime, int x, int y, bool menu = false, const generic_extras *ge = nullptr);
void generic_anim_render_ex(generic_anim* ga, float frametime, int x, int y, int w, int h, bool menu = false, const generic_extras* ge = nullptr);
void generic_anim_reset(generic_anim *ga);
#endif
+1 -1
View File
@@ -441,7 +441,7 @@ void material::set_color(int r, int g, int b, int a)
Clr.xyzw.w = i2fl(a) / 255.0f;
}
void material::set_color(color &clr_in)
void material::set_color(const color &clr_in)
{
if ( clr_in.is_alphacolor ) {
Clr.xyzw.x = i2fl(clr_in.red) / 255.0f;
+1 -1
View File
@@ -133,7 +133,7 @@ public:
void set_color(float red, float green, float blue, float alpha);
void set_color(int r, int g, int b, int a);
void set_color(color &clr_in);
void set_color(const color &clr_in);
const vec4& get_color() const;
void set_color_scale(float scale);
+27 -10
View File
@@ -16,6 +16,8 @@ matrix4 gr_texture_matrix;
matrix4 gr_env_texture_matrix;
float gr_near_plane;
bool gr_htl_projection_matrix_set = false;
static int modelview_matrix_depth = 1;
@@ -116,28 +118,43 @@ void gr_end_instance_matrix()
}
// the projection matrix; fov, aspect ratio, near, far
void gr_set_proj_matrix(float fov, float aspect, float z_near, float z_far) {
void gr_set_proj_matrix(fov_t fov, float aspect, float z_near, float z_far) {
if (gr_screen.rendering_to_texture != -1) {
gr_set_viewport(gr_screen.offset_x, gr_screen.offset_y, gr_screen.clip_width, gr_screen.clip_height);
} else {
gr_set_viewport(gr_screen.offset_x, (gr_screen.max_h - gr_screen.offset_y - gr_screen.clip_height), gr_screen.clip_width, gr_screen.clip_height);
}
float clip_width, clip_height;
clip_height = tan( fov * 0.5f ) * z_near;
clip_width = clip_height * aspect;
gr_last_projection_matrix = gr_projection_matrix;
if (gr_screen.rendering_to_texture != -1) {
create_perspective_projection_matrix(&gr_projection_matrix, -clip_width, clip_width, clip_height, -clip_height, z_near, z_far);
} else {
create_perspective_projection_matrix(&gr_projection_matrix, -clip_width, clip_width, -clip_height, clip_height, z_near, z_far);
if (mpark::holds_alternative<float>(fov)) {
float clip_width, clip_height;
clip_height = tan(mpark::get<float>(fov) * 0.5f) * z_near;
clip_width = clip_height * aspect;
if (gr_screen.rendering_to_texture != -1) {
create_perspective_projection_matrix(&gr_projection_matrix, -clip_width, clip_width, clip_height, -clip_height, z_near, z_far);
} else {
create_perspective_projection_matrix(&gr_projection_matrix, -clip_width, clip_width, -clip_height, clip_height, z_near, z_far);
}
}
else {
const auto& afov = mpark::get<asymmetric_fov>(fov);
float clip_l = tanf(afov.left) * z_near;
float clip_r = tanf(afov.right) * z_near;
float clip_u = tanf(afov.up) * z_near;
float clip_d = tanf(afov.down) * z_near;
if (gr_screen.rendering_to_texture != -1) {
create_perspective_projection_matrix(&gr_projection_matrix, clip_l, clip_r, clip_u, clip_d, z_near, z_far);
}
else {
create_perspective_projection_matrix(&gr_projection_matrix, clip_l, clip_r, clip_d, clip_u, z_near, z_far);
}
}
gr_htl_projection_matrix_set = true;
matrix_uniform_up_to_date = false;
gr_near_plane = z_near;
}
void gr_end_proj_matrix() {
+3 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "graphics/2d.h"
#include "render/3d.h"
extern transform_stack gr_model_matrix_stack;
extern matrix4 gr_view_matrix;
@@ -8,6 +9,7 @@ extern matrix4 gr_model_view_matrix;
extern matrix4 gr_projection_matrix;
extern matrix4 gr_last_projection_matrix;
extern matrix4 gr_env_texture_matrix;
extern float gr_near_plane;
void gr_matrix_on_frame();
@@ -15,7 +17,7 @@ void gr_start_instance_matrix(const vec3d* offset, const matrix* rotation);
void gr_start_angles_instance_matrix(const vec3d* pos, const angles* rotation);
void gr_end_instance_matrix();
void gr_set_proj_matrix(float fov, float aspect, float z_near, float z_far);
void gr_set_proj_matrix(fov_t fov, float aspect, float z_near, float z_far);
void gr_end_proj_matrix();
void gr_set_view_matrix(const vec3d* pos, const matrix* orient);
+6 -2
View File
@@ -27,6 +27,7 @@
#include "graphics/2d.h"
#include "graphics/matrix.h"
#include "libs/renderdoc/renderdoc.h"
#include "lighting/lighting.h"
#include "math/floating.h"
#include "model/model.h"
#include "options/Option.h"
@@ -861,6 +862,9 @@ int opengl_init_display_device()
}
}
if (Cmdline_capture_mouse)
attrs.flags.set(os::ViewPortFlags::Capture_Mouse);
auto viewport = gr_opengl_create_viewport(attrs);
if (!viewport) {
return 1;
@@ -1177,7 +1181,7 @@ static void init_extensions() {
// we require a minimum GLSL version
if (GLSL_version < MIN_REQUIRED_GLSL_VERSION) {
Error(LOCATION, "Current GL Shading Langauge Version of %d is less than the required version of %d. Switch video modes or update your drivers.", GLSL_version, MIN_REQUIRED_GLSL_VERSION);
Error(LOCATION, "Current GL Shading Language Version of %d is less than the required version of %d. Switch video modes or update your drivers.", GLSL_version, MIN_REQUIRED_GLSL_VERSION);
}
GLint max_texture_units;
@@ -1399,7 +1403,7 @@ bool gr_opengl_is_capable(gr_capability capability)
case CAPABILITY_POST_PROCESSING:
return Gr_post_processing_enabled && !Cmdline_no_fbo;
case CAPABILITY_DEFERRED_LIGHTING:
return !Cmdline_no_fbo && !Cmdline_no_deferred_lighting;
return !Cmdline_no_fbo && light_deferred_enabled();
case CAPABILITY_SHADOWS:
case CAPABILITY_THICK_OUTLINE:
return !Cmdline_no_geo_sdr_effects;
+5 -4
View File
@@ -57,7 +57,7 @@ void opengl_clear_deferred_buffers()
void gr_opengl_deferred_lighting_begin(bool clearNonColorBufs)
{
if ( Cmdline_no_deferred_lighting)
if (!light_deferred_enabled())
return;
static const float black[] = {0, 0, 0, 1.0f};
@@ -149,7 +149,7 @@ void gr_opengl_deferred_lighting_msaa()
opengl_set_generic_uniform_data<graphics::generic_data::msaa_data>(
[&](graphics::generic_data::msaa_data* data) {
data->samples = Cmdline_msaa_enabled;
data->fov = Proj_fov;
data->fov = g3_get_hfov(Proj_fov);
});
GL_state.SetAlphaBlendMode(gr_alpha_blend::ALPHA_BLEND_NONE);
GL_state.SetZbufferType(ZBUFFER_TYPE_WRITE);
@@ -181,7 +181,7 @@ void gr_opengl_deferred_lighting_finish()
GR_DEBUG_SCOPE("Deferred lighting finish");
TRACE_SCOPE(tracing::ApplyLights);
if (Cmdline_no_deferred_lighting) {
if (!light_deferred_enabled()) {
return;
}
@@ -254,6 +254,7 @@ void gr_opengl_deferred_lighting_finish()
header->invScreenWidth = 1.0f / gr_screen.max_w;
header->invScreenHeight = 1.0f / gr_screen.max_h;
header->nearPlane = gr_near_plane;
// Only the first directional light uses shaders so we need to know when we already saw that light
bool first_directional = true;
@@ -480,7 +481,7 @@ void gr_opengl_deferred_lighting_finish()
data->noiseColorScale2 = std::get<1>(neb.getNoiseColorScale());
data->noiseColorIntensity = neb.getNoiseColorIntensity();
data->aspect = gr_screen.clip_aspect;
data->fov = Proj_fov;
data->fov = g3_get_hfov(Proj_fov);
});
{
+2 -1
View File
@@ -25,6 +25,7 @@
#include "graphics/matrix.h"
#include "graphics/paths/PathRenderer.h"
#include "graphics/util/uniform_structs.h"
#include "lighting/lighting.h"
#include "render/3d.h"
#include "tracing/tracing.h"
@@ -673,7 +674,7 @@ void gr_opengl_scene_texture_begin()
Scene_texture_v_scale = 1.0f;
}
if ( Cmdline_no_deferred_lighting ) {
if (!light_deferred_enabled()) {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
} else {
+1 -1
View File
@@ -398,7 +398,7 @@ static void handle_includes_impl(SCP_vector<SCP_string>& include_stack,
if (first_quote == SCP_string::npos || second_quote == SCP_string::npos) {
Error(LOCATION,
"Shader %s:%d: Malformed include line. Could not find both quote charaters.",
"Shader %s:%d: Malformed include line. Could not find both quote characters.",
filename.c_str(),
line_num);
}
+22 -20
View File
@@ -47,17 +47,19 @@ extern int GLOWMAP;
extern int SPECMAP;
extern int ENVMAP;
const SCP_vector<std::pair<int, SCP_string>> TextureFilteringValues = {{ 0, "Bilinear" },
{ 1, "Trilinear" }, };
const SCP_vector<std::pair<int, std::pair<const char*, int>>> TextureFilteringValues = {{0, {"Bilinear", 1677}},
{ 1, {"Trilinear", 1678}}};
static auto TextureFilteringOption __UNUSED = options::OptionBuilder<int>("Graphics.TextureFilter", "Texture Filtering", "Texture filtering option")
.importance(1)
.category("Graphics")
.values(TextureFilteringValues)
.default_val(0)
.bind_to_once(&GL_mipmap_filter)
.flags({options::OptionFlags::ForceMultiValueSelection})
.finish();
static auto TextureFilteringOption __UNUSED = options::OptionBuilder<int>("Graphics.TextureFilter",
std::pair<const char*, int>{"Texture Filtering", 1763},
std::pair<const char*, int>{"Texture filtering option", 1764})
.importance(1)
.category("Graphics")
.values(TextureFilteringValues)
.default_val(0)
.bind_to_once(&GL_mipmap_filter)
.flags({options::OptionFlags::ForceMultiValueSelection})
.finish();
static SCP_vector<float> anisotropic_value_enumerator()
{
@@ -100,16 +102,16 @@ static float anisotropic_default()
return max;
}
static auto AnisotropyOption =
options::OptionBuilder<float>("Graphics.Anisotropy", "Anisotropic filtering",
"Controls the amount of anisotropic filtering of the textures.")
.enumerator(anisotropic_value_enumerator)
.category("Graphics")
.display(anisotropic_display)
.default_func(anisotropic_default)
.level(options::ExpertLevel::Advanced)
.importance(78)
.finish();
static auto AnisotropyOption = options::OptionBuilder<float>("Graphics.Anisotropy",
std::pair<const char*, int>{"Anistropic filtering", 1736},
std::pair<const char*, int>{"Controls the amount of anistropic filtering of the textures", 1737})
.enumerator(anisotropic_value_enumerator)
.category("Graphics")
.display(anisotropic_display)
.default_func(anisotropic_default)
.level(options::ExpertLevel::Advanced)
.importance(78)
.finish();
// forward declarations
int opengl_free_texture(tcache_slot_opengl *t);
+2 -2
View File
@@ -974,7 +974,7 @@ void opengl_tnl_set_material_particle(particle_material * material_info)
data->srgb = High_dynamic_range ? 1 : 0;
data->blend_alpha = material_info->get_blend_mode() != ALPHA_BLEND_ADDITIVE;
if (Cmdline_no_deferred_lighting) {
if (!light_deferred_enabled()) {
data->linear_depth = 0;
} else {
data->linear_depth = 1;
@@ -984,7 +984,7 @@ void opengl_tnl_set_material_particle(particle_material * material_info)
Current_shader->program->Uniforms.setTextureUniform("baseMap", 0);
Current_shader->program->Uniforms.setTextureUniform("depthMap", 1);
if (!Cmdline_no_deferred_lighting) {
if (light_deferred_enabled()) {
Assert(Scene_position_texture != 0);
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_position_texture);
+21 -20
View File
@@ -40,27 +40,28 @@ PostEffectUniformType mapUniformNameToType(const SCP_string& uniform_name)
// used by In-Game Options menu
bool Post_processing_enable_lightshafts = true;
auto LightshaftsOption __UNUSED =
options::OptionBuilder<bool>("Graphics.Lightshafts", "Lightshafts", "Enable lightshafts (requires post-processing)")
.category("Graphics")
.default_val(true)
.level(options::ExpertLevel::Advanced)
.bind_to(&Post_processing_enable_lightshafts)
.importance(60)
.finish();
static auto LightshaftsOption __UNUSED = options::OptionBuilder<bool>("Graphics.Lightshafts",
std::pair<const char*, int>{"Lightshafts", 1724},
std::pair<const char*, int>{"Enables or disables lightshafts (requires post-processing)", 1725})
.category("Graphics")
.default_val(true)
.level(options::ExpertLevel::Advanced)
.bind_to(&Post_processing_enable_lightshafts)
.importance(60)
.finish();
int Post_processing_bloom_intensity = 25; // using default value of Cmdline_bloom_intensity
auto BloomIntensityOption __UNUSED = options::OptionBuilder<int>("Graphics.BloomIntensity",
"Bloom intensity",
"Set bloom intensity (requires post-processing)")
.category("Graphics")
.range(0, 200)
.level(options::ExpertLevel::Advanced)
.default_val(25)
.bind_to(&Post_processing_bloom_intensity)
.importance(55)
.finish();
static auto BloomIntensityOption __UNUSED = options::OptionBuilder<int>("Graphics.BloomIntensity",
std::pair<const char*, int>{"Bloom intensity", 1701},
std::pair<const char*, int>{"Sets the bloom intensity (requires post-processing)", 1702})
.category("Graphics")
.range(0, 200)
.level(options::ExpertLevel::Advanced)
.default_val(25)
.bind_to(&Post_processing_bloom_intensity)
.importance(55)
.finish();
} // namespace
bool PostProcessingManager::parse_table()
@@ -223,7 +224,7 @@ bool gr_lightshafts_enabled()
}
if (Using_in_game_options) {
return graphics::Post_processing_enable_lightshafts;
return graphics::LightshaftsOption->getValue();
} else {
return !Cmdline_force_lightshaft_off;
}
@@ -240,7 +241,7 @@ int gr_bloom_intensity()
}
if (Using_in_game_options) {
return graphics::Post_processing_bloom_intensity;
return graphics::BloomIntensityOption->getValue();
} else {
return Cmdline_bloom_intensity;
}
+57 -41
View File
@@ -34,24 +34,20 @@ ShadowQuality Shadow_quality = ShadowQuality::Disabled;
bool Shadow_quality_uses_mod_option = false;
auto ShadowQualityOption =
options::OptionBuilder<ShadowQuality>("Graphics.Shadows", "Shadow Quality", "The quality of the shadows")
.values({{ShadowQuality::Disabled, "Disabled"},
{ShadowQuality::Low, "Low"},
{ShadowQuality::Medium, "Medium"},
{ShadowQuality::High, "High"},
{ShadowQuality::Ultra, "Ultra"}})
.change_listener([](ShadowQuality val, bool initial) {
if (initial) {
Shadow_quality = val;
}
return initial; // No dynamic changes implemented
})
.level(options::ExpertLevel::Advanced)
.category("Graphics")
.default_val(ShadowQuality::Disabled)
.importance(80)
.finish();
auto ShadowQualityOption = options::OptionBuilder<ShadowQuality>("Graphics.Shadows",
std::pair<const char*, int>{"Shadow Quality", 1750},
std::pair<const char*, int>{"The quality of the shadows", 1751})
.values({{ShadowQuality::Disabled, {"Disabled", 1779}},
{ShadowQuality::Low, {"Low", 1160}},
{ShadowQuality::Medium, {"Medium", 1161}},
{ShadowQuality::High, {"High", 1162}},
{ShadowQuality::Ultra, {"Ultra", 1721}}})
.change_listener([](ShadowQuality val, bool initial) {if (initial) {Shadow_quality = val;}return initial;})
.level(options::ExpertLevel::Advanced)
.category("Graphics")
.default_val(ShadowQuality::Disabled)
.importance(80)
.finish();
bool shadows_obj_in_frustum(object *objp, matrix *light_orient, vec3d *min, vec3d *max)
{
@@ -222,14 +218,34 @@ void shadows_debug_show_frustum(matrix* orient, vec3d *pos, float fov, float asp
g3_render_line_3d(true, &far_bottom_left, &far_top_left);
}
void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *light_matrix, matrix *orient, vec3d * /*pos*/, float fov, float aspect, float z_near, float z_far)
void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *light_matrix, matrix *orient, vec3d * /*pos*/, fov_t fov, float aspect, float z_near, float z_far)
{
// find the widths and heights of the near plane and far plane to determine the points of this frustum
float near_height = tanf(fov * 0.5f) * z_near;
float near_width = near_height * aspect;
float near_l, near_r, near_u, near_d;
float far_l, far_r, far_u, far_d;
if (mpark::holds_alternative<float>(fov)) {
near_d = tanf(mpark::get<float>(fov) * 0.5f) * z_near;
near_u = -near_d;
near_r = near_d * aspect;
near_l = -near_r;
float far_height = tanf(fov * 0.5f) * z_far;
float far_width = far_height * aspect;
far_d = tanf(mpark::get<float>(fov) * 0.5f) * z_far;
far_u = -far_d;
far_r = far_d * aspect;
far_l = -far_r;
}
else {
const auto& afov = mpark::get<asymmetric_fov>(fov);
near_d = tanf(-afov.down) * z_near;
near_u = -tanf(afov.up) * z_near;
near_r = tanf(-afov.left) * z_near;
near_l = -tanf(afov.right) * z_near;
far_d = tanf(-afov.down) * z_far;
far_u = -tanf(afov.up) * z_far;
far_r = tanf(-afov.left) * z_far;
far_l = -tanf(afov.right) * z_far;
}
vec3d up_scale = ZERO_VECTOR;
vec3d right_scale = ZERO_VECTOR;
@@ -247,40 +263,40 @@ void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *li
// near top left
up_scale = orient->vec.uvec;
vm_vec_scale(&up_scale, -near_height);
vm_vec_scale(&up_scale, near_u);
right_scale = orient->vec.rvec;
vm_vec_scale(&right_scale, -near_width);
vm_vec_scale(&right_scale, near_l);
vm_vec_add(&near_top_left, &up_scale, &right_scale);
vm_vec_add2(&near_top_left, &forward_scale_near);
// near top right
up_scale = orient->vec.uvec;
vm_vec_scale(&up_scale, -near_height);
vm_vec_scale(&up_scale, near_u);
right_scale = orient->vec.rvec;
vm_vec_scale(&right_scale, near_width);
vm_vec_scale(&right_scale, near_r);
vm_vec_add(&near_top_right, &up_scale, &right_scale);
vm_vec_add2(&near_top_right, &forward_scale_near);
// near bottom left
up_scale = orient->vec.uvec;
vm_vec_scale(&up_scale, near_height);
vm_vec_scale(&up_scale, near_d);
right_scale = orient->vec.rvec;
vm_vec_scale(&right_scale, -near_width);
vm_vec_scale(&right_scale, near_l);
vm_vec_add(&near_bottom_left, &up_scale, &right_scale);
vm_vec_add2(&near_bottom_left, &forward_scale_near);
// near bottom right
up_scale = orient->vec.uvec;
vm_vec_scale(&up_scale, near_height);
vm_vec_scale(&up_scale, near_d);
right_scale = orient->vec.rvec;
vm_vec_scale(&right_scale, near_width);
vm_vec_scale(&right_scale, near_r);
vm_vec_add(&near_bottom_right, &up_scale, &right_scale);
vm_vec_add2(&near_bottom_right, &forward_scale_near);
@@ -292,40 +308,40 @@ void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *li
// far top left
up_scale = orient->vec.uvec;
vm_vec_scale(&up_scale, -far_height);
vm_vec_scale(&up_scale, far_u);
right_scale = orient->vec.rvec;
vm_vec_scale(&right_scale, -far_width);
vm_vec_scale(&right_scale, far_l);
vm_vec_add(&far_top_left, &up_scale, &right_scale);
vm_vec_add2(&far_top_left, &forward_scale_far);
// far top right
up_scale = orient->vec.uvec;
vm_vec_scale(&up_scale, -far_height);
vm_vec_scale(&up_scale, far_u);
right_scale = orient->vec.rvec;
vm_vec_scale(&right_scale, far_width);
vm_vec_scale(&right_scale, far_r);
vm_vec_add(&far_top_right, &up_scale, &right_scale);
vm_vec_add2(&far_top_right, &forward_scale_far);
// far bottom left
up_scale = orient->vec.uvec;
vm_vec_scale(&up_scale, far_height);
vm_vec_scale(&up_scale, far_d);
right_scale = orient->vec.rvec;
vm_vec_scale(&right_scale, -far_width);
vm_vec_scale(&right_scale, far_l);
vm_vec_add(&far_bottom_left, &up_scale, &right_scale);
vm_vec_add2(&far_bottom_left, &forward_scale_far);
// far bottom right
up_scale = orient->vec.uvec;
vm_vec_scale(&up_scale, far_height);
vm_vec_scale(&up_scale, far_d);
right_scale = orient->vec.rvec;
vm_vec_scale(&right_scale, far_width);
vm_vec_scale(&right_scale, far_r);
vm_vec_add(&far_bottom_right, &up_scale, &right_scale);
vm_vec_add2(&far_bottom_right, &forward_scale_far);
@@ -381,7 +397,7 @@ void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *li
shadows_construct_light_proj(shadow_data);
}
matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, float fov, float aspect, float veryneardist, float neardist, float middist, float fardist)
matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float aspect, float veryneardist, float neardist, float middist, float fardist)
{
if(Static_light.empty())
return vmd_identity_matrix;
@@ -419,7 +435,7 @@ void shadows_end_render()
gr_shadow_map_end();
}
void shadows_render_all(float fov, matrix *eye_orient, vec3d *eye_pos)
void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos)
{
if (gr_screen.mode == GR_STUB) {
return;
+4 -3
View File
@@ -12,6 +12,7 @@
#include "globalincs/pstypes.h"
#include "object/object.h"
#include "render/3d.h"
#define MAX_SHADOW_CASCADES 4
@@ -38,11 +39,11 @@ extern bool Shadow_quality_uses_mod_option;
extern matrix4 Shadow_proj_matrix[MAX_SHADOW_CASCADES];
extern float Shadow_cascade_distances[MAX_SHADOW_CASCADES];
void shadows_construct_light_frustum(vec3d *min_out, vec3d *max_out, vec3d light_vec, matrix *orient, vec3d *pos, float fov, float aspect, float z_near, float z_far);
void shadows_construct_light_frustum(vec3d *min_out, vec3d *max_out, vec3d light_vec, matrix *orient, vec3d *pos, fov_t fov, float aspect, float z_near, float z_far);
bool shadows_obj_in_frustum(object *objp, vec3d *min, vec3d *max, matrix *light_orient);
void shadows_render_all(float fov, matrix *eye_orient, vec3d *eye_pos);
void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos);
matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, float fov, float aspect, float veryneardist, float neardist, float middist, float fardist);
matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float aspect, float veryneardist, float neardist, float middist, float fardist);
void shadows_end_render();
/**
+1 -1
View File
@@ -66,7 +66,7 @@ namespace font
{
if (string[i] >= Lcl_special_chars || string[i] < 0)
{
// Special character needs to be handled seperately
// Special character needs to be handled separately
return i;
}
}
+2 -1
View File
@@ -30,8 +30,9 @@ struct deferred_global_data {
float invScreenWidth;
float invScreenHeight;
float nearPlane;
float pad[2];
float pad;
};
/**
+3
View File
@@ -321,6 +321,9 @@ bool VulkanRenderer::initDisplayDevice() const
}
}
if (Cmdline_capture_mouse)
attrs.flags.set(os::ViewPortFlags::Capture_Mouse);
auto viewPort = m_graphicsOps->createViewport(attrs);
if (!viewPort) {
return false;
+29 -66
View File
@@ -71,7 +71,7 @@ SCP_vector<std::unique_ptr<HudGauge>> default_hud_gauges;
#define HUD_NEW_ALPHA_BRIGHT_HI 255
// Externals not related to the HUD code itself
extern float View_zoom;
extern fov_t View_zoom;
// globals that will control the color of the HUD gauges
int HUD_color_red = 0;
@@ -91,10 +91,6 @@ color HUD_color_debug; // grey debug text shown on HUD
static sound_handle Player_engine_snd_loop = sound_handle::invalid();
// HUD render frame offsets
float HUD_offset_x = 0.0f;
float HUD_offset_y = 0.0f;
// the offset of the player's view vector and the ship forward vector in pixels (Swifty)
int HUD_nose_x;
int HUD_nose_y;
@@ -1033,8 +1029,8 @@ void HudGauge::renderCircle(int x, int y, int diameter, bool filled)
void HudGauge::setClip(int x, int y, int w, int h)
{
int hx = fl2i(HUD_offset_x);
int hy = fl2i(HUD_offset_y);
int hx = 0;
int hy = 0;
if ( gr_screen.rendering_to_texture != -1 ) {
gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
@@ -1051,17 +1047,22 @@ void HudGauge::setClip(int x, int y, int w, int h)
gr_set_clip(hx, hy, w, h);
} else {
if ( reticle_follow ) {
if (reticle_follow) {
hx += HUD_nose_x;
hy += HUD_nose_y;
gr_resize_screen_pos(&hx, &hy);
gr_set_screen_scale(base_w, base_h);
gr_unsize_screen_pos(&hx, &hy);
}
else {
gr_set_screen_scale(base_w, base_h);
}
gr_resize_screen_pos(&hx, &hy);
gr_set_screen_scale(base_w, base_h);
x += hx;
y += hy;
gr_resize_screen_pos(&x, &y, &w, &h);
gr_set_clip(hx+x, hy+y, w, h, GR_RESIZE_NONE);
gr_set_clip(x, y, w, h, GR_RESIZE_NONE);
}
gr_reset_screen_scale();
@@ -1085,9 +1086,6 @@ void HudGauge::resetClip()
gr_set_clip(hx, hy, w, h);
} else {
hx = fl2i(HUD_offset_x);
hy = fl2i(HUD_offset_y);
gr_resize_screen_pos(&hx, &hy);
gr_set_screen_scale(base_w, base_h);
@@ -1778,8 +1776,8 @@ void HudGaugeMissionTime::render(float /*frametime*/)
int minutes=0;
int seconds=0;
mission_time = f2fl(Missiontime); // convert to seconds
mission_time = f2fl(Missiontime) + (float)The_mission.HUD_timer_padding; // convert to seconds
minutes=(int)(mission_time/60);
seconds=(int)mission_time%60;
@@ -2021,7 +2019,7 @@ void hud_damage_popup_init()
Damage_flash_timer = 1;
for ( i = 0; i < SUBSYSTEM_MAX; i++ ) {
Pl_hud_subsys_info[i].last_str = 1000.0f;
Pl_hud_subsys_info[i].last_str = 1.0f;
Pl_hud_subsys_info[i].flash_duration_timestamp = 1;
Pl_hud_next_flash_timestamp = 1;
Pl_hud_is_bright = 0;
@@ -2076,6 +2074,11 @@ void HudGaugeDamage::initLineHeight(int h)
line_h = h;
}
void HudGaugeDamage::initDisplayValue(bool value)
{
always_display = value;
}
void HudGaugeDamage::initBitmaps(const char *fname_top, const char *fname_middle, const char *fname_bottom)
{
damage_top.first_frame = bm_load_animation(fname_top, &damage_top.num_frames);
@@ -2136,7 +2139,7 @@ void HudGaugeDamage::render(float /*frametime*/)
for ( pss = GET_FIRST(&Player_ship->subsys_list); pss !=END_OF_LIST(&Player_ship->subsys_list); pss = GET_NEXT(pss) ) {
psub = pss->system_info;
strength = ship_get_subsystem_strength(Player_ship, psub->type);
if ( strength < 1 ) {
if ( always_display || strength < 1 ) {
// display the actual health of this specific subsystem --wookieejedi
if (pss->max_hits > 0) {
// get percentage if this subsystem has hitpoints to begin with
@@ -2273,7 +2276,7 @@ void HudGaugeDamage::render(float /*frametime*/)
// Show hull integrity if it's below 100% or if a subsystem is damaged
// The second case is just to make the display look complete
// The third case is there to make the gauge appear only if needed if the right option is set
if ( screen_integrity < 100 || !info_lines.empty() ) {
if ( always_display || screen_integrity < 100 || !info_lines.empty() ) {
DamageInfo info;
info.name = XSTR( "Hull Integrity", 220);
@@ -3746,42 +3749,8 @@ int hud_objective_notify_active()
* @param wiggedy_wack
* @param eye_orient
*/
void HUD_set_offsets(object *viewer_obj, int wiggedy_wack, matrix *eye_orient)
void HUD_set_offsets()
{
if ( (viewer_obj == Player_obj) && wiggedy_wack ){
vec3d tmp;
vertex pt;
HUD_offset_x = 0.0f;
HUD_offset_y = 0.0f;
vm_vec_scale_add( &tmp, &Eye_position, &eye_orient->vec.fvec, 100.0f );
(void) g3_rotate_vertex(&pt,&tmp);
g3_project_vertex(&pt);
gr_unsize_screen_posf( &pt.screen.xyw.x, &pt.screen.xyw.y );
HUD_offset_x -= 0.45f * (i2fl(gr_screen.clip_width_unscaled)*0.5f - pt.screen.xyw.x);
HUD_offset_y -= 0.45f * (i2fl(gr_screen.clip_height_unscaled)*0.5f - pt.screen.xyw.y);
if ( HUD_offset_x > 100.0f ) {
HUD_offset_x = 100.0f;
} else if ( HUD_offset_x < -100.0f ) {
HUD_offset_x += 100.0f;
}
if ( HUD_offset_y > 100.0f ) {
HUD_offset_y = 100.0f;
} else if ( HUD_offset_y < -100.0f ) {
HUD_offset_y += 100.0f;
}
} else {
HUD_offset_x = 0.0f;
HUD_offset_y = 0.0f;
}
if ( Viewer_mode & ( VM_TOPDOWN | VM_CHASE ) ) {
HUD_nose_x = 0;
HUD_nose_y = 0;
@@ -3845,10 +3814,7 @@ void HUD_get_nose_coordinates(int *x, int *y)
*/
void HUD_reset_clip()
{
int hx = fl2i(HUD_offset_x);
int hy = fl2i(HUD_offset_y);
gr_set_clip(hx, hy, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled);
gr_set_clip(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled);
}
/**
@@ -3856,10 +3822,7 @@ void HUD_reset_clip()
*/
void HUD_set_clip(int x, int y, int w, int h)
{
int hx = fl2i(HUD_offset_x);
int hy = fl2i(HUD_offset_y);
gr_set_clip(hx+x, hy+y, w, h);
gr_set_clip(x, y, w, h);
}
/**
@@ -3869,7 +3832,7 @@ void HUD_set_clip(int x, int y, int w, int h)
void hud_save_restore_camera_data(int save)
{
static vec3d save_view_position;
static float save_view_zoom;
static fov_t save_view_zoom;
static matrix save_view_matrix;
static matrix save_eye_matrix;
static vec3d save_eye_position;
@@ -4135,7 +4098,7 @@ void HudGaugeSupernova::render(float /*frametime*/)
}
HudGaugeFlightPath::HudGaugeFlightPath():
HudGauge(HUD_OBJECT_FLIGHT_PATH, HUD_CENTER_RETICLE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
HudGauge3DAnchor(HUD_OBJECT_FLIGHT_PATH, HUD_CENTER_RETICLE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
{
}
+23 -4
View File
@@ -129,7 +129,7 @@ void hud_render_gauges(int cockpit_display_num = -1);
void hud_stop_looped_engine_sounds();
// set the offset values for this render frame
void HUD_set_offsets(object *viewer_obj, int wiggedy_wack, matrix *eye_orient);
void HUD_set_offsets();
// returns the offset between the player's view vector and the forward vector of the ship in pixels (Swifty)
void HUD_get_nose_coordinates(int *x, int *y);
@@ -277,7 +277,7 @@ public:
void initOriginAndOffset(float originX, float originY, int offsetX, int offsetY);
void initCoords(bool use_coords, int coordsX, int coordsY);
void initSlew(bool slew);
virtual void initSlew(bool slew);
void initCockpitTarget(const char* display_name, int _target_x, int _target_y, int _target_w, int _target_h, int _canvas_w, int _canvas_h);
void initRenderStatus(bool render);
@@ -362,6 +362,21 @@ public:
void resetClip();
};
//Use this instead of HudGauge whenever you have a HudGauge that is anchored in 3D-space (i.e. takes its rendering coordinates from g3_rotate/project_vertex, as these MUST NEVER slew.
class HudGauge3DAnchor : public HudGauge {
public:
HudGauge3DAnchor()
: HudGauge() { }
HudGauge3DAnchor(int _gauge_object, int _gauge_config, bool /*_slew*/, bool _message, int _disabled_views, int r, int g, int b)
: HudGauge(_gauge_object, _gauge_config, false, _message, _disabled_views, r, g, b) { }
// constructor for custom gauges
HudGauge3DAnchor(int _gauge_config, bool /*_slew*/, int r, int g, int b, char* _custom_name, char* _custom_text, char* frame_fname, int txtoffset_x, int txtoffset_y)
: HudGauge(_gauge_config, false, r, g, b, _custom_name, _custom_text, frame_fname, txtoffset_x, txtoffset_y) { }
void initSlew(bool /*slew*/) override {};
};
class HudGaugeMissionTime: public HudGauge // HUD_MISSION_TIME
{
hud_frames time_gauge;
@@ -492,7 +507,10 @@ protected:
int next_flash;
bool flash_status;
public:
bool always_display;
public:
HudGaugeDamage();
void initBitmaps(const char *fname_top, const char *fname_middle, const char *fname_bottom);
void initHeaderOffsets(int x, int y);
@@ -503,6 +521,7 @@ public:
void initSubsysIntegValueOffsetX(int x);
void initBottomBgOffset(int offset);
void initLineHeight(int h);
void initDisplayValue(bool value);
void render(float frametime) override;
void pageIn() override;
void initialize() override;
@@ -562,7 +581,7 @@ public:
void render(float frametime) override;
};
class HudGaugeFlightPath: public HudGauge
class HudGaugeFlightPath: public HudGauge3DAnchor
{
hud_frames Marker;
+1 -1
View File
@@ -331,7 +331,7 @@ int draw_subsys_brackets(graphics::line_draw_list* draw_list, ship_subsys* subsy
}
HudGaugeBrackets::HudGaugeBrackets():
HudGauge(HUD_OBJECT_BRACKETS, HUD_OFFSCREEN_INDICATOR, false, true, VM_DEAD_VIEW, 255, 255, 255)
HudGauge3DAnchor(HUD_OBJECT_BRACKETS, HUD_OFFSCREEN_INDICATOR, false, true, VM_DEAD_VIEW, 255, 255, 255)
{
}
+1 -1
View File
@@ -25,7 +25,7 @@ void draw_brackets_diamond_quick(graphics::line_draw_list* draw_list, int x1, in
int draw_subsys_brackets(graphics::line_draw_list* draw_list, ship_subsys* subsys, int min_width, int min_height, bool draw = true, bool set_color = true, int* draw_coords = NULL);
class HudGaugeBrackets: public HudGauge
class HudGaugeBrackets: public HudGauge3DAnchor
{
protected:
int attacking_dot;
+2 -7
View File
@@ -1266,7 +1266,7 @@ void hud_config_button_do(int n)
case HCB_SAVE_HCF:
int exists;
char *out;
const char *out;
// get the text in the input control
exists = 0;
@@ -1658,10 +1658,6 @@ void hud_config_as_observer(ship *shipp,ai_info *aif)
// store the current hud
hud_config_backup();
// bash these values so the HUD is not offset incorrectly
HUD_offset_x = 0.0f;
HUD_offset_y = 0.0f;
// initialize the observer HUD
hud_observer_init(shipp,aif);
}
@@ -1708,10 +1704,9 @@ void hud_config_color_load(const char *name)
{
int idx;
char str[1024];
char *fname;
ubyte r, g, b, a;
fname = cf_add_ext(name, ".hcf");
const char *fname = cf_add_ext(name, ".hcf");
try
{
+13 -7
View File
@@ -125,19 +125,25 @@ void update_ets(object* objp, float fl_frametime)
// This will translate to 71% max speed at 50% engines, and 31% max speed at 10% engines
//
float eng_current_strength = ship_get_subsystem_strength(ship_p, SUBSYSTEM_ENGINE);
float engine_aggregate_strength;
if (ship_p->flags[Ship::Ship_Flags::Maneuver_despite_engines]) {
// Pretend our strength is 100% when this flag is active
engine_aggregate_strength = 1.0f;
} else {
engine_aggregate_strength = ship_get_subsystem_strength(ship_p, SUBSYSTEM_ENGINE);
}
// only update max speed if aggregate engine health has changed
// which helps minimze amount of overrides to max speed
if (eng_current_strength != ship_p->prev_engine_aggregate_strength) {
// only update max speed if engine_aggregate_strength has changed
// which helps minimize amount of overrides to max speed
if (engine_aggregate_strength != ship_p->prev_engine_aggregate_strength) {
ets_update_max_speed(objp);
ship_p->prev_engine_aggregate_strength = eng_current_strength;
ship_p->prev_engine_aggregate_strength = engine_aggregate_strength;
// check if newly updated max speed should be reduced due to engine damage
// don't let engine strength affect max speed when playing on lowest skill level
if ((objp != Player_obj) || (Game_skill_level > 0)) {
if (eng_current_strength < SHIP_MIN_ENGINES_FOR_FULL_SPEED) {
objp->phys_info.max_vel.xyz.z *= fl_sqrt(eng_current_strength);
if (engine_aggregate_strength < SHIP_MIN_ENGINES_FOR_FULL_SPEED) {
objp->phys_info.max_vel.xyz.z *= fl_sqrt(engine_aggregate_strength);
}
}
}
+1 -1
View File
@@ -107,7 +107,7 @@ void hud_init_missile_lock()
}
HudGaugeLock::HudGaugeLock():
HudGauge(HUD_OBJECT_LOCK, HUD_LEAD_INDICATOR, false, false, VM_DEAD_VIEW, 255, 255, 255)
HudGauge3DAnchor(HUD_OBJECT_LOCK, HUD_LEAD_INDICATOR, false, false, VM_DEAD_VIEW, 255, 255, 255)
{
}
+1 -1
View File
@@ -24,7 +24,7 @@ void hud_do_lock_indicators(float frametime);
void hud_stop_looped_locking_sounds();
void hud_lock_reset(float lock_time_scale=1.0f);
class HudGaugeLock: public HudGauge
class HudGaugeLock: public HudGauge3DAnchor
{
protected:
hud_anim Lock_gauge;
+36 -6
View File
@@ -444,6 +444,10 @@ void HudGaugeMessages::render(float /*frametime*/)
// dependant on max_width, max_lines, and line_height
setClip(position[0], position[1], Window_width, Window_height+2);
//Since setClip already sets makes drawing local, further renderings mustn't additionally slew
bool doSlew = reticle_follow;
reticle_follow = false;
for ( SCP_vector<Hud_display_info>::iterator m = active_messages.begin(); m != active_messages.end(); ++m) {
if ( !timestamp_elapsed(m->total_life) ) {
if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) || !Hidden_by_comms_menu) {
@@ -459,6 +463,8 @@ void HudGaugeMessages::render(float /*frametime*/)
}
}
}
reticle_follow = doSlew;
}
// Similar to HUD printf, but shows only one message at a time, at a fixed location.
@@ -874,6 +880,7 @@ void hud_scrollback_init()
// pause all game sounds
weapon_pause_sounds();
audiostream_pause_all();
message_pause_all();
hud_initialize_scrollback_lines();
@@ -934,6 +941,7 @@ void hud_scrollback_close()
// unpause all game sounds
weapon_unpause_sounds();
audiostream_unpause_all();
message_resume_all();
}
@@ -1180,19 +1188,41 @@ void HudGaugeTalkingHead::render(float frametime)
// hud_set_default_color();
setGaugeColor();
int tablePosX = position[0];
int tablePosY = position[1];
if (reticle_follow && gr_screen.rendering_to_texture == -1) {
int nx = HUD_nose_x;
int ny = HUD_nose_y;
gr_resize_screen_pos(&nx, &ny);
gr_set_screen_scale(base_w, base_h);
gr_unsize_screen_pos(&nx, &ny);
gr_reset_screen_scale();
tablePosX += nx;
tablePosY += ny;
}
// clear
setClip(position[0] + Anim_offsets[0], position[1] + Anim_offsets[1], Anim_size[0], Anim_size[1]);
gr_clear();
resetClip();
//renderBitmap is slew corrected, so use uncorrected position
renderBitmap(Head_frame.first_frame, position[0], position[1]); // head ani border
float scale_x = i2fl(Anim_size[0]) / i2fl(head_anim->width);
float scale_y = i2fl(Anim_size[1]) / i2fl(head_anim->height);
gr_set_screen_scale(fl2ir(base_w / scale_x), fl2ir(base_h / scale_y));
setGaugeColor();
generic_anim_render(head_anim,frametime, fl2ir((position[0] + Anim_offsets[0] + HUD_offset_x) / scale_x), fl2ir((position[1] + Anim_offsets[1] + HUD_offset_y) / scale_y));
int hx = tablePosX + Anim_offsets[0];
int hy = tablePosY + Anim_offsets[1];
if (gr_screen.rendering_to_texture != -1)
gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
else
gr_set_screen_scale(base_w, base_h);
generic_anim_render_ex(head_anim,frametime, hx, hy, Anim_size[0], Anim_size[1]);
gr_reset_screen_scale();
// draw title
gr_set_screen_scale(base_w, base_h);
renderString(position[0] + Header_offsets[0], position[1] + Header_offsets[1], XSTR("message", 217));
} else {
for (int j = 0; j < Num_messages_playing; ++j) {
+1
View File
@@ -18,6 +18,7 @@
#define MAX_HUD_LINE_LEN 256 // maximum number of characters for a HUD message
// If these are changed, the lua 'addMessageToScrollback' method in mission.cpp should be updated.
#define HUD_SOURCE_COMPUTER 0
#define HUD_SOURCE_TRAINING 1
#define HUD_SOURCE_HIDDEN 2
+5
View File
@@ -4143,6 +4143,7 @@ void load_gauge_damage(gauge_settings* settings)
char fname_top[MAX_FILENAME_LEN] = "damage1";
char fname_middle[MAX_FILENAME_LEN] = "damage2";
char fname_bottom[MAX_FILENAME_LEN] = "damage3";
bool always_display = false;
settings->origin[0] = 0.5f;
settings->origin[1] = 0.0f;
@@ -4200,6 +4201,9 @@ void load_gauge_damage(gauge_settings* settings)
if(optional_string("Bottom Background Offset:")) {
stuff_int(&bottom_bg_offset);
}
if(optional_string("Always Display:")) {
stuff_boolean(&always_display);
}
hud_gauge->initBitmaps(fname_top, fname_middle, fname_bottom);
hud_gauge->initHullIntegOffsets(hull_integ_offsets[0], hull_integ_offsets[1]);
@@ -4210,6 +4214,7 @@ void load_gauge_damage(gauge_settings* settings)
hud_gauge->initSubsysIntegValueOffsetX(subsys_integ_val_offset_x);
hud_gauge->initBottomBgOffset(bottom_bg_offset);
hud_gauge->initHeaderOffsets(header_offsets[0], header_offsets[1]);
hud_gauge->initDisplayValue(always_display);
gauge_assign_common(settings, std::move(hud_gauge));
}
+7 -10
View File
@@ -365,8 +365,8 @@ void hud_shield_show_mini(object *objp, int x_force, int y_force, int x_hull_off
if (!Shield_mini_loaded)
return;
sx = (x_force == -1) ? Shield_mini_coords[gr_screen.res][0]+fl2i(HUD_offset_x) : x_force;
sy = (y_force == -1) ? Shield_mini_coords[gr_screen.res][1]+fl2i(HUD_offset_y) : y_force;
sx = (x_force == -1) ? Shield_mini_coords[gr_screen.res][0] : x_force;
sy = (y_force == -1) ? Shield_mini_coords[gr_screen.res][1] : y_force;
// draw the ship first
hud_shield_maybe_flash(HUD_TARGET_MINI_ICON, SHIELD_HIT_TARGET, Shield_hit_data[SHIELD_HIT_TARGET].hull_hit_index);
@@ -596,9 +596,6 @@ void HudGaugeShield::showShields(object *objp, int mode)
sx = position[0];
sy = position[1];
sx += fl2i(HUD_offset_x);
sy += fl2i(HUD_offset_y);
// draw the ship first
maybeFlashShield(SHIELD_HIT_PLAYER, Shield_hit_data[SHIELD_HIT_PLAYER].hull_hit_index);
@@ -635,7 +632,7 @@ void HudGaugeShield::showShields(object *objp, int mode)
g3_set_view_matrix( &finger_vec, &vmd_identity_matrix, 1.0f);
}*/
gr_set_proj_matrix(0.5f*Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
gr_set_proj_matrix(Proj_fov * 0.5f, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
gr_set_view_matrix(&Eye_position, &Eye_matrix);
//We're ready to show stuff
@@ -971,8 +968,8 @@ void HudGaugeShieldMini::showMiniShields(object *objp)
setGaugeColor();
sx = position[0]+fl2i(HUD_offset_x);
sy = position[1]+fl2i(HUD_offset_y);
sx = position[0];
sy = position[1];
// draw the ship first
maybeFlashShield(SHIELD_HIT_TARGET, Shield_hit_data[SHIELD_HIT_TARGET].hull_hit_index);
@@ -1061,8 +1058,8 @@ void HudGaugeShieldMini::showIntegrity(float p_target_integrity)
}
}
final_pos[0] += fl2i( HUD_offset_x ) + position[0];
final_pos[1] += fl2i( HUD_offset_y ) + position[1];
final_pos[0] += position[0];
final_pos[1] += position[1];
sprintf(text_integrity, "%d", numeric_integrity);
if ( numeric_integrity < 100 ) {
+91 -100
View File
@@ -105,32 +105,15 @@ typedef struct mmode_item {
SCP_string text; // text to display on the menu
} mmode_item;
#define MAX_MENU_ITEMS 50 // max number of items in the menu
#define MAX_MENU_DISPLAY 10 // max number that can be displayed
char Squad_msg_title[256] = "";
mmode_item MsgItems[MAX_MENU_ITEMS];
int Num_menu_items = -1; // number of items for a message menu
int First_menu_item= -1; // index of first item in the menu
// -----------
// following set of vars/defines are used to store/restore key bindings for keys that
// are used in messaging mode
// array to temporarily store key bindings that will be in use for the messaging
// system
typedef struct key_store {
int option_num; // which element in the Control_config array is this
int id; // which id (1 or 2) is this key.
int key_value; // which key value to put there.
} key_store;
SCP_string Lua_sqd_msg_cat;
#define MAX_KEYS_NO_SCROLL 10
#define MAX_KEYS_USED 12 // maximum number of keys used for the messaging system
key_store key_save[MAX_KEYS_USED]; // array to save the key information during messaging mode
int num_keys_saved = 0; // number of keys that are saved.
// next array is the array of MAX_KEYS_USED size which are the keys to use for messaging mode
int keys_used[] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
@@ -139,17 +122,6 @@ int keys_used[] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_
#define ID1 1
#define ID2 2
// following are defines and character strings that are used as part of messaging mode
#define NUM_COMM_ORDER_TYPES 6
#define TYPE_SHIP_ITEM 0
#define TYPE_WING_ITEM 1
#define TYPE_ALL_FIGHTERS_ITEM 2
#define TYPE_REINFORCEMENT_ITEM 3
#define TYPE_REPAIR_REARM_ITEM 4
#define TYPE_REPAIR_REARM_ABORT_ITEM 5
SCP_string Comm_order_types[NUM_COMM_ORDER_TYPES];
@@ -242,18 +214,8 @@ void hud_squadmsg_start()
}
}
// int i;
//if ( num_keys_saved < 0 ) // save the keys if they haven't been saved yet
hud_squadmsg_save_keys();
Msg_key = -1;
/*
for (i=0; i<num_keys_saved; i++)
clear_key_binding ( (short) key_save[i].key_value ); // removes all mention of this key from Control_config
*/
Num_menu_items = -1; // reset the menu items
First_menu_item = 0;
Squad_msg_mode = SM_MODE_TYPE_SELECT; // start off at the base state
@@ -274,7 +236,7 @@ void hud_squadmsg_start()
}
}
// functions which will restore all of the key binding stuff when messaging mode is done
// function which will clean things up when messaging mode is done
void hud_squadmsg_end()
{
if (scripting::hooks::OnHudCommMenuClosed->isActive())
@@ -287,17 +249,6 @@ void hud_squadmsg_end()
}
}
/*
int i;
key_store *ksp;
// move through all keys saved and restore their orignal values.
for ( i=0; i<num_keys_saved; i++ ) {
ksp = &key_save[i];
Control_config[ksp->option_num].key_id = (short) ksp->key_value;
}
*/
if ( message_is_playing() == FALSE )
snd_play( gamesnd_get_game_sound(GameSounds::SQUADMSGING_OFF) );
@@ -426,10 +377,6 @@ int hud_squadmsg_count_ships(int add_to_menu)
}
}
// if adding to the menu and we have > 10 items, then don't allow page up and page down to be used.
if ( add_to_menu && (Num_menu_items > MAX_MENU_DISPLAY) )
hud_squadmsg_save_keys(1);
return count;
}
@@ -616,7 +563,7 @@ int hud_squadmsg_get_key()
// use a timestamp to prevent top level key code from possibly reprocessing this key
Msg_eat_key_timestamp = timestamp(MSG_KEY_EAT_TIME);
if ( k == KEY_PAGEDOWN ) { // pageup and pagedown scroll the menu -- deal with these seperately!!
if ( k == KEY_PAGEDOWN ) { // pageup and pagedown scroll the menu -- deal with these separately!!
hud_squadmsg_page_down();
return -1;
} else if ( k == KEY_PAGEUP ) {
@@ -1147,7 +1094,7 @@ int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message,
Objects[ainfo->target_objnum].flags.remove(Object::Object_Flags::Protected);
}
ai_mode = AI_GOAL_DISABLE_SHIP;
ai_mode = (The_mission.ai_profile->flags[AI::Profile_Flags::Hudsquadmsg_tactical_disarm_disable]) ? AI_GOAL_DISABLE_SHIP_TACTICAL : AI_GOAL_DISABLE_SHIP;
ai_submode = -SUBSYSTEM_ENGINE;
message = MESSAGE_DISABLE_TARGET;
break;
@@ -1161,7 +1108,7 @@ int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message,
Objects[ainfo->target_objnum].flags.remove(Object::Object_Flags::Protected);
}
ai_mode = AI_GOAL_DISARM_SHIP;
ai_mode = (The_mission.ai_profile->flags[AI::Profile_Flags::Hudsquadmsg_tactical_disarm_disable]) ? AI_GOAL_DISARM_SHIP_TACTICAL : AI_GOAL_DISARM_SHIP;
ai_submode = -SUBSYSTEM_TURRET;
message = MESSAGE_DISARM_TARGET;
break;
@@ -1433,7 +1380,7 @@ int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message,
Assert(target_shipname);
Assert(wing_team != target_team);
ai_mode = AI_GOAL_DISABLE_SHIP;
ai_mode = (The_mission.ai_profile->flags[AI::Profile_Flags::Hudsquadmsg_tactical_disarm_disable]) ? AI_GOAL_DISABLE_SHIP_TACTICAL : AI_GOAL_DISABLE_SHIP;
ai_submode = -SUBSYSTEM_ENGINE;
message = MESSAGE_DISABLE_TARGET;
break;
@@ -1442,7 +1389,7 @@ int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message,
Assert(target_shipname);
Assert(wing_team != target_team);
ai_mode = AI_GOAL_DISARM_SHIP;
ai_mode = (The_mission.ai_profile->flags[AI::Profile_Flags::Hudsquadmsg_tactical_disarm_disable]) ? AI_GOAL_DISARM_SHIP_TACTICAL : AI_GOAL_DISARM_SHIP;
ai_submode = -SUBSYSTEM_TURRET;
message = MESSAGE_DISARM_TARGET;
break;
@@ -1630,13 +1577,26 @@ void hud_squadmsg_type_select( )
{
int k, i;
int num_order_types = NUM_COMM_ORDER_TYPES;
int lua_order_count = 0;
// Now get a list of all lua categories to add. Meow.
SCP_vector<SCP_string> lua_cat_list = ai_lua_get_general_order_categories();
num_order_types += (int)lua_cat_list.size();
// Add the items
for (i = 0; i < NUM_COMM_ORDER_TYPES; i++)
for (i = 0; i < num_order_types; i++)
{
MsgItems[i].text = Comm_order_types[i];
if (i < NUM_COMM_ORDER_TYPES) {
MsgItems[i].text = Comm_order_types[i];
} else {
MsgItems[i].text = lua_cat_list[i - NUM_COMM_ORDER_TYPES];
}
MsgItems[i].active = 1; // assume active
}
Num_menu_items = NUM_COMM_ORDER_TYPES;
Num_menu_items = num_order_types;
// check to see if the player is a traitor. If so, then he will not
@@ -1667,6 +1627,13 @@ void hud_squadmsg_type_select( )
MsgItems[TYPE_REPAIR_REARM_ITEM].active = 1; // this item will always be available (I think)
MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0;
for(const auto& cat : lua_cat_list){
if (ai_lua_get_general_orders(false, false, cat).size() == 0) {
MsgItems[NUM_COMM_ORDER_TYPES + lua_order_count].active = 0;
}
lua_order_count++;
}
// AL: 10/13/97
// If the player ship communications are severely damaged, then the player
// will only be able to call for repair/rearm ships
@@ -1734,6 +1701,9 @@ do_main_menu:
hud_squadmsg_do_mode( SM_MODE_REPAIR_REARM );
} else if ( k == TYPE_REPAIR_REARM_ABORT_ITEM ) {
hud_squadmsg_do_mode( SM_MODE_REPAIR_REARM_ABORT );
} else if (k >= NUM_COMM_ORDER_TYPES) {
Lua_sqd_msg_cat = lua_cat_list[k - NUM_COMM_ORDER_TYPES];
hud_squadmsg_do_mode( SM_MODE_GENERAL );
}
}
}
@@ -2078,6 +2048,60 @@ void hud_squadmsg_ship_command()
}
}
void hud_squadmsg_msg_general()
{
int k;
Num_menu_items = 0;
for (size_t order_id = 0; order_id < Player_orders.size(); order_id++) {
Assert(Num_menu_items < MAX_MENU_ITEMS);
if (Player_orders[order_id].lua_id <= 0) {
continue;
}
auto lua_porder = ai_lua_find_player_order(Player_orders[order_id].lua_id);
//If it's not a general order then do not add it.
if (!lua_porder->generalOrder) {
continue;
}
//If it's not part of the selected category then do not add it.
if (lua_porder->category != Lua_sqd_msg_cat) {
continue;
}
//Only add it if it is enabled for the mission
if (lua_porder->cur_enabled) {
MsgItems[Num_menu_items].text = Player_orders[order_id].localized_name;
MsgItems[Num_menu_items].instance = Player_orders[order_id].lua_id;
MsgItems[Num_menu_items].active = (int)lua_porder->cur_valid;
// do some other checks to possibly gray out other items.
// if no target, remove any items which are associated with the players target
if (!hud_squadmsg_is_target_order_valid(order_id, nullptr))
MsgItems[Num_menu_items].active = 0;
Num_menu_items++;
}
}
strcpy_s(Squad_msg_title, XSTR("What Command", 321));
k = hud_squadmsg_get_key();
// when we get a valid goal, we must add the goal to the ai ship's goal list
if (k != -1) {
Assert(k < Num_menu_items);
ai_lua_start_general(MsgItems[k].instance, Player_ai->target_objnum);
hud_squadmsg_toggle();
}
}
// function to display list of command for a wing
void hud_squadmsg_wing_command()
{
@@ -2163,43 +2187,6 @@ void hud_squadmsg_wing_command()
//----------------------------------------------------------
// external entry points below!!!!
// when starting messaging mode, we must remove old bindings from the
// keys that are used for messaging mode (which will get restored when
// messaging mode is done).
// this code below will get called only the key config changes (from ControlsConfig.cpp)
// or if the bindings haven't been saved yet. This code doesn't remove the bindings
// but just sets up the array so that the bindings can be removed when messaging
// mode is entered.
//
// do_scroll indicates whether we should save the page up and page down keys
void hud_squadmsg_save_keys( int /*do_scroll*/ )
{
// int i, j;
num_keys_saved = 0;
/*
for ( j=0; j<MAX_KEYS_USED; j++ ) {
for ( i=0; Control_config[i].text[0]; i++ ) { // the text field in this structure is empty at the end of the config list
if ( Control_config[i].key_id == keys_used[j] ) { // this is true if we have a match
// if we are not saving scrolling keys and we are trying to match page up and page down
// then skip them.
if ( !do_scroll && ((keys_used[j] == KEY_PAGEDOWN) || (keys_used[j] == KEY_PAGEUP)) )
continue;
Assert( num_keys_saved < MAX_KEYS_USED );
key_save[num_keys_saved].option_num = i;
key_save[num_keys_saved].key_value = keys_used[j];
num_keys_saved++;
break; // done with this key -- move to next.
}
}
}
*/
}
// function is called once per mission start. Initializes those values
// which only need to be inited once per mission.
void hud_init_squadmsg( void )
@@ -2414,6 +2401,10 @@ int hud_squadmsg_do_frame( )
hud_squadmsg_msg_all_fighters();
break;
case SM_MODE_GENERAL:
hud_squadmsg_msg_general();
break;
default:
Int3(); // get allender -- invalid mode in messaging system
break;
+15 -1
View File
@@ -24,6 +24,7 @@
#define SM_MODE_REPAIR_REARM 7 //repair/rearm player ship
#define SM_MODE_REPAIR_REARM_ABORT 8 //abort repair/rearm of player ship
#define SM_MODE_ALL_FIGHTERS 9 //message all fighters/bombers
#define SM_MODE_GENERAL 10 //general orders, usually luaAI
// define for trapping messages send to "all fighters"
#define MESSAGE_ALL_FIGHTERS -999
@@ -60,6 +61,20 @@ class object;
// used for Message box gauge
#define NUM_MBOX_FRAMES 3
#define MAX_MENU_ITEMS 50 // max number of items in the menu
#define MAX_MENU_DISPLAY 10 // max number that can be displayed
// following are defines and character strings that are used as part of messaging mode
#define NUM_COMM_ORDER_TYPES 6
#define TYPE_SHIP_ITEM 0
#define TYPE_WING_ITEM 1
#define TYPE_ALL_FIGHTERS_ITEM 2
#define TYPE_REINFORCEMENT_ITEM 3
#define TYPE_REPAIR_REARM_ITEM 4
#define TYPE_REPAIR_REARM_ABORT_ITEM 5
typedef struct player_order {
private:
//Needed, because legacy order-id's were not assigned in order
@@ -140,7 +155,6 @@ extern void hud_init_comm_orders();
extern void hud_squadmsg_toggle(); // toggles the state of messaging mode
extern void hud_squadmsg_shortcut( int command ); // use of a shortcut key
extern int hud_squadmsg_hotkey_select( int k ); // a hotkey was hit -- maybe send a message to those ship(s)
extern void hud_squadmsg_save_keys( int do_scroll = 0 ); // saves into local area keys which need to be saved/restored when in messaging mode
extern int hud_squadmsg_do_frame();
extern int hud_query_order_issued(const char *to, const char *order_name, const char *target = nullptr, int timestamp = 0, const char *from = nullptr, const char *special_index = nullptr);
extern int hud_squadmsg_read_key( int k ); // called from high level keyboard code
+61 -36
View File
@@ -2073,7 +2073,7 @@ bool evaluate_ship_as_closest_target(esct *esct_p)
}
// bail if harmless
if ( Ship_info[esct_p->shipp->ship_info_index].class_type > -1 && !(Ship_types[Ship_info[esct_p->shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Target_as_threat])) {
if ( Ship_info[esct_p->shipp->ship_info_index].class_type < 0 || !(Ship_types[Ship_info[esct_p->shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Target_as_threat])) {
return false;
}
@@ -2758,18 +2758,12 @@ void HudGaugeOrientationTee::renderOrientation(object *from_objp, object *to_obj
y1 += position[1];
x1 += position[0];
x1 += HUD_offset_x;
y1 += HUD_offset_y;
y2 = sinf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE - T_LENGTH);
x2 = cosf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE - T_LENGTH);
y2 += position[1];
x2 += position[0];
x2 += HUD_offset_x;
y2 += HUD_offset_y;
x3 = x1 - T_BASE_LENGTH * sinf(dot_product);
y3 = y1 + T_BASE_LENGTH * cosf(dot_product);
x4 = x1 + T_BASE_LENGTH * sinf(dot_product);
@@ -2983,6 +2977,7 @@ void HudGaugeReticleTriangle::renderTriangle(vec3d *hostile_pos, int aspect_flag
float ang;
float xpos,ypos,cur_dist,sin_ang,cos_ang;
int draw_inside=0;
int tablePosX, tablePosY;
// determine if the given object is within the targeting reticle
// (which means the triangle is not drawn)
@@ -2992,6 +2987,24 @@ void HudGaugeReticleTriangle::renderTriangle(vec3d *hostile_pos, int aspect_flag
g3_rotate_vertex(&hostile_vertex, hostile_pos);
g3_project_vertex(&hostile_vertex);
if (reticle_follow) {
int nx = HUD_nose_x;
int ny = HUD_nose_y;
gr_resize_screen_pos(&nx, &ny);
gr_set_screen_scale(base_w, base_h);
gr_unsize_screen_pos(&nx, &ny);
gr_reset_screen_scale();
tablePosX = position[0] + nx;
tablePosY = position[1] + ny;
}
else {
//Technically not non-slewing, but keeps old behaviour
tablePosX = position[0] + fl2i(HUD_nose_x);
tablePosY = position[1] + fl2i(HUD_nose_y);
}
if (hostile_vertex.codes == 0) { // on screen
int projected_x, projected_y;
@@ -3003,8 +3016,8 @@ void HudGaugeReticleTriangle::renderTriangle(vec3d *hostile_pos, int aspect_flag
unsize(&projected_x, &projected_y);
mag_squared = (projected_x - position[0]) * (projected_x - position[0]) +
(projected_y - position[1]) * (projected_y - position[1]);
mag_squared = (projected_x - tablePosX) * (projected_x - tablePosX) +
(projected_y - tablePosY) * (projected_y - tablePosY);
if ( mag_squared < Radius*Radius ) {
if ( show_interior ) {
@@ -3016,28 +3029,21 @@ void HudGaugeReticleTriangle::renderTriangle(vec3d *hostile_pos, int aspect_flag
}
}
int HUD_nose_scaled_x = HUD_nose_x;
int HUD_nose_scaled_y = HUD_nose_y;
// even if the screen position overflowed, it will still be pointing in the correct direction
unsize( &hostile_vertex.screen.xyw.x, &hostile_vertex.screen.xyw.y );
gr_resize_screen_pos(&HUD_nose_scaled_x, &HUD_nose_scaled_y);
unsize( &hostile_vertex.world.xyz.x, &hostile_vertex.world.xyz.y );
ang = atan2_safe(hostile_vertex.world.xyz.y,hostile_vertex.world.xyz.x);
ang = atan2_safe(-(hostile_vertex.screen.xyw.y - tablePosY), hostile_vertex.screen.xyw.x - tablePosX);
sin_ang=sinf(ang);
cos_ang=cosf(ang);
if ( draw_inside ) {
xpos = position[0] + cos_ang*(Radius-7);
ypos = position[1] - sin_ang*(Radius-7);
xpos = tablePosX + cos_ang*(Radius-7);
ypos = tablePosY - sin_ang*(Radius-7);
} else {
xpos = position[0] + cos_ang*(Radius+4);
ypos = position[1] - sin_ang*(Radius+4);
xpos = tablePosX + cos_ang*(Radius+4);
ypos = tablePosY - sin_ang*(Radius+4);
}
xpos += HUD_offset_x + HUD_nose_x;
ypos += HUD_offset_y + HUD_nose_y;
if ( split_tri ) {
// renderTriangleMissileSplit(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
renderTriangleMissileTail(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
@@ -3577,8 +3583,8 @@ void hud_show_hostile_triangle()
continue;
}
// always ignore cargo containers and navbuoys
if ( Ship_info[sp->ship_info_index].class_type > -1 && !(Ship_types[Ship_info[sp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Show_attack_direction]) ) {
// always ignore cargo containers, navbouys, etc, and non-ships
if ( Ship_info[sp->ship_info_index].class_type < 0 || !(Ship_types[Ship_info[sp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Show_attack_direction]) ) {
continue;
}
@@ -3818,7 +3824,7 @@ void polish_predicted_target_pos(weapon_info *wip, object *targetp, vec3d *enemy
}
HudGaugeLeadIndicator::HudGaugeLeadIndicator():
HudGauge(HUD_OBJECT_LEAD, HUD_LEAD_INDICATOR, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
HudGauge3DAnchor(HUD_OBJECT_LEAD, HUD_LEAD_INDICATOR, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
{
}
@@ -4249,11 +4255,32 @@ void HudGaugeLeadSight::renderSight(int frame_offset, vec3d *target_pos, vec3d *
float reticle_target_sx = target_sx - Lead_sight_half[0] - target_lead_sx;
float reticle_target_sy = target_sy - Lead_sight_half[1] - target_lead_sy;
reticle_target_sx += position[0] + 0.5f;
reticle_target_sy += position[1] + 0.5f;
int tablePosX = position[0];
int tablePosY = position[1];
bool do_slew = reticle_follow;
if (do_slew) {
int nx = HUD_nose_x;
int ny = HUD_nose_y;
gr_resize_screen_pos(&nx, &ny);
gr_set_screen_scale(base_w, base_h);
gr_unsize_screen_pos(&nx, &ny);
gr_reset_screen_scale();
tablePosX += nx;
tablePosY += ny;
}
reticle_target_sx += tablePosX + 0.5f;
reticle_target_sy += tablePosY + 0.5f;
setGaugeColor();
renderBitmap(Lead_sight.first_frame + frame_offset, fl2i(reticle_target_sx) + fl2i(HUD_offset_x), fl2i(reticle_target_sy) + fl2i(HUD_offset_y));
//We need to do slewing manually only on the non-3D-dependant part of the hud, so make the rendering function believe that we don't slew
reticle_follow = false;
renderBitmap(Lead_sight.first_frame + frame_offset, fl2i(reticle_target_sx), fl2i(reticle_target_sy));
reticle_follow = do_slew;
}
}
@@ -6364,7 +6391,7 @@ void hud_target_clear_display_list()
}
HudGaugeOffscreen::HudGaugeOffscreen():
HudGauge(HUD_OBJECT_OFFSCREEN, HUD_OFFSCREEN_INDICATOR, false, true, VM_DEAD_VIEW | VM_OTHER_SHIP, 255, 255, 255)
HudGauge3DAnchor(HUD_OBJECT_OFFSCREEN, HUD_OFFSCREEN_INDICATOR, false, true, VM_DEAD_VIEW | VM_OTHER_SHIP, 255, 255, 255)
{
}
@@ -6495,11 +6522,9 @@ void HudGaugeOffscreen::calculatePosition(vertex* target_point, vec3d *tpos, vec
codes_or = (ubyte)(target_point->codes | eye_vertex->codes);
clip_line(&target_point,&eye_vertex,codes_or,0);
if (!(target_point->flags&PF_PROJECTED))
g3_project_vertex(target_point);
g3_project_vertex(target_point);
if (!(eye_vertex->flags&PF_PROJECTED))
g3_project_vertex(eye_vertex);
g3_project_vertex(eye_vertex);
if (eye_vertex->flags&PF_OVERFLOW) {
Int3(); // This is unlikely to happen, but can if a clip goes through the player's eye.
@@ -7416,8 +7441,8 @@ void HudGaugeHardpoints::render(float /*frametime*/)
//unsize(&xc, &yc);
//unsize(&draw_point.screen.xyw.x, &draw_point.screen.xyw.y);
renderCircle((int)draw_point.screen.xyw.x + position[0], (int)draw_point.screen.xyw.y + position[1], 10);
if (!(draw_point.flags & PF_OVERFLOW))
renderCircle((int)draw_point.screen.xyw.x + position[0], (int)draw_point.screen.xyw.y + position[1], 10);
//renderCircle(xc, yc, 25);
} else {
model_render_params weapon_render_info;
+2 -2
View File
@@ -543,7 +543,7 @@ public:
void render(float frametime) override;
};
class HudGaugeOffscreen: public HudGauge
class HudGaugeOffscreen: public HudGauge3DAnchor
{
protected:
float Max_offscreen_tri_seperation;
@@ -562,7 +562,7 @@ public:
void pageIn() override;
};
class HudGaugeLeadIndicator: public HudGauge
class HudGaugeLeadIndicator: public HudGauge3DAnchor
{
protected:
hud_frames Lead_indicator_gauge;
+6 -16
View File
@@ -39,7 +39,7 @@
#endif
extern float View_zoom;
extern fov_t View_zoom;
int Target_window_coords[GR_NUM_RESOLUTIONS][4] =
{
@@ -524,7 +524,7 @@ void HudGaugeTargetBox::renderTargetIntegrity(int disabled,int force_obj_num)
}
}
void HudGaugeTargetBox::renderTargetSetup(vec3d *camera_eye, matrix *camera_orient, float zoom)
void HudGaugeTargetBox::renderTargetSetup(vec3d *camera_eye, matrix *camera_orient, fov_t zoom)
{
// JAS: g3_start_frame uses clip_width and clip_height to determine the
// size to render to. Normally, you would set this by using gr_set_clip,
@@ -943,7 +943,7 @@ void HudGaugeTargetBox::renderTargetWeapon(object *target_objp)
vm_vec_sub(&obj_pos, &viewed_obj->pos, &viewer_obj->pos);
}
renderTargetSetup(&camera_eye, &camera_orient, View_zoom/3);
renderTargetSetup(&camera_eye, &camera_orient, View_zoom * (1.0f/3.0f));
model_clear_instance(viewed_model_num);
model_render_params render_info;
@@ -1269,7 +1269,7 @@ void HudGaugeTargetBox::renderTargetJumpNode(object *target_objp)
matrix camera_orient = IDENTITY_MATRIX;
vec3d orient_vec, up_vector;
float factor, dist;
int hx, hy, w, h;
int hx = 0, hy = 0, w, h;
SCP_list<CJumpNode>::iterator jnp;
for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
@@ -1329,10 +1329,6 @@ void HudGaugeTargetBox::renderTargetJumpNode(object *target_objp)
dist = dist * Hud_unit_multiplier;
}
// account for hud shaking
hx = fl2i(HUD_offset_x);
hy = fl2i(HUD_offset_y);
sprintf(outstr,XSTR( "d: %.0f", 340), dist);
hud_num_make_mono(outstr, font_num);
gr_get_string_size(&w,&h,outstr);
@@ -2111,19 +2107,13 @@ void HudGaugeTargetBox::showTargetData(float /*frametime*/)
break;
}
int hx, hy;
// Account for HUD shaking
hx = fl2i(HUD_offset_x);
hy = fl2i(HUD_offset_y);
// print out the target distance and speed
sprintf(outstr,XSTR( "d: %.0f%s", 350), displayed_target_distance, modifiers[Player_ai->current_target_dist_trend]);
hud_num_make_mono(outstr, font_num);
gr_get_string_size(&w,&h,outstr);
renderString(position[0] + Dist_offsets[0]+hx, position[1] + Dist_offsets[1]+hy, EG_TBOX_DIST, outstr);
renderString(position[0] + Dist_offsets[0], position[1] + Dist_offsets[1], EG_TBOX_DIST, outstr);
#if 0
current_target_speed = vm_vec_dist(&target_objp->pos, &target_objp->last_pos) / frametime;
@@ -2151,7 +2141,7 @@ void HudGaugeTargetBox::showTargetData(float /*frametime*/)
sprintf(outstr, XSTR( "s: %.0f%s", 351), displayed_target_speed, (displayed_target_speed>1)?modifiers[Player_ai->current_target_speed_trend]:"");
hud_num_make_mono(outstr, font_num);
renderString(position[0] + Speed_offsets[0]+hx, position[1] + Speed_offsets[1]+hy, EG_TBOX_SPEED, outstr);
renderString(position[0] + Speed_offsets[0], position[1] + Speed_offsets[1], EG_TBOX_SPEED, outstr);
//
// output target info for debug purposes only, this will be removed later
+1 -1
View File
@@ -128,7 +128,7 @@ public:
void renderTargetDebris(object *target_objp);
void renderTargetAsteroid(object *target_objp);
void renderTargetJumpNode(object *target_objp);
void renderTargetSetup(vec3d *camera_eye, matrix *camera_orient, float zoom);
void renderTargetSetup(vec3d *camera_eye, matrix *camera_orient, fov_t zoom);
void renderTargetClose();
void renderTargetForeground();
void renderTargetIntegrity(int disabled, int force_obj_num = -1);
+52 -44
View File
@@ -238,53 +238,61 @@ SCP_vector<Joystick*> joystick_enumerator()
* Joystick options for the new menu system
* These should be displayed as a dropdown box type widget
*/
auto JoystickOption = options::OptionBuilder<Joystick*>("Input.Joystick", "Joystick", "The current joystick 0.")
.category("Input") // Category this option shows up in the scripting heirachy
.deserializer(joystick_deserialize) // callback for json to C++
.serializer(joystick_serializer) // callback for C++ to json
.display(joystick_display) // callback for constructing the display label
.enumerator(joystick_enumerator) // callback for enumerating/constructing the values this option may take
.level(options::ExpertLevel::Beginner)
.default_val(nullptr) // initial/default value for this option
.flags({options::OptionFlags::ForceMultiValueSelection})
.importance(3)
.finish();
auto JoystickOption = options::OptionBuilder<Joystick*>("Input.Joystick",
std::pair<const char*, int>{"Joystick 0", 1705},
std::pair<const char*, int>{"The current joystick 0", 1706})
.category("Input") // Category this option shows up in the scripting heirachy
.deserializer(joystick_deserialize) // callback for json to C++
.serializer(joystick_serializer) // callback for C++ to json
.display(joystick_display) // callback for constructing the display label
.enumerator(joystick_enumerator) // callback for enumerating/constructing the values this option may take
.level(options::ExpertLevel::Beginner)
.default_val(nullptr) // initial/default value for this option
.flags({options::OptionFlags::ForceMultiValueSelection})
.importance(3)
.finish();
auto JoystickOption1 = options::OptionBuilder<Joystick*>("Input.Joystick1", "Joystick1", "The current joystick 1.")
.category("Input")
.deserializer(joystick_deserialize)
.serializer(joystick_serializer)
.display(joystick_display)
.enumerator(joystick_enumerator)
.level(options::ExpertLevel::Beginner)
.default_val(nullptr)
.flags({ options::OptionFlags::ForceMultiValueSelection })
.importance(3)
.finish();
auto JoystickOption1 = options::OptionBuilder<Joystick*>("Input.Joystick1",
std::pair<const char*, int>{"Joystick 1", 1707},
std::pair<const char*, int>{"The current joystick 1", 1708})
.category("Input")
.deserializer(joystick_deserialize)
.serializer(joystick_serializer)
.display(joystick_display)
.enumerator(joystick_enumerator)
.level(options::ExpertLevel::Beginner)
.default_val(nullptr)
.flags({ options::OptionFlags::ForceMultiValueSelection })
.importance(3)
.finish();
auto JoystickOption2 = options::OptionBuilder<Joystick*>("Input.Joystick2", "Joystick2", "The current joystick 2.")
.category("Input")
.deserializer(joystick_deserialize)
.serializer(joystick_serializer)
.display(joystick_display)
.enumerator(joystick_enumerator)
.level(options::ExpertLevel::Beginner)
.default_val(nullptr)
.flags({ options::OptionFlags::ForceMultiValueSelection })
.importance(3)
.finish();
auto JoystickOption2 = options::OptionBuilder<Joystick*>("Input.Joystick2",
std::pair<const char*, int>{"Joystick 2", 1709},
std::pair<const char*, int>{"The current joystick 2", 1710})
.category("Input")
.deserializer(joystick_deserialize)
.serializer(joystick_serializer)
.display(joystick_display)
.enumerator(joystick_enumerator)
.level(options::ExpertLevel::Beginner)
.default_val(nullptr)
.flags({ options::OptionFlags::ForceMultiValueSelection })
.importance(3)
.finish();
auto JoystickOption3 = options::OptionBuilder<Joystick*>("Input.Joystick3", "Joystick3", "The current joystick 3.")
.category("Input")
.deserializer(joystick_deserialize)
.serializer(joystick_serializer)
.display(joystick_display)
.enumerator(joystick_enumerator)
.level(options::ExpertLevel::Beginner)
.default_val(nullptr)
.flags({ options::OptionFlags::ForceMultiValueSelection })
.importance(3)
.finish();
auto JoystickOption3 = options::OptionBuilder<Joystick*>("Input.Joystick3",
std::pair<const char*, int>{"Joystick 3", 1711},
std::pair<const char*, int>{"The current joystick 3", 1712})
.category("Input")
.deserializer(joystick_deserialize)
.serializer(joystick_serializer)
.display(joystick_display)
.enumerator(joystick_enumerator)
.level(options::ExpertLevel::Beginner)
.default_val(nullptr)
.flags({ options::OptionFlags::ForceMultiValueSelection })
.importance(3)
.finish();
HatPosition convertSDLHat(int val)
{
+22 -19
View File
@@ -46,27 +46,30 @@ static haptic_effect_t pDeathroll1;
static haptic_effect_t pDeathroll2;
static haptic_effect_t pExplode;
static auto ForceFeedbackOption =
options::OptionBuilder<bool>("Input.ForceFeedback", "Force Feedback", "Enable or disable force feedback.")
.category("Input")
.level(options::ExpertLevel::Beginner)
.default_val(false)
.finish();
static auto ForceFeedbackOption = options::OptionBuilder<bool>("Input.ForceFeedback",
std::pair<const char*, int>{"Force Feedback", 1728},
std::pair<const char*, int>{"Enable or disable force feedback", 1729})
.category("Input")
.level(options::ExpertLevel::Beginner)
.default_val(false)
.finish();
static auto HitEffectOption =
options::OptionBuilder<bool>("Input.HitEffect", "Directional Hit", "Enable or disable the directional hit effect.")
.category("Input")
.level(options::ExpertLevel::Beginner)
.default_val(false)
.finish();
static auto HitEffectOption = options::OptionBuilder<bool>("Input.HitEffect",
std::pair<const char*, int>{"Directional Hit", 1730},
std::pair<const char*, int>{"Enable or disable the directional hit effect", 1731})
.category("Input")
.level(options::ExpertLevel::Beginner)
.default_val(false)
.finish();
static auto ForceFeedbackStrength = options::OptionBuilder<float>("Input.FFStrength", "Force Feedback Strength",
"The relative strength of Force Feedback effects.")
.category("Input")
.level(options::ExpertLevel::Beginner)
.range(0, 100)
.default_val(100)
.finish();
static auto ForceFeedbackStrength = options::OptionBuilder<float>("Input.FFStrength",
std::pair<const char*, int>{"Force Feedback Strength", 1756},
std::pair<const char*, int>{"The realtive strength of Force Feedback effects", 1757})
.category("Input")
.level(options::ExpertLevel::Beginner)
.range(0, 100)
.default_val(100)
.finish();
static int joy_ff_create_effects();
static int joy_ff_has_valid_effects();
+20 -11
View File
@@ -166,6 +166,8 @@ void factor_table::resize(size_t size)
}
}
// Cyborg -- You may see a linter or coverity complain about this function, since it basically discards a lot of the result of the float division.
// But using modulo division instead is actually about 60% slower based on some quick testing I did, and it gives the same results.
bool factor_table::isNaturalNumberFactor(size_t factor, size_t n)
{
return ((float)n / (float)factor) == n / factor;
@@ -641,7 +643,7 @@ extern int Framerate_delay;
extern vec3d Eye_position;
extern matrix Eye_matrix;
extern void g3_set_view_matrix(const vec3d *view_pos, const matrix *view_matrix, float zoom);
extern void g3_set_view_matrix(const vec3d *view_pos, const matrix *view_matrix, fov_t zoom);
extern int Show_cpu;
@@ -873,13 +875,17 @@ void process_debug_keys(int k)
do_subobj_hit_stuff(objp, Player_obj, &g_subobj_pos, Player_ai->targeted_subsys->system_info->subobj_num, (float) -Player_ai->targeted_subsys->system_info->type, NULL); //100.0f);
if ( sp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f ) {
mission_log_add_entry(LOG_SHIP_DISABLED, sp->ship_name, NULL );
sp->flags.set(Ship::Ship_Flags::Disabled); // add the disabled flag
if ( Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE ) {
if ( sp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f ) {
mission_log_add_entry(LOG_SHIP_DISABLED, sp->ship_name, NULL );
sp->flags.set(Ship::Ship_Flags::Disabled); // add the disabled flag
}
}
if ( sp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f ) {
mission_log_add_entry(LOG_SHIP_DISARMED, sp->ship_name, NULL );
if ( Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_TURRET ) {
if ( sp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f ) {
mission_log_add_entry(LOG_SHIP_DISARMED, sp->ship_name, NULL );
}
}
}
}
@@ -1320,7 +1326,7 @@ void process_debug_keys(int k)
break;
}
cam->set_fov(cam->get_fov() + 0.1f);
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Camera fov raised to %0.2f" , cam->get_fov());
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Camera fov raised to %0.2f" , g3_get_hfov(cam->get_fov()));
}
break;
@@ -1334,7 +1340,7 @@ void process_debug_keys(int k)
break;
}
cam->set_fov(cam->get_fov() - 0.1f);
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Camera fov lowered to %0.2f" , cam->get_fov());
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Camera fov lowered to %0.2f" , g3_get_hfov(cam->get_fov()));
}
break;
case KEY_DEBUGGED + KEY_Z:
@@ -1511,7 +1517,8 @@ void game_do_end_mission_popup()
game_stop_time();
game_stop_looped_sounds();
audiostream_pause_all();
snd_stop_all();
weapon_pause_sounds();
message_pause_all();
pf_flags = PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON;
choice = popup(pf_flags, 3, POPUP_NO, XSTR( "&Yes, Quit", 28), XSTR( "Yes, &Restart", 29), XSTR( "Do you really want to end the mission?", 30));
@@ -1532,6 +1539,8 @@ void game_do_end_mission_popup()
game_start_subspace_ambient_sound();
}
audiostream_unpause_all();
weapon_unpause_sounds();
message_resume_all();
break;
}
@@ -1831,7 +1840,7 @@ int button_function_critical(int n, net_player *p = NULL)
}
hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
if (ship_select_next_primary(objp, CYCLE_PRIMARY_NEXT)) {
if (ship_select_next_primary(objp, CycleDirection::NEXT)) {
ship* shipp = &Ships[objp->instance];
if ( timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank]) ) {
shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank] = timestamp(BANK_SWITCH_DELAY); // 1/4 second delay until can fire
@@ -1852,7 +1861,7 @@ int button_function_critical(int n, net_player *p = NULL)
}
hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
if (ship_select_next_primary(objp, CYCLE_PRIMARY_PREV)) {
if (ship_select_next_primary(objp, CycleDirection::PREV)) {
ship* shipp = &Ships[objp->instance];
if ( timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank]) ) {
shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank] = timestamp(BANK_SWITCH_DELAY); // 1/4 second delay until can fire
+21 -18
View File
@@ -56,28 +56,31 @@ int Mouse_dz = 0;
int Mouse_sensitivity = 4;
static auto MouseSensitivityOption __UNUSED =
options::OptionBuilder<int>("Input.MouseSensitivity", "Sensitivity", "The sentitivity of the mouse input.")
.category("Input")
.range(0, 9)
.level(options::ExpertLevel::Beginner)
.default_val(4)
.bind_to(&Mouse_sensitivity)
.importance(0)
.finish();
static auto MouseSensitivityOption __UNUSED = options::OptionBuilder<int>("Input.MouseSensitivity",
std::pair<const char*, int>{"Sensitivity", 1374},
std::pair<const char*, int>{"The sensitivity of the mouse input", 1747})
.category("Input")
.range(0, 9)
.level(options::ExpertLevel::Beginner)
.default_val(4)
.bind_to(&Mouse_sensitivity)
.importance(0)
.finish();
bool Use_mouse_to_fly = false;
static SCP_string mouse_mode_display(bool mode) { return mode ? "Joy-0" : "Mouse"; }
static SCP_string mouse_mode_display(bool mode) { return mode ? XSTR("Joy-0", 1699) : XSTR("Mouse", 1774); }
static auto UseMouseOption __UNUSED = options::OptionBuilder<bool>("Input.UseMouse", "Mouse", "Use the mouse for flying")
.category("Input")
.display(mouse_mode_display)
.level(options::ExpertLevel::Beginner)
.default_val(false)
.bind_to(&Use_mouse_to_fly)
.importance(1)
.finish();
static auto UseMouseOption __UNUSED = options::OptionBuilder<bool>("Input.UseMouse",
std::pair<const char*, int>{"Mouse", 1376},
std::pair<const char*, int>{"Whether or not to use the mouse for flying", 1765})
.category("Input")
.display(mouse_mode_display)
.level(options::ExpertLevel::Beginner)
.default_val(false)
.bind_to(&Use_mouse_to_fly)
.importance(1)
.finish();
const std::shared_ptr<scripting::Hook<>> OnMouseWheelHook = scripting::Hook<>::Factory(
"On Mouse Wheel", "Called when the mouse wheel is moved in any direction.",
+24
View File
@@ -20,6 +20,7 @@
#include "math/vecmat.h"
#include "model/modelrender.h"
#include "render/3d.h"
#include "options/Option.h"
SCP_vector<light> Lights;
@@ -100,6 +101,29 @@ DCF(light,"Changes lighting parameters")
}
}
// used by In-Game Options menu
static bool DeferredLightingEnabled = true;
static auto DeferredLightingOption = options::OptionBuilder<bool>("Graphics.DeferredLighting",
std::pair<const char*, int>{"Deferred Lighting", 1782},
std::pair<const char*, int>{"Enables or disables deferred lighting", 1783})
.category("Graphics")
.default_val(true)
.level(options::ExpertLevel::Advanced)
.bind_to_once(&DeferredLightingEnabled)
.importance(60)
.finish();
bool light_deferred_enabled()
{
if (Using_in_game_options) {
return DeferredLightingOption->getValue();
} else {
return !Cmdline_no_deferred_lighting;
}
}
void light_reset()
{
Static_light.clear();
+2
View File
@@ -114,4 +114,6 @@ extern int light_get_global_count();
extern int light_get_global_dir(vec3d *pos, int n);
bool light_compare_by_type(const light &a, const light &b);
bool light_deferred_enabled();
#endif
+35 -2
View File
@@ -20,6 +20,7 @@
#include "playerman/player.h"
#include "mod_table/mod_table.h"
#include <tl/optional.hpp>
// ------------------------------------------------------------------------------------------------------------
// LOCALIZE DEFINES/VARS
@@ -62,7 +63,7 @@ bool *Lcl_unexpected_tstring_check = nullptr;
// NOTE: with map storage of XSTR strings, the indexes no longer need to be contiguous,
// but internal strings should still increment XSTR_SIZE to avoid collisions.
// retail XSTR_SIZE = 1570
// #define XSTR_SIZE 1675
// #define XSTR_SIZE 1784 // This is the next available ID
// struct to allow for strings.tbl-determined x offset
@@ -481,6 +482,35 @@ void parse_tstringstbl(const char *filename)
parse_stringstbl_common(filename, true);
}
struct xstr_delayed_order {
SCP_string& toFill;
const char* name;
int xstr;
};
static void lcl_delayed_xstr_internal(tl::optional<xstr_delayed_order> to_init) {
static SCP_vector<xstr_delayed_order> delayed_init;
if (to_init) {
if (Xstr_inited)
to_init->toFill = XSTR(to_init->name, to_init->xstr);
else
delayed_init.emplace_back(std::move(*to_init));
}
else {
Assertion(Xstr_inited, "Tried to resolve delayed XSTR before XSTR init!");
for (const auto& delayed : delayed_init)
delayed.toFill = XSTR(delayed.name, delayed.xstr);
delayed_init.clear();
}
}
void lcl_delayed_xstr(SCP_string& str, const char* name, int xstr) {
lcl_delayed_xstr_internal(xstr_delayed_order{ str, name, xstr });
}
// initialize the xstr table
void lcl_xstr_init()
{
@@ -549,6 +579,8 @@ void lcl_xstr_init()
Xstr_inited = true;
lcl_delayed_xstr_internal(tl::nullopt);
}
@@ -1183,6 +1215,7 @@ const char *XSTR(const char *str, int index, bool force_lookup)
{
if(!Xstr_inited)
{
//If you're here then you should use lcl_delayed_xstr instead!
Int3();
return str;
}
@@ -1735,4 +1768,4 @@ void lcl_translate_medal_name_pl(char *name)
} else if (!strcmp(name, "SOC Unit Crest")) {
strcpy(name, "Tarcza S\xB3u\xBF\x62 Specjalnych");
}
}
}
+2
View File
@@ -135,4 +135,6 @@ void lcl_translate_targetbox_name_pl(char *name);
void lcl_translate_medal_name_gr(char *name);
void lcl_translate_medal_name_pl(char *name);
void lcl_delayed_xstr(SCP_string& str, const char* name, int xstr);
#endif // defined __FREESPACE2_LOCALIZATION_UTILITIES_HEADER_FILE
+1 -1
View File
@@ -2811,7 +2811,7 @@ void vm_matrix4_get_orientation(matrix *out, const matrix4 *m)
out->a2d[2][2] = m->a2d[2][2];
}
void vm_matrix4_get_offset(vec3d *out, matrix4 *m)
void vm_matrix4_get_offset(vec3d *out, const matrix4 *m)
{
out->xyz.x = m->vec.pos.xyzw.x;
out->xyz.y = m->vec.pos.xyzw.y;
+7 -1
View File
@@ -565,7 +565,7 @@ void vm_matrix4_set_transform(matrix4 *out, matrix *m, vec3d *v);
void vm_matrix4_get_orientation(matrix *out, const matrix4 *m);
void vm_matrix4_get_offset(vec3d *out, matrix4 *m);
void vm_matrix4_get_offset(vec3d *out, const matrix4 *m);
void vm_vec_transform(vec4 *dest, const vec4 *src, const matrix4 *m);
void vm_vec_transform(vec3d *dest, const vec3d *src, const matrix4 *m, bool pos = true);
@@ -669,6 +669,12 @@ inline vec3d operator*(const vec3d& left, float right)
vm_vec_copy_scale(&out, &left, right);
return out;
}
inline vec3d operator*(float left, const vec3d& right)
{
vec3d out;
vm_vec_copy_scale(&out, &right, left);
return out;
}
inline vec3d& operator*=(vec3d& left, float right)
{
vm_vec_scale(&left, right);
+1
View File
@@ -114,6 +114,7 @@ const char *fs2_open_credit_text =
"libpcp - Copyright (c) 2013 by Cisco Systems, Inc.\n"
"This software uses libraries from the FFmpeg project under the LGPLv2.1\n"
"Dear ImGui by Omar Cornut and contributors\n"
"dkjson.lua - Copyright (C) 2010-2014 David Heiko Kolf"
"\n"
"\n"
"\n";
+9 -1
View File
@@ -2610,7 +2610,7 @@ void parse_one_main_hall(bool replace, int num_resolutions, int &hall_idx, int &
err_msg += Main_hall_region_map[i].name;
}
Error(LOCATION, "Unkown Door Region '%s'! Expected one of: %s", temp_scp_string.c_str(), err_msg.c_str());
Error(LOCATION, "Unknown Door Region '%s'! Expected one of: %s", temp_scp_string.c_str(), err_msg.c_str());
}
m->regions[idx].action = action;
@@ -2717,3 +2717,11 @@ void main_hall_unpause()
main_hall_start_ambient();
}
/**
* Toggle the help overlay
*/
void main_hall_toggle_help(bool enable)
{
help_overlay_set_state(Main_hall_overlay_id, main_hall_get_overlay_resolution_index(), (int)enable);
}
+2
View File
@@ -216,4 +216,6 @@ void main_hall_vasudan_funny();
void main_hall_pause();
void main_hall_unpause();
void main_hall_toggle_help(bool enable);
#endif
+18 -6
View File
@@ -1804,7 +1804,7 @@ void options_multi_vox_do()
}
// if the currently selected player is muted
if((Om_vox_player_select != NULL) && !Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)]){
if((Om_vox_player_select != NULL) && options_multi_vox_plist_get(Om_vox_player_select) > -1 && !Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)]){
Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_MUTE].button.draw_forced(2);
}
@@ -1910,7 +1910,15 @@ void options_multi_vox_button_pressed(int n)
// mute/unmute button
case OM_VOX_VOICE_MUTE:
if(Om_vox_player_select != NULL){
Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)] = !Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)];
int index = options_multi_vox_plist_get(Om_vox_player_select);
// there's already an assertion for this in options_multi_vox_plist_get, just ignore in release
if (index < 0){
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
break;
}
Om_vox_player_flags[index] = !Om_vox_player_flags[index];
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
} else {
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
@@ -2105,13 +2113,17 @@ int options_multi_vox_plist_get(net_player *pl)
for(idx=0;idx<Om_vox_num_players;idx++){
if(pl == Om_vox_players[idx]){
return idx;
break;
}
}
// should neve get here. hmmm.
Int3();
return -1;
// Should never get a bad value here, but the places where this is called can now handle the negative return value, as it's not catastrophic.
// Still Assert to assist with bug tracking in debug.
Assertion(idx < Om_vox_num_players, "options_multi_vox_plist_get() could not find the correct net player. Please report to an SCP member!");
if (idx >= Om_vox_num_players)
return -1;
else
return idx;
}
// scroll the player list down
+1 -1
View File
@@ -402,7 +402,7 @@ void player_select_do()
// Goober5000 - display a popup warning about problems in the mod
if ((Global_warning_count > 10 || Global_error_count > 0) && !Startup_warning_dialog_displayed) {
char text[512];
sprintf(text, XSTR ("Warning!\n\nThe currently active mod has generated %d warnings and/or errors during program startup. These could have been caused by anything from incorrectly formated table files to corrupt models.\n\nWhile FreeSpace Open will attempt to compensate for these issues, it cannot guarantee a trouble-free gameplay experience.\n\nPlease contact the authors of the mod for assistance.", 1640), Global_warning_count + Global_error_count);
sprintf(text, XSTR ("Warning!\n\nThe currently active mod has generated %d warnings and/or errors during program startup. These could have been caused by anything from incorrectly formatted table files to corrupt models.\n\nWhile FreeSpace Open will attempt to compensate for these issues, it cannot guarantee a trouble-free gameplay experience.\n\nPlease contact the authors of the mod for assistance.", 1640), Global_warning_count + Global_error_count);
popup(PF_TITLE_BIG | PF_TITLE_RED | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, text);
Startup_warning_dialog_displayed = true;
}
+26 -104
View File
@@ -191,8 +191,8 @@ int Sim_room_slider_coords[GR_NUM_RESOLUTIONS][4] = {
#define READYROOM_FLAG_FROM_VOLITION (1<<0) // volition made
static struct {
int type; // see READYROOM_LINE_* defines above
char *name;
char *filename;
const char *name;
const char *filename;
int x; // X coordinate of line
int y; // Y coordinate of line
int flags; // special flags, see READYROOM_FLAG_* defines above
@@ -238,7 +238,7 @@ static UI_SLIDER2 Sim_room_slider;
typedef struct hash_node {
hash_node *next;
char *filename;
const char *filename;
} hash_node;
static hash_node *Campaign_mission_hash_table[CAMPAIGN_MISSION_HASH_SIZE];
@@ -294,7 +294,7 @@ int hash_filename(const char *filename) {
// insert filename into Campaign_mission_hash_table
//
// returns 1 if successful, 0 if could not allocate memory
int hash_insert(char *filename) {
int hash_insert(const char *filename) {
int hash_val = hash_filename(filename);
hash_node *cur_node;
@@ -403,7 +403,7 @@ void campaign_mission_hash_table_delete()
// add a line of sim_room smuck to end of list
int sim_room_line_add(int type, char *name, char *filename, int x, int y, int flags)
int sim_room_line_add(int type, const char *name, const char *filename, int x, int y, int flags)
{
if (Num_lines >= MAX_LINES)
return 0;
@@ -458,8 +458,7 @@ int sim_room_standalone_mission_filter(const char *filename)
int build_standalone_mission_list_do_frame(bool API_Access)
{
int font_height = gr_get_font_height();
char filename[MAX_FILENAME_LEN];
char str[256];
SCP_string popup_str;
bool lcl_weirdness = false;
// When no standalone missions in data directory
@@ -474,15 +473,12 @@ int build_standalone_mission_list_do_frame(bool API_Access)
if (Num_standalone_missions > 0) { // sanity check
if (strlen(Mission_filenames[Num_standalone_missions_with_info]) < MAX_FILENAME_LEN - 4) { // sanity check?
strcpy_s(filename, Mission_filenames[Num_standalone_missions_with_info]);
// update popup
memset(str, 0, 256);
sprintf(str, XSTR("Single Mission\n\n%s",989), filename);
popup_change_text(str);
// tack on an extension
strcat_s(filename, FS_MISSION_FILE_EXT);
auto filename = cf_add_ext(Mission_filenames[Num_standalone_missions_with_info], FS_MISSION_FILE_EXT);
// update popup
sprintf(popup_str, XSTR("Single Mission\n\n%s", 989), filename);
popup_change_text(popup_str.c_str());
// activate tstrings check
Lcl_unexpected_tstring_check = &lcl_weirdness;
@@ -516,7 +512,7 @@ int build_standalone_mission_list_do_frame(bool API_Access)
// determine some extra information
int flags = 0;
fs_builtin_mission *fb = game_find_builtin_mission(filename);
auto fb = game_find_builtin_mission(filename);
if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
flags |= READYROOM_FLAG_FROM_VOLITION;
}
@@ -545,8 +541,7 @@ int build_standalone_mission_list_do_frame(bool API_Access)
int build_campaign_mission_list_do_frame(bool API_Access)
{
int font_height = gr_get_font_height();
char str[256];
char filename[MAX_FILENAME_LEN];
SCP_string popup_str;
static int valid_missions_with_info = 0; // we use this to avoid blank entries in the mission list
// When no campaign files in data directory
@@ -556,9 +551,8 @@ int build_campaign_mission_list_do_frame(bool API_Access)
}
// change popup
memset(str, 0, 256);
sprintf(str, XSTR("Campaign Mission\n\n%s",990), Campaign.missions[Num_campaign_missions_with_info].name);
popup_change_text(str);
sprintf(popup_str, XSTR("Campaign Mission\n\n%s", 990), Campaign.missions[Num_campaign_missions_with_info].name);
popup_change_text(popup_str.c_str());
// Set global variable so we we'll have list available next time
Campaign_mission_names[Num_campaign_missions_with_info] = NULL;
@@ -569,7 +563,7 @@ int build_campaign_mission_list_do_frame(bool API_Access)
{
if (!get_mission_info(Campaign.missions[Num_campaign_missions_with_info].name))
{
strcpy_s(filename, Campaign.missions[Num_campaign_missions_with_info].name);
auto filename = Campaign.missions[Num_campaign_missions_with_info].name;
// add to list
Campaign_mission_names[Num_campaign_missions_with_info] = vm_strdup(The_mission.name);
@@ -590,7 +584,7 @@ int build_campaign_mission_list_do_frame(bool API_Access)
// determine some extra information
int flags = 0;
fs_builtin_mission *fb = game_find_builtin_mission(Campaign.missions[Num_campaign_missions_with_info].name);
auto fb = game_find_builtin_mission(Campaign.missions[Num_campaign_missions_with_info].name);
if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION))
{
flags |= READYROOM_FLAG_FROM_VOLITION;
@@ -618,7 +612,6 @@ void sim_room_build_listing()
{
int i, y, max_num_entries_viewable;
int font_height = gr_get_font_height();
char full_filename[256];
Num_lines = y = 0;
list_y = Mission_list_coords[gr_screen.res][1];
@@ -637,9 +630,8 @@ void sim_room_build_listing()
if (Standalone_mission_names[i]) {
// determine some extra information
int flags = 0;
memset(full_filename, 0, 256);
strcpy_s(full_filename, cf_add_ext(Mission_filenames[i], FS_MISSION_FILE_EXT));
fs_builtin_mission *fb = game_find_builtin_mission(full_filename);
auto full_filename = cf_add_ext(Mission_filenames[i], FS_MISSION_FILE_EXT);
auto fb = game_find_builtin_mission(full_filename);
if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
flags |= READYROOM_FLAG_FROM_VOLITION;
}
@@ -665,9 +657,8 @@ void sim_room_build_listing()
if (Campaign_mission_names[i]) {
// determine some extra information
int flags = 0;
memset(full_filename, 0, 256);
strcpy_s(full_filename, cf_add_ext(Campaign.missions[i].name, FS_MISSION_FILE_EXT));
fs_builtin_mission *fb = game_find_builtin_mission(full_filename);
auto full_filename = cf_add_ext(Campaign.missions[i].name, FS_MISSION_FILE_EXT);
auto fb = game_find_builtin_mission(full_filename);
if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
flags |= READYROOM_FLAG_FROM_VOLITION;
}
@@ -837,70 +828,6 @@ int sim_room_can_resume_savegame(char * /*savegame_filename*/)
#endif
}
// Decide wether to resume a save game or not
// exit: 1 => savegame has been restored
// 0 => no restore, proceed to briefing
// -1 => don't start mission at all
int sim_room_maybe_resume_savegame()
{
// MWA -- 3/26/98 -- removed all savegame references in game
return 0;
/*
char savegame_filename[_MAX_FNAME];
int popup_rval = -1, resume_savegame = 0;
// Generate the save-game filename for this campaign
memset(savegame_filename, 0, _MAX_FNAME);
mission_campaign_savefile_generate_root(savegame_filename);
strcat_s(savegame_filename, NOX("svg"));
// Decide if we should offer choice to resume this savegame
if ( sim_room_can_resume_savegame(savegame_filename) ) {
popup_rval = popup(0, 3, XSTR("&Cancel",-1), XSTR("&Overwrite",-1), XSTR("&Resume",-1), XSTR("A save game for this mission exists.", -1));
switch ( popup_rval ) {
case 0:
case -1:
resume_savegame = -1;
break;
case 1:
resume_savegame = 0;
break;
case 2:
resume_savegame = 1;
break;
default:
Int3();
resume_savegame = -1;
break;
}
} else {
resume_savegame = 0;
}
if (resume_savegame == 1) {
if ( state_restore_all(savegame_filename) == -1 ) {
popup_rval = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR("Error\nSaved misison could not be loaded.\nDo you wish to start this mission from the beginning?", -1));
if (popup_rval == 1) {
resume_savegame = 0;
} else {
resume_savegame = -1;
}
} else {
resume_savegame = 1;
}
}
// If we are resuming this savegame, then delete the file
if (resume_savegame == 1) {
cf_delete(savegame_filename);
}
return resume_savegame;
*/
}
int readyroom_continue_campaign()
{
// check for possible mission loop, need to do this before calling mission_campaign_next_mission()!
@@ -1052,7 +979,7 @@ void sim_room_init()
{
int i;
sim_room_buttons *b;
char wild_card[256];
char wild_card[8];
list_x1 = Mission_list_coords[gr_screen.res][0];
list_x2 = Campaign_list_coords[gr_screen.res][0];
@@ -1150,7 +1077,6 @@ void sim_room_init()
gr_flip();
Get_file_list_filter = sim_room_standalone_mission_filter;
memset(wild_card, 0, 256);
strcpy_s(wild_card, NOX("*"));
strcat_s(wild_card, FS_MISSION_FILE_EXT);
@@ -1239,7 +1165,7 @@ void sim_room_close()
//functionality without a complete refactor.
void api_sim_room_build_mission_list(bool API_Access)
{
char wild_card[256];
char wild_card[8];
Num_campaign_missions = 0;
Get_file_list_filter = sim_room_campaign_mission_filter;
@@ -1252,7 +1178,6 @@ void api_sim_room_build_mission_list(bool API_Access)
}
Get_file_list_filter = sim_room_standalone_mission_filter;
memset(wild_card, 0, 256);
strcpy_s(wild_card, NOX("*"));
strcat_s(wild_card, FS_MISSION_FILE_EXT);
@@ -1440,10 +1365,8 @@ void sim_room_do_frame(float /*frametime*/)
gr_printf_menu(list_x2, Mission_list_coords[gr_screen.res][1], "%s", buf);
// blit the proper icons if necessary
char full_name[CF_MAX_PATHNAME_LENGTH + 4];
memset(full_name, 0, CF_MAX_PATHNAME_LENGTH + 4);
strcpy_s(full_name, cf_add_ext(Campaign.filename,FS_CAMPAIGN_FILE_EXT));
fs_builtin_mission *fb = game_find_builtin_mission(full_name);
auto full_name = cf_add_ext(Campaign.filename,FS_CAMPAIGN_FILE_EXT);
auto fb = game_find_builtin_mission(full_name);
if(fb != NULL){
// sim_room_blit_icons(0, Mission_list_coords[gr_screen.res][1], fb, 0);
}
@@ -1605,8 +1528,7 @@ void campaign_room_build_listing()
if (Campaign_names[i] != NULL) {
// determine some extra information
int flags = 0;
fs_builtin_mission *fb = game_find_builtin_mission(Campaign_file_names[i]);
auto fb = game_find_builtin_mission(Campaign_file_names[i]);
if (fb != NULL) {
if (fb->flags & FSB_FROM_VOLITION) {
flags |= READYROOM_FLAG_FROM_VOLITION;
+5
View File
@@ -1082,6 +1082,7 @@ static void intel_info_init(intel_data* inteli)
inteli->desc = "";
inteli->anim_filename[0] = '\0';
inteli->flags = IIF_DEFAULT_VALUE;
inteli->custom_data = {};
}
void parse_intel_table(const char* filename)
@@ -1156,6 +1157,10 @@ void parse_intel_table(const char* filename)
stuff_string(intel_p->desc, F_MULTITEXT);
}
if (optional_string("$Custom data:")) {
parse_string_map(intel_p->custom_data, "$end_custom_data", "+Val:");
}
//retail table doesn't have #end so we have to check for the start of the next entry
//or for the end of the file instead. I have also Added #end compatibility here to
//bring the table in line with other tables for absent minded modders - Mjn
+2 -1
View File
@@ -15,11 +15,12 @@
#include "globalincs/globals.h"
#include "globalincs/pstypes.h"
typedef struct {
typedef struct intel_data {
char name[NAME_LENGTH];
SCP_string desc;
char anim_filename[NAME_LENGTH];
int flags;
SCP_map<SCP_string, SCP_string> custom_data;
} intel_data;
// flags by Goober5000
+1
View File
@@ -35,6 +35,7 @@ namespace Mission {
Toggle_start_chase_view, // Toggles (versus the default) whether the player starts the mission in chase view - Goober5000
Neb2_fog_color_override, // Whether to use explicit fog colors instead of checking the palette - Goober5000
Fullneb_background_bitmaps, // Show background bitmaps despite fullneb
Preload_subspace, // Preload the subspace tunnel for both the sexp and specs checkbox (for scripts) - MjnMixael
NUM_VALUES
};
+9 -8
View File
@@ -127,14 +127,15 @@ debriefing *Debriefing; // pointer to correct debriefing
bool Briefing_voice_enabled = true; // flag which turn on/off voice playback of briefings/debriefings
static auto BriefingVoiceOption __UNUSED = options::OptionBuilder<bool>("Audio.BriefingVoice", "Briefing voice",
"Enable or disable voice playback in the briefing.")
.category("Audio")
.level(options::ExpertLevel::Beginner)
.default_val(true)
.bind_to(&Briefing_voice_enabled)
.importance(4)
.finish();
static auto BriefingVoiceOption __UNUSED = options::OptionBuilder<bool>("Audio.BriefingVoice",
std::pair<const char*, int>{"Briefing voice", 1368},
std::pair<const char*, int>{"Enable or disable voice playback in the briefing", 1716})
.category("Audio")
.level(options::ExpertLevel::Beginner)
.default_val(true)
.bind_to(&Briefing_voice_enabled)
.importance(4)
.finish();
// --------------------------------------------------------------------------------------
// Module global data
Loaded 100 of 258 files, more files were not shown because too many files have changed in this diff. Show more