Compare commits

..
Author SHA1 Message Date
Tyler Blount 6b7fc0b6a3 Don't reset target restrictions during table load
Prevents an issue with load order causing target restrictions to be reset.
2020-05-20 14:45:24 -06:00
Tyler Blount 2e4bef0d09 Remove unneeded code and some style changes 2020-05-20 12:25:13 -06:00
Tyler Blount 172952cb0f Correctly implement fixes from #73 2020-05-20 12:25:13 -06:00
Tyler Blount 72c27bdbe8 CI and Lock fixes 2020-05-20 12:25:13 -06:00
Tyler Blount bd4de16007 Use correct comparison 2020-05-20 12:25:13 -06:00
Tyler Blount 2e560f2ddb Fix some CI errors 2020-05-20 12:25:12 -06:00
Tyler Blount d35d3d25f8 Finally fix no dumbfire
also some miscellaneous changes
2020-05-20 12:25:12 -06:00
Tyler Blount 2d5111f14b Perhaps this works? 2020-05-20 12:25:12 -06:00
Tyler Blount d95754d6b4 No Dumbfire fix
Another fix for MLMs associated with the No Dumbfire flag.
2020-05-20 12:25:12 -06:00
Tyler Blount 5e3d4c6cfd More fixes
Contains changes related to failed CI builds and feedback.
2020-05-20 12:25:12 -06:00
Tyler Blount deca820c9e Lost fixes
These were lost during a rebase at one point. Will leave this commit as separate to avoid issues in the future.
2020-05-20 12:25:12 -06:00
Swifty 21c298271b Initial commit of multilock missile features. 2020-05-20 12:25:11 -06:00
Goober5000 6ec28f6de5 Merge pull request #2389 from Goober5000/ai_goal_target_name_fix
make Goal_target_names dynamic
2020-05-19 23:27:40 -04:00
Goober5000 8667891fc2 use dynamic strings 2020-05-19 13:48:06 -04:00
Goober5000 5f74388790 add newline 2020-05-19 13:48:06 -04:00
Goober5000 067b250be9 make Goal_target_names dynamic
If ever there were more than 100 goal targets in a mission, the old version of ai_get_goal_target_name would start corrupting memory.  Rather than making a hacky workaround, this just makes it dynamic.
2020-05-19 13:48:06 -04:00
Goober5000 775984ae36 Merge pull request #2390 from Goober5000/xstr_revert
clean up handling of translated vs. untranslated English
2020-05-19 12:51:41 -04:00
Goober5000 3a7e604af9 Merge pull request #2401 from Goober5000/small_sexp_fix
make some debug code only run in debug
2020-05-19 12:50:19 -04:00
Goober5000 d4c429f3b1 Merge pull request #2394 from Goober5000/weapon_stats
Weapon statistics for comparison and extended descriptions
2020-05-19 12:49:35 -04:00
Goober5000 933f20efaa Merge pull request #2392 from Goober5000/modify_weapons_loadout_check
modify weapons loadout check
2020-05-19 12:48:59 -04:00
m!m 6ca29a79ba Merge pull request #2400 from asarium/build/clangReadability
Enable sensible clang readability checks
2020-05-19 17:04:29 +02:00
Goober5000 498e5ef42e make some debug code only run in debug
This code is highly inefficient (find_parent_operator() is O(n) for Num_sexp_nodes, and it runs every frame for all sexps with negative values) and should not actually be necessary.  But it is useful as a debug tool, so I added some #ifdef's.
2020-05-18 21:05:41 -04:00
asarium d29e236250 Enable sensible clang readability checks
This enables the clang readability checks that I found to be sensible. I
disabled a few that would have caused a lot of unnecessary warnings in
our code.
2020-05-18 20:57:19 +02:00
Goober5000 880005f758 or this 2020-05-16 14:22:46 -04:00
Goober5000 ec1a35f624 better variable name 2020-05-16 14:22:46 -04:00
Goober5000 575573334b modify weapons loadout check
Based on feedback, this PR now allows gaps in weapon loadouts, but if any gaps exist they must come at the bottom.
2020-05-16 14:22:46 -04:00
Goober5000 82b9a392e1 fix CI thing 2020-05-16 14:22:00 -04:00
Goober5000 ab2a2d46d6 make cmdline-friendly 2020-05-16 14:22:00 -04:00
Goober5000 b0f812b808 some fixes 2020-05-16 14:22:00 -04:00
Goober5000 7e8ffcec86 also list weapons whose base weapon is player-allowed 2020-05-16 14:22:00 -04:00
Goober5000 7a9bebecf6 fix multiplier based on new info 2020-05-16 14:22:00 -04:00
Goober5000 c005aa5ce2 tweak 2020-05-16 14:22:00 -04:00
Goober5000 1d00c296dd include additional missiles 2020-05-16 14:22:00 -04:00
Goober5000 dd4036d8df add useful comment 2020-05-16 14:22:00 -04:00
Goober5000 4b42829aa0 MVP weapon stats, and fix shockwave determination 2020-05-16 14:22:00 -04:00
Goober5000 7f8eaa275c weapon stats for spreadsheet 2020-05-16 14:22:00 -04:00
Goober5000 329fd023ec Merge pull request #2396 from Goober5000/2298_followup
revert the clang-tidy changes from #2298
2020-05-16 14:20:04 -04:00
Goober5000 2e0a84010e revert the clang-tidy changes from #2298
These are far too pedantic, and were not related to the feature in #2298 anyway.
2020-05-16 00:01:06 -04:00
Goober5000 9e0c37364c Merge pull request #2298 from asarium/feature/pureLuaScriptTable
Add capability to have a pure lua script
2020-05-15 15:39:46 -04:00
Goober5000 d83d1b41ee make clang happy 2020-05-15 15:34:54 -04:00
Goober5000 d26a12464c clean up handling of translated vs. untranslated English
Since the original release of FS2, internal strings have been translated to their English entries in strings.tbl but external strings have not been translated to their English entries in tstrings.tbl.  It appears that string translation was universal until it was disabled via a hack, probably due to FRED idiosyncrasies (see comment in `fred_init()`), and then not re-enabled for release.  PR #2343 re-enabled it, but this turned out to have undesirable side-effects since many mods used incorrect XSTR indexes for external strings.

This PR restores the previous behavior and also cleanly separates localization into translated and untranslated modes.  FRED will always run in untranslated mode.  FSO will run in translated mode for all languages except English, where it will run in untranslated mode UNLESS the new option `$Use tabled strings for the default language` is enabled in game_settings.tbl.
2020-05-14 19:19:52 -04:00
Goober5000 f80693213c Merge pull request #2387 from Goober5000/fix_improper_ai_get_goal_target_name
fix improper usage of ai_get_goal_target_name
2020-05-14 19:08:21 -04:00
Goober5000 dbc26548f5 Merge pull request #2377 from wookieejedi/fix-warp-flag-issues
Fix warp flag and code issues
2020-05-13 23:41:31 -04:00
Goober5000 1ef90d79c4 Apply suggestions from code review
some comment tweaks
2020-05-13 20:15:36 -04:00
Goober5000 caf3eaad98 fix improper usage of ai_get_goal_target_name
The ship goals dialog was incorrectly assigning a goal target name for every dockpoint, which could quickly cause the limit to be exceeded if the FREDder browsed a lot of dockpoints.

This also indicates a need to fix the goal target name system as the counter is never decremented.  That will be in a subsequent PR.
2020-05-13 03:05:17 -04:00
Goober5000 efbace48b8 Merge pull request #2332 from Goober5000/mantis_3147_flag
add an ai-profiles flag to govern the Mantis 3147 fix
2020-05-13 00:48:44 -04:00
Goober5000 d961fa1bb6 use mod_supports_version 2020-05-12 21:46:19 -04:00
Tyler Blount c6a999ab7a Merge pull request #2384 from TRBlount/fix/weapon_render_type
Make sure modular tables can overwrite weapon model info
2020-05-12 00:20:01 -06:00
wookieejedi 5df12322e5 Incorporate feedback 2020-05-11 18:05:21 -04:00
wookieejedi f7d474cb77 Merge branch 'master' into fix-warp-flag-issues 2020-05-11 16:35:37 -04:00
Tyler Blount 764a74dbd2 Make sure modular tables can overwrite weapon model info 2020-05-11 13:54:50 -06:00
Goober5000 1d471e254b Merge pull request #2342 from Goober5000/mantis_1947
implement Mantis 1947, treat ship comm systems the same as player comm systems
2020-05-10 15:36:45 -04:00
Goober5000 771d0c7a38 Merge pull request #2383 from wookieejedi/update-pow)-to-multiply
Update cases of pow(x,2) to just multiply values
2020-05-10 14:45:47 -04:00
wookieejedi 94bfb2fe75 Update cases of pow(x,2) to just multiply values
Having recalled that other SCP folks stated pow shoud not be used to do simple squaring, I figured it might be a good idea to update the two instances I found where that was the case.
2020-05-10 13:17:03 -04:00
Fabian Woltermann 8fd2813ddd Merge pull request #2382 from The-E/fix/particles_in_lab
do on-frame processing for particle effects in the lab
2020-05-10 17:38:11 +02:00
Fabian Woltermann a0180fc004 Init the particle system and use the correct frametime counter 2020-05-10 15:29:28 +02:00
Fabian Woltermann 7a6aa5334d do on-frame processing for particle effects in the lab 2020-05-10 13:20:18 +02:00
Goober5000 d8f388996d Merge pull request #2376 from The-E/feature/kill_ship_in_lab
Make explosions happen in the lab
2020-05-10 00:31:56 -04:00
wookieejedi da55499817 Merge pull request #2381 from wookieejedi/fix-texture-replace-with-showship
Fixes texture replace not working with show ship
2020-05-09 23:32:21 -04:00
wookieejedi e1dbcf4dd6 Add back in disabled check for future use of the engines ok function 2020-05-09 17:18:31 -04:00
wookieejedi cb5a1000a6 Fixes texture replace not working with show ship
This PR allows the `ship_render_show_ship_cockpit` function to properly detect replacement textures. Fortunately just a one line fix.
2020-05-09 15:48:34 -04:00
Fabian Woltermann 94bd8da5db Merge pull request #2373 from Goober5000/parse_tweaks
some code tweaks from an old branch
2020-05-09 20:21:01 +02:00
Fabian Woltermann 03926b8dde Merge pull request #2379 from The-E/fix/naked_int3_in_missionmessage
Remove naked int3() call in missionmessage
2020-05-09 18:44:18 +02:00
Fabian Woltermann 764bbac99f The original Volition code called Int3() here; since there is a way out that does not require us to crash, we should just not crash. 2020-05-09 10:43:13 +02:00
wookieejedi 280ff2d4f0 Incorporate feedback 2020-05-08 22:40:58 -04:00
wookieejedi 45775a0d97 One more update to the scripting function calls 2020-05-08 11:29:07 -04:00
wookieejedi c4d405e33e change to nullptr 2020-05-08 11:14:53 -04:00
wookieejedi 86e1ebaf08 fixed copy paste error with new script function 2020-05-08 10:34:20 -04:00
wookieejedi 82921467a5 Update ship.cpp 2020-05-08 09:58:35 -04:00
wookieejedi c2820f10c8 Updates and fixes multiple warp out related code 2020-05-08 09:53:32 -04:00
Goober5000 d52bddf0dc Merge pull request #2347 from EatThePath/disable-doublefire-take-2
Disable doublefire take 2
2020-05-08 02:01:40 -04:00
Fabian Woltermann cd88663a92 Avoid a signed/unsigned comparison 2020-05-08 07:56:08 +02:00
Goober5000 0e9a72a37b make clang happy 2020-05-07 21:57:18 -04:00
Fabian Woltermann 1c10479d2f Pause the AI while in the lab 2020-05-07 22:27:47 +02:00
David Gibson b88ccfb924 added an explanatory comment. Also reunited some related code I'd inadvertantly pushed apart, per review. 2020-05-07 15:09:19 -05:00
Fabian Woltermann 11e9fa27c6 REdo a few things 2020-05-07 22:00:19 +02:00
Fabian Woltermann f4f6e74456 Add new "Actions" window to the lab viewer and add a "Destroy Ship" action to make explosions happen 2020-05-07 21:35:25 +02:00
Fabian Woltermann de2e9ada72 WIP 2020-05-07 21:34:12 +02:00
Fabian Woltermann 4005a60b32 Merge pull request #2375 from The-E/fix/damage_lightning_in_lab
Make the lab's "show damage lightning" flag work again.
2020-05-07 21:31:48 +02:00
Fabian Woltermann 2e6306d85f Merge pull request #2374 from The-E/fix/texture_replacement_in_lab
Apply tabled texture replacements when loading ships in the F3 lab
2020-05-07 20:36:16 +02:00
Fabian Woltermann e1f231fcf0 Use the tabled max hull strength instead of a constant 2020-05-07 19:01:36 +02:00
Fabian Woltermann 86e898c9ea Use modern loops 2020-05-07 16:04:06 +02:00
Fabian Woltermann df1491fd9e Make the lab's "show damage lightning" flag work again.
Note that it may take a few frames for lightning to disappear when unchecking that checkbox.
2020-05-07 15:18:56 +02:00
Fabian Woltermann 0b8f203371 Apply tabled texture replacements when loading ships in the F3 lab 2020-05-07 14:48:14 +02:00
wookieejedi 1d152fcc73 Merge pull request #2371 from wookieejedi/on-depature-started-hook
Add `On Departure Started` Hook
2020-05-06 22:48:42 -04:00
Goober5000 c9fb046024 update header 2020-05-06 14:38:40 -04:00
Goober5000 c4b8020ebc some code tweaks from an old branch
Minor edits from an obsolete branch that are still useful in cleaning up the code.
2020-05-06 14:25:08 -04:00
wookieejedi a2c7a344da Add On Departure Started Hook
This PR adds a scripting hook which triggers when a ship begins it's departure process. It has one variable that is the ship. I have tested and it works as expected. This new hook is very useful for triggering events whenever a ship needs an alteration to it's departure methods. For example, in Fate of the Galaxy we require ships to warp out along a certain vector. This hook allows us to see when the ship is begining that depature and then break the ship's warp, order the ship to instead follow a waypoint path, and then jump out.
2020-05-06 09:25:10 -04:00
Goober5000 f8e5c9f7a3 Merge pull request #2343 from Goober5000/fix_mantis_2942
implement XSTR check as described in Mantis 2942
2020-05-05 22:28:20 -04:00
wookieejedi bebb69e28f Merge pull request #2369 from wookieejedi/pain-flash-hook
Adds `On Pain Flash` Scripting Hook
2020-05-05 16:59:45 -04:00
Goober5000 aef4c1ba3c address feedback 2020-05-05 14:38:50 -04:00
Goober5000 801a6031e3 Merge pull request #2367 from Goober5000/aiturret_fix
fix some code rot
2020-05-05 14:01:34 -04:00
Goober5000 4c573e2989 Merge pull request #2368 from Goober5000/support_ship_override
add two useful cheat keys
2020-05-05 14:00:59 -04:00
wookieejedi aa4e300153 switch to correct hook type 2020-05-05 13:55:57 -04:00
wookieejedi 3695f0ff93 remove unneeded CHAPAINFLASH 2020-05-05 13:51:36 -04:00
wookieejedi 7dc38f7752 Update to new script format 2020-05-05 13:46:53 -04:00
Goober5000 5ab611a8b1 this reason is only applicable in debug builds 2020-05-05 12:08:22 -04:00
Goober5000 0577b69d69 address CI complaint 2020-05-05 11:48:46 -04:00
wookieejedi 6897024b29 Adds On Pain Flash Scripting Hook
This PR adds a scripting hook which triggers when the pain flash is shown. It has one variable that lists what type of flash was shown, shield with `Pain_Type == 0` and hull with `Pain_Type == 1` .  I have tested and it works as expected.
2020-05-05 10:03:48 -04:00
Fabian Woltermann ffd5fdc764 Merge pull request #2366 from wookieejedi/carry-glowmap-status
Updates glowmap settings for a ship to also apply to ship's cockpit
2020-05-05 10:21:53 +02:00
Goober5000 e985961ed5 add two useful cheat keys
`+Shift+R will enable or disable support ships

`+Shift+U will scan or unscan the targeted ship, or ship subsystem
2020-05-05 01:02:17 -04:00
Goober5000 89a722d41a placeholder for future feature implementation 2020-05-05 00:14:16 -04:00
Goober5000 3ca187127d move comment 2020-05-05 00:01:30 -04:00
Goober5000 35e67d4574 possibly fix some code rot 2020-05-04 23:33:06 -04:00
wookieejedi 8b7253cfc1 Updates glowmap settings for a ship to also apply to ship's cockpit
The sexps that activate and deactive glowmaps are very useful for ships, though previously the glowmap status was not passed to the ship's cockpit. This PR adds the ability for glowmap status to carry to this ship's cockpit (if it has one).
2020-05-04 22:07:49 -04:00
Goober5000 7ec425f341 Merge pull request #2345 from Goober5000/fix_mantis_2791
Fix mantis 2791
2020-05-04 20:27:29 -04:00
Goober5000 e4b2791c4e Merge pull request #2353 from Goober5000/support_ship_cleanup
clean up the support ship code a bit
2020-05-04 16:13:06 -04:00
Fabian Woltermann e5c2b76469 Merge pull request #2363 from The-E/fix/post_process_aa_cmdline
Unify the way post-process antialiasing is switched on
2020-05-04 22:12:39 +02:00
Goober5000 4c5bc1fa6c Merge pull request #2360 from Goober5000/fix_2335
fix issue 2335
2020-05-04 16:12:18 -04:00
Fabian Woltermann 857a622caa Make Travis happy 2020-05-04 21:29:53 +02:00
Fabian Woltermann bdc6145b2c Deprecates the -fxaa and -smaa cmdline parameters in favour of a more generalized -aa; This also ensures that the smaa render targets are always initialized. 2020-05-04 18:30:11 +02:00
wookieejedi 07ce8a9c70 Merge pull request #2362 from wookieejedi/fix-glowmap-explode-flag
Fix Glowmap flag not carrying over to final ship explosion
2020-05-04 07:35:36 -04:00
wookieejedi e42ed47c61 Previously if a ship's glowmaps were deactivated via sexp, then the glowmaps would reaactivie at the ship's final explosinos. This PR fixes that by carrying that flag into the ship explosion FX code.
Previously if a ship's glowmaps were deactivated via sexp, then the glowmaps would reaactivie at the ship's final explosinos. This PR fixes that by carrying that flag into the ship explosion FX code.
2020-05-03 23:36:42 -04:00
Goober5000 ce3b9b62b1 Merge pull request #2336 from Goober5000/hotkey_fixes
add +Hotkey Team: to override IFF designation in hotkey list
2020-05-03 17:29:35 -04:00
Goober5000 794863c5e7 Merge pull request #2348 from Goober5000/mantis_2867
bring save format up to date (Mantis 2867)
2020-05-03 15:42:48 -04:00
asarium 544029f66f Add capability to have a pure lua script
This adds the capability to have a Lua script that does not need to use
our table syntax at all but instead defines the hooks by calling API
functions. This also makes it possible to add hook functions at runtime
if the script requires that.

Also clean up some unused debug stuff.
2020-05-03 14:33:42 +02:00
Goober5000 38e04b6151 Merge pull request #2354 from Goober5000/cue_voice_tweak
add overlooked generic file
2020-05-02 16:48:56 -04:00
Goober5000 3c76cc6524 add overlooked generic file
It turns out that the extant placeholder wav has an underscore in the filename, so add that to the list.
2020-05-02 13:54:13 -04:00
niffiwan 3c7f73f4d8 Merge pull request #2349 from Goober5000/allow_empty_weapons_on_locked_ships
allow empty weapons on locked ships
2020-05-02 20:01:23 +10:00
Fabian Woltermann 5366073356 Merge pull request #2358 from The-E/fix/aa_shader_unloading
A followup to the recent SMAA fixes
2020-05-01 13:30:56 +02:00
Fabian Woltermann 23fbdf7e64 Clean up the aa shader loading/unloading code to handle cases where we switch between FXAA and SMAA correctly 2020-05-01 10:55:20 +02:00
Goober5000 4cb3b393da clean up the support ship code a bit
Due to code rot, the support ship hull repair code was a bit more tangled than it should have been, so I tidied it up.  I also fixed a bug Sarn noticed where a support ship would think all subsystems were repaired if the hull repair rate was set to 0.
2020-04-29 01:39:18 -04:00
Goober5000 9c44e77da0 allow empty weapons on locked ships
As noticed in Discord, in loop1-2 some of your wingmen have missing weapons, but as the ships are locked it's impossible for the player to modify them.  These ships should not be part of the check.
2020-04-28 23:51:37 -04:00
Goober5000 d9d372121a missed one 2020-04-28 23:24:13 -04:00
Goober5000 6df90a8ec7 newline 2020-04-28 23:12:51 -04:00
Goober5000 289defdeff smol fix 2020-04-28 23:11:33 -04:00
Goober5000 a54ebc3114 bring save format up to date (Mantis 2867)
FSO-specific features should now all be properly marked off with checks for retail format.
2020-04-28 23:02:10 -04:00
David Gibson 4a5ff2860b now check doublefire validity flags before drawing doublefire HUD indicator 2020-04-28 16:06:20 -05:00
David Gibson f8f1b44405 Check doublefire flag validity before firing any missile 2020-04-28 14:50:33 -05:00
David Gibson f13faf4cba added new flags and flag loading for the doublefire disabling options 2020-04-28 13:57:44 -05:00
Goober5000 9dd6bcf81d warn if there is a gap in the path sequence 2020-04-28 00:39:46 -04:00
Goober5000 3356edff2b warn if model paths do not have unique names 2020-04-28 00:16:06 -04:00
Goober5000 07acdde69a fix Mantis 2791
This is a pretty easy fix; sorry it took seven years!

Instead of firing an Int3() when a ship has no arrival paths, just log it and continue.  And change the upstream warnings, which should have been more visible, from WarningEx to Warning.
2020-04-27 23:43:45 -04:00
Goober5000 c89ea8e8dd update test case 2020-04-27 23:22:34 -04:00
Goober5000 d6c1a3dec4 implement XSTR check as described in Mantis 2942
Certain mods provide their own tstrings.tbl, but this runs into trouble when you localize data meant for one mod using a tstrings.tbl meant for another mod.  See the Mantis ticket:
http://scp.indiegames.us/mantis/view.php?id=2942
and also the HLP post:
http://www.hard-light.net/forums/index.php?topic=85041.msg1716600#msg1716600

This PR adds an optional flag that will check the untranslated string against the "default language" translated string.  The two strings should be identical.  If they aren't, the code assumes there is a tstrings.tbl mismatch.

I modified the mission listing code to enable this check when localizing mission info (titles and descriptions), and to avoid adding the mission to the list if there is a mismatch.  Fortunately, I think mission listing is the only place where this bug is likely to happen.
2020-04-27 22:42:13 -04:00
Goober5000 30d41eff8b fix issue 2335
Modify the early-out condition to remain in the function if we still need to render a dumbfire lead indicator.

Fixes #2335.
2020-04-27 20:32:45 -04:00
Goober5000 cc39cfe6f2 don't send builtin messages for ships with damaged comms, and don't select them when looking for unsilenced ships either 2020-04-27 16:32:43 -04:00
Goober5000 d0daa43ccf since the code assumes all subsystems are dead if the ship is dying, add some flag checks to allow death messages to play (and be scrambled appropriately) if we have the ai_profiles flag active 2020-04-27 02:30:42 -04:00
Goober5000 e0cd2806f8 move quick-abort checks to before message priority checking
From the comments:

	// Do some checks to see if we are actually going to play this message at all.  These checks have been moved above the
	// "if ( Num_messages_playing )" block because we don't want to cancel an existing message if we have nothing to replace it with.

Additionally, by not incrementing `Num_playing_messages` if the code skips to `all_done`, this prevents messages that aren't playing from delaying messages in the queue that are actually valid and should play.
2020-04-27 02:24:26 -04:00
Goober5000 4f026a66ab fix small bug where speech was stopped when it wasn't supposed to be 2020-04-27 02:17:42 -04:00
Goober5000 28df82a03f fix bug where scramble status overrode subystem status 2020-04-27 02:13:08 -04:00
Goober5000 06f1b7d872 fix some int/bool stuff 2020-04-26 18:16:08 -04:00
Goober5000 1dfe3a8d9f implement Mantis 1947 feature request
This adds an ai_profiles flag that checks comm systems for non-player ships.  If their comm system is damaged, they will not send messages and you will not be able to issue squadmsg orders.

See also:
http://scp.indiegames.us/mantis/view.php?id=1947
2020-04-26 18:08:28 -04:00
Goober5000 aa46ae6639 add version-specific enabling of bugfix flags 2020-04-26 17:13:13 -04:00
Goober5000 c8b1f56a09 missed one 2020-04-16 13:37:36 -04:00
Goober5000 8cc9eb2352 change to bool, for great C++ justice 2020-04-16 13:20:39 -04:00
Goober5000 3d08119734 add +Hotkey Team: to override IFF designation in hotkey list
This is in response to Mantis 1424:
http://scp.indiegames.us/mantis/view.php?id=1424

Since retail, the hotkey lists have been determined based on what attacks the player.  Mod makers may want to change this, so this PR adds the following options:

Default - retail behavior
Friendly/Enemy (or Hostile) - display in the specified list regardless of who attacks whom
None - don't display in the hotkey list at all

This PR also slightly cleans up the hotkey list function with a small optimization and a few tweaky fixes for edge cases.
2020-04-15 13:47:11 -04:00
Goober5000 6f5e500a1e add an ai-profiles flag to govern the Mantis 3147 fix 2020-04-14 17:50:16 -04:00
83 changed files with 3440 additions and 1272 deletions

No files matched your search

+13
View File
@@ -14,6 +14,17 @@ misc-*,\
-misc-static-assert,\
-misc-unused-parameters,\
-misc-non-private-member-variables-in-classes,\
readability-*,\
-readability-function-size,\
-readability-identifier-naming,\
-readability-implicit-bool-conversion,\
-readability-inconsistent-declaration-parameter-name,\
-readability-isolate-declaration,\
-readability-magic-numbers,\
-readability-named-parameter,\
-readability-qualified-auto,\
-readability-uppercase-literal-suffix,\
-readability-else-after-return,\
"
WarningsAsErrors: ''
HeaderFilterRegex: 'code/.*$|freespace2/.*$|qtfred/.*$|test/src/.*$|build/.*$'
@@ -23,5 +34,7 @@ CheckOptions:
value: 'assert,Assert,Assertion'
- key: 'modernize-use-emplace.ContainersWithPushBack'
value: 'std::vector;std::deque;std::list;SCP_vector;SCP_deque;SCP_list'
- key: 'readability-braces-around-statements.ShortStatementLines'
value: '4' # Avoid flagging simple if (...) return false; statements
...
+15 -13
View File
@@ -18,8 +18,7 @@
#include "ship/ship.h"
int Total_goal_target_names = 0;
char Goal_target_names[MAX_GOAL_TARGET_NAMES][NAME_LENGTH];
SCP_vector<char *> Goal_target_names;
ai_info Ai_info[MAX_AI_INFO];
ai_info *Player_ai;
@@ -82,22 +81,25 @@ void set_wingnum(int objnum, int wingnum)
Ai_info[ai_index].wing = wingnum;
}
char *ai_get_goal_target_name(const char *name, int *index)
const char *ai_get_goal_target_name(const char *name, int *index)
{
Assert(name != NULL);
Assert(index != NULL);
int i;
Assertion(name != nullptr && index != nullptr, "Arguments cannot be null!");
for (i=0; i < Total_goal_target_names; i++)
for (int i = 0; i < static_cast<int>(Goal_target_names.size()); ++i) {
if (!stricmp(name, Goal_target_names[i])) {
*index = i;
return Goal_target_names[i];
}
}
Assert(Total_goal_target_names < MAX_GOAL_TARGET_NAMES);
Assertion(strlen(name) <= NAME_LENGTH - 1, "Goal target name %s is too long. Needs to be 31 characters or less.", name);
i = Total_goal_target_names++;
strcpy_s(Goal_target_names[i], name);
*index = i;
return Goal_target_names[i];
*index = static_cast<int>(Goal_target_names.size());
Goal_target_names.push_back(vm_strdup(name));
return Goal_target_names[*index];
}
void ai_clear_goal_target_names()
{
for (auto ptr : Goal_target_names)
vm_free(ptr);
Goal_target_names.clear();
}
+5 -8
View File
@@ -83,7 +83,7 @@ typedef struct ai_goal {
fix time; // time at which this goal was issued.
int priority; // how important is this goal -- number 0 - 100
char *target_name; // name of the thing that this goal acts upon
const char *target_name; // name of the thing that this goal acts upon
int target_name_index; // index of goal_target_name in Goal_target_names[][]
waypoint_list *wp_list; // waypoints that this ship might fly.
int target_instance; // instance of thing this ship might be chasing (currently only used for weapons; note, not the same as objnum!)
@@ -93,19 +93,17 @@ typedef struct ai_goal {
// (AIGF_DOCKER_INDEX_VALID and AIGF_DOCKEE_INDEX_VALID tell us to use indexes; otherwise we use names)
// these are the dockpoints used on the docker and dockee ships, not the ships themselves
union {
char *name;
const char *name;
int index;
} docker;
union {
char *name;
const char *name;
int index;
} dockee;
} ai_goal;
#define MAX_GOAL_TARGET_NAMES 100
#define AIM_CHASE 0
#define AIM_EVADE 1
#define AIM_GET_BEHIND 2
@@ -513,8 +511,6 @@ typedef struct {
#define BURST_DURATION 500 // decay time over which Player->damage_this_burst falls from MAX_BURST_DAMAGE to 0
extern int Mission_all_attack; // !0 means all teams attack all teams.
extern int Total_goal_target_names;
extern char Goal_target_names[MAX_GOAL_TARGET_NAMES][NAME_LENGTH];
extern void update_ai_info_for_hit(int hitter_obj, int hit_obj);
extern void ai_frame_all(void);
@@ -556,7 +552,8 @@ void ai_process( object * obj, int ai_index, float frametime );
int get_wingnum(int objnum);
void set_wingnum(int objnum, int wingnum);
char *ai_get_goal_target_name(const char *name, int *index);
const char *ai_get_goal_target_name(const char *name, int *index);
void ai_clear_goal_target_names();
extern void init_ai_system(void);
extern void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp, int ship_info_index = -1);
+4
View File
@@ -84,13 +84,17 @@ namespace AI {
Assist_scoring_scales_with_damage,
Beams_damage_weapons,
Big_ships_can_attack_beam_turrets_on_untargeted_ships,
Check_comms_for_non_player_ships,
Disable_linked_fire_penalty,
Disable_player_secondary_doublefire,
Disable_ai_secondary_doublefire,
Disable_weapon_damage_scaling,
Dont_insert_random_turret_fire_delay,
Fix_ai_class_bug,
Fix_ai_path_order_bug,
Fix_heat_seeker_stealth_bug,
Fix_linked_primary_bug,
Fix_ramming_stationary_targets_bug,
Force_beam_turret_fov,
Free_afterburner_use,
Glide_decay_requires_thrust,
+28 -2
View File
@@ -7,11 +7,11 @@
#include "globalincs/pstypes.h"
#include "def_files/def_files.h"
#include "ai/ai_profiles.h"
#include "def_files/def_files.h"
#include "globalincs/pstypes.h"
#include "localization/localize.h"
#include "mod_table/mod_table.h"
#include "parse/parselo.h"
#include "ship/ship.h"
#include "weapon/weapon.h"
@@ -371,10 +371,16 @@ void parse_ai_profiles_tbl(const char *filename)
set_flag(profile, "$navigation subsystem governs warpout capability:", AI::Profile_Flags::Navigation_subsys_governs_warp);
set_flag(profile, "$check communications for non-player ships:", AI::Profile_Flags::Check_comms_for_non_player_ships);
set_flag(profile, "$ignore lower bound for minimum speed of docked ship:", AI::Profile_Flags::No_min_dock_speed_cap);
set_flag(profile, "$disable linked fire penalty:", AI::Profile_Flags::Disable_linked_fire_penalty);
set_flag(profile, "$disable player secondary doublefire:", AI::Profile_Flags::Disable_player_secondary_doublefire);
set_flag(profile, "$disable ai secondary doublefire:", AI::Profile_Flags::Disable_ai_secondary_doublefire);
set_flag(profile, "$disable weapon damage scaling:", AI::Profile_Flags::Disable_weapon_damage_scaling);
set_flag(profile, "$use additive weapon velocity:", AI::Profile_Flags::Use_additive_weapon_velocity);
@@ -391,6 +397,8 @@ void parse_ai_profiles_tbl(const char *filename)
set_flag(profile, "$fix linked primary weapon decision bug:", AI::Profile_Flags::Fix_linked_primary_bug);
set_flag(profile, "$fix ramming stationary targets bug:", AI::Profile_Flags::Fix_ramming_stationary_targets_bug);
set_flag(profile, "$prevent turrets targeting too distant bombs:", AI::Profile_Flags::Prevent_targeting_bombs_beyond_range);
set_flag(profile, "$smart subsystem targeting for turrets:", AI::Profile_Flags::Smart_subsystem_targeting_for_turrets);
@@ -624,4 +632,22 @@ void ai_profile_t::reset()
for (int i = 0; i <= MAX_DETAIL_LEVEL; ++i) {
detail_distance_mult[i] = 0;
}
// via Github #2332, enable bugfixes if we are targeting version 20 and up
if (mod_supports_version(20, 0, 0)) {
flags.set(AI::Profile_Flags::Huge_turret_weapons_ignore_bombs);
flags.set(AI::Profile_Flags::Fix_linked_primary_bug);
flags.set(AI::Profile_Flags::Prevent_targeting_bombs_beyond_range);
flags.set(AI::Profile_Flags::Fix_heat_seeker_stealth_bug);
flags.set(AI::Profile_Flags::Allow_vertical_dodge);
flags.set(AI::Profile_Flags::Fix_ai_class_bug);
flags.set(AI::Profile_Flags::Ai_guards_specific_ship_in_wing);
flags.set(AI::Profile_Flags::Fix_ai_path_order_bug);
flags.set(AI::Profile_Flags::Aspect_invulnerability_fix);
flags.set(AI::Profile_Flags::Use_actual_primary_range);
}
// this flag has been enabled ever since 3.7.2
if (mod_supports_version(3, 7, 2)) {
flags.set(AI::Profile_Flags::Fix_ramming_stationary_targets_bug);
}
}
+27 -8
View File
@@ -66,6 +66,7 @@
#include "ship/ship.h"
#include "ship/shipfx.h"
#include "ship/shiphit.h"
#include "ship/subsysdamage.h"
#include "weapon/beam.h"
#include "weapon/flak.h"
#include "weapon/swarm.h"
@@ -274,7 +275,7 @@ typedef struct {
int team;
int weapon_index;
int max_fire_count;
char *shipname;
const char *shipname;
} huge_fire_info;
SCP_vector<huge_fire_info> Ai_huge_fire_info;
@@ -5674,7 +5675,16 @@ int ai_fire_primary_weapon(object *objp)
float dot;
vec3d v2t;
if ( !(vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE ) && !(aip->submode == SM_AVOID) ) {
bool condition;
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ramming_stationary_targets_bug]) {
// fixed by Mantis 3147 - Avoid bashing orientation if trying to avoid a collision.
condition = !(vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE) && (aip->submode != SM_AVOID);
} else {
// retail behavior
condition = !(vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE);
}
if ( condition ) {
if ( vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
dot = vm_vec_dot(&v2t, &objp->orient.vec.fvec);
@@ -8224,9 +8234,15 @@ void ai_chase()
if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f)) {
predicted_enemy_pos = enemy_pos;
} else if (aip->ai_flags[AI::AI_Flags::Seek_lock]) {
set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &aip->last_aim_enemy_pos, &aip->last_aim_enemy_vel, aip); // Set G_fire_pos
predicted_enemy_pos = enemy_pos;
G_predicted_pos = predicted_enemy_pos;
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ramming_stationary_targets_bug]) {
// fixed by Mantis 3147 - Bash orientation if aspect seekers are equipped.
set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &aip->last_aim_enemy_pos, &aip->last_aim_enemy_vel, aip); // Set G_fire_pos
predicted_enemy_pos = enemy_pos;
G_predicted_pos = predicted_enemy_pos;
} else {
// retail behavior
predicted_enemy_pos = enemy_pos;
}
} else {
// Set predicted_enemy_pos.
// See if attacking a subsystem.
@@ -12552,13 +12568,13 @@ int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int allowed_path_
polymodel *pm = model_get( Ship_info[parent_shipp->ship_info_index].model_num );
ship_bay *bay = pm->ship_bay;
if ( bay == NULL ) {
WarningEx(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay exists on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
if ( bay == nullptr ) {
Warning(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay exists on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
return -1;
}
if ( bay->num_paths <= 0 ) {
WarningEx(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay paths exist on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
Warning(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay paths exist on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
return -1;
}
@@ -14530,6 +14546,9 @@ void process_friendly_hit_message( int message, object *objp )
if (Ships[objp->instance].flags[Ship::Ship_Flags::No_builtin_messages]) {
index = -1;
}
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(&Ships[objp->instance]) <= COMM_DAMAGED) {
index = -1;
}
// Karajorma - pick a random ship to send Command messages if command is silenced.
if (index < 0 && (The_mission.flags[Mission::Mission_Flags::No_builtin_command]) ) {
+10 -14
View File
@@ -310,7 +310,7 @@ void ai_clear_wing_goals( int wingnum )
void ai_mission_wing_goal_complete( int wingnum, ai_goal *remove_goalp )
{
int mode, submode, priority, i;
char *name;
const char *name;
ai_goal *aigp;
wing *wingp;
@@ -388,7 +388,7 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai
{
int i, j;
ai_goal *purge_goal;
char *name;
const char *name;
int mode, ship_index, wingnum;
// get locals for easer access
@@ -1413,21 +1413,17 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
{
ship *shipp = &Ships[objp->instance];
// always valid if has subspace drive
if (!(shipp->flags[Ship::Ship_Flags::No_subspace_drive]))
// always valid if has working subspace drive, not disabled, and not limited by subsystem strength
if ( ship_can_warp_full_check(shipp) )
return AI_GOAL_ACHIEVABLE;
// if no subspace drive, only valid if our mothership is present
// check that we have a mothership and that we can depart to it
if (shipp->departure_location == DEPART_AT_DOCK_BAY)
{
int anchor_shipnum = ship_name_lookup(Parse_names[shipp->departure_anchor]);
if (anchor_shipnum >= 0 && ship_useful_for_departure(anchor_shipnum, shipp->departure_path_mask))
return AI_GOAL_ACHIEVABLE;
// if we can't warp, we can only depart if the ship (or its wing) departs to a fighter bay and the mothership is present
if (ship_can_bay_depart(shipp)) {
return AI_GOAL_ACHIEVABLE;
}
else {
return AI_GOAL_NOT_KNOWN;
}
return AI_GOAL_NOT_KNOWN;
}
+6 -11
View File
@@ -2544,7 +2544,6 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
// Ok, the turret is lined up... now line up a particular gun.
bool ok_to_fire = false;
bool something_was_ok_to_fire=false;
vec3d tv2e; //so flak can get their jitter without screwing up other guns
if (in_fov ) {
@@ -2581,7 +2580,9 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
ss->turret_next_fire_pos = ffp_pos;
}
ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
// We're ready to fire... now get down to specifics, like where is the
// actual gun point and normal, not just the one for whole turret.
ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
// Fire in the direction the turret is facing, not right at the target regardless of turret dir.
vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
@@ -2592,12 +2593,6 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
wip = get_turret_weapon_wip(&ss->weapons, valid_weapons[0]);
else
wip = get_turret_weapon_wip(&ss->weapons, valid_weapons[i]);
// We're ready to fire... now get down to specifics, like where is the
// actual gun point and normal, not just the one for whole turret.
// moved here as if there are two weapons with indentical fire stamps
// they would have shared the fire point.
tv2e = gvec;
// make sure to reset this for current weapon
ok_to_fire = false;
@@ -2605,7 +2600,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
// if the weapon is a flak gun, add some jitter to its aim so it fires in a "cone"
// to make a cool visual effect and make them less lethal
if(wip->wi_flags[Weapon::Info_Flags::Flak]){
flak_jitter_aim(&tv2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS), wip);
flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS), wip);
}
// Fire if:
@@ -2716,9 +2711,9 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
//Pass along which gun we are using
if (tp->flags[Model::Subsystem_Flags::Turret_salvo])
turret_fire_weapon(valid_weapons[0], ss, parent_objnum, &gpos, &tv2e, &predicted_enemy_pos);
turret_fire_weapon(valid_weapons[0], ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);
else
turret_fire_weapon(valid_weapons[i], ss, parent_objnum, &gpos, &tv2e, &predicted_enemy_pos);
turret_fire_weapon(valid_weapons[i], ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);
} else {
// make sure salvo fire mode does not turn into autofire
if ((tp->flags[Model::Subsystem_Flags::Turret_salvo]) && ((i + 1) == number_of_firings)) {
+1 -1
View File
@@ -57,7 +57,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
{ CF_TYPE_MAPS, "data" DIR_SEPARATOR_STR "maps", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
{ CF_TYPE_TEXT, "data" DIR_SEPARATOR_STR "text", ".txt .net", CF_TYPE_DATA },
{ CF_TYPE_MODELS, "data" DIR_SEPARATOR_STR "models", ".pof", CF_TYPE_DATA },
{ CF_TYPE_TABLES, "data" DIR_SEPARATOR_STR "tables", ".tbl .tbm", CF_TYPE_DATA },
{ CF_TYPE_TABLES, "data" DIR_SEPARATOR_STR "tables", ".tbl .tbm .lua", CF_TYPE_DATA },
{ CF_TYPE_SOUNDS, "data" DIR_SEPARATOR_STR "sounds", ".wav .ogg", CF_TYPE_DATA },
{ CF_TYPE_SOUNDS_8B22K, "data" DIR_SEPARATOR_STR "sounds" DIR_SEPARATOR_STR "8b22k", ".wav .ogg", CF_TYPE_SOUNDS },
{ CF_TYPE_SOUNDS_16B11K, "data" DIR_SEPARATOR_STR "sounds" DIR_SEPARATOR_STR "16b11k", ".wav .ogg", CF_TYPE_SOUNDS },
+72 -33
View File
@@ -162,8 +162,7 @@ Flag exe_params[] =
{ "-3dshockwave", "Enable 3D shockwaves", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-3dshockwave" },
{ "-no_post_process", "Disable post-processing", true, EASY_DEFAULT | EASY_HI_MEM_OFF, EASY_ALL_ON | EASY_HI_MEM_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_post_process" },
{ "-soft_particles", "Enable soft particles", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-soft_particles" },
{ "-fxaa", "Enable FXAA anti-aliasing", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fxaa" },
{ "-smaa", "Enable SMAA anti-aliasing", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-smaa" },
{ "-aa", "Enable Post-process anti-aliasing", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-aa" },
{ "-nolightshafts", "Disable lightshafts", true, EASY_DEFAULT | EASY_HI_MEM_OFF, EASY_ALL_ON | EASY_HI_MEM_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nolightshafts"},
{ "-fb_explosions", "Enable Framebuffer Shockwaves", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fb_explosions", },
{ "-fb_thrusters", "Enable Framebuffer Thrusters", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fb_thrusters", },
@@ -231,7 +230,8 @@ Flag exe_params[] =
{ "-pos", "Show position of camera", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-pos", },
{ "-stats", "Show statistics", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-stats", },
{ "-coords", "Show coordinates", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-coords", },
{ "-pofspew", "Generate all ibx files immediately", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-pofspew", },
{ "-pofspew", "Dump model information to pofspew.txt", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-pofspew", },
{ "-weaponspew", "Dump weapon stats and spreadsheets", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-weaponspew", },
{ "-tablecrcs", "Dump table CRCs for multi validation", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-tablecrcs", },
{ "-missioncrcs", "Dump mission CRCs for multi validation", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-missioncrcs", },
{ "-dis_collisions", "Disable collisions", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-dis_collisions", },
@@ -275,6 +275,7 @@ cmdline_parm port_arg("-port", "Multiplayer network port", AT_INT);
cmdline_parm multilog_arg("-multilog", NULL, AT_NONE); // Cmdline_multi_log
cmdline_parm client_dodamage("-clientdamage", NULL, AT_NONE); // Cmdline_client_dodamage
cmdline_parm pof_spew("-pofspew", NULL, AT_NONE); // Cmdline_spew_pof_info
cmdline_parm weapon_spew("-weaponspew", nullptr, AT_NONE); // Cmdline_spew_weapon_stats
cmdline_parm mouse_coords("-coords", NULL, AT_NONE); // Cmdline_mouse_coords
cmdline_parm timeout("-timeout", "Multiplayer network timeout (secs)", AT_INT); // Cmdline_timeout
cmdline_parm bit32_arg("-32bit", "Deprecated", AT_NONE); // (only here for retail compatibility reasons, doesn't actually do anything)
@@ -296,6 +297,7 @@ int Cmdline_multi_stream_chat_to_file = 0;
int Cmdline_network_port = -1;
int Cmdline_restricted_game = 0;
int Cmdline_spew_pof_info = 0;
bool Cmdline_spew_weapon_stats = false;
int Cmdline_start_netgame = 0;
int Cmdline_timeout = -1;
int Cmdline_use_last_pilot = 0;
@@ -322,10 +324,12 @@ cmdline_parm enable_3d_shockwave_arg("-3dshockwave", NULL, AT_NONE);
cmdline_parm softparticles_arg("-soft_particles", NULL, AT_NONE);
cmdline_parm no_postprocess_arg("-no_post_process", "Disables post-processing", AT_NONE);
cmdline_parm bloom_intensity_arg("-bloom_intensity", "Set bloom intensity, requires post-processing", AT_INT);
cmdline_parm fxaa_arg("-fxaa", NULL, AT_NONE);
cmdline_parm fxaa_preset_arg("-fxaa_preset", "FXAA quality (0-9), requires post-processing and -fxaa", AT_INT);
cmdline_parm smaa_arg("-smaa", nullptr, AT_NONE);
cmdline_parm smaa_preset_arg("-smaa_preset", "SMAA quality (0-9), requires post-processing and -smaa", AT_INT);
cmdline_parm post_process_aa_arg("-aa", "Enables post-process antialiasing", AT_NONE);
cmdline_parm post_process_aa_preset_arg("-aa_preset", "Sets the AA effect to use. See the wiki for details", AT_INT);
cmdline_parm deprecated_fxaa_arg("-fxaa", nullptr, AT_NONE);
cmdline_parm deprecated_fxaa_preset_arg("-fxaa_preset", "FXAA quality (0-2), requires post-processing and -fxaa", AT_INT);
cmdline_parm deprecated_smaa_arg("-smaa", nullptr, AT_NONE);
cmdline_parm deprecated_smaa_preset_arg("-smaa_preset", "SMAA quality (0-3), requires post-processing and -smaa", AT_INT);
cmdline_parm fb_explosions_arg("-fb_explosions", NULL, AT_NONE);
cmdline_parm fb_thrusters_arg("-fb_thrusters", NULL, AT_NONE);
cmdline_parm flightshaftsoff_arg("-nolightshafts", NULL, AT_NONE);
@@ -1667,6 +1671,11 @@ bool SetCmdlineParams()
Cmdline_spew_pof_info = 1;
}
// spew weapon stats
if (weapon_spew.found()) {
Cmdline_spew_weapon_stats = true;
}
// mouse coords
if(mouse_coords.found()){
Cmdline_mouse_coords = 1;
@@ -1863,40 +1872,31 @@ bool SetCmdlineParams()
if ( height_arg.found() ) {
Cmdline_height = 0;
}
if (fxaa_arg.found() ) {
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
if (fxaa_preset_arg.found()) {
auto val = fxaa_preset_arg.get_int();
if (val > 6) {
Gr_aa_mode = AntiAliasMode::FXAA_High;
} else if (val > 3) {
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
} else {
if (post_process_aa_arg.found() || post_process_aa_preset_arg.found()) {
Gr_aa_mode = AntiAliasMode::SMAA_Low;
if (post_process_aa_preset_arg.found()) {
switch (post_process_aa_preset_arg.get_int()) {
case 0:
Gr_aa_mode = AntiAliasMode::FXAA_Low;
}
}
}
if (smaa_arg.found()) {
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
if (smaa_preset_arg.found()) {
switch (smaa_preset_arg.get_int()) {
case 0:
break;
case 1:
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
break;
case 2:
Gr_aa_mode = AntiAliasMode::FXAA_High;
break;
case 3:
Gr_aa_mode = AntiAliasMode::SMAA_Low;
break;
case 1:
case 4:
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
break;
case 2:
case 5:
Gr_aa_mode = AntiAliasMode::SMAA_High;
break;
case 3:
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
break;
default:
case 6:
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
break;
}
@@ -2210,6 +2210,45 @@ bool SetCmdlineParams()
if (deprecated_postprocess_arg.found()) {
Cmdline_deprecated_postprocess = true;
}
if (deprecated_fxaa_arg.found() ) {
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
if (deprecated_fxaa_preset_arg.found()) {
auto val = deprecated_fxaa_preset_arg.get_int();
if (val > 6) {
Gr_aa_mode = AntiAliasMode::FXAA_High;
} else if (val > 3) {
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
} else {
Gr_aa_mode = AntiAliasMode::FXAA_Low;
}
}
}
if (deprecated_smaa_arg.found()) {
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
if (deprecated_smaa_preset_arg.found()) {
switch (deprecated_smaa_preset_arg.get_int()) {
case 0:
Gr_aa_mode = AntiAliasMode::SMAA_Low;
break;
case 1:
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
break;
case 2:
Gr_aa_mode = AntiAliasMode::SMAA_High;
break;
case 3:
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
break;
default:
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
break;
}
}
}
return true;
}
+2
View File
@@ -149,5 +149,7 @@ extern bool Cmdline_show_video_info;
extern bool Cmdline_debug_window;
extern bool Cmdline_graphics_debug_output;
extern bool Cmdline_log_to_stdout;
extern bool Cmdline_spew_weapon_stats;
#endif
+2 -2
View File
@@ -286,7 +286,7 @@ std::unique_ptr<DecoderStatus> initializeStatus(std::unique_ptr<InputStream>& st
if (status->subtitleStream == nullptr) {
// We don't have an external subtitle stream or loading from that file has failed
auto& current_language = Lcl_languages[Lcl_current_lang];
auto& current_language = Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang];
for (uint32_t i = 0; i < ctx->nb_streams; ++i) {
auto test_stream = ctx->streams[i];
@@ -428,7 +428,7 @@ std::unique_ptr<InputStream> openInputStream(const SCP_string& name) {
std::unique_ptr<InputStream> openSubtitleStream(const SCP_string& name)
{
auto& current_language = Lcl_languages[Lcl_current_lang];
auto& current_language = Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang];
for (auto ext : CHECKED_SUBT_EXTENSIONS) {
SCP_string fileName;
+5 -4
View File
@@ -240,11 +240,12 @@ void debris_process_post(object * obj, float frame_time)
MONITOR_INC(NumSmallDebris,1);
}
if ( db->lifeleft >= 0.0f) {
if (db->lifeleft >= 0.0f) {
db->lifeleft -= frame_time;
if ( db->lifeleft < 0.0f ) {
debris_start_death_roll(obj, db);
}
}
if ( db->lifeleft < 0.0f ) {
debris_start_death_roll(obj, db);
}
maybe_delete_debris(db); // Make this debris go away if it's very far away.
+1 -1
View File
@@ -45,7 +45,7 @@ void main()
fragDepthLinear = ( 2.0 * farZ * nearZ ) / ( farZ + nearZ - gl_FragCoord.z * (farZ-nearZ) );
}
// assume UV of 0.5, 0.5 is the centroid of this sphere volume
float depthOffset = sqrt(pow(fragRadius, 2.0) - pow(offset_len, 2.0));
float depthOffset = sqrt((fragRadius*fragRadius) - (offset_len*offset_len));
float frontDepth = fragDepthLinear - depthOffset;
float backDepth = fragDepthLinear + depthOffset;
float intensity = smoothstep(max(nearZ, frontDepth), backDepth, sceneDepthLinear);
+1 -1
View File
@@ -55,7 +55,7 @@ struct version {
version() = default;
version(int major, int minor, int build, int revision);
version(int major, int minor = 0, int build = 0, int revision = 0);
bool isValid() const;
+8 -16
View File
@@ -7,12 +7,15 @@
*
*/
#include "2d.h"
#ifdef _WIN32
#include <windows.h>
#include <windowsx.h>
#endif
#include "globalincs/alphacolors.h"
#include "globalincs/systemvars.h"
#include "debug.h"
#include "2d.h"
#include "grinternal.h"
#include "grstub.h"
#include "light.h"
@@ -44,11 +47,6 @@
#include <algorithm>
#include <climits>
#ifdef _WIN32
#include <windows.h>
#include <windowsx.h>
#endif
#if (SDL_VERSION_ATLEAST(1, 2, 7))
#include "SDL_cpuinfo.h"
#endif
@@ -992,17 +990,13 @@ bool gr_unsize_screen_posf(float *x, float *y, float *w, float *h, int resize_mo
void gr_close()
{
if (!Gr_inited) {
if ( !Gr_inited ) {
return;
}
graphics::debug::cleanup();
if (Gr_original_gamma_ramp != nullptr && os::getSDLMainWindow() != nullptr) {
SDL_SetWindowGammaRamp(os::getSDLMainWindow(),
Gr_original_gamma_ramp,
(Gr_original_gamma_ramp + 256),
(Gr_original_gamma_ramp + 512));
SDL_SetWindowGammaRamp(os::getSDLMainWindow(), Gr_original_gamma_ramp, (Gr_original_gamma_ramp + 256),
(Gr_original_gamma_ramp + 512));
}
// This is valid even if Gr_original_gamma_ramp is nullptr
@@ -2635,8 +2629,6 @@ void gr_flip(bool execute_scripting)
Script_system.EndFrame();
}
graphics::debug::render_elements();
gr_reset_immediate_buffer();
// Do per frame operations on the matrix state
-146
View File
@@ -1,146 +0,0 @@
#include "debug.h"
#include "render/3dinternal.h"
namespace graphics {
namespace debug {
namespace {
enum DebugElementType {
Line,
Text,
};
struct LineParams {
vertex projected_from;
vertex projected_to;
color line_color;
};
struct TextParams {
vertex projected_pos;
color text_color;
SCP_string text;
};
struct DebugElement {
DebugElementType type;
// A variant/union would be good here but our C++ version does not support that yet
LineParams lineParams;
TextParams textParams;
};
bool vertex_offscreen(const vertex& v) { return v.flags & PF_OVERFLOW; }
SCP_vector<DebugElement> debugElements;
void render_line(LineParams& lineParams)
{
if (vertex_offscreen(lineParams.projected_from) && vertex_offscreen(lineParams.projected_to)) {
return;
}
gr_set_color_fast(&lineParams.line_color);
gr_line(lineParams.projected_from.screen.xyw.x,
lineParams.projected_from.screen.xyw.y,
lineParams.projected_to.screen.xyw.x,
lineParams.projected_to.screen.xyw.y,
GR_RESIZE_NONE);
}
void render_text(TextParams& params)
{
if (vertex_offscreen(params.projected_pos)) {
return;
}
font::set_font(font::FONT1);
gr_set_color_fast(&params.text_color);
gr_string(params.projected_pos.screen.xyw.x,
params.projected_pos.screen.xyw.y,
params.text.c_str(),
GR_RESIZE_NONE);
}
void render_element(DebugElement& el)
{
switch (el.type) {
case Line:
render_line(el.lineParams);
break;
case Text:
render_text(el.textParams);
break;
default:
UNREACHABLE("Unknown debug element type!");
}
}
} // namespace
void line_3d(const vec3d* from, const vec3d* to, const color* color)
{
DebugElement el;
el.type = DebugElementType::Line;
bool do_g3 = G3_count < 1;
if (do_g3)
g3_start_frame(0);
g3_rotate_vertex(&el.lineParams.projected_from, from);
g3_project_vertex(&el.lineParams.projected_from);
g3_rotate_vertex(&el.lineParams.projected_to, to);
g3_project_vertex(&el.lineParams.projected_to);
if (do_g3)
g3_end_frame();
el.lineParams.line_color = *color;
debugElements.push_back(el);
}
void text_3d(const vec3d* world_pos, const color* color, SCP_string text)
{
DebugElement el;
el.type = DebugElementType::Text;
bool do_g3 = G3_count < 1;
if (do_g3)
g3_start_frame(0);
g3_rotate_vertex(&el.textParams.projected_pos, world_pos);
g3_project_vertex(&el.textParams.projected_pos);
if (do_g3)
g3_end_frame();
el.textParams.text_color = *color;
el.textParams.text = std::move(text);
debugElements.push_back(el);
}
void render_elements()
{
if (debugElements.empty()) {
return;
}
for (auto& dbgEl : debugElements) {
render_element(dbgEl);
}
debugElements.clear();
}
void cleanup() { debugElements.clear(); }
} // namespace debug
} // namespace graphics
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#include "graphics/2d.h"
#include "math/vecmat.h"
namespace graphics {
namespace debug {
void line_3d(const vec3d* from, const vec3d* to, const color* color);
void text_3d(const vec3d* world_pos, const color* color, SCP_string text);
void render_elements();
void cleanup();
} // namespace debug
} // namespace graphics
+19 -15
View File
@@ -254,25 +254,29 @@ void gr_opengl_post_process_begin()
Post_in_frame = true;
}
SCP_vector<shader_type> get_aa_shader_types(AntiAliasMode aa_mode) {
auto stypes = SCP_vector<shader_type>();
if (gr_is_fxaa_mode(aa_mode)) {
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_FXAA);
}
else if (gr_is_smaa_mode(aa_mode)) {
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT);
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_EDGE);
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING);
}
return stypes;
}
void recompile_aa_shader() {
auto stype = SCP_vector<shader_type>();
if (gr_is_fxaa_mode(Gr_aa_mode)) {
stype.push_back(shader_type::SDR_TYPE_POST_PROCESS_FXAA);
}
else if (gr_is_smaa_mode(Gr_aa_mode)) {
stype.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT);
stype.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_EDGE);
stype.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING);
}
mprintf(("Recompiling AA shader(s)...\n"));
for (auto sdr : stype) {
// start recompile by grabbing deleting the current shader we have, assuming it's already created
for (auto sdr : get_aa_shader_types(Gr_aa_mode_last_frame)) {
opengl_delete_shader( gr_opengl_maybe_create_shader(sdr, 0) );
}
// then recreate it again. shader loading code will be updated with the new AA presets
for (auto sdr : get_aa_shader_types(Gr_aa_mode)) {
gr_opengl_maybe_create_shader(sdr, 0);
}
@@ -1251,7 +1255,7 @@ static bool opengl_post_init_framebuffer()
opengl_setup_bloom_textures();
if (gr_is_smaa_mode(Gr_aa_mode)) {
if (Gr_aa_mode != AntiAliasMode::None) {
setup_smaa_resources();
}
+1050 -21
View File
File diff suppressed because it is too large. Load diff
+3
View File
@@ -18,6 +18,7 @@ void hud_init_missile_lock();
void hud_calculate_lock_position(float frametime);
void hud_calculate_lock_start_pos();
void hud_do_lock_indicator(float frametime);
void hud_do_lock_indicators(float frametime);
void hud_stop_looped_locking_sounds();
void hud_lock_reset(float lock_time_scale=1.0f);
@@ -54,7 +55,9 @@ public:
void initLoopLockedAnim(bool loop);
void render(float frametime) override;
void renderOld(float frametime);
void renderLockTriangles(int center_x, int center_y, float frametime);
void renderLockTrianglesNew(int center_x, int center_y, int start_timestamp);
void renderLockTrianglesOld(int center_x, int center_y, int radius);
void pageIn() override;
void initialize() override;
+44 -83
View File
@@ -238,8 +238,9 @@ SCP_vector<squadmsg_history> Squadmsg_history;
// forward declarations
void hud_add_issued_order(const char *name, int order);
void hud_update_last_order(char *target, int order_source, int special_index);
int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip = NULL );
int hud_squadmsg_ship_order_valid( int shipnum, int order );
bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip = nullptr);
bool hud_squadmsg_ship_valid(ship *shipp, object *objp = nullptr);
bool hud_squadmsg_ship_order_valid( int shipnum, int order );
// function to set up variables needed when messaging mode is started
void hud_squadmsg_start()
@@ -300,31 +301,15 @@ bool hud_squadmsg_exist_fighters( )
{
objp = &Objects[so->objnum];
shipp = &Ships[objp->instance];
Assert ( shipp->objnum != -1 );
Assertion(shipp->objnum != -1, "hud_squadmsg_exist_fighters() discovered that ship #%d ('%s') has an objnum of -1. Since the ship was retrieved from its object number (%d), this should be impossible; get a coder!\n", objp->instance, shipp->ship_name, so->objnum);
// check fighter is accepting orders
if (shipp->orders_accepted == 0)
continue;
// be sure ship is on correct team
#ifdef NDEBUG
if (shipp->team != Player_ship->team)
continue;
#else
if (!Msg_enemies && (shipp->team != Player_ship->team))
continue;
#endif
// cannot be my ship or an instructor
if ((objp == Player_obj) || is_instructor(objp))
continue;
// ship must be a fighter/bomber
if ( !(Ship_info[shipp->ship_info_index].is_fighter_bomber()) )
if (!(Ship_info[shipp->ship_info_index].is_fighter_bomber()))
continue;
// this ship satisfies everything
return true;
if (hud_squadmsg_ship_valid(shipp, objp))
return true;
}
return false;
@@ -337,7 +322,7 @@ bool hud_squadmsg_ship_valid(ship *shipp, object *objp)
if (!objp) // For simplicity's sake, allow it to be called with just the ship pointer
objp = &Objects[shipp->objnum];
// ships must be able to receive a message
// ships must be able to receive orders
if ( Ship_info[shipp->ship_info_index].class_type < 0 || !(Ship_types[Ship_info[shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::AI_accept_player_orders]) )
return false;
@@ -374,6 +359,10 @@ bool hud_squadmsg_ship_valid(ship *shipp, object *objp)
return false;
}
// maybe check comm system
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(shipp) != COMM_OK)
return false;
// If we got to this point, the ship must be valid.
return true;
}
@@ -420,14 +409,14 @@ int hud_squadmsg_count_ships(int add_to_menu)
}
// routine to return true if a wing should be put onto the messaging menu
int hud_squadmsg_wing_valid(wing *wingp)
bool hud_squadmsg_wing_valid(wing *wingp)
{
int idx, ship_num;
// a couple of special cases to account for before adding to count (or to menu). Don't count
// wings that are leaving or left.
if ( wingp->flags[Ship::Wing_Flags::Gone, Ship::Wing_Flags::Departing] )
return 0;
return false;
// Goober5000 - instead of checking wing leader, let's check all ships in wing;
// there are several significant cases when e.g. wings contain ships of different IFFs
@@ -436,34 +425,13 @@ int hud_squadmsg_wing_valid(wing *wingp)
ship_num = wingp->ship_index[idx];
Assert(ship_num >= 0);
// don't check player
if (ship_num == SHIP_INDEX(Player_ship))
continue;
// be sure ship is on same team
#ifdef NDEBUG
if (Ships[ship_num].team != Player_ship->team)
continue;
#else
if (!Msg_enemies && (Ships[ship_num].team != Player_ship->team))
continue;
#endif
// check if ship is accepting orders
if (Ships[ship_num].orders_accepted == 0)
continue;
// if doing a message shortcut is being used, be sure the ship can "accept" the command.
if ( Msg_shortcut_command >= 0 && !(Ships[ship_num].orders_accepted & Msg_shortcut_command) )
return 0;
// at least one ship in this wing is valid, and that's all we need
return 1;
// if at least one ship in this wing is valid, that's all we need
if (hud_squadmsg_ship_valid(&Ships[ship_num]))
return true;
}
// no ships in the wing were valid
return 0;
return false;
}
// function like above, except for wings
@@ -766,8 +734,8 @@ void hud_squadmsg_repair_rearm_abort( int toggle_state, object *obj)
}
// Goober5000 - redone and added a bit
// returns 1 if an order is valid for a ship. Applies to things like departure when engines are blown, etc.
int hud_squadmsg_ship_order_valid( int shipnum, int order )
// returns true if an order is valid for a ship. Applies to things like departure when engines are blown, etc.
bool hud_squadmsg_ship_order_valid( int shipnum, int order )
{
// Goober5000
Assert( shipnum >= 0 && shipnum < MAX_SHIPS );
@@ -778,31 +746,24 @@ int hud_squadmsg_ship_order_valid( int shipnum, int order )
case DEPART_ITEM:
// disabled ships can't depart.
if (shipp->flags[Ship::Ship_Flags::Disabled])
return 0;
return false;
// Goober5000: also can't depart if no subspace drives and no valid mothership
if (shipp->flags[Ship::Ship_Flags::No_subspace_drive])
if ( !(ship_can_warp_full_check(shipp)) )
{
// check that we have a mothership and that we can depart to it
if (shipp->departure_location == DEPART_AT_DOCK_BAY)
{
int anchor_shipnum = ship_name_lookup(Parse_names[shipp->departure_anchor]);
if (anchor_shipnum >= 0 && ship_useful_for_departure(anchor_shipnum, shipp->departure_path_mask))
return 1;
}
return 0;
// function accounts for ship's wing and returns true if ship has bay departure and mothership present, false otherwise
return ship_can_bay_depart(shipp);
}
break;
}
return 1;
return true;
}
// returns true or false if the Players target is valid for the given order
// find_order is true when we need to search the comm_orders array for the order entry. We have
// to do this action in some cases since all we know is the actual "value" of the order
int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
{
int target_objnum, i;
ship *shipp, *ordering_shipp;
@@ -823,22 +784,22 @@ int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
// orders which don't operate on targets are always valid
if ( !(Comm_orders[order].item & TARGET_MESSAGES) )
return 1;
return true;
target_objnum = aip->target_objnum;
// order isn't valid if there is no player target
if ( target_objnum == -1 ) {
return 0;
return false;
}
objp = &Objects[target_objnum];
ordering_shipp = &Ships[aip->shipnum];
// target isn't a ship, then return 0
// target isn't a ship, then return false
if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_WEAPON) )
return 0;
return false;
// if it's a weapon, then it needs to be a WIF_BOMB weapon. Only attack order valid, and only
// valid on bombs not on the player's team
@@ -846,54 +807,54 @@ int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
if ( objp->type == OBJ_WEAPON ) {
if (Weapons[objp->instance].lssm_stage==3){
return 0;
return false;
}
if ( (Comm_orders[order].item == ATTACK_TARGET_ITEM )
&& ((Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) || (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Fighter_Interceptable]))
&& (Weapons[objp->instance].team != ordering_shipp->team) )
&& (Weapons[objp->instance].team != ordering_shipp->team) ) {
return true;
}
return 1;
return 0;
return false;
}
Assert( objp->type == OBJ_SHIP );
shipp = &Ships[objp->instance];
// if target is a navbouy, return 0
// if target is a navbouy, return false
if ( Ship_info[shipp->ship_info_index].flags[Ship::Info_Flags::Navbuoy] ){
return 0;
return false;
}
// if we are messaging a ship, and that ship is our target, no target type orders are ever active
if ( (Squad_msg_mode == SM_MODE_SHIP_COMMAND) && (Objects[target_objnum].instance == Msg_instance) ){
return 0;
return false;
}
// if the order is a disable order or depart, and the ship is disabled, order isn't active
if ( (Comm_orders[order].item == DISABLE_TARGET_ITEM) && (shipp->flags[Ship::Ship_Flags::Disabled]) ){
return 0;
return false;
}
// same as above except for disarmed.
if ( (Comm_orders[order].item == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].type_count == 0) || (shipp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f)) ){
return 0;
return false;
}
// if order is disable subsystem, and no subsystem targeted or no hits, then order not valid
if ( (Comm_orders[order].item == DISABLE_SUBSYSTEM_ITEM) && ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) ){
return 0;
return false;
}
// check based on target's and player's team
if ( (shipp->team == ordering_shipp->team) && (FRIENDLY_TARGET_MESSAGES & Comm_orders[order].item) ){
return 1;
return true;
} else if ( (shipp->team != ordering_shipp->team) && (ENEMY_TARGET_MESSAGES & Comm_orders[order].item) ){
return 1;
return true;
} else {
return 0;
return false;
}
}
+20 -13
View File
@@ -3431,6 +3431,7 @@ void hud_show_targeting_gauges(float frametime)
vec3d target_pos;
hud_show_hostile_triangle();
hud_do_lock_indicators(frametime);
if (Player_ai->target_objnum == -1)
return;
@@ -3480,8 +3481,6 @@ void hud_show_targeting_gauges(float frametime)
// display the lock indicator
if (!Player->target_is_dying) {
hud_do_lock_indicator(frametime);
// update and render artillery
hud_artillery_update();
hud_artillery_render();
@@ -3975,12 +3974,14 @@ void HudGaugeLeadIndicator::renderLeadCurrentTarget()
}
frame_offset = pickFrame(prange, srange, dist_to_target);
if ( frame_offset < 0 ) {
if ( frame_offset < 0 && srange != -1.0f ) {
return;
}
hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
renderIndicator(frame_offset, targetp, &lead_target_pos);
if (frame_offset >= 0) {
hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
renderIndicator(frame_offset, targetp, &lead_target_pos);
}
//do dumbfire lead indicator - color is orange (255,128,0) - bright, (192,96,0) - dim
//phreak changed 9/01/02
@@ -4556,7 +4557,7 @@ int hud_sensors_ok(ship *sp, int show_msg)
}
}
int hud_communications_state(ship *sp)
int hud_communications_state(ship *sp, bool for_death_scream)
{
float str;
@@ -4570,11 +4571,7 @@ int hud_communications_state(ship *sp)
if ((sp == Player_ship) && (sp->flags[Ship::Ship_Flags::Dying]))
return COMM_OK;
// Goober5000 - check for scrambled communications
if ( emp_active_local() || sp->flags[Ship::Ship_Flags::Scramble_messages] )
return COMM_SCRAMBLED;
str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION );
str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION, for_death_scream );
if ( (str <= 0.01) || ship_subsys_disrupted(sp, SUBSYSTEM_COMMUNICATION) ) {
return COMM_DESTROYED;
@@ -4582,6 +4579,10 @@ int hud_communications_state(ship *sp)
return COMM_DAMAGED;
}
// Goober5000 - check for scrambled communications
if (emp_active_local() || sp->flags[Ship::Ship_Flags::Scramble_messages])
return COMM_SCRAMBLED;
return COMM_OK;
}
@@ -6074,7 +6075,10 @@ void HudGaugeWeapons::render(float /*frametime*/)
renderPrintf(position[0] + Weapon_sunlinked_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
// indicate if this is linked
if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] ) {
// don't draw the link indicator if the fire can't be fired link.
// the link flag is ignored rather than cleared so the player can cycle past a no-doublefire weapon without the setting being cleared
if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] && !wip->wi_flags[Weapon::Info_Flags::No_doublefire] &&
!The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] ) {
renderPrintf(position[0] + Weapon_slinked_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
}
@@ -6992,7 +6996,10 @@ void HudGaugeSecondaryWeapons::render(float /*frametime*/)
renderPrintf(position[0] + _sunlinked_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
// indicate if this is linked
if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] ) {
// don't draw the link indicator if the fire can't be fired link.
// the link flag is ignored rather than cleared so the player can cycle past a no-doublefire weapon without the setting being cleared
if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] && !wip->wi_flags[Weapon::Info_Flags::No_doublefire] &&
!The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] ) {
renderPrintf(position[0] + _slinked_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
}
+1 -1
View File
@@ -125,7 +125,7 @@ void hud_update_weapon_flash();
void hud_process_homing_missiles(void);
int hud_sensors_ok(ship *sp, int show_msg = 1);
int hud_communications_state(ship *sp);
int hud_communications_state(ship *sp, bool for_death_scream = false);
int hud_get_best_primary_bank(float *range);
void hud_target_toggle_hidden_from_sensors();
+20 -7
View File
@@ -360,6 +360,23 @@ void iff_init()
num_observed_colors[cur_iff]++;
}
// get F3 override
iff->hotkey_team = IFF_hotkey_team::Default;
if (optional_string("+Hotkey Team:"))
{
char temp[NAME_LENGTH];
stuff_string(temp, F_NAME, NAME_LENGTH);
if (!stricmp(temp, "Friendly"))
iff->hotkey_team = IFF_hotkey_team::Friendly;
else if (!stricmp(temp, "Hostile") || !stricmp(temp, "Enemy"))
iff->hotkey_team = IFF_hotkey_team::Hostile;
else if (!stricmp(temp, "None"))
iff->hotkey_team = IFF_hotkey_team::None;
else
Warning(LOCATION, "Unrecognized +Hotkey Tean: %s\n", temp);
}
// get flags ----------------------------------------------------------
@@ -560,10 +577,8 @@ int iff_get_attacker_mask(int attackee_team)
*
* @param team_x Team of attacker
* @param team_y Team of attackee
*
* @return >0 if true, 0 if false
*/
int iff_x_attacks_y(int team_x, int team_y)
bool iff_x_attacks_y(int team_x, int team_y)
{
return iff_matches_mask(team_y, iff_get_attackee_mask(team_x));
}
@@ -583,12 +598,10 @@ int iff_get_mask(int team)
*
* @param team Team to test
* @param mask Mask
*
* @return 1 if matches, 0 if does not match
*/
int iff_matches_mask(int team, int mask)
bool iff_matches_mask(int team, int mask)
{
return (iff_get_mask(team) & mask) ? 1 : 0;
return (iff_get_mask(team) & mask) != 0;
}
/**
+5 -2
View File
@@ -22,6 +22,8 @@ class object;
#define IFF_COLOR_TAGGED 2
#define MAX_IFF_COLORS (MAX_IFFS + 3)
enum IFF_hotkey_team { Default = -1, None = 0, Friendly, Hostile };
// iff flags
#define IFFF_SUPPORT_ALLOWED (1 << 0) // this IFF can call for support
#define IFFF_EXEMPT_FROM_ALL_TEAMS_AT_WAR (1 << 1) // self-explanatory
@@ -41,6 +43,7 @@ typedef struct iff_info {
int attackee_bitmask; // treat this as private and use iff_get_attackee_mask or iff_x_attacks_y
int attackee_bitmask_all_teams_at_war; // treat this as private and use iff_get_attackee_mask or iff_x_attacks_y
int observed_color_index[MAX_IFFS]; // treat this as private and use iff_get_color or iff_get_color_by_team
IFF_hotkey_team hotkey_team;
// flags
int flags;
@@ -74,11 +77,11 @@ extern int iff_lookup(const char *iff_name);
// If he fires at you, you don't react unless you are coded to attack him, because you are oblivious.
extern int iff_get_attackee_mask(int attacker_team);
extern int iff_get_attacker_mask(int attackee_team);
extern int iff_x_attacks_y(int team_x, int team_y);
extern bool iff_x_attacks_y(int team_x, int team_y);
// mask stuff
extern int iff_get_mask(int team);
extern int iff_matches_mask(int team, int mask);
extern bool iff_matches_mask(int team, int mask);
// get color stuff
extern color *iff_get_color(int color_index, int is_bright);
+52
View File
@@ -17,6 +17,7 @@
#include "io/joy.h"
#include "io/timer.h"
#include "ship/ship.h"
#include "ship/ship_flags.h"
#include "playerman/player.h"
#include "weapon/weapon.h"
#include "hud/hud.h"
@@ -1095,6 +1096,57 @@ void process_debug_keys(int k)
break;
}
// Goober5000
case KEY_DEBUGGED + KEY_SHIFTED + KEY_R:
case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_R:
{
// toggle support for this mission
if (The_mission.support_ships.max_support_ships == 0)
{
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Setting maximum number of support ships to infinite.", 1643));
The_mission.support_ships.max_support_ships = -1;
}
else
{
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Setting maximum number of support ships to zero.", 1644));
The_mission.support_ships.max_support_ships = 0;
}
break;
}
// Goober5000
case KEY_DEBUGGED + KEY_SHIFTED + KEY_U:
case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_U:
{
// toggle the current target between scanned and unscanned
if (Player_ai->target_objnum >= 0)
{
object *objp = &Objects[Player_ai->target_objnum];
if (objp->type == OBJ_SHIP)
{
ship *targeted_shipp = &Ships[objp->instance];
if (Player_ai->targeted_subsys == nullptr)
{
if (targeted_shipp->flags[Ship::Ship_Flags::Cargo_revealed])
ship_do_cargo_hidden(targeted_shipp);
else
ship_do_cargo_revealed(targeted_shipp);
}
else
{
if (Player_ai->targeted_subsys->flags[Ship::Subsystem_Flags::Cargo_revealed])
ship_do_cap_subsys_cargo_hidden(targeted_shipp, Player_ai->targeted_subsys);
else
ship_do_cap_subsys_cargo_revealed(targeted_shipp, Player_ai->targeted_subsys);
}
}
}
break;
}
case KEY_DEBUGGED + KEY_SHIFTED + KEY_UP:
Game_detail_level++;
HUD_printf( XSTR( "Detail level set to %+d\n", 22), Game_detail_level );
+88 -3
View File
@@ -25,9 +25,12 @@
#include "nebula/neb.h"
#include "object/objectsnd.h"
#include "playerman/managepilot.h"
#include "particle/particle.h"
#include "render/3d.h"
#include "render/batching.h"
#include "ship/ship.h"
#include "ship/shiphit.h"
#include "debris/debris.h"
#include "species_defs/species_defs.h"
#include "starfield/nebula.h"
#include "starfield/starfield.h"
@@ -47,6 +50,7 @@
#define LAB_FLAG_SHOW_WEAPONS (1 << 6) // determines if external weapons models are displayed
#define LAB_FLAG_INITIAL_ROTATION (1 << 7) // initial rotation setting
#define LAB_FLAG_DESTROYED_SUBSYSTEMS (1 << 8) // render model as if all subsystems are destroyed
#define LAB_FLAG_SHIP_EXPLODING (1 << 9) // execute the ship kill stuff
// modes
#define LAB_MODE_NONE 0 // not showing anything
@@ -64,6 +68,7 @@ static Window* Lab_material_override_window = nullptr;
static Window* Lab_variables_window = nullptr;
static Window* Lab_description_window = nullptr;
static Window* Lab_background_window = nullptr;
static Window* Lab_actions_window = nullptr;
static Text* Lab_description_text = nullptr;
static TreeItem** Lab_species_nodes = nullptr;
@@ -349,7 +354,7 @@ void labviewer_render_model(float frametime)
if (dx || dy) {
// Rotate the ship
if (Trackball_mode == 1) {
if (Trackball_mode == 1 && !(Lab_viewer_flags & LAB_FLAG_SHIP_EXPLODING)) {
angles rot_angle;
vm_extract_angles_matrix_alternate(&rot_angle, &Lab_model_orient);
@@ -436,10 +441,16 @@ void labviewer_render_model(float frametime)
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_heightmap, Lab_Heightmap_override);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_miscmap, Lab_Miscmap_override);
ship_process_post(obj, frametime);
ship_model_update_instance(obj);
Ships[obj->instance].team_name = Lab_team_color;
if (Lab_viewer_flags & LAB_FLAG_LIGHTNING_ARCS) {
obj->hull_strength = 1.0f;
}
else {
obj->hull_strength = Ship_info[Ships[obj->instance].ship_info_index].max_hull_strength;
}
}
if (Lab_render_wireframe)
@@ -460,6 +471,11 @@ void labviewer_render_model(float frametime)
Ships[obj->instance].flags.set(Ship::Ship_Flags::No_thrusters);
}
obj_move_all(frametime);
particle::move_all(frametime);
particle::ParticleManager::get()->doFrame(frametime);
shockwave_move_all(frametime);
Trail_render_override = true;
game_render_frame(Lab_cam);
Trail_render_override = false;
@@ -1574,7 +1590,30 @@ void labviewer_change_ship_lod(Tree* caller)
}
Lab_selected_object = ship_create(&vmd_identity_matrix, &Lab_model_pos, ship_index);
Objects[Lab_selected_object].flags.set(Object::Object_Flags::Player_ship);
auto ship_objp = &Ships[Objects[Lab_selected_object].instance];
auto ship_infop = &Ship_info[ship_objp->ship_info_index];
// This is normally set during mission parse
ship_objp->special_exp_damage = -1;
// If the ship class defines replacement textures, load them and apply them to the ship
// load the texture
auto replacements = SCP_vector<texture_replace>();
for (auto tr : ship_infop->replacement_textures) {
if (!stricmp(tr.new_texture, "invisible"))
{
// invisible is a special case
tr.new_texture_id = REPLACE_WITH_INVISIBLE;
}
else
{
// try to load texture or anim as normal
tr.new_texture_id = bm_load_either(tr.new_texture);
}
replacements.push_back(tr);
}
ship_objp->apply_replacement_textures(replacements);
lab_cam_distance = Objects[Lab_selected_object].radius * 1.6f;
@@ -1599,6 +1638,8 @@ void labviewer_change_ship_lod(Tree* caller)
labviewer_update_flags_window();
labviewer_update_variables_window();
labviewer_recalc_camera();
Lab_viewer_flags &= ~LAB_FLAG_SHIP_EXPLODING;
}
void labviewer_change_ship(Tree* caller)
@@ -2046,6 +2087,34 @@ void labviewer_make_background_window(Button* /*caller*/)
}
}
// ----------------------------- Actions window --------------------------------
void lab_actions_window_close(GUIObject* /*caller*/) {
Lab_actions_window = nullptr;
}
void labviewer_actions_destroy_ship(Button* /*caller*/) {
auto obj = &Objects[Lab_selected_object];
if (obj->type == OBJ_SHIP) {
obj->flags.remove(Object::Object_Flags::Player_ship);
ship_self_destruct(obj);
Lab_viewer_flags |= LAB_FLAG_SHIP_EXPLODING;
}
}
void labviewer_make_actions_window(Button* /*caller*/)
{
if (Lab_actions_window != nullptr)
return;
Lab_actions_window = (Window*)Lab_screen->Add(
new Window("Actions", gr_screen.center_offset_x + 250, gr_screen.center_offset_y + 50)
);
Lab_actions_window->SetCloseFunction(lab_actions_window_close);
Lab_actions_window->AddChild(new Button("Destroy Ship", 0, 0, labviewer_actions_destroy_ship));
}
// ----------------------------- Lab functions ---------------------------------
bool is_same_obj_type(int curr, int next)
{
@@ -2178,6 +2247,9 @@ void lab_init()
x += cbp->GetWidth() + 10;
cbp = Lab_toolbar->AddChild(new Button("Backgrounds", x, 0, labviewer_make_background_window));
x += cbp->GetWidth() + 10;
cbp = Lab_toolbar->AddChild(new Button("Actions", x, 0, labviewer_make_actions_window));
x += cbp->GetWidth() + 20;
cbp = Lab_toolbar->AddChild(new Button("Exit", x, 0, labviewer_exit));
@@ -2247,6 +2319,15 @@ void lab_init()
Lab_selected_mission = "None";
Lab_skybox_orientation = vmd_identity_matrix;
fireball_init();
debris_init();
extern void debris_page_in();
debris_page_in();
shockwave_level_init();
particle::init();
ai_paused = 1;
}
#include "lab.h"
@@ -2500,4 +2581,8 @@ void lab_close()
cam_delete(Lab_cam);
obj_init();
shockwave_level_close();
ai_paused = 0;
}
+1 -5
View File
@@ -490,13 +490,9 @@ void light_add_cone(const vec3d *pos, const vec3d *dir, float angle, float inner
Num_lights++;
vec3d screen_dir;
vm_vec_rotate(&screen_dir, dir, &Eye_matrix);
screen_dir.xyz.z = -screen_dir.xyz.z;
l.type = Light_Type::Cone;
l.vec = *pos;
l.vec2= screen_dir;
l.vec2= *dir;
l.cone_angle = angle;
l.cone_inner_angle = inner_angle;
l.dual_cone = dual_cone;
+156 -74
View File
@@ -27,13 +27,13 @@
// general language/localization data ---------------------
// current language
int Lcl_current_lang = FS2_OPEN_DEFAULT_LANGUAGE;
int Lcl_current_lang = LCL_UNTRANSLATED;
SCP_vector<lang_info> Lcl_languages;
// These are the original languages supported by FS2. The code expects these languages to be supported even if the tables don't
lang_info Lcl_builtin_languages[NUM_BUILTIN_LANGUAGES] = {
{ "English", "", {127,0,176,0,0}, 589986744}, // English
{ "English", "", {127,0,176,0,0}, 589986744}, // English ("" is correct; the game data files do not use a language extension for English)
{ "German", "gr", {164,0,176,0,0}, -1132430286 }, // German
{ "French", "fr", {164,0,176,0,0}, 0 }, // French
{ "Polish", "pl", {127,0,176,0,0}, -1131728960}, // Polish
@@ -46,7 +46,9 @@ int Lcl_special_chars;
int Lcl_fr = 0;
int Lcl_gr = 0;
int Lcl_pl = 0;
int Lcl_english = 1;
int Lcl_en = 1;
bool *Lcl_unexpected_tstring_check = nullptr;
// executable string localization data --------------------
@@ -56,7 +58,7 @@ int Lcl_english = 1;
// the english version (in the code) to a foreign version (in the table). Thus, if you
// add a new string to the code, you must assign it a new index. Use the number below for
// that index and increase the number below by one.
#define XSTR_SIZE 1643
#define XSTR_SIZE 1645
// struct to allow for strings.tbl-determined x offset
@@ -109,8 +111,8 @@ void lcl_init(int lang_init)
// initialize encryption
encrypt_init();
// setup English
Lcl_languages.push_back(Lcl_builtin_languages[FS2_OPEN_DEFAULT_LANGUAGE]);
// set up the first language (which should be English)
Lcl_languages.push_back(Lcl_builtin_languages[0]);
// check string.tbl to see which languages we support
try
@@ -124,8 +126,8 @@ void lcl_init(int lang_init)
parse_modular_table(NOX("*-lcl.tbm"), parse_stringstbl_quick);
// if the only language we have at this point is English, we need to setup the builtin languages as we might be dealing with an old style strings.tbl
// which doesn't support anything beyond the builtin languages. Note, we start at i = 1 because we added English above.
// if we only have one language at this point, we need to setup the builtin languages as we might be dealing with an old style strings.tbl
// which doesn't support anything beyond the builtin languages. Note, we start at i = 1 because we added the first language above.
if ((int)Lcl_languages.size() == 1) {
for (i=1; i<NUM_BUILTIN_LANGUAGES; i++) {
Lcl_languages.push_back(Lcl_builtin_languages[i]);
@@ -133,18 +135,12 @@ void lcl_init(int lang_init)
}
// read the language from the registry
if(lang_init < 0){
if (lang_init < 0) {
memset(lang_string, 0, 128);
// default to DEFAULT_LANGUAGE (which should be English so we don't have to put German text
// default to DEFAULT_LANGUAGE (which should be English so we don't have to put German text
// in tstrings in the #default section)
ret = os_config_read_string(NULL, "Language", Lcl_languages[FS2_OPEN_DEFAULT_LANGUAGE].lang_name);
if(ret == NULL){
Error(LOCATION, "Default language not found.");
}
strcpy_s(lang_string, ret);
ret = os_config_read_string(nullptr, "Language", Lcl_languages[LCL_DEFAULT].lang_name);
strcpy_s(lang_string, ret);
// look it up
lang = -1;
@@ -155,13 +151,18 @@ void lcl_init(int lang_init)
}
}
if(lang < 0){
lang = 0;
}
lang = LCL_DEFAULT;
}
} else {
Assert((lang_init >= 0) && (lang_init < (int)Lcl_languages.size()));
Assert(lang_init == LCL_UNTRANSLATED || (lang_init >= 0 && lang_init < (int)Lcl_languages.size()));
lang = lang_init;
}
// and after all that... the default language reverts to untranslated unless we are specifically translating it
if (lang == LCL_DEFAULT && !Use_tabled_strings_for_default_language) {
lang = LCL_UNTRANSLATED;
}
// set the language (this function takes care of setting up file pointers)
lcl_set_language(lang);
}
@@ -250,14 +251,14 @@ void parse_stringstbl_common(const char *filename, const bool external)
// move down to the proper section
memset(language_tag, 0, sizeof(language_tag));
strcpy_s(language_tag, "#");
if (external && Lcl_current_lang == FS2_OPEN_DEFAULT_LANGUAGE){
if (external && (Lcl_current_lang == LCL_UNTRANSLATED || Lcl_current_lang == LCL_DEFAULT)) {
strcat_s(language_tag, "default");
} else {
strcat_s(language_tag, Lcl_languages[Lcl_current_lang].lang_name);
strcat_s(language_tag, Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang].lang_name);
}
if ( skip_to_string(language_tag) != 1 ) {
mprintf(("Current language not found in %s\n", filename));
mprintf(("Language tag %s not found in %s\n", language_tag, filename));
return;
}
@@ -482,46 +483,62 @@ void lcl_set_language(int lang)
{
Lcl_current_lang = lang;
nprintf(("General", "Setting language to %s\n", Lcl_languages[lang].lang_name));
if (lang == LCL_UNTRANSLATED) {
nprintf(("General", "Setting language to UNTRANSLATED\n"));
// but for the purposes of array access, we use the default
lang = LCL_DEFAULT;
} else {
nprintf(("General", "Setting language to %s\n", Lcl_languages[lang].lang_name));
}
Assertion((Lcl_current_lang >= 0) && (Lcl_current_lang < (int)Lcl_languages.size()), "Attempt to set language to an invalid language");
Assertion((lang >= 0) && (lang < (int)Lcl_languages.size()), "Attempt to set language to an invalid language");
// flag the proper language as being active
Lcl_special_chars = Lcl_languages[Lcl_current_lang].special_char_indexes[0];
Lcl_special_chars = Lcl_languages[lang].special_char_indexes[0];
Lcl_fr = 0;
Lcl_gr = 0;
Lcl_pl = 0;
Lcl_english = 0;
if (!strcmp(Lcl_languages[Lcl_current_lang].lang_name, Lcl_builtin_languages[LCL_ENGLISH].lang_name)) {
Lcl_english = 1;
} else if (!strcmp(Lcl_languages[Lcl_current_lang].lang_name, Lcl_builtin_languages[LCL_FRENCH].lang_name)) {
Lcl_en = 0;
if (!strcmp(Lcl_languages[lang].lang_name, Lcl_builtin_languages[LCL_ENGLISH].lang_name)) {
Lcl_en = 1;
} else if (!strcmp(Lcl_languages[lang].lang_name, Lcl_builtin_languages[LCL_FRENCH].lang_name)) {
Lcl_fr = 1;
} else if (!strcmp(Lcl_languages[Lcl_current_lang].lang_name, Lcl_builtin_languages[LCL_GERMAN].lang_name)) {
} else if (!strcmp(Lcl_languages[lang].lang_name, Lcl_builtin_languages[LCL_GERMAN].lang_name)) {
Lcl_gr = 1;
} else if (!strcmp(Lcl_languages[Lcl_current_lang].lang_name, Lcl_builtin_languages[LCL_POLISH].lang_name)) {
} else if (!strcmp(Lcl_languages[lang].lang_name, Lcl_builtin_languages[LCL_POLISH].lang_name)) {
Lcl_pl = 1;
}
}
ubyte lcl_get_font_index(int font_num)
{
int lang = Lcl_current_lang;
if (Lcl_current_lang == LCL_UNTRANSLATED) {
lang = LCL_DEFAULT;
}
if (Unicode_text_mode) {
// In Unicode mode there are no special characters. Some of the code still uses this function in that mode so
// we just return 0 to signify that there are no special characters in this font
return 0;
} else {
Assertion((font_num >= 0) && (font_num < LCL_MAX_FONTS), "Passed an invalid font index");
Assertion((Lcl_current_lang >= 0) && (Lcl_current_lang < (int)Lcl_languages.size()), "Current language is not valid, can't get font indexes");
Assertion((lang >= 0) && (lang < (int)Lcl_languages.size()), "Current language is not valid, can't get font indexes");
return Lcl_languages[Lcl_current_lang].special_char_indexes[font_num];
return Lcl_languages[lang].special_char_indexes[font_num];
}
}
// maybe add localized directory to full path with file name when opening a localized file
int lcl_add_dir_to_path_with_filename(char *current_path, size_t path_max)
{
int lang = Lcl_current_lang;
if (Lcl_current_lang == LCL_UNTRANSLATED) {
lang = LCL_DEFAULT;
}
// if the disk extension is 0 length, don't add anything
if (strlen(Lcl_languages[Lcl_current_lang].lang_ext) <= 0) {
if (strlen(Lcl_languages[lang].lang_ext) <= 0) {
return 1;
}
@@ -542,7 +559,7 @@ int lcl_add_dir_to_path_with_filename(char *current_path, size_t path_max)
}
// add extension
strcat_s(current_path, path_max, Lcl_languages[Lcl_current_lang].lang_ext);
strcat_s(current_path, path_max, Lcl_languages[lang].lang_ext);
strcat_s(current_path, path_max, DIR_SEPARATOR_STR );
// copy rest of filename from temp
@@ -626,15 +643,18 @@ void lcl_fred_replace_stuff(SCP_string &text)
// XSTR("whee", 20)
// and these should cover all the externalized string cases
// fills in id if non-NULL. a value of -2 indicates it is not an external string
void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
// returns true if we were able to extract the XSTR elements (text_str and maybe id are populated)
bool lcl_ext_localize_sub(const char *in, char *text_str, char *out, size_t max_len, int *id)
{
char text_str[PARSE_BUF_SIZE]="";
int str_id;
size_t str_len;
Assert(in);
Assert(out);
// NOTE: "Token too long" warnings are disabled when Lcl_unexpected_tstring_check is active,
// because in such cases we actually anticipate that the length might be exceeded.
// default (non-external string) value
if (id != NULL) {
*id = -2;
@@ -644,7 +664,7 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
// if the string is < 9 chars, it can't be an XSTR("",) tag, so just copy it
if (str_len < 9) {
if (str_len > max_len)
if (str_len > max_len && !Lcl_unexpected_tstring_check)
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", in, str_len, max_len);
strncpy(out, in, max_len);
@@ -652,13 +672,13 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
if (id != NULL)
*id = -2;
return;
return false;
}
// otherwise, check to see if it's an XSTR() tag
if (strnicmp(in, "XSTR", 4) != 0) {
// NOT an XSTR() tag
if (str_len > max_len)
if (str_len > max_len && !Lcl_unexpected_tstring_check)
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", in, str_len, max_len);
strncpy(out, in, max_len);
@@ -666,12 +686,12 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
if (id != NULL)
*id = -2;
return;
return false;
}
// at this point we _know_ its an XSTR() tag, so split off the strings and id sections
if (!lcl_ext_get_text(in, text_str)) {
if (str_len > max_len)
if (str_len > max_len && !Lcl_unexpected_tstring_check)
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", in, str_len, max_len);
strncpy(out, in, max_len);
@@ -679,10 +699,10 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
if (id != NULL)
*id = -1;
return;
return false;
}
if (!lcl_ext_get_id(in, &str_id)) {
if (str_len > max_len)
if (str_len > max_len && !Lcl_unexpected_tstring_check)
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", in, str_len, max_len);
strncpy(out, in, max_len);
@@ -690,12 +710,12 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
if (id != NULL)
*id = -1;
return;
return false;
}
// if the localization file is not open, or we're running in the default language, return the original string
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == FS2_OPEN_DEFAULT_LANGUAGE) ) {
if ( strlen(text_str) > max_len )
// if the localization file is not open, or there's no entry, return the original string
if ( !Xstr_inited || (str_id < 0) ) {
if ( strlen(text_str) > max_len && !Lcl_unexpected_tstring_check )
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", text_str, strlen(text_str), max_len);
strncpy(out, text_str, max_len);
@@ -703,20 +723,20 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
if (id != NULL)
*id = str_id;
return;
return true;
}
// get the string if it exists
if (Lcl_ext_str.find(str_id) != Lcl_ext_str.end()) {
// copy to the outgoing string
if ( strlen(Lcl_ext_str[str_id]) > max_len )
if ( strlen(Lcl_ext_str[str_id]) > max_len && !Lcl_unexpected_tstring_check )
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", Lcl_ext_str[str_id], strlen(Lcl_ext_str[str_id]), max_len);
strncpy(out, Lcl_ext_str[str_id], max_len);
}
// otherwise use what we have - probably should Int3() or assert here
else {
if ( strlen(text_str) > max_len )
if ( strlen(text_str) > max_len && !Lcl_unexpected_tstring_check )
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", text_str, strlen(text_str), max_len);
strncpy(out, text_str, max_len);
@@ -726,12 +746,13 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
if (id != NULL) {
*id = str_id;
}
return true;
}
// ditto for SCP_string
void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
bool lcl_ext_localize_sub(const SCP_string &in, SCP_string &text_str, SCP_string &out, int *id)
{
SCP_string text_str = "";
int str_id;
// default (non-external string) value
@@ -746,7 +767,7 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
if (id != NULL)
*id = -2;
return;
return false;
}
// otherwise, check to see if it's an XSTR() tag
@@ -757,7 +778,7 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
if (id != NULL)
*id = -2;
return;
return false;
}
// at this point we _know_ its an XSTR() tag, so split off the strings and id sections
@@ -767,7 +788,7 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
if (id != NULL)
*id = -1;
return;
return false;
}
if (!lcl_ext_get_id(in, &str_id)) {
out = in;
@@ -775,17 +796,17 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
if (id != NULL)
*id = -1;
return;
return false;
}
// if the localization file is not open, or we're running in the default language, return the original string
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == FS2_OPEN_DEFAULT_LANGUAGE) ) {
// if the localization file is not open, or there's no entry, or we're not translating, return the original string
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == LCL_UNTRANSLATED) ) {
out = text_str;
if (id != NULL)
*id = str_id;
return;
return true;
}
// get the string if it exists
@@ -802,15 +823,43 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
if (id != NULL){
*id = str_id;
}
return true;
}
// Goober5000 - wrapper for lcl_ext_localize_sub; used because lcl_replace_stuff has to
// be called *after* the translation is done, and the original function returned in so
// many places that it would be messy to call lcl_replace_stuff everywhere
// Addendum: Now, of course, it provides a handy way to encapsulate the unexpected tstring check.
void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id)
{
// do XSTR translation
lcl_ext_localize_sub(in, out, max_len, id);
// buffer for the untranslated string inside the XSTR tag
char text_str[PARSE_BUF_SIZE] = "";
// if we're doing this extra check, then we have to compare the untranslated string with the default language string and see if they're different
if (Lcl_unexpected_tstring_check)
{
int saved_language = Lcl_current_lang;
Lcl_current_lang = LCL_DEFAULT;
bool extracted = lcl_ext_localize_sub(in, text_str, out, max_len, id);
// the untranslated and default-translated strings should always be identical, so if they're different, it might mean we have some data from a different mod
if (extracted && strcmp(text_str, out) != 0)
*Lcl_unexpected_tstring_check = true;
// at this point, we go back to our usual language and do the translation for real
if (saved_language != Lcl_current_lang)
{
Lcl_current_lang = saved_language;
lcl_ext_localize_sub(in, text_str, out, max_len, id);
}
}
// most of the time we're not going to do the check, so localize as normal
else
{
// do XSTR translation
lcl_ext_localize_sub(in, text_str, out, max_len, id);
}
// do translation of $callsign, $rank, etc.
lcl_replace_stuff(out, max_len);
@@ -819,8 +868,33 @@ void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id)
// ditto for SCP_string
void lcl_ext_localize(const SCP_string &in, SCP_string &out, int *id)
{
// do XSTR translation
lcl_ext_localize_sub(in, out, id);
// buffer for the untranslated string inside the XSTR tag
SCP_string text_str = "";
// if we're doing this extra check, then we have to compare the untranslated string with the default language string and see if they're different
if (Lcl_unexpected_tstring_check)
{
int saved_language = Lcl_current_lang;
Lcl_current_lang = LCL_DEFAULT;
bool extracted = lcl_ext_localize_sub(in, text_str, out, id);
// the untranslated and default-translated strings should always be identical, so if they're different, it might mean we have some data from a different mod
if (extracted && text_str != out)
*Lcl_unexpected_tstring_check = true;
// at this point, we go back to our usual language and do the translation for real
if (saved_language != Lcl_current_lang)
{
Lcl_current_lang = saved_language;
lcl_ext_localize_sub(in, text_str, out, id);
}
}
// most of the time we're not going to do the check, so localize as normal
else
{
// do XSTR translation
lcl_ext_localize_sub(in, text_str, out, id);
}
// do translation of $callsign, $rank, etc.
lcl_replace_stuff(out);
@@ -835,15 +909,20 @@ const char *XSTR(const char *str, int index)
return str;
}
// perform a lookup
if (index >= 0 && index < XSTR_SIZE)
if (Lcl_current_lang != LCL_UNTRANSLATED)
{
// return translation of string
if (Xstr_table[index].str)
return Xstr_table[index].str;
// perform a lookup
if (index >= 0 && index < XSTR_SIZE)
{
// return translation of string
if (Xstr_table[index].str)
return Xstr_table[index].str;
else
mprintf(("No XSTR entry in strings.tbl for index %d\n", index));
}
}
// can't translate; return original english string
// can't translate; return original English string
return str;
}
@@ -1092,9 +1171,12 @@ int lcl_ext_get_id(const SCP_string &xstr, int *out)
void lcl_get_language_name(char *lang_name)
{
Assert(Lcl_current_lang < (int)Lcl_languages.size());
int lang = Lcl_current_lang;
if (Lcl_current_lang == LCL_UNTRANSLATED)
lang = LCL_DEFAULT;
strcpy(lang_name, Lcl_languages[Lcl_current_lang].lang_name);
Assert(lang >= 0 && lang < (int)Lcl_languages.size());
strcpy(lang_name, Lcl_languages[lang].lang_name);
}
// ------------------------------------------------------------------
+9 -5
View File
@@ -19,13 +19,14 @@
// LOCALIZE DEFINES/VARS
//
// language defines
// language defines (English should always be index 0)
#define LCL_ENGLISH 0
#define LCL_GERMAN 1
#define LCL_FRENCH 2
#define LCL_POLISH 3
#define FS2_OPEN_DEFAULT_LANGUAGE 0
#define LCL_UNTRANSLATED 10 // this should be higher than the highest builtin language
#define LCL_DEFAULT 0
// for language name strings
#define LCL_LANG_NAME_LEN 32
@@ -56,11 +57,14 @@ extern int Lcl_special_chars;
extern int Lcl_fr;
extern int Lcl_gr;
extern int Lcl_pl;
extern int Lcl_english;
extern int Lcl_en;
// The currently active language. Index into Lcl_languages.
extern int Lcl_current_lang;
// special check for misplaced mod data (see Mantis #2942)
extern bool *Lcl_unexpected_tstring_check;
// ------------------------------------------------------------------------------------------------------------
// LOCALIZE FUNCTIONS
@@ -110,8 +114,8 @@ void lcl_fred_replace_stuff(SCP_string &text);
// XSTR("whee", 20)
// and these should cover all the externalized string cases
// fills in id if non-NULL. a value of -2 indicates it is not an external string
void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id = NULL);
void lcl_ext_localize(const SCP_string &in, SCP_string &out, int *id = NULL);
void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id = nullptr);
void lcl_ext_localize(const SCP_string &in, SCP_string &out, int *id = nullptr);
// translate the specified string based upon the current language
const char *XSTR(const char *str, int index);
+2 -8
View File
@@ -541,25 +541,19 @@ inline vec3d& operator-=(vec3d& left, const vec3d& right)
return left;
}
inline vec3d operator*(const vec3d& left, float right)
inline vec3d operator*(vec3d& left, float right)
{
vec3d out;
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);
return left;
}
inline vec3d operator/(const vec3d& left, float right)
inline vec3d operator/(vec3d& left, float right)
{
vec3d out;
vm_vec_copy_scale(&out, &left, 1.0f / right);
+21 -2
View File
@@ -450,6 +450,7 @@ int build_standalone_mission_list_do_frame()
int font_height = gr_get_font_height();
char filename[MAX_FILENAME_LEN];
char str[256];
bool lcl_weirdness = false;
// When no standalone missions in data directory
if (Num_standalone_missions == 0) {
@@ -473,8 +474,16 @@ int build_standalone_mission_list_do_frame()
// tack on an extension
strcat_s(filename, FS_MISSION_FILE_EXT);
// check if we can list the mission and if loading basic info didn't return an error code
if (!mission_is_ignored(filename) && !get_mission_info(filename)) {
// activate tstrings check
Lcl_unexpected_tstring_check = &lcl_weirdness;
// check if we can list the mission, if loading basic info didn't return an error code, and if we didn't find an unexpected XSTR mismatch
bool condition = !mission_is_ignored(filename) && !get_mission_info(filename) && !lcl_weirdness;
// deactivate tstrings check
Lcl_unexpected_tstring_check = nullptr;
if (condition) {
Standalone_mission_names[Num_standalone_missions_with_info] = vm_strdup(The_mission.name);
Standalone_mission_flags[Num_standalone_missions_with_info] = The_mission.game_type;
int y = Num_lines * (font_height + 2);
@@ -1099,7 +1108,17 @@ void sim_room_init()
memset(wild_card, 0, 256);
strcpy_s(wild_card, NOX("*"));
strcat_s(wild_card, FS_MISSION_FILE_EXT);
#ifndef NDEBUG
// Activate the check while we fill the mission list so that we don't potentially end up with dozens of string length warnings.
// We don't actually remove mismatched missions from the list here; that will happen in build_standalone_mission_list_do_frame().
bool dummy_buffer;
Lcl_unexpected_tstring_check = &dummy_buffer;
#endif
Num_standalone_missions = cf_get_file_list(MAX_MISSIONS, Mission_filenames, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
#ifndef NDEBUG
Lcl_unexpected_tstring_check = nullptr;
#endif
// set up slider with 0 items to start
Sim_room_slider.create(&Ui_window, Sim_room_slider_coords[gr_screen.res][X_COORD], Sim_room_slider_coords[gr_screen.res][Y_COORD], Sim_room_slider_coords[gr_screen.res][W_COORD], Sim_room_slider_coords[gr_screen.res][H_COORD], 0, Sim_room_slider_filename[gr_screen.res], &sim_room_scroll_screen_up, &sim_room_scroll_screen_down, &sim_room_scroll_capture);
+52 -18
View File
@@ -569,63 +569,88 @@ int hotkey_build_team_listing(int enemy_team_mask, int y, bool list_enemies)
const char *str = NULL;
int i, j, s, z, start, team_mask;
int font_height = gr_get_font_height();
IFF_hotkey_team hotkey_team;
if (list_enemies)
{
str = XSTR( "Enemy ships", 403);
team_mask = enemy_team_mask;
hotkey_team = IFF_hotkey_team::Hostile;
}
else
{
str = XSTR( "Friendly ships", 402);
team_mask = ~enemy_team_mask;
hotkey_team = IFF_hotkey_team::Friendly;
}
for (i=0; i<MAX_SHIPS; i++)
if (iff_matches_mask(Ships[i].team, team_mask))
break;
if (i >= MAX_SHIPS)
return y;
hotkey_line_add(str, HOTKEY_LINE_HEADING, 0, y);
y += 2;
start = Num_lines;
for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
int shipnum;
bool add_it;
// don't process non-ships, or the player ship
if ( (Game_mode & GM_NORMAL) && (so->objnum == OBJ_INDEX(Player_obj)) )
continue;
shipnum = Objects[so->objnum].instance;
int shipnum = Objects[so->objnum].instance;
ship *shipp = &Ships[shipnum];
// filter out ships in wings
if ( shipp->wingnum >= 0 )
continue;
// filter out cargo containers, navbouys, etc, and non-ships
if ( Ship_info[Ships[shipnum].ship_info_index].class_type < 0 || !(Ship_types[Ship_info[Ships[shipnum].ship_info_index].class_type].flags[Ship::Type_Info_Flags::Hotkey_on_list] ))
if ( Ship_info[shipp->ship_info_index].class_type < 0 || !(Ship_types[Ship_info[shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Hotkey_on_list]) )
continue;
// don't process ships invisible to sensors, dying or departing
if ( Ships[shipnum].flags[Ship::Ship_Flags::Hidden_from_sensors] || Ships[shipnum].is_dying_or_departing() )
if ( shipp->flags[Ship::Ship_Flags::Hidden_from_sensors] || shipp->is_dying_or_departing() )
continue;
// if a ship's hotkey is the last hotkey on the list, then maybe make the hotkey -1 if
// we are now in mission. Otherwise, skip this ship
if ( Ships[shipnum].hotkey == MAX_KEYED_TARGETS ) {
if ( shipp->hotkey == MAX_KEYED_TARGETS ) {
if ( !(Game_mode & GM_IN_MISSION) )
continue; // skip to next ship
Ships[shipnum].hotkey = -1;
shipp->hotkey = -1;
}
// be sure this ship isn't in a wing, and that the teams match
if ( iff_matches_mask(Ships[shipnum].team, team_mask) && (Ships[shipnum].wingnum < 0) ) {
hotkey_line_add_sorted(Ships[shipnum].get_display_string(), HOTKEY_LINE_SHIP, shipnum, start);
// check IFF override and team mask
if (Iff_info[shipp->team].hotkey_team == IFF_hotkey_team::Default) {
add_it = iff_matches_mask(shipp->team, team_mask);
} else {
add_it = (Iff_info[shipp->team].hotkey_team == hotkey_team);
}
// add it if the teams match or if the IFF says to
if (add_it) {
hotkey_line_add_sorted(shipp->get_display_string(), HOTKEY_LINE_SHIP, shipnum, start);
}
}
for (i=0; i<Num_wings; i++) {
if (Wings[i].current_count && (Wings[i].ship_index[Wings[i].special_ship] >= 0) && iff_matches_mask(Ships[Wings[i].ship_index[Wings[i].special_ship]].team, team_mask)) {
bool add_it;
// the wing has to be valid
if (Wings[i].current_count && Wings[i].ship_index[Wings[i].special_ship] >= 0) {
ship *shipp = &Ships[Wings[i].ship_index[Wings[i].special_ship]];
// check IFF override and team mask
if (Iff_info[shipp->team].hotkey_team == IFF_hotkey_team::Default) {
add_it = iff_matches_mask(shipp->team, team_mask);
} else {
add_it = (Iff_info[shipp->team].hotkey_team == hotkey_team);
}
} else {
add_it = false;
}
// add it if the teams match or if the IFF says to
if (add_it) {
// special check for the player's wing. If he's in a wing, and the only guy left, don't
// do anything
if ( (Player_ship->wingnum == i) && (Wings[i].current_count == 1) )
@@ -652,12 +677,21 @@ int hotkey_build_team_listing(int enemy_team_mask, int y, bool list_enemies)
if (Wings[i].flags[Ship::Wing_Flags::Expanded]) {
for (j=0; j<Wings[i].current_count; j++) {
s = Wings[i].ship_index[j];
z = hotkey_line_insert(z + 1, Ships[s].get_display_string(), HOTKEY_LINE_SUBSHIP, s);
if (!Ships[s].is_dying_or_departing()) {
z = hotkey_line_insert(z + 1, Ships[s].get_display_string(), HOTKEY_LINE_SUBSHIP, s);
}
}
}
}
}
// see if we actually added any lines
if (start == Num_lines) {
// roll back the heading, then return as if nothing happened
Num_lines--;
return y - 2;
}
for (i=start; i<Num_lines; i++) {
if (Hotkey_lines[i].type == HOTKEY_LINE_SUBSHIP)
y += font_height;
+65 -52
View File
@@ -233,8 +233,8 @@ const auto OnMessageReceivedHook = scripting::Hook::Factory(
});
// forward declarations
void message_maybe_distort_text(char *text, int shipnum);
int comm_between_player_and_ship(int other_shipnum);
void message_maybe_distort_text(char *text, int shipnum, bool for_death_scream);
int comm_between_player_and_ship(int other_shipnum, bool for_death_scream);
// following functions to parse messages.tbl -- code pretty much ripped from weapon/ship table parsing code
@@ -625,6 +625,7 @@ void parse_msgtbl()
generic_message_filenames.clear();
generic_message_filenames.push_back("none");
generic_message_filenames.push_back("cuevoice");
generic_message_filenames.push_back("cue_voice");
generic_message_filenames.push_back("emptymsg");
generic_message_filenames.push_back("generic");
generic_message_filenames.push_back("msgstart");
@@ -864,9 +865,8 @@ void message_kill_all( int kill_all )
if ( kill_all ) {
Num_messages_playing = 0;
fsspeech_stop();
}
fsspeech_stop();
}
// function to kill nth playing message
@@ -1145,7 +1145,7 @@ void message_calc_anim_start_frame(int time, generic_anim *ani, int reverse)
}
if ( start_frame < 0 ) {
Int3();
mprintf(("Calculated start frame for animation %s was less than 0, setting to 0.", ani->filename));
start_frame=0;
}
@@ -1450,6 +1450,47 @@ void message_queue_process()
Assert ( q->priority != -1 );
Assert ( q->time_added != -1 );
int provisional_message_shipnum = -1;
// Do some checks to see if we are actually going to play this message at all. These checks have been moved above the
// "if ( Num_messages_playing )" block because we don't want to cancel an existing message if we have nothing to replace it with.
// Goober5000 - argh, don't conflate special sources with ships!
// NOTA BENE: don't check for != HUD_SOURCE_TERRAN_CMD, because with the new command persona code, Command could be a ship
if ( q->source != HUD_SOURCE_IMPORTANT ) {
provisional_message_shipnum = ship_name_lookup( q->who_from );
// see if we need to check if sending ship is alive
if ( (provisional_message_shipnum < 0) && (q->flags & MQF_CHECK_ALIVE) ) {
goto all_done;
}
}
// AL: added 07/14/97.. only play avi/sound if in gameplay
if ( gameseq_get_state() != GS_STATE_GAME_PLAY )
goto all_done;
// AL 4-7-98: Can't receive messages if comm is destroyed
if ( hud_communications_state(Player_ship) == COMM_DESTROYED ) {
goto all_done;
}
// G5K 4-26-20: Can't send messages if comm is destroyed
if ( The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(&Ships[provisional_message_shipnum], (q->builtin_type == MESSAGE_WINGMAN_SCREAM)) == COMM_DESTROYED ) {
goto all_done;
}
// Don't play death scream unless a small ship.
if ( q->builtin_type == MESSAGE_WINGMAN_SCREAM ) {
if (provisional_message_shipnum < 0) {
goto all_done;
}
if (!(Ship_info[Ships[provisional_message_shipnum].ship_info_index].is_small_ship() || (Ships[provisional_message_shipnum].flags[Ship::Ship_Flags::Always_death_scream])) ) {
goto all_done;
}
}
// At this point we think we're going to play the queued message.
if ( Num_messages_playing ) {
// peek at the first message on the queue to see if it should interrupt, or overlap a currently
// playing message. Mission specific messages will always interrupt builtin messages. They
@@ -1509,30 +1550,23 @@ void message_queue_process()
if ( Num_messages_playing == MAX_PLAYING_MESSAGES )
return;
// Goober5000 - argh, don't conflate special sources with ships!
// NOTA BENE: don't check for != HUD_SOURCE_TERRAN_CMD, because with the new command persona code, Command could be a ship
if ( q->source != HUD_SOURCE_IMPORTANT ) {
Message_shipnum = ship_name_lookup( q->who_from );
// see if we need to check if sending ship is alive
if ( (Message_shipnum < 0) && (q->flags & MQF_CHECK_ALIVE) ) {
goto all_done;
}
}
// if this is a ship, then don't play anything if this ship is already talking
if ( Message_shipnum != -1 ) {
if ( provisional_message_shipnum != -1 ) {
for ( i = 0; i < Num_messages_playing; i++ ) {
if ( (Playing_messages[i].shipnum != -1) && (Playing_messages[i].shipnum == Message_shipnum) ){
if ( (Playing_messages[i].shipnum != -1) && (Playing_messages[i].shipnum == provisional_message_shipnum) ){
return;
}
}
}
// At this point we are definitely going to play the queued message.
Message_shipnum = provisional_message_shipnum;
// set up module globals for this message
m = &Messages[q->message_num];
Playing_messages[Num_messages_playing].anim_data = NULL;
Playing_messages[Num_messages_playing].wave = sound_handle::invalid();
Playing_messages[Num_messages_playing].wave = sound_handle::invalid();
Playing_messages[Num_messages_playing].id = q->message_num;
Playing_messages[Num_messages_playing].priority = q->priority;
Playing_messages[Num_messages_playing].shipnum = Message_shipnum;
@@ -1547,24 +1581,6 @@ void message_queue_process()
message_translate_tokens(buf, q->special_message);
Message_expire = timestamp(static_cast<int>(42 * strlen(buf)));
// AL: added 07/14/97.. only play avi/sound if in gameplay
if ( gameseq_get_state() != GS_STATE_GAME_PLAY )
goto all_done;
// AL 4-7-98: Can't receive messages if comm is destroyed
if ( hud_communications_state(Player_ship) == COMM_DESTROYED ) {
goto all_done;
}
// Don't play death scream unless a small ship.
if ( q->builtin_type == MESSAGE_WINGMAN_SCREAM ) {
if (Message_shipnum < 0) {
goto all_done;
}
if (!(Ship_info[Ships[Message_shipnum].ship_info_index].is_small_ship() || (Ships[Message_shipnum].flags[Ship::Ship_Flags::Always_death_scream])) ) {
goto all_done;
}
}
// play wave first, since need to know duration for picking anim start frame
if(message_play_wave(q) == false) {
@@ -1575,7 +1591,7 @@ void message_queue_process()
message_play_anim(q);
// distort the message if comms system is damaged
message_maybe_distort_text(buf, Message_shipnum);
message_maybe_distort_text(buf, Message_shipnum, (q->builtin_type == MESSAGE_WINGMAN_SCREAM));
#ifndef NDEBUG
// debug only -- if the message is a builtin message, put in parens whether or not the voice played
@@ -1620,8 +1636,8 @@ void message_queue_process()
scripting::hook_param("Builtin", 'b', builtinMessage),
scripting::hook_param("Sender", 'o', sender)));
Num_messages_playing++; // this has to be done at the end because some sound functions use it to index into the array
all_done:
Num_messages_playing++;
message_remove_from_queue(q);
}
@@ -2195,7 +2211,7 @@ void message_maybe_distort()
was_muted = 0;
if ( comm_between_player_and_ship(Playing_messages[i].shipnum) != COMM_OK) {
if ( comm_between_player_and_ship(Playing_messages[i].shipnum, Playing_messages[i].builtin_type == MESSAGE_WINGMAN_SCREAM) != COMM_OK ) {
was_muted = Message_wave_muted;
if ( timestamp_elapsed(Next_mute_time) ) {
Next_mute_time = fl2i(Distort_patterns[Distort_num][Distort_next++] * Message_wave_duration);
@@ -2226,11 +2242,11 @@ void message_maybe_distort()
// Blank out portions of the sound based on Distort_num, this this is that same
// data that will be used to blank out portions of the audio playback
//
void message_maybe_distort_text(char *text, int shipnum)
void message_maybe_distort_text(char *text, int shipnum, bool for_death_scream)
{
int voice_duration;
if (comm_between_player_and_ship(shipnum) == COMM_OK) {
if (comm_between_player_and_ship(shipnum, for_death_scream) == COMM_OK) {
return;
}
@@ -2371,31 +2387,28 @@ bool change_message(const char* name, const char* message, int persona_index, in
}
/**
* Ideally, this would return the minimum of the comm state between the player and the other ship. In practice, retail has no checks whatsoever on a ship's ability
* to send messages unless that ship is the player, so such a change would require an AI profiles option and we must default to the player's state. However, we
* have a bit of wiggle room with COMM_SCRAMBLED, because EMP effects are either transient or set by the newly enhanced scramble-messages SEXP. Thus any comm
* dropout does not cause an unanticipated deviation in the mission design.
* This returns the minimum of the comm state between the player and the other ship. In practice, retail has no checks whatsoever on a ship's ability
* to send messages unless that ship is the player, so such a change requires an AI profiles option and we must default to the player's state. However, we
* have a bit of wiggle room with COMM_SCRAMBLED, because EMP effects are either transient or set by the scramble-messages SEXP.
*/
int comm_between_player_and_ship(int other_shipnum)
int comm_between_player_and_ship(int other_shipnum, bool for_death_scream)
{
int player_comm_state = hud_communications_state(Player_ship);
if (other_shipnum < 0)
return player_comm_state;
int other_comm_state = hud_communications_state(&Ships[other_shipnum]);
int other_comm_state = hud_communications_state(&Ships[other_shipnum], for_death_scream);
/* here is where you would check the flag
if (hypothetical_ai_profiles_flag)
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships])
{
return MIN(player_comm_state, other_comm_state);
}
else
*/
{
if (player_comm_state == COMM_OK && other_comm_state == COMM_OK)
return COMM_OK;
else if (player_comm_state == COMM_SCRAMBLED || other_comm_state == COMM_SCRAMBLED)
else if (player_comm_state == COMM_OK && other_comm_state == COMM_SCRAMBLED)
return COMM_SCRAMBLED;
else
return player_comm_state;
+27 -48
View File
@@ -60,6 +60,7 @@
#include "object/waypoint.h"
#include "parse/generic_log.h"
#include "parse/parselo.h"
#include "scripting/hook_api.h"
#include "scripting/scripting.h"
#include "playerman/player.h"
#include "popup/popup.h"
@@ -406,6 +407,11 @@ void convertFSMtoFS2();
MONITOR(NumShipArrivals)
MONITOR(NumShipDepartures)
const std::shared_ptr<scripting::Hook> OnDepartureStartedHook = scripting::Hook::Factory(
"On Departure Started", "Called when a ship starts the departure process.",
{
{"Ship", "ship", "The ship that has began the depture process."},
});
// Goober5000
void parse_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, char *string_field_640, char *string_field_1024)
@@ -2024,29 +2030,7 @@ int parse_create_object_sub(p_object *p_objp)
shipp->base_texture_anim_frametime = game_get_overall_frametime();
// handle the replacement textures
if (!p_objp->replacement_textures.empty())
{
shipp->ship_replacement_textures = (int *) vm_malloc( MAX_REPLACEMENT_TEXTURES * sizeof(int));
for (i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
shipp->ship_replacement_textures[i] = -1;
}
// now fill them in
for (SCP_vector<texture_replace>::iterator tr = p_objp->replacement_textures.begin(); tr != p_objp->replacement_textures.end(); ++tr)
{
pm = model_get(sip->model_num);
// look for textures
for (j = 0; j < pm->n_textures; j++)
{
texture_map *tmap = &pm->maps[j];
int tnum = tmap->FindTexture(tr->old_texture);
if(tnum > -1)
shipp->ship_replacement_textures[j * TM_NUM_TYPES + tnum] = tr->new_texture_id;
}
}
shipp->apply_replacement_textures(p_objp->replacement_textures);
// Copy across the alt classes (if any) for FRED
if (Fred_running) {
@@ -6011,15 +5995,18 @@ void parse_init(bool basic)
{
reset_parse();
for (int i = 0; i < MAX_CARGO; i++)
Cargo_names[i] = Cargo_names_buf[i]; // make a pointer array for compatibility
// if we just want basic info then we don't need some of this initialization
if (!basic)
{
for (int i = 0; i < MAX_CARGO; i++)
Cargo_names[i] = Cargo_names_buf[i]; // make a pointer array for compatibility
Total_goal_target_names = 0;
ai_clear_goal_target_names();
// if we are just wanting basic info then we shouldn't need sexps
// (prevents memory fragmentation with the now dynamic Sexp_nodes[])
if ( !basic )
// if we are just wanting basic info then we shouldn't need sexps
// (prevents memory fragmentation with the now dynamic Sexp_nodes[])
init_sexp();
}
}
// main parse routine for parsing a mission. The default parameter flags tells us which information
@@ -6643,7 +6630,8 @@ int mission_set_arrival_location(int anchor, int location, int dist, int objnum,
// if we get an error, just let the ship arrive(?)
if ( ai_acquire_emerge_path(&Objects[objnum], anchor_objnum, path_mask, &pos, &fvec) == -1 ) {
Int3(); // get MWA or AL -- not sure what to do here when we cannot acquire a path
// get MWA or AL -- not sure what to do here when we cannot acquire a path
mprintf(("Unable to acquire arrival path on anchor ship %s\n", Ships[shipnum].ship_name));
return -1;
}
Objects[objnum].pos = pos;
@@ -7115,21 +7103,6 @@ void mission_eval_arrivals()
Mission_arrival_timestamp = timestamp(ARRIVAL_TIMESTAMP);
}
/**
* Checks the warp drive; we might be able to depart some other way (e.g. by entering a docking bay)
*/
int ship_can_use_warp_drive(ship *shipp)
{
// must *have* a subspace drive
if (shipp->flags[Ship::Ship_Flags::No_subspace_drive])
return 0;
// navigation must work
if (!ship_navigation_ok_to_warp(shipp))
return 0;
return 1;
}
/**
* Called to make object objp depart. Rewritten and expanded by Goober5000.
@@ -7143,6 +7116,11 @@ int mission_do_departure(object *objp, bool goal_is_to_warp)
mprintf(("Entered mission_do_departure() for %s\n", shipp->ship_name));
// add scripting hook for 'On Depature Started' --wookieejedi
// hook is placed at the begining of this function to allow the scripter to
// actually have access to the ship's departure decisions before they are all executed
OnDepartureStartedHook->run(scripting::hook_param_list(scripting::hook_param("Ship", 'o', objp)));
// abort rearm, because if we entered this function we're either going to depart via hyperspace, depart via bay,
// or revert to our default behavior
ai_abort_rearm_request(objp);
@@ -7157,7 +7135,7 @@ int mission_do_departure(object *objp, bool goal_is_to_warp)
mprintf(("Looks like we were ordered to depart; initiating the standard departure logic\n"));
}
// since our goal is to warp, then if we can warp, jump directly to the warping part
else if (ship_can_use_warp_drive(shipp))
else if (ship_can_warp_full_check(shipp))
{
mprintf(("Our current goal is to warp! Trying to warp...\n"));
goto try_to_warp;
@@ -7227,7 +7205,7 @@ int mission_do_departure(object *objp, bool goal_is_to_warp)
try_to_warp:
// make sure we can actually warp
if (ship_can_use_warp_drive(shipp))
if (ship_can_warp_full_check(shipp))
{
mprintf(("Setting mode to warpout\n"));
@@ -7280,7 +7258,8 @@ void mission_eval_departures()
// don't process a ship that is already departing or dying or disabled
// AL 12-30-97: Added SF_CANNOT_WARP to check
// Goober5000 - fixed so that it WILL eval when SF_CANNOT_WARP if departing to dockbay
if ( shipp->is_dying_or_departing() || (shipp->cannot_warp() && (shipp->departure_location != DEPART_AT_DOCK_BAY)) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) {
// wookieejedi - fixed so it accounts for break and never warp too
if ( shipp->is_dying_or_departing() || ( !(ship_can_warp_full_check(shipp)) && (shipp->departure_location != DEPART_AT_DOCK_BAY)) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) {
continue;
}
+1 -1
View File
@@ -1992,7 +1992,7 @@ int brief_only_allow_briefing()
return 1;
}
if ( The_mission.flags[Mission::Mission_Flags::Scramble] || The_mission.flags[Mission::Mission_Flags::Red_alert] ) {
if ( The_mission.flags[Mission::Mission_Flags::Scramble, Mission::Mission_Flags::Red_alert] ) {
return 1;
}
+29 -6
View File
@@ -1853,29 +1853,51 @@ bool is_weapon_carried(int weapon_index)
return false;
}
bool is_a_weapon_slot_empty()
bool check_for_gaps_in_weapon_slots()
{
for (int slot = 0; slot < MAX_WING_BLOCKS*MAX_WING_SLOTS; ++slot)
{
// a ship must exist in this slot
if (Wss_slots[slot].ship_class >= 0)
{
// if the player can't modify the weapons, then an empty bank is not his fault
auto ss_slot = &Ss_wings[slot / MAX_WING_SLOTS].ss_slots[slot % MAX_WING_SLOTS];
if (ss_slot->status & WING_SLOT_WEAPONS_DISABLED)
continue;
ship_info *sip = &Ship_info[Wss_slots[slot].ship_class];
bool empty_slot = false;
// check primary banks
for (int bank = 0; bank < sip->num_primary_banks; ++bank) // NOLINT(modernize-loop-convert)
{
// is the slot empty?
if (Wss_slots[slot].wep_count[bank] <= 0)
{
empty_slot = true;
}
// is there a full slot following a gap?
else if (empty_slot)
{
return true;
}
}
empty_slot = false;
// check secondary banks
for (int bank = 0; bank < sip->num_secondary_banks; ++bank)
{
// is the slot empty?
if (Wss_slots[slot].wep_count[MAX_SHIP_PRIMARY_BANKS+bank] <= 0)
if (Wss_slots[slot].wep_count[MAX_SHIP_PRIMARY_BANKS + bank] <= 0)
{
empty_slot = true;
}
// is there a full slot following a gap?
else if (empty_slot)
{
return true;
}
}
}
}
@@ -1950,12 +1972,13 @@ void commit_pressed()
}
}
// Goober5000 - check that all weapon slots are filled (Mantis 2715)
// Goober5000 - check that there are no gaps in weapon slots (Mantis 2715)
// Any incomplete loadouts that make it through the loadout screen will be condensed so that gaps are moved to the bottom. This check adds that responsibility to the player.
// Note: don't check for training, scramble, or red-alert missions
// Note2: don't check missions without briefings either
if (is_a_weapon_slot_empty() && !(The_mission.game_type & MISSION_TYPE_TRAINING) && !The_mission.flags[Mission::Mission_Flags::Scramble] && !The_mission.flags[Mission::Mission_Flags::Red_alert] && !The_mission.flags[Mission::Mission_Flags::No_briefing])
if (check_for_gaps_in_weapon_slots() && !(The_mission.game_type & MISSION_TYPE_TRAINING) && !The_mission.flags[Mission::Mission_Flags::Scramble, Mission::Mission_Flags::Red_alert, Mission::Mission_Flags::No_briefing])
{
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("At least one ship has an empty weapon bank. All weapon banks must have weapons assigned.", 1642), weapon_list.c_str());
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("At least one ship has an empty weapon bank before a full weapon bank.\n\nAll weapon banks must have weapons assigned, or if there are any gaps, they must be at the bottom of the set of banks.", 1642), weapon_list.c_str());
return;
}
@@ -2197,7 +2220,7 @@ void draw_wing_block(int wb_num, int hot_slot, int selected_slot, int class_sele
color_to_draw = &Icon_colors[ICON_FRAME_DISABLED];
// in multiplayer, determine if this it the special case where the slot is disabled, and
// it is also _my_ slot (ie, team capatains/host have not locked players yet)
// it is also _my_ slot (ie, team captains/hosts have not locked players yet)
if((Game_mode & GM_MULTIPLAYER) && multi_ts_disabled_high_slot(slot_index)){
if(icon->model_index == -1)
bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_DISABLED_HIGH];
+13 -2
View File
@@ -42,6 +42,7 @@ float Shield_pain_flash_factor;
gameversion::version Targetted_version; // Defaults to retail
SCP_string Window_title;
bool Unicode_text_mode;
bool Use_tabled_strings_for_default_language;
SCP_string Movie_subtitle_font;
bool Enable_scripts_in_fred; // By default FRED does not initialize the scripting system
SCP_string Window_icon_path;
@@ -99,6 +100,12 @@ void parse_mod_table(const char *filename)
mprintf(("Game settings table: Unicode mode: %s\n", Unicode_text_mode ? "yes" : "no"));
}
if (optional_string("$Use tabled strings for the default language:")) {
stuff_boolean(&Use_tabled_strings_for_default_language);
mprintf(("Game settings table: Use tabled strings (translations) for the default language: %s\n", Use_tabled_strings_for_default_language ? "yes" : "no"));
}
optional_string("#CAMPAIGN SETTINGS");
if (optional_string("$Default Campaign File Name:")) {
@@ -535,11 +542,14 @@ void mod_table_init()
// parse any modular tables
parse_modular_table("*-mod.tbm", parse_mod_table);
}
bool mod_supports_version(int major, int minor, int build) {
bool mod_supports_version(int major, int minor, int build)
{
return Targetted_version >= gameversion::version(major, minor, build, 0);
}
void mod_table_reset() {
void mod_table_reset()
{
Directive_wait_time = 3000;
True_loop_argument_sexps = false;
Fixed_turret_collisions = false;
@@ -565,6 +575,7 @@ void mod_table_reset() {
Targetted_version = gameversion::version(2, 0, 0, 0); // Defaults to retail
Window_title = "";
Unicode_text_mode = false;
Use_tabled_strings_for_default_language = false;
Movie_subtitle_font = "font01.vf";
Enable_scripts_in_fred = false;
Window_icon_path = "app_icon_sse";
+1
View File
@@ -33,6 +33,7 @@ extern float Generic_pain_flash_factor;
extern float Shield_pain_flash_factor;
extern SCP_string Window_title;
extern bool Unicode_text_mode;
extern bool Use_tabled_strings_for_default_language;
extern SCP_string Movie_subtitle_font;
extern bool Enable_scripts_in_fred;
extern SCP_string Window_icon_path;
+1 -1
View File
@@ -371,7 +371,7 @@ typedef struct mp_vert {
} mp_vert;
typedef struct model_path {
char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD
char name[MAX_NAME_LEN]; // name of the path. Should be unique
char parent_name[MAX_NAME_LEN]; // parent name of submodel that path is linked to in POF
int parent_submodel;
int nverts;
+22 -2
View File
@@ -2273,7 +2273,15 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
memset( pm->paths, 0, sizeof(model_path) * pm->n_paths );
for (i=0; i<pm->n_paths; i++ ) {
cfread_string_len(pm->paths[i].name , MAX_NAME_LEN-1, fp);
cfread_string_len(pm->paths[i].name, MAX_NAME_LEN-1, fp);
// check for reused path names... not fatal, but maybe problematic
for (j = 0; j < i; j++) {
if (!stricmp(pm->paths[i].name, pm->paths[j].name)) {
Warning(LOCATION, "Path '%s' in model %s has a name that is not unique!", pm->paths[i].name, pm->filename);
}
}
if ( pm->version >= 2002 ) {
// store the sub_model name number of the parent
cfread_string_len(pm->paths[i].parent_name , MAX_NAME_LEN-1, fp);
@@ -3073,6 +3081,8 @@ void model_set_bay_path_nums(polymodel *pm)
pm->ship_bay->arrive_flags = 0; // bitfield, set to 1 when that path number is reserved for an arrival
pm->ship_bay->depart_flags = 0; // bitfield, set to 1 when that path number is reserved for a departure
// sanity part 1
memset(pm->ship_bay->path_indexes, -1, MAX_SHIP_BAY_PATHS * sizeof(int));
// iterate through the paths that exist in the polymodel, searching for $bayN pathnames
bool too_many_paths = false;
@@ -3108,6 +3118,16 @@ void model_set_bay_path_nums(polymodel *pm)
{
Warning(LOCATION, "Model '%s' has too many bay paths - max is %d", pm->filename, MAX_SHIP_BAY_PATHS);
}
// sanity part 2
for (i = 0; i < pm->ship_bay->num_paths; i++)
{
if (pm->ship_bay->path_indexes[i] < 0)
{
Warning(LOCATION, "Model '%s' does not have a '$bay%.2d' path specified! A total of %d bay paths were counted. Either there is a gap in the path sequence, or a path has a duplicate name.", pm->filename, i + 1, pm->ship_bay->num_paths);
pm->ship_bay->path_indexes[i] = 0; // avoid crashes
}
}
}
// Get "parent" submodel for live debris submodel
@@ -3991,7 +4011,7 @@ int model_rotate_gun(int model_num, model_subsystem *turret, matrix *orient, ang
{
base_delta = vm_delta_from_interp_angle( base_angles->h, desired_angles.h );
gun_delta = vm_delta_from_interp_angle( gun_angles->p, desired_angles.p );
ss->points_to_target = sqrt( pow(base_delta,2) + pow(gun_delta,2));
ss->points_to_target = sqrt((base_delta*base_delta) + (gun_delta*gun_delta));
}
return 1;
+25 -48
View File
@@ -9,12 +9,9 @@
#include "model/modelrender.h"
#include "globalincs/alphacolors.h"
#include "asteroid/asteroid.h"
#include "cmdline/cmdline.h"
#include "gamesequence/gamesequence.h"
#include "graphics/debug.h"
#include "graphics/light.h"
#include "graphics/matrix.h"
#include "graphics/shadows.h"
@@ -1812,6 +1809,7 @@ void model_render_glowpoint(int point_num, vec3d *pos, matrix *orient, glow_poin
vec3d cone_dir_rot;
vec3d cone_dir_model;
vec3d cone_dir_world;
vec3d cone_dir_screen;
vec3d unused;
if ( gpo->rotating ) {
@@ -1822,7 +1820,9 @@ void model_render_glowpoint(int point_num, vec3d *pos, matrix *orient, glow_poin
find_submodel_instance_point_normal(&unused, &cone_dir_model, shipp->model_instance_num, bank->submodel_parent, &unused, &cone_dir_rot);
vm_vec_unrotate(&cone_dir_world, &cone_dir_model, orient);
light_add_cone(&world_pnt, &cone_dir_world, gpo->cone_angle, gpo->cone_inner_angle, gpo->dualcone, 1.0f, w * gpo->radius_multi, 1, pulse * gpo->light_color.xyz.x + (1.0f-pulse) * gpo->light_mix_color.xyz.x, pulse * gpo->light_color.xyz.y + (1.0f-pulse) * gpo->light_mix_color.xyz.y, pulse * gpo->light_color.xyz.z + (1.0f-pulse) * gpo->light_mix_color.xyz.z, -1);
vm_vec_rotate(&cone_dir_screen, &cone_dir_world, &Eye_matrix);
cone_dir_screen.xyz.z = -cone_dir_screen.xyz.z;
light_add_cone(&world_pnt, &cone_dir_screen, gpo->cone_angle, gpo->cone_inner_angle, gpo->dualcone, 1.0f, w * gpo->radius_multi, 1, pulse * gpo->light_color.xyz.x + (1.0f-pulse) * gpo->light_mix_color.xyz.x, pulse * gpo->light_color.xyz.y + (1.0f-pulse) * gpo->light_mix_color.xyz.y, pulse * gpo->light_color.xyz.z + (1.0f-pulse) * gpo->light_mix_color.xyz.z, -1);
} else {
light_add_point(&world_pnt, 1.0f, w * gpo->radius_multi, 1, pulse * gpo->light_color.xyz.x + (1.0f-pulse) * gpo->light_mix_color.xyz.x, pulse * gpo->light_color.xyz.y + (1.0f-pulse) * gpo->light_mix_color.xyz.y, pulse * gpo->light_color.xyz.z + (1.0f-pulse) * gpo->light_mix_color.xyz.z, -1);
}
@@ -2012,6 +2012,7 @@ void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, in
int n_q = 0;
size_t k;
vec3d norm, norm2, fvec, pnt, npnt;
thruster_bank *bank = NULL;
vertex p;
bool do_render = false;
@@ -2030,7 +2031,7 @@ void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, in
// get an initial count to figure out how man geo batchers we need allocated
for (i = 0; i < pm->n_thrusters; i++ ) {
const auto bank = &pm->thrusters[i];
bank = &pm->thrusters[i];
n_q += bank->num_points;
}
@@ -2072,10 +2073,10 @@ void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, in
vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
bool submodel_rotation = false;
const auto bank = &pm->thrusters[i];
bank = &pm->thrusters[i];
// don't draw this thruster if the engine is destroyed or just not on
if (!model_should_render_engine_glow(objnum, bank->obj_num))
if ( !model_should_render_engine_glow(objnum, bank->obj_num) )
continue;
// If bank is attached to a submodel, prepare to account for rotations
@@ -2084,21 +2085,18 @@ void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, in
// set the the necessary submodel instance info needed here. The second
// condition is thus a hack to disable the feature while in the lab, and
// can be removed if the lab is re-structured accordingly. -zookeeper
if (bank->submodel_num > -1 && pm->submodel[bank->submodel_num].can_move &&
(gameseq_get_state_idx(GS_STATE_LAB) == -1)) {
model_find_submodel_offset(&submodel_static_offset,
Ship_info[shipp->ship_info_index].model_num,
bank->submodel_num);
if ( bank->submodel_num > -1 && pm->submodel[bank->submodel_num].can_move && (gameseq_get_state_idx(GS_STATE_LAB) == -1) ) {
model_find_submodel_offset(&submodel_static_offset, Ship_info[shipp->ship_info_index].model_num, bank->submodel_num);
submodel_rotation = true;
}
for (j = 0; j < bank->num_points; j++) {
Assert(bank->points != NULL);
Assert( bank->points != NULL );
float d, D;
vec3d tempv;
glow_point* gpt = &bank->points[j];
glow_point *gpt = &bank->points[j];
vec3d loc_offset = gpt->pnt;
vec3d loc_norm = gpt->norm;
vec3d world_pnt;
@@ -2149,21 +2147,21 @@ void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, in
D = d = vm_vec_dot(&tempv, &world_norm);
// ADAM: Min throttle draws rad*MIN_SCALE, max uses max.
const auto NOISE_SCALE = 0.5f;
const auto MIN_SCALE = 3.4f;
const auto MAX_SCALE = 4.7f;
#define NOISE_SCALE 0.5f
#define MIN_SCALE 3.4f
#define MAX_SCALE 4.7f
float magnitude;
vec3d scale_vec = {{{1.0f, 0.0f, 0.0f}}};
vec3d scale_vec = { { { 1.0f, 0.0f, 0.0f } } };
// normalize banks, in case of incredibly big normals
if (!IS_VEC_NULL_SQ_SAFE(&world_norm))
if ( !IS_VEC_NULL_SQ_SAFE(&world_norm) )
vm_vec_copy_normalize(&scale_vec, &world_norm);
// adjust for thrust
(scale_vec.xyz.x *= thruster_info.length.xyz.x) -= 0.1f;
(scale_vec.xyz.y *= thruster_info.length.xyz.y) -= 0.1f;
(scale_vec.xyz.z *= thruster_info.length.xyz.z) -= 0.1f;
(scale_vec.xyz.z *= thruster_info.length.xyz.z) -= 0.1f;
// get magnitude, which we will use as the scaling reference
magnitude = vm_vec_normalize(&scale_vec);
@@ -2291,9 +2289,9 @@ void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, in
// Where the particles emit from
pe.pos = npnt;
// Initial velocity of all the particles
pe.vel = Objects[shipp->objnum].phys_info.desired_vel;
pe.vel = Objects[shipp->objnum].phys_info.desired_vel;
pe.min_vel = v * 0.75f;
pe.max_vel = v * 1.25f;
pe.max_vel = v * 1.25f;
// What normal the particle emit around
pe.normal = orient->vec.fvec;
vm_vec_negate(&pe.normal);
@@ -2302,37 +2300,16 @@ void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, in
pe.num_low = tp->n_low;
// Highest number of particles to create
pe.num_high = tp->n_high;
pe.min_rad = gpt->radius * tp->min_rad;
pe.max_rad = gpt->radius * tp->max_rad;
pe.min_rad = gpt->radius * tp->min_rad;
pe.max_rad = gpt->radius * tp->max_rad;
// How close they stick to that normal 0=on normal, 1=180, 2=360 degree
pe.normal_variance = tp->variance;
pe.min_life = 0.0f;
pe.max_life = 1.0f;
pe.min_life = 0.0f;
pe.max_life = 1.0f;
particle::emit(&pe, particle::PARTICLE_BITMAP, tp->thruster_bitmap.first_frame);
particle::emit( &pe, particle::PARTICLE_BITMAP, tp->thruster_bitmap.first_frame);
}
}
// vec3d debug_pnt = world_pnt + (10.f * magnitude) * world_norm;
// graphics::debug::line_3d(&world_pnt, &debug_pnt, &Color_red);
//
// graphics::debug::text_3d(&debug_pnt, &Color_white, std::to_string(magnitude));
const auto lightRadius = std::max(1.0f, gpt->radius) * 250.f;
const auto outerAngle = cosf(50.0f / 180.0f * PI);
const auto innerAngle = cosf(25.0f / 180.0f * PI);
light_add_cone(&world_pnt,
&world_norm,
outerAngle,
innerAngle,
false,
lightRadius,
lightRadius,
magnitude,
0.1f,
0.1f,
0.9f,
-1);
}
}
}
+9 -2
View File
@@ -4449,9 +4449,10 @@ void multi_create_list_load_missions()
}
for (idx = 0; idx < file_count; idx++) {
int flags,max_players;
int flags, max_players;
char *filename;
uint m_respawn;
bool lcl_weirdness = false;
fname = file_list[idx];
@@ -4462,10 +4463,16 @@ void multi_create_list_load_missions()
std_gen_set_text(filename, 2);
}
// activate tstrings check
Lcl_unexpected_tstring_check = &lcl_weirdness;
flags = mission_parse_is_multi(filename, mission_name);
// deactivate tstrings check
Lcl_unexpected_tstring_check = nullptr;
// if the mission is a multiplayer mission, and we can list it, then add it to the mission list
if (flags && !mission_is_ignored(filename)) {
if (flags && !mission_is_ignored(filename) && !lcl_weirdness) {
max_players = mission_parse_get_multi_mission_info( filename );
m_respawn = The_mission.num_respawns;
+1 -1
View File
@@ -2157,7 +2157,7 @@ void multi_warpout_all_players()
// if we're an observer, or we're respawning, or we can't warp out. so just jump into the debrief state
if((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Net_player->flags & NETINFO_FLAG_RESPAWNING) ||
((Player_obj->type == OBJ_SHIP) && ( Player_ship->cannot_warp())) ){
((Player_obj->type == OBJ_SHIP) && !(ship_can_warp_full_check(Player_ship)))){
multi_handle_sudden_mission_end();
+7 -1
View File
@@ -781,10 +781,16 @@ void obj_player_fire_stuff( object *objp, control_info ci )
// single player and multiplayer masters do all of the following
if ( !MULTIPLAYER_CLIENT ) {
if ( ci.fire_secondary_count ) {
ship_fire_secondary( objp );
if ( !ship_start_secondary_fire(objp) ) {
ship_fire_secondary( objp );
}
// kill the secondary count
ci.fire_secondary_count = 0;
} else {
if ( ship_stop_secondary_fire(objp) ) {
ship_fire_secondary( objp );
}
}
}
-13
View File
@@ -89,8 +89,6 @@ namespace
int Global_warning_count = 0;
int Global_error_count = 0;
extern lua_Debug Ade_debug_info;
namespace os
{
namespace dialogs
@@ -166,17 +164,6 @@ namespace os
msgStream << "\n";
msgStream << "\n";
msgStream << Separator;
msgStream << "ADE Debug:";
msgStream << "\n";
msgStream << Separator;
LuaDebugPrint(msgStream, Ade_debug_info);
msgStream << Separator;
msgStream << "\n";
msgStream << "\n";
msgStream << Separator;
// Get the stack via the debug.traceback() function
+58 -54
View File
@@ -244,7 +244,7 @@ int get_line_num()
while (p < stoploc)
{
if (*p == '\0')
Assert(0);
Warning(LOCATION, "Unexpected end-of-file while looking for error line!");
if ( !incomment && (*p == COMMENT_CHAR) )
incomment = 1;
@@ -314,8 +314,6 @@ void advance_to_eoln(const char *more_terminators)
terminators[1] = 0;
if (more_terminators != NULL)
strcat_s(terminators, more_terminators);
else
terminators[1] = 0;
while (strchr(terminators, *Mp) == NULL)
Mp++;
@@ -324,7 +322,7 @@ void advance_to_eoln(const char *more_terminators)
// Advance Mp to the next white space (ignoring white space inside of " marks)
void advance_to_next_white()
{
int in_quotes = 0;
bool in_quotes = false;
while ((*Mp != EOLN) && (*Mp != '\0')) {
if (*Mp == '\"')
@@ -765,8 +763,6 @@ void copy_to_eoln(char *outstr, const char *more_terminators, const char *instr,
terminators[1] = 0;
if (more_terminators != NULL)
strcat_s(terminators, more_terminators);
else
terminators[1] = 0;
while (((ch = *instr++) != 0) && (strchr(terminators, ch) == NULL) && (count < max)) {
*outstr++ = ch;
@@ -791,8 +787,6 @@ void copy_to_eoln(SCP_string &outstr, const char *more_terminators, const char *
terminators[1] = 0;
if (more_terminators != NULL)
strcat_s(terminators, more_terminators);
else
terminators[1] = 0;
outstr = "";
while (((ch = *instr++) != 0) && (strchr(terminators, ch) == NULL)) {
@@ -802,10 +796,10 @@ void copy_to_eoln(SCP_string &outstr, const char *more_terminators, const char *
// Copy characters from instr to outstr until next white space is found, or until max
// characters have been copied (including terminator).
void copy_to_next_white(char *outstr, char *instr, int max)
void copy_to_next_white(char *outstr, const char *instr, int max)
{
int count = 0;
int in_quotes = 0;
bool in_quotes = false;
char ch;
while (((ch = *instr++)>0) && (ch != EOLN) && (ch != '\0') && (count < max)) {
@@ -831,9 +825,9 @@ void copy_to_next_white(char *outstr, char *instr, int max)
}
// Ditto for SCP_string.
void copy_to_next_white(SCP_string &outstr, char *instr)
void copy_to_next_white(SCP_string &outstr, const char *instr)
{
int in_quotes = 0;
bool in_quotes = false;
char ch;
outstr = "";
@@ -854,10 +848,10 @@ void copy_to_next_white(SCP_string &outstr, char *instr)
}
//Returns a null-terminated character string allocated with vm_malloc() with the data
char* alloc_text_until(char* instr, char* endstr)
char* alloc_text_until(const char* instr, const char* endstr)
{
Assert(instr && endstr);
char *foundstr = stristr(instr, endstr);
auto foundstr = stristr(instr, endstr);
if(foundstr == NULL)
{
@@ -888,12 +882,11 @@ char* alloc_text_until(char* instr, char* endstr)
// Copy text until a certain string is matched.
// For example, this is used to copy mission notes, scanning until $END NOTES:
// is found.
void copy_text_until(char *outstr, char *instr, const char *endstr, int max_chars)
void copy_text_until(char *outstr, const char *instr, const char *endstr, int max_chars)
{
char *foundstr;
Assert(outstr && instr && endstr);
foundstr = stristr(instr, endstr);
auto foundstr = stristr(instr, endstr);
if (foundstr == NULL) {
nprintf(("Error", "Error. Looking for [%s], but never found it.\n", endstr));
@@ -915,12 +908,11 @@ void copy_text_until(char *outstr, char *instr, const char *endstr, int max_char
}
// Ditto for SCP_string.
void copy_text_until(SCP_string &outstr, char *instr, const char *endstr)
void copy_text_until(SCP_string &outstr, const char *instr, const char *endstr)
{
char *foundstr;
Assert(instr && endstr);
foundstr = stristr(instr, endstr);
auto foundstr = stristr(instr, endstr);
if (foundstr == NULL) {
nprintf(("Error", "Error. Looking for [%s], but never found it.\n", endstr));
@@ -1367,7 +1359,7 @@ void stuff_string_line(SCP_string &outstr)
// Exactly the same as stuff string only Malloc's the buffer.
// Supports various FreeSpace primitive types. If 'len' is supplied, it will override
// the default string length if using the F_NAME case.
char *stuff_and_malloc_string(int type, char *terminators)
char *stuff_and_malloc_string(int type, const char *terminators)
{
SCP_string tmp_result;
@@ -1380,7 +1372,7 @@ char *stuff_and_malloc_string(int type, char *terminators)
return vm_strdup(tmp_result.c_str());
}
void stuff_malloc_string(char **dest, int type, char *terminators)
void stuff_malloc_string(char **dest, int type, const char *terminators)
{
Assert(dest != NULL); //wtf?
@@ -1397,53 +1389,62 @@ void stuff_malloc_string(char **dest, int type, char *terminators)
}
// After reading a multitext string, you can call this function to convert any newlines into
// spaces, so it's a one paragraph string (I.e. as in MS-Word).
//
// spaces, so it's a one paragraph string (i.e. as in MS-Word).
void compact_multitext_string(char *str)
{
size_t i;
size_t len = strlen(str);
size_t num_cr = 0;
auto p_dest = str;
auto p_src = str;
for (i=0; i<len; i++)
while (*p_src)
{
char ch = *p_src;
p_src++;
// skip CR
// convert LF to space
// copy characters backwards if any CRs previously encountered
if (str[i] == '\r')
num_cr++;
else if (str[i] == '\n')
str[i-num_cr] = ' ';
else if (num_cr > 0)
str[i-num_cr] = str[i];
if (ch != '\r')
{
if (ch == '\n')
*p_dest = ' ';
else if (p_dest != p_src)
*p_dest = *p_src;
p_dest++;
}
}
if (num_cr > 0)
str[len-num_cr] = 0;
if (p_dest != p_src)
*p_dest = 0;
}
// ditto for SCP_string
void compact_multitext_string(SCP_string &str)
{
size_t i;
size_t len = str.length();
size_t num_cr = 0;
auto p_dest = str.begin();
auto p_src = str.begin();
for (i=0; i<len; i++)
while (p_src != str.end())
{
char ch = *p_src;
p_src++;
// skip CR
// convert LF to space
// copy characters backwards if any CRs previously encountered
if (str[i] == '\r')
num_cr++;
else if (str[i] == '\n')
str[i-num_cr] = ' ';
else if (num_cr > 0)
str[i-num_cr] = str[i];
if (ch != '\r')
{
if (ch == '\n')
*p_dest = ' ';
else if (p_dest != p_src)
*p_dest = *p_src;
p_dest++;
}
}
if (num_cr > 0)
str.resize(len-num_cr);
if (p_dest != p_src)
str.erase(p_dest);
}
// Converts a character from Windows-1252 to CP437.
@@ -1987,13 +1988,12 @@ void strip_comments(char *line, bool &in_quote, bool &in_multiline_comment_a, bo
}
}
int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, char *cur)
int parse_get_line(char *lineout, int max_line_len, const char *start, int max_size, const char *cur)
{
char * t = lineout;
int i, num_chars_read=0;
char c;
for ( i = 0; i < max_line_len-1; i++ ) {
do {
if ( (cur - start) >= max_size ) {
@@ -2019,7 +2019,7 @@ int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, c
// Read mission text, stripping comments.
// When a comment is found, it is removed. If an entire line
// consisted of a comment, a blank line is left in the input file.
// Goober5000 - added ability to read somewhere other than Mission_text
// Goober5000 - added ability to read somewhere other than Parse_text
void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
{
// copy the filename
@@ -2134,7 +2134,7 @@ void allocate_parse_text(size_t size)
Parse_text_raw = (char *) vm_malloc(sizeof(char) * size, memory::quiet_alloc);
if ( (Parse_text == nullptr) || (Parse_text_raw == nullptr) ) {
Error(LOCATION, "Unable to allocate enough memory for Mission_text! Aborting...\n");
Error(LOCATION, "Unable to allocate enough memory for Parse_text! Aborting...\n");
}
memset( Parse_text, 0, sizeof(char) * size );
@@ -2170,7 +2170,7 @@ void read_raw_file_text(const char *filename, int mode, char *raw_text)
// that if we have control over the raw_text pointer which is only the case if it's null.
auto can_reallocate = raw_text == nullptr;
if (raw_text == nullptr) {
// allocate, or reallocate, memory for Mission_text and Mission_text_raw based on size we need now
// allocate, or reallocate, memory for Parse_text and Parse_text_raw based on size we need now
allocate_parse_text((size_t) (file_len + 1));
// NOTE: this always has to be done *after* the allocate_mission_text() call!!
raw_text = Parse_text_raw;
@@ -4309,8 +4309,12 @@ int parse_modular_table(const char *name_check, void (*parse_callback)(const cha
Parsing_modular_table = true;
const auto ext = strrchr(name_check, '.');
for (i = 0; i < num_files; i++){
tbl_file_names[i] += ".tbm";
if (ext != nullptr) {
tbl_file_names[i] += ext;
}
mprintf(("TBM => Starting parse of '%s' ...\n", tbl_file_names[i].c_str()));
(*parse_callback)(tbl_file_names[i].c_str());
}
+5 -5
View File
@@ -116,7 +116,7 @@ extern int required_string_one_of(int arg_count, ...);
// stuff
extern void copy_to_eoln(char *outstr, const char *more_terminators, const char *instr, int max);
extern void copy_text_until(char *outstr, char *instr, const char *endstr, int max_chars);
extern void copy_text_until(char *outstr, const char *instr, const char *endstr, int max_chars);
extern void stuff_string_white(char *outstr, int len = 0);
extern void stuff_string_until(char *outstr, const char *endstr, int len = 0);
extern void stuff_string(char *outstr, int type, int len, const char *terminators = NULL);
@@ -124,7 +124,7 @@ extern void stuff_string_line(char *outstr, int len);
// SCP_string stuff
extern void copy_to_eoln(SCP_string &outstr, const char *more_terminators, const char *instr);
extern void copy_text_until(SCP_string &outstr, char *instr, const char *endstr);
extern void copy_text_until(SCP_string &outstr, const char *instr, const char *endstr);
extern void stuff_string_white(SCP_string &outstr);
extern void stuff_string_until(SCP_string &outstr, const char *endstr);
extern void stuff_string(SCP_string &outstr, int type, const char *terminators = NULL);
@@ -136,8 +136,8 @@ extern char* alloc_block(const char* startstr, const char* endstr, int extra_cha
// Exactly the same as stuff string only Malloc's the buffer.
// Supports various FreeSpace primitive types. If 'len' is supplied, it will override
// the default string length if using the F_NAME case.
extern char *stuff_and_malloc_string(int type, char *terminators = NULL);
extern void stuff_malloc_string(char **dest, int type, char *terminators = NULL);
extern char *stuff_and_malloc_string(int type, const char *terminators = nullptr);
extern void stuff_malloc_string(char **dest, int type, const char *terminators = nullptr);
extern void stuff_float(float *f);
extern int stuff_float_optional(float *f, bool raw = false);
extern int stuff_int_optional(int *i, bool raw = false);
@@ -252,7 +252,7 @@ extern void reset_parse(char *text = NULL);
extern void display_parse_diagnostics();
extern void pause_parse();
extern void unpause_parse();
// stop parsing, basically just free's up the memory from Mission_text and Mission_text_raw
// stop parsing, basically just frees up the memory from Parse_text and Parse_text_raw
extern void stop_parse();
// utility
+3
View File
@@ -26480,6 +26480,7 @@ int eval_sexp(int cur_node, int referenced_node)
return sexp_val;
}
#ifndef NDEBUG
// now, reconcile positive and negative - Goober5000
if (sexp_val < 0 && sexp_val > SEXP_UNLIKELY_RETURN_VALUE_BOUND)
{
@@ -26494,10 +26495,12 @@ int eval_sexp(int cur_node, int referenced_node)
// if we need a positive value, make it positive
if (query_operator_argument_type(get_operator_index(parent_node), arg_num) == OPF_POSITIVE)
{
Warning(LOCATION, "Parent node %s, argument %d (value %d) is negative, but is required to be positive!", Sexp_nodes[parent_node].text, arg_num + 1, sexp_val);
sexp_val *= -1;
}
}
}
#endif
if ( sexp_val ){
Sexp_nodes[cur_node].value = SEXP_TRUE;
+1 -1
View File
@@ -1038,7 +1038,7 @@ void read_player_controls(object *objp, float frametime)
target_warpout_speed = ship_get_warpout_speed(objp);
// check if warp ability has been disabled
if (!(Warpout_forced) && (Ships[objp->instance].flags[Ship::Ship_Flags::Warp_broken] || Ships[objp->instance].flags[Ship::Ship_Flags::Warp_never]) ) {
if (!(Warpout_forced) && !(ship_can_warp_full_check(&Ships[objp->instance]))) {
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Cannot warp out at this time.", 81));
snd_play(gamesnd_get_game_sound(GameSounds::PLAYER_WARP_FAIL));
gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP );
+8 -2
View File
@@ -36,12 +36,18 @@
/**
* Start the frame. Pass non-zero to enable zbuffering
*/
extern void g3_start_frame(int zbuffer_flag);
#define g3_start_frame(zbuffer_flag) g3_start_frame_func(zbuffer_flag, __FILE__, __LINE__ )
/**
* Use the g3_start_frame macro instead of calling this directly.
*/
extern void g3_start_frame_func(int zbuffer_flag, const char *filename, int lineno);
/**
* End the frame
*/
extern void g3_end_frame();
#define g3_end_frame() g3_end_frame_func( __FILE__, __LINE__ )
extern void g3_end_frame_func(const char *filename, int lineno);
/**
* Currently in frame?
+2 -2
View File
@@ -72,7 +72,7 @@ int g3_in_frame()
* Start the frame
* Pass true for zbuffer_flag to turn on zbuffering
*/
void g3_start_frame(int zbuffer_flag)
void g3_start_frame_func(int zbuffer_flag, const char * /*filename*/, int /*lineno*/)
{
float s;
int width, height;
@@ -113,7 +113,7 @@ void g3_start_frame(int zbuffer_flag)
/**
* This doesn't do anything, but is here for completeness
*/
void g3_end_frame()
void g3_end_frame_func(const char * /*filename*/, int /*lineno*/)
{
G3_count--;
Assert( G3_count == 0 );
+4 -2
View File
@@ -323,7 +323,8 @@ ADE_FUNC(getCurrentLanguage,
"Determines the language that is being used by the engine. This returns the full name of the language (e.g. \"English\").",
"string",
"The current game language") {
return ade_set_args(L, "s", Lcl_languages[Lcl_current_lang].lang_name);
auto lang_name = (Lcl_current_lang == LCL_UNTRANSLATED) ? "UNTRANSLATED" : Lcl_languages[Lcl_current_lang].lang_name;
return ade_set_args(L, "s", lang_name);
}
ADE_FUNC(getCurrentLanguageExtension,
@@ -334,7 +335,8 @@ ADE_FUNC(getCurrentLanguageExtension,
"This will return an empty string for the default language.",
"string",
"The current game language") {
return ade_set_args(L, "s", Lcl_languages[Lcl_current_lang].lang_ext);
int lang = (Lcl_current_lang == LCL_UNTRANSLATED) ? LCL_DEFAULT : Lcl_current_lang;
return ade_set_args(L, "s", Lcl_languages[lang].lang_ext);
}
ADE_FUNC(getVersionString, l_Base, nullptr,
+83
View File
@@ -0,0 +1,83 @@
//
//
#include "engine.h"
#include "scripting/scripting.h"
namespace scripting {
namespace api {
//**********LIBRARY: Engine
ADE_LIB(l_Engine, "Engine", "engine", "Basic engine access functions");
ADE_FUNC(addHook, l_Engine,
"string name, function hookFunction, [table conditionals = {}], [function override_func = return false]",
"Adds a function to be called from the specified game hook", "boolean",
"true if hook was installed properly, false otherwise")
{
using namespace luacpp;
const char* hook_name;
LuaFunction hook;
LuaTable conditionals;
LuaFunction override_func;
if (!ade_get_args(L, "su|tu", &hook_name, &hook, &conditionals, &override_func)) {
return ADE_RETURN_FALSE;
}
if (!hook.isValid()) {
LuaError(L, "Hook function is invalid!");
return ADE_RETURN_FALSE;
}
ConditionedHook cond_hook;
script_action action;
action.action_type = scripting_string_to_action(hook_name);
if (action.action_type == CHA_NONE) {
LuaError(L, "Invalid hook name '%s'!", hook_name);
return ADE_RETURN_FALSE;
}
action.hook.hook_function.language = SC_LUA;
action.hook.hook_function.function = hook;
if (override_func.isValid()) {
action.hook.override_function.language = SC_LUA;
action.hook.override_function.function = override_func;
}
cond_hook.AddAction(&action);
if (conditionals.isValid()) {
for (const auto& tableEntry : conditionals) {
const auto& key = tableEntry.first;
const auto& value = tableEntry.second;
if (!key.is(ValueType::STRING)) {
LuaError(L, "Conditional key '%s' is not a string", key.getValue<SCP_string>().c_str());
return ADE_RETURN_FALSE;
}
if (!value.is(ValueType::STRING)) {
LuaError(L, "Conditional value '%s' for key '%s' is not a string", value.getValue<SCP_string>().c_str(),
key.getValue<SCP_string>().c_str());
return ADE_RETURN_FALSE;
}
script_condition cond;
cond.condition_type = scripting_string_to_condition(key.getValue<SCP_string>().c_str());
cond.condition_string = value.getValue<SCP_string>();
cond_hook.AddCondition(&cond);
}
}
Script_system.AddConditionedHook(std::move(cond_hook));
return ADE_RETURN_TRUE;
}
} // namespace api
} // namespace scripting
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "scripting/ade_api.h"
namespace scripting {
namespace api {
DECLARE_ADE_LIB(l_Engine);
}
}
+21 -4
View File
@@ -1470,7 +1470,7 @@ ADE_FUNC(warpOut, l_Ship, NULL, "Warps ship out", "boolean", "True if successful
return ADE_RETURN_TRUE;
}
ADE_FUNC(canWarp, l_Ship, NULL, "Checks whether ship has a working subspace drive and is allowed to use it", "boolean", "True if successful, or nil if ship handle is invalid")
ADE_FUNC(canWarp, l_Ship, nullptr, "Checks whether ship has a working subspace drive, is allowed to use it, and is not disabled or limited by subsystem strength.", "boolean", "True if successful, or nil if ship handle is invalid")
{
object_h *objh;
if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
@@ -1480,11 +1480,28 @@ ADE_FUNC(canWarp, l_Ship, NULL, "Checks whether ship has a working subspace driv
return ADE_RETURN_NIL;
ship *shipp = &Ships[objh->objp->instance];
if(shipp->flags[Ship::Ship_Flags::No_subspace_drive]){
return ADE_RETURN_FALSE;
if( ship_can_warp_full_check(shipp) ){
return ADE_RETURN_TRUE;
}
return ADE_RETURN_TRUE;
return ADE_RETURN_FALSE;
}
ADE_FUNC(canBayDepart, l_Ship, nullptr, "Checks whether ship has a bay departure location and if its mother ship is present.", "boolean", "True if successful, or nil if ship handle is invalid")
{
object_h *objh;
if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
return ADE_RETURN_NIL;
if(!objh->IsValid())
return ADE_RETURN_NIL;
ship *shipp = &Ships[objh->objp->instance];
if( ship_can_bay_depart(shipp) ){
return ADE_RETURN_TRUE;
}
return ADE_RETURN_FALSE;
}
// Aardwolf's function for finding if a ship should be drawn as blue on the radar/minimap
+1 -1
View File
@@ -73,7 +73,7 @@ ADE_FUNC(attacks, l_Team, "team", "Checks the IFF status of another team", "bool
{
int x, y;
ade_get_args(L, "oo", l_Team.Get(&x), l_Team.Get(&y));
if (iff_x_attacks_y(x, y) > 0)
if (iff_x_attacks_y(x, y))
return ADE_RETURN_TRUE;
return ADE_RETURN_FALSE;
+1 -51
View File
@@ -58,6 +58,7 @@
#include "scripting/api/libs/base.h"
#include "scripting/api/libs/bitops.h"
#include "scripting/api/libs/cfile.h"
#include "scripting/api/libs/engine.h"
#include "scripting/api/libs/graphics.h"
#include "scripting/api/libs/hookvars.h"
#include "scripting/api/libs/hud.h"
@@ -76,46 +77,6 @@ using namespace scripting;
using namespace scripting::api;
// *************************Housekeeping*************************
//WMC - The miraculous lines of code that make Lua debugging worth something.
lua_Debug Ade_debug_info;
char debug_stack[4][32];
void ade_debug_ret(lua_State *L, lua_Debug *ar)
{
Assert(L != NULL);
Assert(ar != NULL);
lua_getstack(L, 1, ar);
lua_getinfo(L, "nSlu", ar);
memcpy(&Ade_debug_info, ar, sizeof(lua_Debug));
int n;
for (n = 0; n < 4; n++) {
debug_stack[n][0] = '\0';
}
for (n = 0; n < 4; n++) {
if (lua_getstack(L,n+1, ar) == 0)
break;
lua_getinfo(L,"n", ar);
if (ar->name == NULL)
break;
strcpy_s(debug_stack[n],ar->name);
}
}
//WMC - because the behavior of the return keyword
//was changed, I now have to use this in hooks.
static int ade_return_hack(lua_State *L)
{
int i = 0;
int num = lua_gettop(L);
for(i = 0; i < num; i++)
{
lua_pushvalue(L, i+1);
}
return num;
}
static void *vm_lua_alloc(void*, void *ptr, size_t, size_t nsize) {
if (nsize == 0)
@@ -165,11 +126,6 @@ int script_state::CreateLuaState()
}
lua_pop(L, 1); //os table
//*****SET DEBUG HOOKS
#ifndef NDEBUG
lua_sethook(L, ade_debug_ret, LUA_MASKRET, 0);
#endif
//*****INITIALIZE ADE
uint i;
mprintf(("LUA: Beginning ADE initialization\n"));
@@ -180,12 +136,6 @@ int script_state::CreateLuaState()
ade_manager::getInstance()->getEntry(i).SetTable(L, LUA_GLOBALSINDEX, LUA_GLOBALSINDEX); //Oh the miracles of OOP.
}
//*****INITIALIZE RETURN HACK FUNCTION
lua_pushstring(L, "ade_return_hack");
lua_pushboolean(L, 0);
lua_pushcclosure(L, ade_return_hack, 2);
lua_setglobal(L, "ade_return_hack");
//*****INITIALIZE ENUMERATION CONSTANTS
mprintf(("ADE: Initializing enumeration constants...\n"));
enum_h eh;
+4 -4
View File
@@ -66,7 +66,7 @@ bool LuaFunction::setEnvironment(const LuaTable& table) {
return ret;
}
LuaValueList LuaFunction::operator()(const LuaValueList& args) {
LuaValueList LuaFunction::operator()(const LuaValueList& args) const {
return this->call(args);
}
@@ -91,7 +91,7 @@ void LuaFunction::setReference(const LuaReference& ref) {
}
}
LuaValueList LuaFunction::call(const LuaValueList& args) {
LuaValueList LuaFunction::call(const LuaValueList& args) const {
int err_idx = 0;
int stackTop;
@@ -108,8 +108,8 @@ LuaValueList LuaFunction::call(const LuaValueList& args) {
this->pushValue();
// Push the arguments onto the stack
for (LuaValueList::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
iter->pushValue();
for (const auto& arg : args) {
arg.pushValue();
}
// actually call the function now!
+2 -2
View File
@@ -110,13 +110,13 @@ class LuaFunction: public LuaValue {
* @exception LuaException If an error occurs while executing the function an exception is thrown
* with the message of the error.
*/
LuaValueList call(const LuaValueList& arguments = LuaValueList());
LuaValueList call(const LuaValueList& arguments = LuaValueList()) const;
/**
* @brief Calls the function. See call().
* @return Same as call().
*/
LuaValueList operator()(const LuaValueList& arguments = LuaValueList());
LuaValueList operator()(const LuaValueList& arguments = LuaValueList()) const;
private:
LuaReference _errorFunction;
};
+128 -56
View File
@@ -174,6 +174,36 @@ void script_parse_table(const char* filename)
return;
}
}
void script_parse_lua_script(const char *filename) {
using namespace luacpp;
CFILE *cfp = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_TABLES);
if(cfp == nullptr)
{
Warning(LOCATION, "Could not open lua script file '%s'", filename);
return;
}
int len = cfilelength(cfp);
SCP_string source;
source.resize((size_t) len);
cfread(&source[0], len, 1, cfp);
cfclose(cfp);
try {
auto function = LuaFunction::createFromCode(Script_system.GetLuaSession(), source, filename);
function.setErrorFunction(LuaFunction::createFromCFunction(Script_system.GetLuaSession(), ade_friendly_error));
script_function func;
func.language = SC_LUA;
func.function = function;
Script_system.AddGameInitFunction(func);
} catch (const LuaException& e) {
LuaError(Script_system.GetLuaSession(), "Failed to parse %s: %s", filename, e.what());
}
}
static void json_doc_generate_class(json_t* elObj, const DocumentationElementClass* lib)
{
@@ -375,6 +405,8 @@ void script_init()
mprintf(("SCRIPTING: Beginning main hook parse sequence....\n"));
script_parse_table("scripting.tbl");
parse_modular_table(NOX("*-sct.tbm"), script_parse_table);
mprintf(("SCRIPTING: Parsing pure Lua scripts\n"));
parse_modular_table(NOX("*-sct.lua"), script_parse_lua_script);
mprintf(("SCRIPTING: Inititialization complete.\n"));
}
/*
@@ -436,7 +468,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
case CHC_STATE:
if(gameseq_get_depth() < 0)
return false;
if(stricmp(GS_state_text[gameseq_get_state(0)], scp->data.name) != 0)
if(stricmp(GS_state_text[gameseq_get_state(0)], scp->condition_string.c_str()) != 0)
return false;
break;
case CHC_SHIPTYPE:
@@ -445,19 +477,19 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
sip = &Ship_info[Ships[objp->instance].ship_info_index];
if(sip->class_type < 0)
return false;
if(stricmp(Ship_types[sip->class_type].name, scp->data.name) != 0)
if(stricmp(Ship_types[sip->class_type].name, scp->condition_string.c_str()) != 0)
return false;
break;
case CHC_SHIPCLASS:
if(objp == NULL || objp->type != OBJ_SHIP)
return false;
if(stricmp(Ship_info[Ships[objp->instance].ship_info_index].name, scp->data.name) != 0)
if(stricmp(Ship_info[Ships[objp->instance].ship_info_index].name, scp->condition_string.c_str()) != 0)
return false;
break;
case CHC_SHIP:
if(objp == NULL || objp->type != OBJ_SHIP)
return false;
if(stricmp(Ships[objp->instance].ship_name, scp->data.name) != 0)
if(stricmp(Ships[objp->instance].ship_name, scp->condition_string.c_str()) != 0)
return false;
break;
case CHC_MISSION:
@@ -470,7 +502,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
return false;
if(len > 4 && !stricmp(&Mission_filename[len-4], ".fs2"))
len -= 4;
if(strnicmp(scp->data.name, Mission_filename, len) != 0)
if(strnicmp(scp->condition_string.c_str(), Mission_filename, len) != 0)
return false;
break;
}
@@ -481,21 +513,21 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
return false;
if(len > 4 && !stricmp(&Mission_filename[len-4], ".fc2"))
len -= 4;
if(strnicmp(scp->data.name, Mission_filename, len) != 0)
if(strnicmp(scp->condition_string.c_str(), Mission_filename, len) != 0)
return false;
break;
}
case CHC_WEAPONCLASS:
{
if (action == CHA_COLLIDEWEAPON) {
if (stricmp(Weapon_info[more_data].name, scp->data.name) != 0)
if (stricmp(Weapon_info[more_data].name, scp->condition_string.c_str()) != 0)
return false;
} else if (!(action == CHA_ONWPSELECTED || action == CHA_ONWPDESELECTED || action == CHA_ONWPEQUIPPED || action == CHA_ONWPFIRED || action == CHA_ONTURRETFIRED )) {
if(objp == NULL || (objp->type != OBJ_WEAPON && objp->type != OBJ_BEAM))
return false;
else if (( objp->type == OBJ_WEAPON) && (stricmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, scp->data.name) != 0 ))
else if (( objp->type == OBJ_WEAPON) && (stricmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, scp->condition_string.c_str()) != 0 ))
return false;
else if (( objp->type == OBJ_BEAM) && (stricmp(Weapon_info[Beams[objp->instance].weapon_info_index].name, scp->data.name) != 0 ))
else if (( objp->type == OBJ_BEAM) && (stricmp(Weapon_info[Beams[objp->instance].weapon_info_index].name, scp->condition_string.c_str()) != 0 ))
return false;
} else if(objp == NULL || objp->type != OBJ_SHIP) {
return false;
@@ -509,27 +541,35 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
bool primary = false, secondary = false, prev_primary = false, prev_secondary = false;
switch (action) {
case CHA_ONWPSELECTED:
if (shipp->weapons.current_primary_bank >= 0)
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->data.name) == 0;
if (shipp->weapons.current_secondary_bank >= 0)
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->data.name) == 0;
if (!(primary || secondary))
return false;
if (shipp->weapons.current_primary_bank >= 0) {
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->condition_string.c_str()) == 0;
}
if (shipp->weapons.current_secondary_bank >= 0) {
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->condition_string.c_str()) == 0;
}
if ((shipp->flags[Ship::Ship_Flags::Primary_linked]) && primary && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags[Weapon::Info_Flags::Nolink]))
if (!(primary || secondary)) {
return false;
}
if ((shipp->flags[Ship::Ship_Flags::Primary_linked]) && primary && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags[Weapon::Info_Flags::Nolink])) {
return false;
}
break;
case CHA_ONWPDESELECTED:
if (shipp->weapons.current_primary_bank >= 0)
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->data.name) == 0;
if (shipp->weapons.previous_primary_bank >= 0)
prev_primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].name, scp->data.name) == 0;
if (shipp->weapons.current_secondary_bank >= 0)
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->data.name) == 0;
if (shipp->weapons.previous_secondary_bank >= 0)
prev_secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.previous_secondary_bank]].name, scp->data.name) == 0;
if (shipp->weapons.current_primary_bank >= 0) {
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->condition_string.c_str()) == 0;
}
if (shipp->weapons.previous_primary_bank >= 0) {
prev_primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].name, scp->condition_string.c_str()) == 0;
}
if (shipp->weapons.current_secondary_bank >= 0) {
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->condition_string.c_str()) == 0;
}
if (shipp->weapons.previous_secondary_bank >= 0) {
prev_secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.previous_secondary_bank]].name, scp->condition_string.c_str()) == 0;
}
if ((shipp->flags[Ship::Ship_Flags::Primary_linked]) && prev_primary && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].wi_flags[Weapon::Info_Flags::Nolink]))
return true;
@@ -537,10 +577,10 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
if ( !prev_secondary && ! secondary && !prev_primary && !primary )
return false;
if ( (!prev_secondary && !secondary) && (prev_primary && primary) )
if ( (!prev_secondary && !secondary) && (prev_primary && primary) )
return false;
if ( (!prev_secondary && !secondary) && (!prev_primary && primary) )
if ( (!prev_secondary && !secondary) && (!prev_primary && primary) )
return false;
if ( (!prev_primary && !primary) && (prev_secondary && secondary) )
@@ -554,7 +594,8 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
bool equipped = false;
for(int j = 0; j < MAX_SHIP_PRIMARY_BANKS; j++) {
if (!equipped && (shipp->weapons.primary_bank_weapons[j] >= 0) && (shipp->weapons.primary_bank_weapons[j] < weapon_info_size()) ) {
if ( !stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[j]].name, scp->data.name) ) {
if (!stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[j]].name,
scp->condition_string.c_str())) {
equipped = true;
break;
}
@@ -564,7 +605,8 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
if (!equipped) {
for(int j = 0; j < MAX_SHIP_SECONDARY_BANKS; j++) {
if (!equipped && (shipp->weapons.secondary_bank_weapons[j] >= 0) && (shipp->weapons.secondary_bank_weapons[j] < weapon_info_size()) ) {
if ( !stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[j]].name, scp->data.name) ) {
if (!stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[j]].name,
scp->condition_string.c_str())) {
equipped = true;
break;
}
@@ -579,11 +621,11 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
}
case CHA_ONWPFIRED: {
if (more_data == 1) {
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->data.name) == 0;
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->condition_string.c_str()) == 0;
secondary = false;
} else {
primary = false;
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->data.name) == 0;
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->condition_string.c_str()) == 0;
}
if ((shipp->flags[Ship::Ship_Flags::Primary_linked]) && primary && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags[Weapon::Info_Flags::Nolink]))
@@ -594,22 +636,22 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
break;
}
case CHA_ONTURRETFIRED: {
if (! (stricmp(Weapon_info[shipp->last_fired_turret->last_fired_weapon_info_index].name, scp->data.name) == 0))
if (! (stricmp(Weapon_info[shipp->last_fired_turret->last_fired_weapon_info_index].name, scp->condition_string.c_str()) == 0))
return false;
break;
}
case CHA_PRIMARYFIRE: {
if (stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->data.name) != 0)
if (stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->condition_string.c_str()) != 0)
return false;
break;
}
case CHA_SECONDARYFIRE: {
if (stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->data.name) != 0)
if (stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->condition_string.c_str()) != 0)
return false;
break;
}
case CHA_BEAMFIRE: {
if (!(stricmp(Weapon_info[more_data].name, scp->data.name) == 0))
if (!(stricmp(Weapon_info[more_data].name, scp->condition_string.c_str()) == 0))
return false;
break;
}
@@ -622,7 +664,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
case CHC_OBJECTTYPE:
if(objp == NULL)
return false;
if(stricmp(Object_type_names[objp->type], scp->data.name) != 0)
if(stricmp(Object_type_names[objp->type], scp->condition_string.c_str()) != 0)
return false;
break;
case CHC_KEYPRESS:
@@ -633,7 +675,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
if(Current_key_down == 0)
return false;
//WMC - could be more efficient, but whatever.
if(stricmp(textify_scancode(Current_key_down), scp->data.name) != 0)
if(stricmp(textify_scancode(Current_key_down), scp->condition_string.c_str()) != 0)
return false;
break;
}
@@ -644,7 +686,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
int action_index = more_data;
if (action_index <= 0 || stricmp(scp->data.name, Control_config[action_index].text) != 0)
if (action_index <= 0 || stricmp(scp->condition_string.c_str(), Control_config[action_index].text) != 0)
return false;
break;
}
@@ -653,13 +695,13 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
// Goober5000: I'm going to assume scripting doesn't care about SVN revision
char buf[32];
sprintf(buf, "%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD);
if(stricmp(buf, scp->data.name) != 0)
if(stricmp(buf, scp->condition_string.c_str()) != 0)
{
//In case some people are lazy and say "3.7" instead of "3.7.0" or something
if(FS_VERSION_BUILD == 0)
{
sprintf(buf, "%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR);
if(stricmp(buf, scp->data.name) != 0)
if(stricmp(buf, scp->condition_string.c_str()) != 0)
return false;
}
else
@@ -673,12 +715,12 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
{
if(Fred_running)
{
if(stricmp("FRED2_Open", scp->data.name) != 0 && stricmp("FRED2Open", scp->data.name) != 0 && stricmp("FRED 2", scp->data.name) != 0 && stricmp("FRED", scp->data.name) != 0)
if(stricmp("FRED2_Open", scp->condition_string.c_str()) != 0 && stricmp("FRED2Open", scp->condition_string.c_str()) != 0 && stricmp("FRED 2", scp->condition_string.c_str()) != 0 && stricmp("FRED", scp->condition_string.c_str()) != 0)
return false;
}
else
{
if(stricmp("FS2_Open", scp->data.name) != 0 && stricmp("FS2Open", scp->data.name) != 0 && stricmp("Freespace 2", scp->data.name) != 0 && stricmp("Freespace", scp->data.name) != 0)
if(stricmp("FS2_Open", scp->condition_string.c_str()) != 0 && stricmp("FS2Open", scp->condition_string.c_str()) != 0 && stricmp("Freespace 2", scp->condition_string.c_str()) != 0 && stricmp("Freespace", scp->condition_string.c_str()) != 0)
return false;
}
}
@@ -1282,13 +1324,13 @@ int script_state::RunBytecode(script_function& hd)
return 1;
}
int script_parse_condition()
ConditionalType script_parse_condition()
{
char buf[NAME_LENGTH];
for (int i = 0; i < Num_script_conditions; i++) {
sprintf(buf, "$%s:", Script_conditions[i].name);
if (optional_string(buf))
return Script_conditions[i].def;
if(optional_string(buf))
return static_cast<ConditionalType>(Script_conditions[i].def);
}
return CHC_NONE;
@@ -1305,7 +1347,29 @@ const scripting::HookBase* script_parse_action()
return nullptr;
}
void script_state::ParseGlobalChunk(int hookType, const char* debug_str) {
int32_t scripting_string_to_action(const char* action)
{
for (const auto& hook : scripting::getHooks()) {
if (hook->getHookName() == action)
return hook->getHookId();
}
return CHA_NONE;
}
ConditionalType scripting_string_to_condition(const char* condition)
{
for (int i = 0; i < Num_script_conditions; i++) {
if (!stricmp(Script_conditions[i].name, condition)) {
return static_cast<ConditionalType>(Script_conditions[i].def);
}
}
return CHC_NONE;
}
void script_state::ParseGlobalChunk(ConditionalActions hookType, const char* debug_str) {
ConditionedHook hook;
script_action sat;
@@ -1320,10 +1384,9 @@ void script_state::ParseGlobalChunk(int hookType, const char* debug_str) {
}
bool script_state::ParseCondition(const char *filename)
{
ConditionedHook *chp = NULL;
int condition;
ConditionedHook *chp = nullptr;
for(condition = script_parse_condition(); condition != CHC_NONE; condition = script_parse_condition())
for(auto condition = script_parse_condition(); condition != CHC_NONE; condition = script_parse_condition())
{
script_condition sct;
sct.condition_type = condition;
@@ -1341,7 +1404,7 @@ bool script_state::ParseCondition(const char *filename)
case CHC_VERSION:
case CHC_APPLICATION:
default:
stuff_string(sct.data.name, F_NAME, CONDITION_LENGTH);
stuff_string(sct.condition_string, F_NAME);
break;
}
@@ -1368,13 +1431,10 @@ bool script_state::ParseCondition(const char *filename)
sat.action_type = action->getHookId();
// WMC - build error string
char* buf = (char*)vm_malloc(strlen(filename) + action->getHookName().size() + 4);
SCP_string buf;
sprintf(buf, "%s - %s", filename, action->getHookName().c_str());
ParseChunk(&sat.hook, buf);
// Free error string
vm_free(buf);
ParseChunk(&sat.hook, buf.c_str());
// Add the action
if (chp->AddAction(&sat))
@@ -1391,6 +1451,10 @@ bool script_state::ParseCondition(const char *filename)
return true;
}
void script_state::AddConditionedHook(ConditionedHook hook) { ConditionalHooks.push_back(std::move(hook)); }
void script_state::AddGameInitFunction(script_function func) { GameInitFunctions.push_back(std::move(func)); }
//*************************CLASS: script_state*************************
bool script_state::IsOverride(script_hook &hd)
{
@@ -1403,6 +1467,14 @@ bool script_state::IsOverride(script_hook &hd)
return b;
}
void script_state::RunInitFunctions() {
for (const auto& initFunc : GameInitFunctions) {
initFunc.function();
}
// We don't need this anymore so no need to keep references to those functions around anymore
GameInitFunctions.clear();
}
void scripting_state_init()
{
// nothing to do here
+32 -30
View File
@@ -43,20 +43,22 @@ extern bool script_hook_valid(script_hook *hook);
#define MAX_HOOK_CONDITIONS 8
//Conditionals
#define CHC_NONE -1
#define CHC_MISSION 0
#define CHC_SHIP 1
#define CHC_SHIPCLASS 2
#define CHC_SHIPTYPE 3
#define CHC_STATE 4
#define CHC_CAMPAIGN 5
#define CHC_WEAPONCLASS 6
#define CHC_OBJECTTYPE 7
#define CHC_KEYPRESS 8
#define CHC_ACTION 9
#define CHC_VERSION 10
#define CHC_APPLICATION 11
// Conditionals
enum ConditionalType {
CHC_NONE = -1,
CHC_MISSION = 0,
CHC_SHIP = 1,
CHC_SHIPCLASS = 2,
CHC_SHIPTYPE = 3,
CHC_STATE = 4,
CHC_CAMPAIGN = 5,
CHC_WEAPONCLASS = 6,
CHC_OBJECTTYPE = 7,
CHC_KEYPRESS = 8,
CHC_ACTION = 9,
CHC_VERSION = 10,
CHC_APPLICATION = 11,
};
//Actions
enum ConditionalActions : int32_t {
@@ -116,25 +118,18 @@ void scripting_state_init();
void scripting_state_close();
void scripting_state_do_frame(float frametime);
class script_condition
{
public:
int condition_type{CHC_NONE};
union
{
char name[CONDITION_LENGTH];
} data;
int32_t scripting_string_to_action(const char* action);
ConditionalType scripting_string_to_condition(const char* condition);
script_condition()
{
memset(data.name, 0, sizeof(data.name));
}
struct script_condition
{
ConditionalType condition_type = CHC_NONE;
SCP_string condition_string;
};
class script_action
struct script_action
{
public:
int action_type{CHA_NONE};
int32_t action_type {CHA_NONE};
script_hook hook;
};
@@ -235,6 +230,8 @@ private:
SCP_vector<image_desc> ScriptImages;
SCP_vector<ConditionedHook> ConditionalHooks;
SCP_vector<script_function> GameInitFunctions;
private:
void ParseChunkSub(script_function& out_func, const char* debug_str=NULL);
@@ -294,8 +291,11 @@ public:
const char* debug_str = nullptr);
bool EvalString(const char* string, const char* debug_str = nullptr);
void ParseChunk(script_hook *dest, const char* debug_str=NULL);
void ParseGlobalChunk(int hookType, const char* debug_str=NULL);
void ParseGlobalChunk(ConditionalActions hookType, const char* debug_str=nullptr);
bool ParseCondition(const char *filename="<Unknown>");
void AddConditionedHook(ConditionedHook hook);
void AddGameInitFunction(script_function func);
//***Hook running functions
template <typename T>
@@ -305,6 +305,8 @@ public:
int RunCondition(int condition, object* objp = nullptr, int more_data = 0);
bool IsConditionOverride(int action, object *objp=NULL);
void RunInitFunctions();
//*****Other functions
void EndFrame();
+351 -83
View File
@@ -446,6 +446,7 @@ flag_def_list_new<Weapon::Info_Flags> ai_tgt_weapon_flags[] = {
{ "big ship", Weapon::Info_Flags::Big_only, true, false },
{ "child", Weapon::Info_Flags::Child, true, false },
{ "no dumbfire", Weapon::Info_Flags::No_dumbfire, true, false },
{ "no doublefire", Weapon::Info_Flags::No_doublefire, true, false },
{ "thruster", Weapon::Info_Flags::Thruster, true, false },
{ "in tech database", Weapon::Info_Flags::In_tech_database, true, false },
{ "player allowed", Weapon::Info_Flags::Player_allowed, true, false },
@@ -1611,7 +1612,7 @@ ship_info::ship_info()
sup_hull_repair_rate = 0.15f;
sup_shield_repair_rate = 0.20f;
sup_subsys_repair_rate = 0.15f;
sup_subsys_repair_rate = 0.15f; // The retail default is 0.10, but it actually used the hull rate of 0.15
vm_vec_zero(&closeup_pos);
closeup_zoom = 0.5f;
@@ -1819,7 +1820,7 @@ static void parse_ship(const char *filename, bool replace)
}
//Remove @ symbol
//these used to be used to denote weapons that would
//these used to denote weapons that would
//only be parsed in demo builds
if ( fname[0] == '@' ) {
backspace(fname);
@@ -6036,6 +6037,33 @@ const char* ship::get_display_string() {
}
}
void ship::apply_replacement_textures(SCP_vector<texture_replace> &replacements)
{
if (!replacements.empty())
{
ship_replacement_textures = (int *) vm_malloc( MAX_REPLACEMENT_TEXTURES * sizeof(int));
for (auto i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
ship_replacement_textures[i] = -1;
}
// now fill them in
for (auto tr : replacements)
{
auto pm = model_get(Ship_info[ship_info_index].model_num);
// look for textures
for (auto j = 0; j < pm->n_textures; j++)
{
texture_map *tmap = &pm->maps[j];
int tnum = tmap->FindTexture(tr.old_texture);
if(tnum > -1)
ship_replacement_textures[j * TM_NUM_TYPES + tnum] = tr.new_texture_id;
}
}
}
void ship_weapon::clear()
{
flags.reset();
@@ -6899,9 +6927,16 @@ void ship_render_cockpit(object *objp)
//Deal with the model
model_clear_instance(sip->cockpit_model_num);
uint render_flags = MR_NORMAL;
render_flags |= MR_NO_FOGGING;
if (shipp->flags[Ship::Ship_Flags::Glowmaps_disabled]) {
render_flags |= MR_NO_GLOWMAPS;
}
model_render_params render_info;
render_info.set_detail_level_lock(0);
render_info.set_flags(MR_NO_FOGGING);
render_info.set_flags(render_flags);
render_info.set_replacement_textures(Player_cockpit_textures);
model_render_immediate(&render_info, sip->cockpit_model_num, &eye_ori, &pos);
@@ -6931,6 +6966,7 @@ void ship_render_show_ship_cockpit(object *objp)
model_render_params render_info;
render_info.set_object_number(OBJ_INDEX(objp));
render_info.set_replacement_textures(Ships[objp->instance].ship_replacement_textures);
render_info.set_detail_level_lock(0);
model_render_immediate(&render_info, Ship_info[Ships[objp->instance].ship_info_index].model_num, &objp->orient,
@@ -10610,8 +10646,6 @@ int ship_stop_fire_primary(object * obj)
int tracers[MAX_SHIPS][4][4];
float ship_get_subsystem_strength( ship *shipp, int type );
/**
* Checks whether a ship would use autoaim if it were to fire its primaries at
* the given object, taking lead into account.
@@ -11810,33 +11844,34 @@ int ship_fire_secondary( object *obj, int allow_swarm )
// Ensure if this is a "require-lock" missile, that a lock actually exists
if ( wip->wi_flags[Weapon::Info_Flags::No_dumbfire] ) {
if ( aip->current_target_is_locked <= 0 ) {
if ( obj == Player_obj ) {
if ( !Weapon_energy_cheat ) {
if (!aip->current_target_is_locked && !ship_lock_present(shipp) && !(shipp->missile_locks_firing.size() > 0)) {
if (obj == Player_obj) {
if (!Weapon_energy_cheat) {
float max_dist;
max_dist = wip->lifetime * wip->max_speed;
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm]){
max_dist= wip->lssm_lock_range;
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm]) {
max_dist = wip->lssm_lock_range;
}
if ((aip->target_objnum != -1) && (vm_vec_dist_quick(&obj->pos, &Objects[aip->target_objnum].pos) > max_dist)) {
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Too far from target to acquire lock", 487));
if ((aip->target_objnum != -1) &&
(vm_vec_dist_quick(&obj->pos, &Objects[aip->target_objnum].pos) > max_dist)) {
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Too far from target to acquire lock", 487));
} else {
char missile_name[NAME_LENGTH];
strcpy_s(missile_name, wip->get_display_string());
end_string_at_first_hash_symbol(missile_name);
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot fire %s without a lock", 488), missile_name);
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Cannot fire %s without a lock", 488), missile_name);
}
snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::OUT_OF_MISSLES)) );
swp->next_secondary_fire_stamp[bank] = timestamp(800); // to avoid repeating messages
snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::OUT_OF_MISSLES)));
swp->next_secondary_fire_stamp[bank] = timestamp(800); // to avoid repeating messages
return 0;
}
} else {
// multiplayer clients should always fire the weapon here, so return only if not
// a multiplayer client.
if ( !MULTIPLAYER_CLIENT ) {
if (!MULTIPLAYER_CLIENT) {
return 0;
}
}
@@ -11867,16 +11902,21 @@ int ship_fire_secondary( object *obj, int allow_swarm )
}
}
if ( !allow_swarm && obj == Player_obj ) {
ship_queue_missile_locks(shipp);
}
// if trying to fire a swarm missile, make sure being called from right place
if ( (wip->wi_flags[Weapon::Info_Flags::Swarm]) && !allow_swarm ) {
Assert(wip->swarm_count > 0);
if(wip->swarm_count <= 0){
if (wip->multi_lock && obj == Player_obj) {
shipp->num_swarm_missiles_to_fire = (int)shipp->missile_locks_firing.size();
} else if(wip->swarm_count <= 0){
shipp->num_swarm_missiles_to_fire = SWARM_DEFAULT_NUM_MISSILES_FIRED;
} else {
shipp->num_swarm_missiles_to_fire = wip->swarm_count;
}
shipp->swarm_missile_bank = bank;
return 1; // Note: Missiles didn't get fired, but the frame interval code will fire them.
}
@@ -11885,7 +11925,11 @@ int ship_fire_secondary( object *obj, int allow_swarm )
if ( (wip->wi_flags[Weapon::Info_Flags::Corkscrew]) && !allow_swarm ) {
//phreak 11-9-02
//changed this from 4 to custom number defined in tables
shipp->num_corkscrew_to_fire = (ubyte)(shipp->num_corkscrew_to_fire + (ubyte)wip->cs_num_fired);
if (wip->multi_lock && obj == Player_obj) {
shipp->num_corkscrew_to_fire = (ubyte)shipp->missile_locks_firing.size();
} else {
shipp->num_corkscrew_to_fire = (ubyte)(shipp->num_corkscrew_to_fire + (ubyte)wip->cs_num_fired);
}
shipp->corkscrew_missile_bank = bank;
return 1; // Note: Missiles didn't get fired, but the frame interval code will fire them.
}
@@ -11899,7 +11943,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
t = Weapon_info[weapon_idx].fire_wait; // They can fire 5 times a second
swp->burst_counter[bank_adjusted] = 0;
}
swp->next_secondary_fire_stamp[bank] = timestamp((int) (t * 1000.0f));
swp->next_secondary_fire_stamp[bank] = timestamp((int)(t * 1000.0f));
swp->last_secondary_fire_stamp[bank] = timestamp();
// Here is where we check if weapons subsystem is capable of firing the weapon.
@@ -11927,6 +11971,33 @@ int ship_fire_secondary( object *obj, int allow_swarm )
return 0; // we can make a quick out here!!!
}
int target_objnum;
ship_subsys *target_subsys;
int locked;
if ( obj == Player_obj ) {
// use missile lock slots
if ( shipp->missile_locks_firing.size() > 0 ) {
lock_info lock_data = shipp->missile_locks_firing.back();
if ( wip->multi_lock ) {
shipp->missile_locks_firing.pop_back();
}
target_objnum = OBJ_INDEX(lock_data.obj);
target_subsys = lock_data.subsys;
locked = 1;
} else {
target_objnum = -1;
target_subsys = nullptr;
locked = 0;
}
} else {
target_objnum = aip->target_objnum;
target_subsys = aip->targeted_subsys;
locked = aip->current_target_is_locked;
}
num_slots = pm->missile_banks[bank].num_slots;
// determine if there is enough ammo left to fire weapons on this bank. As with primary
@@ -11950,6 +12021,21 @@ int ship_fire_secondary( object *obj, int allow_swarm )
goto done_secondary;
}
// Handle the optional disabling of dual fire
// dual fire/doublefire can be disabled for individual weapons, for players, or for AIs
// if any of these apply to the current weapon, unset the dual fire flag on the ship
// then proceed as normal.
// This is only handled at firing time so dualfire isn't lost when cycling through weapons
if (shipp->flags[Ship_Flags::Secondary_dual_fire] &&
( wip->wi_flags[Weapon::Info_Flags::No_doublefire] ||
( The_mission.ai_profile->flags[AI::Profile_Flags::Disable_ai_secondary_doublefire] &&
shipp->objnum != OBJ_INDEX(Player_obj) ) ||
( The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] &&
shipp->objnum == OBJ_INDEX(Player_obj) ))
) {
shipp->flags.remove(Ship_Flags::Secondary_dual_fire);
}
int start_slot, end_slot;
if ( shipp->flags[Ship_Flags::Secondary_dual_fire] && num_slots > 1) {
@@ -12022,7 +12108,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
// create the weapon -- for multiplayer, the net_signature is assigned inside
// of weapon_create
weapon_num = weapon_create( &firing_pos, &firing_orient, weapon_idx, OBJ_INDEX(obj), -1, aip->current_target_is_locked);
weapon_num = weapon_create( &firing_pos, &firing_orient, weapon_idx, OBJ_INDEX(obj), -1, locked);
if (weapon_num == -1) {
// Weapon most likely failed to fire
@@ -12034,7 +12120,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
if (weapon_num >= 0) {
weapon_idx = Weapons[Objects[weapon_num].instance].weapon_info_index;
weapon_set_tracking_info(weapon_num, OBJ_INDEX(obj), aip->target_objnum, aip->current_target_is_locked, aip->targeted_subsys);
weapon_set_tracking_info(weapon_num, OBJ_INDEX(obj), target_objnum, locked, target_subsys);
has_fired = true;
// create the muzzle flash effect
@@ -13303,7 +13389,7 @@ int ship_get_subsys_index(ship *shipp, ship_subsys *subsys)
// 0.0 and 1.0 which is the relative combined strength of the given subsystem type. The number
// calculated for the engines is slightly different. Once an engine reaches < 15% of its hits, its
// output drops to that %. A dead engine has no output.
float ship_get_subsystem_strength( ship *shipp, int type )
float ship_get_subsystem_strength( ship *shipp, int type, bool skip_dying_check )
{
float strength;
ship_subsys *ssp;
@@ -13311,7 +13397,7 @@ float ship_get_subsystem_strength( ship *shipp, int type )
Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) );
// For a dying ship, all subsystem strengths are zero.
if (Objects[shipp->objnum].hull_strength <= 0.0f)
if (Objects[shipp->objnum].hull_strength <= 0.0f && !skip_dying_check)
return 0.0f;
// short circuit 1
@@ -13515,8 +13601,8 @@ float ship_calculate_rearm_duration( object *objp )
//
int ship_do_rearm_frame( object *objp, float frametime )
{
int i, banks_full, primary_banks_full, subsys_type, subsys_all_ok, last_ballistic_idx = -1;
float shield_str, max_shield_str = 0.0f, repair_delta, repair_allocated, max_hull_repair, max_subsys_repair;
int i, banks_full, primary_banks_full, subsys_type, last_ballistic_idx = -1;
float shield_str, max_shield_str = 0.0f, repair_delta, repair_allocated, max_hull_repair = 0, max_subsys_repair;
ship *shipp;
ship_weapon *swp;
ship_info *sip;
@@ -13538,6 +13624,7 @@ int ship_do_rearm_frame( object *objp, float frametime )
}
swp->num_primary_banks = sip->num_primary_banks;
}
// AL 12-30-97: Repair broken warp drive
if ( shipp->flags[Ship_Flags::Warp_broken] ) {
// TODO: maybe do something here like informing player warp is fixed?
@@ -13546,7 +13633,7 @@ int ship_do_rearm_frame( object *objp, float frametime )
shipp->flags.remove(Ship_Flags::Warp_broken);
}
// AL 1-16-97: Replenish countermeasures
// AL 1-16-98: Replenish countermeasures
shipp->cmeasure_count = sip->cmeasure_max;
// Do shield repair here
@@ -13567,53 +13654,36 @@ int ship_do_rearm_frame( object *objp, float frametime )
}
// Repair the ship integrity (subsystems + hull). This works by applying the repair points
// to the subsystems. Ships integrity is stored is objp->hull_strength, so that always is
// to the subsystems. Ships integrity is stored in objp->hull_strength, so that always is
// incremented by repair_allocated
repair_allocated = shipp->ship_max_hull_strength * frametime * sip->sup_hull_repair_rate;
// AL 11-24-97: remove increase to hull integrity
// Comments removed by PhReAk; Note that this is toggled on/off with a mission flag
//Figure out how much of the ship's hull we can repair
max_hull_repair = shipp->ship_max_hull_strength * (The_mission.support_ships.max_hull_repair_val * 0.01f);
//Don't "reverse-repair" the hull if it's already above the max repair threshold
if (objp->hull_strength > max_hull_repair)
// AL 11-24-97: remove increase to hull integrity
// Comments removed by PhReAk; Note that this is toggled on/off with a mission flag
if (The_mission.flags[Mission::Mission_Flags::Support_repairs_hull])
{
max_hull_repair = objp->hull_strength;
}
if(The_mission.flags[Mission::Mission_Flags::Support_repairs_hull])
{
objp->hull_strength += repair_allocated;
if ( objp->hull_strength > max_hull_repair ) {
objp->hull_strength = max_hull_repair;
}
if ( objp->hull_strength > shipp->ship_max_hull_strength )
//Figure out how much of the ship's hull we can repair
//Don't "reverse-repair" the hull if it's already above the max repair threshold
max_hull_repair = shipp->ship_max_hull_strength * (The_mission.support_ships.max_hull_repair_val * 0.01f);
if (objp->hull_strength < max_hull_repair)
{
objp->hull_strength = shipp->ship_max_hull_strength;
repair_allocated -= ( shipp->ship_max_hull_strength - objp->hull_strength);
objp->hull_strength += shipp->ship_max_hull_strength * frametime * sip->sup_hull_repair_rate;
if (objp->hull_strength > max_hull_repair)
objp->hull_strength = max_hull_repair;
}
}
// figure out repairs for subsystems
if(sip->sup_subsys_repair_rate == 0.0f)
repair_allocated = 0.0f;
else if(sip->sup_hull_repair_rate == 0.0f)
repair_allocated = shipp->ship_max_hull_strength * frametime * sip->sup_subsys_repair_rate;
else if(!(sip->sup_hull_repair_rate == sip->sup_subsys_repair_rate))
repair_allocated = repair_allocated * sip->sup_subsys_repair_rate / sip->sup_hull_repair_rate;
repair_allocated = shipp->ship_max_hull_strength * frametime * sip->sup_subsys_repair_rate;
// check the subsystems of the ship.
subsys_all_ok = 1;
bool subsys_all_ok = true;
ssp = GET_FIRST(&shipp->subsys_list);
while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
//Figure out how much we *can* repair the current subsystem -C
max_subsys_repair = ssp->max_hits * (The_mission.support_ships.max_subsys_repair_val * 0.01f);
if ( ssp->current_hits < max_subsys_repair && repair_allocated > 0 ) {
subsys_all_ok = 0;
subsys_all_ok = false;
subsys_type = ssp->system_info->type;
if ( objp == Player_obj ) {
@@ -13779,7 +13849,8 @@ int ship_do_rearm_frame( object *objp, float frametime )
if ((aip->rearm_first_ballistic_primary == TRUE) && (i == last_ballistic_idx))
{
if (primary_banks_full != swp->num_primary_banks) {
if (primary_banks_full != swp->num_primary_banks)
{
// Goober5000
gamesnd_id sound_index;
if (gamesnd_game_sound_valid(GameSounds::BALLISTIC_START_LOAD))
@@ -13805,23 +13876,24 @@ int ship_do_rearm_frame( object *objp, float frametime )
aip->rearm_first_ballistic_primary = TRUE;
}
int shields_full = 0;
int shields_full = false;
if ( (objp->flags[Object::Object_Flags::No_shields]) ) {
shields_full = 1;
shields_full = true;
} else {
if ( shield_get_strength(objp) >= max_shield_str )
shields_full = 1;
shields_full = true;
if (sip->sup_shield_repair_rate == 0.0f)
shields_full = 1;
shields_full = true;
}
int hull_ok = 0;
if(objp->hull_strength >= max_hull_repair)
hull_ok = 1;
if(sip->sup_hull_repair_rate == 0.0f) {
subsys_all_ok = 1;
hull_ok = 1;
bool hull_ok = false;
if (!(The_mission.flags[Mission::Mission_Flags::Support_repairs_hull])) {
hull_ok = true;
} else {
if (objp->hull_strength >= max_hull_repair)
hull_ok = true;
if (sip->sup_hull_repair_rate == 0.0f)
hull_ok = true;
}
// return 1 if at end of subsystem list, hull damage at 0, and shields full and all secondary banks full.
@@ -13846,7 +13918,8 @@ int ship_do_rearm_frame( object *objp, float frametime )
}
}
if (objp == Player_obj) Player_rearm_eta -= frametime;
if (objp == Player_obj)
Player_rearm_eta -= frametime;
return 0;
}
@@ -14317,8 +14390,9 @@ static void ship_add_ship_type_kill_count( int ship_info_index )
return;
}
//Add it
Ship_type_counts[type].killed++;
//Add it if we are actually in gameplay
if (Ship_type_counts.size() > static_cast<size_t>(type))
Ship_type_counts[type].killed++;
}
int ship_query_general_type(int ship)
@@ -14411,14 +14485,16 @@ int ship_get_random_player_wing_ship( int flags, float max_dist, int persona_ind
if ( Ships[ship_index].flags[Ship_Flags::Dying] ) {
continue;
}
// see if ship meets our criterea
// see if ship meets our criteria
if ( (flags == SHIP_GET_NO_PLAYERS || flags == SHIP_GET_UNSILENCED) && (Objects[Ships[ship_index].objnum].flags[Object::Object_Flags::Player_ship]) ){
continue;
}
if ( (flags == SHIP_GET_UNSILENCED) && (Ships[ship_index].flags[Ship_Flags::No_builtin_messages]) )
{
continue;
if (flags == SHIP_GET_UNSILENCED) {
if (Ships[ship_index].flags[Ship_Flags::No_builtin_messages])
continue;
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(&Ships[ship_index]) <= COMM_DAMAGED)
continue;
}
// don't process ships on a different team
@@ -14652,6 +14728,57 @@ int ship_navigation_ok_to_warp(ship *sp)
return 1;
}
// wookieejedi
// checks both the warp flags and ship_engine_ok_to_warp() and ship_navigation_ok_to_warp() --wookieejedi
// returns true if ship is able to warp, otherwise return false
bool ship_can_warp_full_check(ship* sp)
{
if (!(sp->cannot_warp_flags()) && ship_navigation_ok_to_warp(sp) && ship_engine_ok_to_warp(sp)) {
// there are no warp flags that are preventing us from warping and the nav and engines are good
// thus warp is allowed
return true;
} else {
// warp is not allowed since one of those conditions was not met
return false;
}
}
// wookieejedi
// check to see if a ship can depart via bay
// takes into account if the ship is in a wing
// returns true if the ship has a bay departure and the mothership is present, false otherwise
bool ship_can_bay_depart(ship* sp)
{
// if this ship belongs to a wing, then use the wing departure information
int departure_location;
int departure_anchor;
int departure_path_mask;
if (sp->wingnum >= 0)
{
wing *wingp = &Wings[sp->wingnum];
departure_location = wingp->departure_location;
departure_anchor = wingp->departure_anchor;
departure_path_mask = wingp->departure_path_mask;
} else {
departure_location = sp->departure_location;
departure_anchor = sp->departure_anchor;
departure_path_mask = sp->departure_path_mask;
}
if ( departure_location == DEPART_AT_DOCK_BAY )
{
Assertion( departure_anchor >= 0, "Ship %s must have a valid departure anchor", sp->ship_name );
int anchor_shipnum = ship_name_lookup(Parse_names[departure_anchor]);
if (anchor_shipnum >= 0 && ship_useful_for_departure(anchor_shipnum, departure_path_mask)) {
// can bay depart at this time
return true;
}
}
// cannot bay depart at this time
return false;
}
// Calculate the normal vector from a subsystem position and its first path point
// input: sp => pointer to ship that is parent of subsystem
// ss => pointer to subsystem of interest
@@ -15464,6 +15591,9 @@ void ship_maybe_praise_self(ship *deader_sp, ship *killer_sp)
if ( killer_sp->flags[Ship_Flags::No_builtin_messages] ) {
return;
}
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(killer_sp) != COMM_OK) {
return;
}
message_send_builtin_to_player(MESSAGE_PRAISE_SELF, killer_sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, -1, -1);
Player->praise_self_timestamp = timestamp(Builtin_messages[MESSAGE_PRAISE_SELF].min_delay);
@@ -15559,8 +15689,12 @@ play_ask_help:
if (!(Ship_info[sp->ship_info_index].is_fighter_bomber())) //If we're still here, only continue if we're a fighter or bomber.
return;
if (!(sp->flags[Ship_Flags::No_builtin_messages])) // Karajorma - Only unsilenced ships should ask for help
{
// only unsilenced ships should ask for help
if (sp->flags[Ship_Flags::No_builtin_messages])
return;
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(&Ships[objp->instance]) <= COMM_DAMAGED)
return;
message_send_builtin_to_player(MESSAGE_HELP, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
Player->allow_ask_help_timestamp = timestamp(Builtin_messages[MESSAGE_HELP].min_delay);
@@ -15569,7 +15703,6 @@ play_ask_help:
Player->allow_scream_timestamp = timestamp(15000);
Player->ask_help_count++;
}
}
/**
@@ -15608,9 +15741,9 @@ void ship_scream(ship *sp)
// Bail if the ship is silenced
if (sp->flags[Ship_Flags::No_builtin_messages])
{
return;
}
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(sp, true) <= COMM_DAMAGED)
return;
message_send_builtin_to_player(MESSAGE_WINGMAN_SCREAM, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
Player->allow_scream_timestamp = timestamp(Builtin_messages[MESSAGE_WINGMAN_SCREAM].min_delay);
@@ -15695,6 +15828,8 @@ void ship_maybe_tell_about_low_ammo(ship *sp)
if (sp->flags[Ship_Flags::No_builtin_messages]) {
return;
}
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(sp) <= COMM_DAMAGED)
return;
// don't mention low ammo if we're docked, for the same reason as in maybe_request_support
if (object_is_docked(&Objects[sp->objnum]))
@@ -15760,6 +15895,8 @@ void ship_maybe_tell_about_rearm(ship *sp)
// Silent ships should remain just that
if (sp->flags[Ship_Flags::No_builtin_messages])
return;
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(sp) <= COMM_DAMAGED)
return;
// AL 1-4-98: If ship integrity is low, tell player you want to get repaired. Otherwise, tell
// the player you want to get re-armed.
@@ -19128,3 +19265,134 @@ bool ship::is_arriving(ship::warpstage stage, bool dock_leader_or_single)
Assertion(false, "ship::is_arriving didn't handle all possible states; get a coder!");
return false;
}
void ship_clear_lock(lock_info *slot) {
vec3d zero_vec = ZERO_VECTOR;
slot->accumulated_x_pixels = 0;
slot->accumulated_y_pixels = 0;
slot->catch_up_distance = 0.0f;
slot->catching_up = 0;
slot->current_target_sx = -1;
slot->current_target_sy = -1;
slot->dist_to_lock = 0.0f;
slot->indicator_start_x = -1;
slot->indicator_start_y = -1;
slot->indicator_visible = false;
slot->indicator_x = -1;
slot->indicator_y = -1;
slot->last_dist_to_target = 0.0f;
slot->locked_timestamp = 0;
slot->maintain_lock_count = 0;
slot->need_new_start_pos = false;
slot->target_in_lock_cone = false;
slot->world_pos = zero_vec;
slot->locked = false;
slot->obj = nullptr;
slot->subsys = nullptr;
slot->time_to_lock = -1.0f;
}
void ship_queue_missile_locks(ship *shipp)
{
shipp->missile_locks_firing.clear();
// queue up valid missile locks
std::copy_if(shipp->missile_locks.begin(), shipp->missile_locks.end(), std::back_inserter(shipp->missile_locks_firing), [](lock_info lock) { return lock.locked; });
}
bool ship_lock_present(ship *shipp)
{
return std::any_of(shipp->missile_locks.begin(), shipp->missile_locks.end(), [](lock_info lock) { return lock.locked; });
}
bool ship_start_secondary_fire(object* objp)
{
Assert( objp != nullptr);
if ( objp->type != OBJ_SHIP ) {
return false;
}
int n = objp->instance;
Assert( n >= 0 && n < MAX_SHIPS );
Assert( Ships[n].objnum == OBJ_INDEX(objp) );
ship *shipp = &Ships[n];
ship_info *sip = &Ship_info[shipp->ship_info_index];
ship_weapon* swp = &shipp->weapons;
int bank = swp->current_secondary_bank;
if ( bank < 0 || bank >= sip->num_secondary_banks ) {
return false;
}
Assert( (swp->secondary_bank_weapons[bank] >= 0) && (swp->secondary_bank_weapons[bank] < weapon_info_size()) );
weapon_info *wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
if ( wip->trigger_lock ) {
swp->flags.set(Ship::Weapon_Flags::Trigger_Lock);
return true;
}
return false;
}
bool ship_stop_secondary_fire(object* objp)
{
Assert( objp != nullptr);
if ( objp->type != OBJ_SHIP ) {
return false;
}
int n = objp->instance;
if ( n < 0 || n >= MAX_SHIPS ) {
return false;
}
Assert( Ships[n].objnum == OBJ_INDEX(objp) );
ship *shipp = &Ships[n];
ship_info *sip = &Ship_info[shipp->ship_info_index];
ship_weapon *swp = &shipp->weapons;
int bank = swp->current_secondary_bank;
if ( bank < 0 || bank >= sip->num_secondary_banks ) {
return false;
}
Assert( (swp->secondary_bank_weapons[bank] >= 0) && (swp->secondary_bank_weapons[bank] < weapon_info_size()) );
weapon_info *wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
if ( wip->trigger_lock && swp->flags[Ship::Weapon_Flags::Trigger_Lock]) {
swp->flags.remove(Ship::Weapon_Flags::Trigger_Lock);
return true;
}
return false;
}
+52 -2
View File
@@ -31,6 +31,7 @@
#include "weapon/trails.h"
#include "ship/ship_flags.h"
#include "weapon/weapon_flags.h"
#include "ai/ai.h"
#include <string>
#include <particle/ParticleManager.h>
@@ -283,6 +284,36 @@ typedef struct cockpit_display_info {
int size[2];
} cockpit_display_info;
// structure to keep track of ship locks
typedef struct lock_info {
object *obj;
ship_subsys *subsys;
vec3d world_pos;
int current_target_sx;
int current_target_sy;
bool locked;
int maintain_lock_count;
int indicator_x;
int indicator_y;
int indicator_start_x;
int indicator_start_y;
bool indicator_visible;
float time_to_lock;
float dist_to_lock;
int catching_up;
float catch_up_distance;
float last_dist_to_target;
double accumulated_x_pixels;
double accumulated_y_pixels;
bool need_new_start_pos;
bool target_in_lock_cone;
int locked_timestamp;
} lock_info;
// structure definition for a linked list of subsystems for a ship. Each subsystem has a pointer
// to the static data for the subsystem. The obj_subsystem data is defined and read in the model
// code. Other dynamic data (such as current_hits) should remain in this structure.
@@ -560,6 +591,9 @@ public:
vec3d wash_rot_axis;
int wash_timestamp;
SCP_vector<lock_info> missile_locks;
SCP_vector<lock_info> missile_locks_firing;
int num_swarm_missiles_to_fire; // number of swarm missiles that need to be launched
int next_swarm_fire; // timestamp of next swarm missile to fire
int next_swarm_path; // next path number for swarm missile to take
@@ -722,11 +756,13 @@ public:
//Helper functions
bool is_arriving(ship::warpstage stage = ship::warpstage::BOTH, bool dock_leader_or_single = false);
inline bool is_departing() { return flags[Ship::Ship_Flags::Depart_warp, Ship::Ship_Flags::Depart_dockbay]; }
inline bool cannot_warp() { return flags[Ship::Ship_Flags::Warp_broken, Ship::Ship_Flags::Warp_never, Ship::Ship_Flags::Disabled]; }
inline bool cannot_warp_flags() { return flags[Ship::Ship_Flags::Warp_broken, Ship::Ship_Flags::Warp_never, Ship::Ship_Flags::Disabled, Ship::Ship_Flags::No_subspace_drive]; }
inline bool is_dying_or_departing() { return is_departing() || flags[Ship::Ship_Flags::Dying]; }
bool has_display_name();
const char* get_display_string();
void apply_replacement_textures(SCP_vector<texture_replace> &replacements);
};
struct ai_target_priority {
@@ -1407,6 +1443,9 @@ extern bool in_autoaim_fov(ship *shipp, int bank_to_fire, object *obj);
extern int ship_stop_fire_primary(object * obj);
extern int ship_fire_primary(object * objp, int stream_weapons, int force = 0);
extern int ship_fire_secondary(object * objp, int allow_swarm = 0 );
bool ship_start_secondary_fire(object* objp);
bool ship_stop_secondary_fire(object* objp);
extern int ship_launch_countermeasure(object *objp, int rand_val = -1);
// for special targeting lasers
@@ -1495,7 +1534,7 @@ extern ship_subsys *ship_get_indexed_subsys( ship *sp, int index, vec3d *attacke
extern int ship_get_index_from_subsys(ship_subsys *ssp, int objnum);
extern int ship_get_subsys_index(ship *sp, const char* ss_name); // returns numerical index in linked list of subsystems
extern int ship_get_subsys_index(ship *shipp, ship_subsys *subsys);
extern float ship_get_subsystem_strength( ship *shipp, int type );
extern float ship_get_subsystem_strength( ship *shipp, int type, bool skip_dying_check = false );
extern ship_subsys *ship_get_subsys(ship *shipp, const char *subsys_name);
extern int ship_get_num_subsys(ship *shipp);
extern ship_subsys *ship_get_closest_subsys_in_sight(ship *sp, int subsys_type, vec3d *attacker_pos);
@@ -1536,6 +1575,8 @@ int ship_secondary_bank_has_ammo(int shipnum); // check if current secondary ban
int ship_engine_ok_to_warp(ship *sp); // check if ship has engine power to warp
int ship_navigation_ok_to_warp(ship *sp); // check if ship has navigation power to warp
bool ship_can_warp_full_check(ship *sp); // checks both the warp flags and ship_engine_ok_to_warp() and ship_navigation_ok_to_warp() --wookieejedi
bool ship_can_bay_depart(ship *sp); // checks to see if a ship has a departure location as a bay and if the mothership is present
int ship_return_subsys_path_normal(ship *sp, ship_subsys *ss, vec3d *gsubpos, vec3d *norm);
int ship_subsystem_in_sight(object* objp, ship_subsys* subsys, vec3d *eye_pos, vec3d* subsys_pos, int do_facing_check=1, float *dot_out=NULL, vec3d *vec_out=NULL);
@@ -1796,4 +1837,13 @@ extern void toggle_ignore_list_flag(Ship::Ship_Flags flag);
ship_subsys* ship_get_subsys_for_submodel(ship* shipp, int submodel);
// Clears a lock_info struct with defaults
void ship_clear_lock(lock_info *slot);
// queues up locks
void ship_queue_missile_locks(ship *shipp);
// snoops missile locks to see if any are ready to fire.
bool ship_lock_present(ship *shipp);
#endif
+1
View File
@@ -9,6 +9,7 @@ namespace Ship {
Beam_Free = 0, // if this is a beam weapon, its free to fire
Turret_Lock, // is this turret is free to fire or locked
Tagged_Only, // only fire if target is tagged
Trigger_Lock, // // indicates that the trigger is held down
NUM_VALUES
};
+4
View File
@@ -1582,6 +1582,10 @@ void shipfx_queue_render_ship_halves_and_debris(model_draw_list *scene, clip_shi
render_flags |= MR_NO_TEXTURING | MR_NO_LIGHTING;
}
if (shipp->flags[Ship::Ship_Flags::Glowmaps_disabled]) {
render_flags |= MR_NO_GLOWMAPS;
}
for (int i = 0; i < pm->num_debris_objects; i++ ) {
// draw DEBRIS_FREE in test only
if (half_ship->draw_debris[i] == DEBRIS_DRAW) {
+14
View File
@@ -38,6 +38,7 @@
#include "object/objectshield.h"
#include "object/objectsnd.h"
#include "parse/parselo.h"
#include "scripting/hook_api.h"
#include "scripting/scripting.h"
#include "scripting/api/objs/subsystem.h"
#include "playerman/player.h"
@@ -72,6 +73,12 @@ vec3d Original_vec_to_deader;
static bool global_damage = false;
const std::shared_ptr<scripting::Hook> OnPainFlashHook = scripting::Hook::Factory(
"On Pain Flash", "Called when a pain flash is displayed.",
{
{"Pain_Type", "number", "The type of pain flash displayed: shield = 0 and hull = 1."},
});
//WMC - Camera rough draft stuff
/*
camid dead_get_camera()
@@ -2637,6 +2644,7 @@ void ship_hit_pain(float damage, int quadrant)
if (!(Player_obj->flags[Object::Object_Flags::Invulnerable]))
{
int pain_type;
if (Shield_pain_flash_factor != 0.0f && quadrant >= 0)
{
float effect = (Shield_pain_flash_factor * Player_obj->shield_quadrant[quadrant] * Player_obj->n_quadrants) / shield_get_max_strength(Player_obj);
@@ -2645,9 +2653,15 @@ void ship_hit_pain(float damage, int quadrant)
effect -= Shield_pain_flash_factor;
game_flash((sip->shield_color[0] * effect) / 255.0f, (sip->shield_color[1] * effect) / 255.0f, (sip->shield_color[2] * effect) / 255.0f);
pain_type = 0;
}
else
{
game_flash(damage * Generic_pain_flash_factor / 15.0f, -damage * Generic_pain_flash_factor / 30.0f, -damage * Generic_pain_flash_factor / 30.0f);
pain_type = 1;
}
// add scripting hook for 'On Pain Flash' --wookieejedi
OnPainFlashHook->run(scripting::hook_param_list(scripting::hook_param("Pain_Type", 'i', pain_type)));
}
// kill any active popups when you get hit.
+1 -1
View File
@@ -56,7 +56,7 @@ extern int button_function(int n);
extern void hud_squadmsg_msg_all_fighters();
extern void hud_squadmsg_shortcut( int command );
extern bool hud_squadmsg_ship_valid(ship *shipp, object *objp = nullptr);
extern int hud_squadmsg_wing_valid(wing *wingp);
extern bool hud_squadmsg_wing_valid(wing *wingp);
extern int Msg_instance;;
extern int Msg_shortcut_command;
+2 -2
View File
@@ -349,8 +349,6 @@ add_file_folder("GlobalIncs\\\\Toolchain"
add_file_folder("Graphics"
graphics/2d.cpp
graphics/2d.h
graphics/debug.cpp
graphics/debug.h
graphics/decal_draw_list.cpp
graphics/decal_draw_list.h
graphics/grbatch.cpp
@@ -1075,6 +1073,8 @@ add_file_folder("Scripting\\\\Api\\\\Libs"
scripting/api/libs/bitops.h
scripting/api/libs/cfile.cpp
scripting/api/libs/cfile.h
scripting/api/libs/engine.cpp
scripting/api/libs/engine.h
scripting/api/libs/graphics.cpp
scripting/api/libs/graphics.h
scripting/api/libs/hookvars.cpp
+30 -1
View File
@@ -38,6 +38,15 @@ extern int Num_weapon_subtypes;
#define WRT_LASER 1
#define WRT_POF 2
// constants for weapon lock acquire methods
#define WLOCK_PIXEL 0
#define WLOCK_TIMER 1
// constants for weapon lock restrictions
#define LR_CURRENT_TARGET 0 // Only lock current target and subsystem
#define LR_CURRENT_TARGET_SUBSYS 1 //
#define LR_ANY_TARGETS 2
//particle names go here -nuke
#define PSPEW_NONE -1 //used to disable a spew, useful for xmts
#define PSPEW_DEFAULT 0 //std fs2 pspew
@@ -315,11 +324,24 @@ struct weapon_info
short swarm_count; // how many swarm missiles are fired for this weapon
int SwarmWait; // *Swarm firewait, default is 150 -Et1
int target_restrict;
bool multi_lock;
int max_seeking; // how many seekers can be active at a time if multilock is enabled. A value of one will lock stuff up one by one.
int max_seekers_per_target; // how many seekers can be attached to a target.
SCP_vector<int> ship_type_restrict;
SCP_vector<SCP_string> ship_type_restrict_temp;
bool trigger_lock; // Trigger must be held down and released to lock and fire.
bool launch_reset_locks; // Lock indicators reset after firing
// Specific to ASPECT homing missiles.
int acquire_method;
float min_lock_time; // minimum time (in seconds) to achieve lock
int lock_pixels_per_sec; // pixels/sec moved while locking
int catchup_pixels_per_sec; // pixels/sec moved while catching-up for a lock
int catchup_pixel_penalty; // number of extra pixels to move while locking as a penalty for catching up for a lock
int catchup_pixel_penalty; // number of extra pixels to move while locking as a penalty for catching up for a lock
float lock_fov;
// Specific to HEAT homing missiles.
float fov;
@@ -629,4 +651,11 @@ void validate_SSM_entries();
void shield_impact_explosion(vec3d *hitpos, object *objp, float radius, int idx);
// Swifty - return number of max simultaneous locks
int weapon_get_max_missile_seekers(weapon_info *wip);
// return if this weapon can lock on this ship type
bool weapon_can_lock_on_ship_type(weapon_info *wip, int ship_type);
#endif
+1
View File
@@ -23,6 +23,7 @@ namespace Weapon {
Bomb, // Bomb-type missile, can be targeted
Huge, // Huge damage (generally 500+), probably only fired at huge ships.
No_dumbfire, // Missile cannot be fired dumbfire (ie requires aspect lock)
No_doublefire, // Disables linked firing for secondaries - EatThePath
Thruster, // Has thruster cone and/or glow
In_tech_database,
Player_allowed, // allowed to be on starting wing ships/in weaponry pool
+304 -5
View File
@@ -115,6 +115,7 @@ flag_def_list_new<Weapon::Info_Flags> Weapon_Info_Flags[] = {
{ "child", Weapon::Info_Flags::Child, true, false },
{ "bomb", Weapon::Info_Flags::Bomb, true, false },
{ "no dumbfire", Weapon::Info_Flags::No_dumbfire, true, false },
{ "no doublefire", Weapon::Info_Flags::No_doublefire, true, false },
{ "in tech database", Weapon::Info_Flags::In_tech_database, true, false },
{ "player allowed", Weapon::Info_Flags::Player_allowed, true, false },
{ "particle spew", Weapon::Info_Flags::Particle_spew, true, false },
@@ -221,6 +222,7 @@ int Weapon_impact_timer; // timer, initialized at start of each mission
#define REARM_NUM_BALLISTIC_PRIMARIES_PER_BATCH 100
extern int compute_num_homing_objects(object *target_objp);
void weapon_spew_stats();
weapon_explosions::weapon_explosions()
@@ -808,7 +810,7 @@ int parse_weapon(int subtype, bool replace, const char *filename)
}
//Remove @ symbol
//these used to be used to denote weapons that would
//these used to denote weapons that would
//only be parsed in demo builds
if ( fname[0] == '@' ) {
backspace(fname);
@@ -984,8 +986,10 @@ int parse_weapon(int subtype, bool replace, const char *filename)
if ( optional_string("$Model file:") ) {
stuff_string(wip->pofbitmap_name, F_NAME, MAX_FILENAME_LEN);
if ( VALID_FNAME(wip->pofbitmap_name) )
if (VALID_FNAME(wip->pofbitmap_name))
wip->render_type = WRT_POF;
else
wip->render_type = WRT_NONE;
diag_printf("Model pof file -- %s\n", wip->pofbitmap_name );
}
@@ -1016,7 +1020,7 @@ int parse_weapon(int subtype, bool replace, const char *filename)
if (wip->render_type == WRT_POF) {
mprintf(("WARNING: Weapon '%s' has both LASER and POF render types! Will only use POF type!\n", wip->name));
generic_anim_init(&wip->laser_bitmap, NULL);
generic_anim_init(&wip->laser_bitmap, nullptr);
} else {
generic_anim_init(&wip->laser_bitmap, fname);
wip->render_type = WRT_LASER;
@@ -1394,6 +1398,56 @@ int parse_weapon(int subtype, bool replace, const char *filename)
}
}
if (optional_string("+Target Lock Restriction:")) {
if (optional_string("current target, any subsystem")) {
wip->target_restrict = LR_CURRENT_TARGET_SUBSYS;
}
else if (optional_string("any target")) {
wip->target_restrict = LR_ANY_TARGETS;
}
else if (optional_string("current target only")) {
wip->target_restrict = LR_CURRENT_TARGET;
}
else {
wip->target_restrict = LR_CURRENT_TARGET;
}
}
if (optional_string("+Independent Seekers:")) {
stuff_boolean(&wip->multi_lock);
}
if (optional_string("+Trigger Hold:")) {
stuff_boolean(&wip->trigger_lock);
}
if (optional_string("+Reset On Launch:")) {
stuff_boolean(&wip->launch_reset_locks);
}
if (optional_string("+Max Seekers Per Target:")) {
stuff_int(&wip->max_seekers_per_target);
}
if (optional_string("+Max Active Seekers:")) {
stuff_int(&wip->max_seeking);
}
if (optional_string("+Ship Types:")) {
stuff_string_list(wip->ship_type_restrict_temp);
}
if (wip->is_locked_homing()) {
// locked homing missiles have a much longer lifespan than the AI think they do
wip->max_lifetime = wip->lifetime * LOCKED_HOMING_EXTENDED_LIFE_FACTOR;
@@ -3546,6 +3600,9 @@ void weapon_init()
}
weapon_level_init();
if (Cmdline_spew_weapon_stats)
weapon_spew_stats();
}
@@ -3592,6 +3649,7 @@ void weapon_close()
void weapon_level_init()
{
int i;
extern int ships_inited;
// Reset everything between levels
Num_weapons = 0;
@@ -3603,6 +3661,19 @@ void weapon_level_init()
for (i = 0; i < weapon_info_size(); i++) {
Weapon_info[i].damage_type_idx = Weapon_info[i].damage_type_idx_sav;
Weapon_info[i].shockwave.damage_type_idx = Weapon_info[i].shockwave.damage_type_idx_sav;
if ( ships_inited ) {
// populate ship type lock restrictions
for ( int j = 0; j < (int)Weapon_info[i].ship_type_restrict_temp.size(); ++j ) {
int idx = ship_type_name_lookup((char*)Weapon_info[i].ship_type_restrict_temp[j].c_str());
if ( idx >= 0 ) {
Weapon_info[i].ship_type_restrict.push_back(idx);
}
}
Weapon_info[i].ship_type_restrict_temp.clear();
}
}
trail_level_init(); // reset all missile trails
@@ -7693,12 +7764,25 @@ void weapon_info::reset()
// *Default is 150 -Et1
this->SwarmWait = SWARM_MISSILE_DELAY;
this->target_restrict = LR_CURRENT_TARGET;
this->multi_lock = false;
this->trigger_lock = false;
this->launch_reset_locks = false;
this->max_seeking = 1;
this->max_seekers_per_target = 1;
this->ship_type_restrict.clear();
this->ship_type_restrict_temp.clear();
this->acquire_method = WLOCK_PIXEL;
this->min_lock_time = 0.0f;
this->lock_pixels_per_sec = 50;
this->catchup_pixels_per_sec = 50;
this->catchup_pixel_penalty = 50;
this->fov = 0; //should be cos(pi), not pi
this->seeker_strength = 1.0f;
this->lock_fov = 0.85f;
this->pre_launch_snd = gamesnd_id();
this->pre_launch_snd_min_interval = 0;
@@ -7889,13 +7973,228 @@ void weapon_info::reset()
// Reset using default constructor
this->impact_decal = decals::creation_info();
}
const char* weapon_info::get_display_string() {
const char* weapon_info::get_display_string()
{
if (has_alternate_name()) {
return alt_name;
} else {
return name;
}
}
bool weapon_info::has_alternate_name() {
bool weapon_info::has_alternate_name()
{
return alt_name[0] != '\0';
}
void weapon_spew_stats()
{
#ifndef NDEBUG
// csv weapon stats for comparisons
mprintf(("Name,Type,Velocity,Range,Damage Hull,DPS Hull,Damage Shield,DPS Shield,Damage Subsystem,DPS Subsystem,Power Use,Fire Wait,ROF,Reload,1/Reload,Area Effect,Shockwave\n"));
for (auto &wi : Weapon_info)
{
if (wi.subtype != WP_LASER)
continue;
weapon_info *wip_base = nullptr;
if (strchr(wi.name, '#'))
{
char temp[NAME_LENGTH];
strcpy_s(temp, wi.name);
end_string_at_first_hash_symbol(temp);
int idx = weapon_info_lookup(temp);
if (idx >= 0)
wip_base = &Weapon_info[idx];
}
if (wi.wi_flags[Weapon::Info_Flags::Player_allowed] || (wip_base != nullptr && wip_base->wi_flags[Weapon::Info_Flags::Player_allowed]))
{
mprintf(("%s,%s,", wi.name, "Primary"));
mprintf(("%.2f,%.2f,", wi.max_speed, wi.max_speed * wi.lifetime));
float multiplier = (wi.shockwave.speed > 0.0f) ? 2.0f : 1.0f;
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.armor_factor, multiplier * wi.damage * wi.armor_factor / wi.fire_wait));
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.shield_factor, multiplier * wi.damage * wi.shield_factor / wi.fire_wait));
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.subsystem_factor, multiplier * wi.damage * wi.subsystem_factor / wi.fire_wait));
mprintf(("%.2f,", wi.energy_consumed / wi.fire_wait));
mprintf(("%.2f,%.2f,", wi.fire_wait, 1.0f / wi.fire_wait));
mprintf((",,")); // no reload for primaries
if (wi.shockwave.inner_rad > 0.0f || wi.shockwave.outer_rad > 0.0f)
mprintf(("Yes,"));
else
mprintf((","));
if (wi.shockwave.speed > 0.0f)
mprintf(("Yes\n"));
else
mprintf(("\n"));
}
}
for (auto &wi : Weapon_info)
{
if (wi.subtype != WP_MISSILE)
continue;
weapon_info *wip_base = nullptr;
if (strchr(wi.name, '#'))
{
char temp[NAME_LENGTH];
strcpy_s(temp, wi.name);
end_string_at_first_hash_symbol(temp);
int idx = weapon_info_lookup(temp);
if (idx >= 0)
wip_base = &Weapon_info[idx];
}
if (wi.wi_flags[Weapon::Info_Flags::Player_allowed] || (wip_base != nullptr && wip_base->wi_flags[Weapon::Info_Flags::Player_allowed]) || wi.wi_flags[Weapon::Info_Flags::Child])
{
mprintf(("%s,%s,", wi.name, "Secondary"));
mprintf(("%.2f,%.2f,", wi.max_speed, wi.max_speed * wi.lifetime));
float multiplier = (wi.shockwave.speed > 0.0f) ? 2.0f : 1.0f;
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.armor_factor, multiplier * wi.damage * wi.armor_factor / wi.fire_wait));
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.shield_factor, multiplier * wi.damage * wi.shield_factor / wi.fire_wait));
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.subsystem_factor, multiplier * wi.damage * wi.subsystem_factor / wi.fire_wait));
mprintf((",")); // no power use for secondaries
mprintf(("%.2f,%.2f,", wi.fire_wait, 1.0f / wi.fire_wait));
mprintf(("%.2f,%.2f,", wi.reloaded_per_batch / wi.rearm_rate, wi.rearm_rate / wi.reloaded_per_batch)); // rearm_rate is actually the reciprocal of what is in weapons.tbl
if (wi.shockwave.inner_rad > 0.0f || wi.shockwave.outer_rad > 0.0f)
mprintf(("Yes,"));
else
mprintf((","));
if (wi.shockwave.speed > 0.0f)
mprintf(("Yes\n"));
else
mprintf(("\n"));
}
}
// mvp-style stats
mprintf(("\n"));
for (auto &wi : Weapon_info)
{
if (wi.subtype != WP_LASER)
continue;
weapon_info *wip_base = nullptr;
if (strchr(wi.name, '#'))
{
char temp[NAME_LENGTH];
strcpy_s(temp, wi.name);
end_string_at_first_hash_symbol(temp);
int idx = weapon_info_lookup(temp);
if (idx >= 0)
wip_base = &Weapon_info[idx];
}
if (wi.wi_flags[Weapon::Info_Flags::Player_allowed] || (wip_base != nullptr && wip_base->wi_flags[Weapon::Info_Flags::Player_allowed]))
{
mprintf(("%s\n", wi.name));
mprintf(("\tVelocity: %-11.0fRange: %.0f\n", wi.max_speed, wi.max_speed * wi.lifetime));
float multiplier = (wi.shockwave.inner_rad > 0.0f || wi.shockwave.outer_rad > 0.0f) ? 2.0f : 1.0f;
mprintf(("\tDPS: "));
mprintf(("%.0f Hull, ", multiplier * wi.damage * wi.armor_factor / wi.fire_wait));
mprintf(("%.0f Shield, ", multiplier * wi.damage * wi.shield_factor / wi.fire_wait));
mprintf(("%.0f Subsystem\n", multiplier * wi.damage * wi.subsystem_factor / wi.fire_wait));
char watts[NAME_LENGTH];
sprintf(watts, "%.1f", wi.energy_consumed / wi.fire_wait);
char *p = strstr(watts, ".0");
if (p)
*p = 0;
strcat(watts, "W");
char rof[NAME_LENGTH];
sprintf(rof, "%.1f", 1.0f / wi.fire_wait);
p = strstr(rof, ".0");
if (p)
*p = 0;
strcat(rof, "/s");
mprintf(("\tPower Use: %-10sROF: %s\n\n", watts, rof));
}
}
for (auto &wi : Weapon_info)
{
if (wi.subtype != WP_MISSILE)
continue;
weapon_info *wip_base = nullptr;
if (strchr(wi.name, '#'))
{
char temp[NAME_LENGTH];
strcpy_s(temp, wi.name);
end_string_at_first_hash_symbol(temp);
int idx = weapon_info_lookup(temp);
if (idx >= 0)
wip_base = &Weapon_info[idx];
}
if (wi.wi_flags[Weapon::Info_Flags::Player_allowed] || (wip_base != nullptr && wip_base->wi_flags[Weapon::Info_Flags::Player_allowed]) || wi.wi_flags[Weapon::Info_Flags::Child])
{
mprintf(("%s\n", wi.name));
mprintf(("\tVelocity: %-11.0fRange: %.0f\n", wi.max_speed, wi.max_speed * wi.lifetime));
float multiplier = (wi.shockwave.inner_rad > 0.0f || wi.shockwave.outer_rad > 0.0f) ? 2.0f : 1.0f;
mprintf(("\tDamage: "));
mprintf(("%.0f Hull, ", multiplier * wi.damage * wi.armor_factor));
mprintf(("%.0f Shield, ", multiplier * wi.damage * wi.shield_factor));
mprintf(("%.0f Subsystem\n", multiplier * wi.damage * wi.subsystem_factor));
char wait[NAME_LENGTH];
sprintf(wait, "%.1f", wi.fire_wait);
char *p = strstr(wait, ".0");
if (p)
*p = 0;
strcat(wait, "s");
bool flip = wi.rearm_rate <= 1.0f;
char flip_str[NAME_LENGTH];
sprintf(flip_str, "%d/", wi.reloaded_per_batch);
char reload[NAME_LENGTH];
sprintf(reload, "%.1f", flip ? wi.reloaded_per_batch / wi.rearm_rate : wi.rearm_rate);
p = strstr(reload, ".0");
if (p)
*p = 0;
mprintf(("\tFire Wait: %-10sReload: %s%s%ss\n\n", wait, !flip ? flip_str : "", reload, flip ? "/" : ""));
}
}
#endif
}
// Given a weapon, figure out how many independent locks we can have with it.
int weapon_get_max_missile_seekers(weapon_info *wip)
{
int max_target_locks;
if ( wip->multi_lock ) {
if ( wip->wi_flags[Weapon::Info_Flags::Swarm] ) {
max_target_locks = wip->swarm_count;
} else if ( wip->wi_flags[Weapon::Info_Flags::Corkscrew] ) {
max_target_locks = wip->cs_num_fired;
} else {
max_target_locks = 1;
}
} else {
max_target_locks = 1;
}
return max_target_locks;
}
bool weapon_can_lock_on_ship_type(weapon_info *wip, int ship_type)
{
// Determine if there are any restrictions, treating an empty list as true
return (wip->ship_type_restrict.size() == 0) ||
std::any_of(wip->ship_type_restrict.begin(), wip->ship_type_restrict.end(), [ship_type](int type) { return type == ship_type; });
}
+3 -6
View File
@@ -231,9 +231,8 @@ BOOL CFREDApp::InitInstance() {
Lighting_on = GetProfileInt("Preferences", "Lighting On", Lighting_on);
// Goober5000
Format_fs2_open = GetProfileInt("Preferences", "FS2 open format", Format_fs2_open);
Format_fs2_retail = GetProfileInt("Preferences", "FS2 retail format", Format_fs2_retail);
Format_fs1_retail = GetProfileInt("Preferences", "FS1 retail format", Format_fs1_retail);
Mission_save_format = GetProfileInt("Preferences", "FS2 open format", FSO_FORMAT_STANDARD);
Mission_save_format = GetProfileInt("Preferences", "Mission save format", Mission_save_format);
read_window("Main window", &Main_wnd_data);
read_window("Ship window", &Ship_wnd_data);
@@ -493,9 +492,7 @@ void CFREDApp::write_ini_file(int degree) {
WriteProfileInt("Preferences", "Lighting On", Lighting_on);
// Goober5000
WriteProfileInt("Preferences", "FS2 open format", Format_fs2_open);
WriteProfileInt("Preferences", "FS2 retail format", Format_fs2_retail);
WriteProfileInt("Preferences", "FS1 retail format", Format_fs1_retail);
WriteProfileInt("Preferences", "Mission save format", Mission_save_format);
if (!degree) {
record_window_data(&Waypoint_wnd_data, &Waypoint_editor_dialog);
+9 -19
View File
@@ -107,9 +107,7 @@ Marking_box marking_box;
object_orient_pos rotation_backup[MAX_OBJECTS];
// Goober5000 (currently, FS1 retail not implemented)
int Format_fs2_open = FSO_FORMAT_STANDARD;
int Format_fs2_retail = 0;
int Format_fs1_retail = 0;
int Mission_save_format = FSO_FORMAT_STANDARD;
// used by error checker, but needed in more than just one function.
char *names[MAX_OBJECTS], flags[MAX_OBJECTS];
@@ -4523,9 +4521,7 @@ void CFREDView::OnDumpStats()
void CFREDView::OnFormatFs2Open()
{
Format_fs2_open = FSO_FORMAT_STANDARD;
Format_fs2_retail = 0;
Format_fs1_retail = 0;
Mission_save_format = FSO_FORMAT_STANDARD;
theApp.write_ini_file();
Update_window = 1;
@@ -4533,14 +4529,12 @@ void CFREDView::OnFormatFs2Open()
void CFREDView::OnUpdateFormatFs2Open(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Format_fs2_open == FSO_FORMAT_STANDARD);
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_STANDARD);
}
void CFREDView::OnFormatFs2OpenComp()
{
Format_fs2_open = FSO_FORMAT_COMPATIBILITY_MODE;
Format_fs2_retail = 0;
Format_fs1_retail = 0;
Mission_save_format = FSO_FORMAT_COMPATIBILITY_MODE;
theApp.write_ini_file();
Update_window = 1;
@@ -4548,14 +4542,12 @@ void CFREDView::OnFormatFs2OpenComp()
void CFREDView::OnUpdateFormatFs2OpenComp(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Format_fs2_open == FSO_FORMAT_COMPATIBILITY_MODE);
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE);
}
void CFREDView::OnFormatFs2Retail()
{
Format_fs2_open = FSO_FORMAT_RETAIL;
Format_fs2_retail = 1;
Format_fs1_retail = 0;
Mission_save_format = FSO_FORMAT_RETAIL;
theApp.write_ini_file();
Update_window = 1;
@@ -4563,14 +4555,12 @@ void CFREDView::OnFormatFs2Retail()
void CFREDView::OnUpdateFormatFs2Retail(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Format_fs2_retail);
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_RETAIL);
}
void CFREDView::OnFormatFs1Retail()
{
Format_fs2_open = FSO_FORMAT_RETAIL;
Format_fs2_retail = 0;
Format_fs1_retail = 1;
Mission_save_format = FSO_FORMAT_RETAIL;
theApp.write_ini_file();
Update_window = 1;
@@ -4578,7 +4568,7 @@ void CFREDView::OnFormatFs1Retail()
void CFREDView::OnUpdateFormatFs1Retail(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Format_fs1_retail);
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_RETAIL);
}
void CFREDView::OnEditorsSetGlobalShipFlags()
+1 -5
View File
@@ -364,11 +364,7 @@ enum FSO_FORMAT
FSO_FORMAT_STANDARD = 1,
FSO_FORMAT_COMPATIBILITY_MODE = 2
};
// Goober5000 (currently, FS1 retail not implemented)
extern int Format_fs2_open;
extern int Format_fs2_retail;
extern int Format_fs1_retail;
extern int Mission_save_format;
extern CFREDView *Fred_view_wnd;
+5 -4
View File
@@ -333,13 +333,13 @@ bool fred_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
// Load game_settings.tbl
mod_table_init();
// initialize localization module. Make sure this is done AFTER initialzing OS.
// NOTE : Fred should ALWAYS run in English. Otherwise it might swap in another language
// initialize localization module. Make sure this is done AFTER initializing OS.
// NOTE: Fred should ALWAYS run without localization. Otherwise it might swap in another language
// when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
// trust me on this :)
lcl_init(FS2_OPEN_DEFAULT_LANGUAGE);
lcl_init(LCL_UNTRANSLATED);
// Goober5000 - force init XSTRs (so they work, but only work in English, based on above comment)
// Goober5000 - force init XSTRs (so they work, but only work untranslated, based on above comment)
extern int Xstr_inited;
Xstr_inited = 1;
@@ -466,6 +466,7 @@ bool fred_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
Id_select_type_waypoint = (int)(Ship_info.size());
Fred_main_wnd -> init_tools();
Script_system.RunInitFunctions();
Script_system.RunCondition(CHA_GAMEINIT);
return true;
+187 -122
View File
@@ -51,7 +51,8 @@
#include <freespace.h>
int CFred_mission_save::autosave_mission_file(char *pathname) {
int CFred_mission_save::autosave_mission_file(char *pathname)
{
char backup_name[256], name2[256];
int i;
@@ -73,7 +74,8 @@ int CFred_mission_save::autosave_mission_file(char *pathname) {
return err;
}
void CFred_mission_save::bypass_comment(const char *comment, const char *end) {
void CFred_mission_save::bypass_comment(const char *comment, const char *end)
{
char *ch = strstr(raw_ptr, comment);
if (ch != NULL) {
if (end != NULL) {
@@ -96,20 +98,23 @@ void CFred_mission_save::bypass_comment(const char *comment, const char *end) {
}
}
void CFred_mission_save::convert_special_tags_to_retail(char *text, int max_len) {
void CFred_mission_save::convert_special_tags_to_retail(char *text, int max_len)
{
replace_all(text, "$quote", "''", max_len);
replace_all(text, "$semicolon", ",", max_len);
}
void CFred_mission_save::convert_special_tags_to_retail(SCP_string &text) {
void CFred_mission_save::convert_special_tags_to_retail(SCP_string &text)
{
replace_all(text, "$quote", "''");
replace_all(text, "$semicolon", ",");
}
void CFred_mission_save::convert_special_tags_to_retail() {
void CFred_mission_save::convert_special_tags_to_retail()
{
int i, team, stage;
if (Format_fs2_open != FSO_FORMAT_RETAIL)
if (Mission_save_format != FSO_FORMAT_RETAIL)
return;
for (team = 0; team < Num_teams; team++) {
@@ -134,7 +139,8 @@ void CFred_mission_save::convert_special_tags_to_retail() {
}
}
int CFred_mission_save::fout(char *format, ...) {
int CFred_mission_save::fout(char *format, ...)
{
SCP_string str;
va_list args;
@@ -150,7 +156,8 @@ int CFred_mission_save::fout(char *format, ...) {
return 0;
}
int CFred_mission_save::fout_ext(char *pre_str, char *format, ...) {
int CFred_mission_save::fout_ext(char *pre_str, char *format, ...)
{
SCP_string str_scp;
SCP_string str_out_scp;
va_list args;
@@ -252,7 +259,8 @@ int CFred_mission_save::fout_ext(char *pre_str, char *format, ...) {
return 0;
}
int CFred_mission_save::fout_version(char *format, ...) {
int CFred_mission_save::fout_version(char *format, ...)
{
SCP_string str_scp;
char *ch = NULL;
va_list args;
@@ -261,9 +269,14 @@ int CFred_mission_save::fout_version(char *format, ...) {
return err;
}
// if we're saving in retail format, we can completely skip this
if (Mission_save_format == FSO_FORMAT_RETAIL) {
return 0;
}
// output the version first thing, but skip the special case where we use
// fout_version() for multiline value strings (typically indicated by an initial space)
if ((Format_fs2_open == FSO_FORMAT_COMPATIBILITY_MODE) && (*format != ' ') && !fso_ver_comment.empty()) {
if ((Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE) && (*format != ' ') && !fso_ver_comment.empty()) {
while (*format == '\n') {
str_scp.append(1, *format);
format++;
@@ -284,7 +297,7 @@ int CFred_mission_save::fout_version(char *format, ...) {
char *str_c = vm_strdup(str_scp.c_str());
// this could be a multi-line string, so we've got to handle it all properly
if ((Format_fs2_open == FSO_FORMAT_COMPATIBILITY_MODE) && !fso_ver_comment.empty()) {
if ((Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE) && !fso_ver_comment.empty()) {
bool first_line = true;
char *str_p = str_c;
@@ -341,7 +354,8 @@ int CFred_mission_save::fout_version(char *format, ...) {
return 0;
}
void CFred_mission_save::parse_comments(int newlines) {
void CFred_mission_save::parse_comments(int newlines)
{
char *comment_start = NULL;
int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0;
bool version_added = false;
@@ -501,7 +515,8 @@ void CFred_mission_save::parse_comments(int newlines) {
return;
}
void CFred_mission_save::save_ai_goals(ai_goal *goalp, int ship) {
void CFred_mission_save::save_ai_goals(ai_goal *goalp, int ship)
{
char *str = NULL, buf[80];
int i, valid, flag = 1;
@@ -654,7 +669,8 @@ void CFred_mission_save::save_ai_goals(ai_goal *goalp, int ship) {
fso_comment_pop(true);
}
int CFred_mission_save::save_asteroid_fields() {
int CFred_mission_save::save_asteroid_fields()
{
int i;
fred_parse_flag = 0;
@@ -748,7 +764,8 @@ int CFred_mission_save::save_asteroid_fields() {
return err;
}
int CFred_mission_save::save_bitmaps() {
int CFred_mission_save::save_bitmaps()
{
int i;
uint j;
@@ -894,7 +911,8 @@ int CFred_mission_save::save_bitmaps() {
return err;
}
int CFred_mission_save::save_briefing() {
int CFred_mission_save::save_briefing()
{
int i, j, k, nb;
SCP_string sexp_out;
brief_stage *bs;
@@ -1027,13 +1045,13 @@ int CFred_mission_save::save_briefing() {
parse_comments();
fout(" %d", (bi->flags & BI_HIGHLIGHT) ? 1 : 0);
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
required_string_fred("$mirror:");
parse_comments();
fout(" %d", (bi->flags & BI_MIRROR_ICON) ? 1 : 0);
}
if ((Format_fs2_open != FSO_FORMAT_RETAIL) && (bi->flags & BI_USE_WING_ICON)) {
if ((Mission_save_format != FSO_FORMAT_RETAIL) && (bi->flags & BI_USE_WING_ICON)) {
required_string_fred("$use wing icon:");
parse_comments();
fout(" %d", (bi->flags & BI_USE_WING_ICON) ? 1 : 0);
@@ -1070,7 +1088,8 @@ int CFred_mission_save::save_briefing() {
return err;
}
int CFred_mission_save::save_campaign_file(char *pathname) {
int CFred_mission_save::save_campaign_file(char *pathname)
{
int i, j, m, flag;
Campaign_tree_formp->save_tree(); // flush all changes so they get saved.
@@ -1109,7 +1128,7 @@ int CFred_mission_save::save_campaign_file(char *pathname) {
}
// campaign flags - Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
optional_string_fred("$Flags:");
parse_comments();
fout(" %d\n", Campaign.flags);
@@ -1153,7 +1172,7 @@ int CFred_mission_save::save_campaign_file(char *pathname) {
parse_comments();
// Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
// don't save Bastion flag
fout(" %d", Campaign.missions[m].flags & ~CMISSION_FLAG_BASTION);
@@ -1201,7 +1220,7 @@ int CFred_mission_save::save_campaign_file(char *pathname) {
//save_campaign_sexp(Links[j].sexp, Campaign.missions[Links[j].to].name);
if (Links[j].is_mission_loop) {
mission_loop = true;
} else if (Links[j].is_mission_fork) {
} else if (Links[j].is_mission_fork && (Mission_save_format != FSO_FORMAT_RETAIL)) {
mission_fork = true;
} else {
save_campaign_sexp(Links[j].sexp, Links[j].to);
@@ -1312,7 +1331,8 @@ int CFred_mission_save::save_campaign_file(char *pathname) {
return err;
}
void CFred_mission_save::save_campaign_sexp(int node, int link_num) {
void CFred_mission_save::save_campaign_sexp(int node, int link_num)
{
SCP_string sexp_out;
Assert(node >= 0);
@@ -1332,7 +1352,8 @@ void CFred_mission_save::save_campaign_sexp(int node, int link_num) {
}
}
int CFred_mission_save::save_cmd_brief() {
int CFred_mission_save::save_cmd_brief()
{
int stage;
stage = 0;
@@ -1370,7 +1391,8 @@ int CFred_mission_save::save_cmd_brief() {
return err;
}
int CFred_mission_save::save_cmd_briefs() {
int CFred_mission_save::save_cmd_briefs()
{
int i;
for (i = 0; i < Num_teams; i++) {
@@ -1381,7 +1403,8 @@ int CFred_mission_save::save_cmd_briefs() {
return err;
}
void CFred_mission_save::fso_comment_push(char *ver) {
void CFred_mission_save::fso_comment_push(char *ver)
{
if (fso_ver_comment.empty()) {
fso_ver_comment.push_back(SCP_string(ver));
return;
@@ -1414,7 +1437,8 @@ void CFred_mission_save::fso_comment_push(char *ver) {
}
}
void CFred_mission_save::fso_comment_pop(bool pop_all) {
void CFred_mission_save::fso_comment_pop(bool pop_all)
{
if (fso_ver_comment.empty()) {
return;
}
@@ -1427,7 +1451,8 @@ void CFred_mission_save::fso_comment_pop(bool pop_all) {
fso_ver_comment.pop_back();
}
int CFred_mission_save::save_common_object_data(object *objp, ship *shipp) {
int CFred_mission_save::save_common_object_data(object *objp, ship *shipp)
{
int j, z;
ship_subsys *ptr = NULL;
ship_info *sip = NULL;
@@ -1446,7 +1471,7 @@ int CFred_mission_save::save_common_object_data(object *objp, ship *shipp) {
}
// Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL && (shipp->special_hitpoints)) {
if (Mission_save_format != FSO_FORMAT_RETAIL && (shipp->special_hitpoints > 0)) {
temp_max_hull_strength = (float) shipp->special_hitpoints;
} else {
temp_max_hull_strength = sip->max_hull_strength;
@@ -1602,8 +1627,9 @@ int CFred_mission_save::save_common_object_data(object *objp, ship *shipp) {
return err;
}
void CFred_mission_save::save_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, const char *string_field_640, const char *string_field_1024, int blank_lines) {
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
void CFred_mission_save::save_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, const char *string_field_640, const char *string_field_1024, int blank_lines)
{
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if ((*string_field_640 != '\0') || (*string_field_1024 != '\0')) {
while (blank_lines-- > 0)
fout("\n");
@@ -1619,13 +1645,14 @@ void CFred_mission_save::save_custom_bitmap(const char *expected_string_640, con
}
}
int CFred_mission_save::save_cutscenes() {
int CFred_mission_save::save_cutscenes()
{
char type[NAME_LENGTH];
SCP_string sexp_out;
// Let's just assume it has them for now -
if (!(The_mission.cutscenes.empty())) {
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (optional_string_fred("#Cutscenes")) {
parse_comments(2);
} else {
@@ -1675,7 +1702,7 @@ int CFred_mission_save::save_cutscenes() {
required_string_fred("#end");
parse_comments();
} else {
MessageBox(NULL, "Warning: This mission contains cutscene data, but you are saving in the retail mission format. This information will be lost", "Incompatibility with retail mission format", MB_OK);
MessageBox(nullptr, "Warning: This mission contains cutscene data, but you are saving in the retail mission format. This information will be lost.", "Incompatibility with retail mission format", MB_OK);
}
}
@@ -1683,7 +1710,8 @@ int CFred_mission_save::save_cutscenes() {
return err;
}
int CFred_mission_save::save_debriefing() {
int CFred_mission_save::save_debriefing()
{
int j, i;
SCP_string sexp_out;
@@ -1739,7 +1767,8 @@ int CFred_mission_save::save_debriefing() {
return err;
}
int CFred_mission_save::save_events() {
int CFred_mission_save::save_events()
{
SCP_string sexp_out;
int i, j, add_flag;
@@ -1778,7 +1807,7 @@ int CFred_mission_save::save_events() {
fout(" %d", Mission_events[i].repeat_count);
}
if (Format_fs2_open != FSO_FORMAT_RETAIL && Mission_events[i].trigger_count != 1) {
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_events[i].trigger_count != 1) {
fso_comment_push(";;FSO 3.6.11;;");
if (optional_string_fred("+Trigger Count:", "$Formula:")) {
parse_comments();
@@ -1851,7 +1880,7 @@ int CFred_mission_save::save_events() {
// save flags, if any
// (the transient flags are not used in FRED, so if the flags field is nonzero they will all be meaningful flags from the flag list)
if (Format_fs2_open != FSO_FORMAT_RETAIL && Mission_events[i].flags != 0) {
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_events[i].flags != 0) {
fso_comment_push(";;FSO 20.0.0;;");
if (optional_string_fred("+Event Flags: (", "$Formula:")) {
parse_comments();
@@ -1868,7 +1897,7 @@ int CFred_mission_save::save_events() {
fout(" )");
}
if (Format_fs2_open != FSO_FORMAT_RETAIL && Mission_events[i].mission_log_flags != 0) {
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_events[i].mission_log_flags != 0) {
fso_comment_push(";;FSO 3.6.11;;");
if (optional_string_fred("+Event Log Flags: (", "$Formula:")) {
parse_comments();
@@ -1894,9 +1923,10 @@ int CFred_mission_save::save_events() {
return err;
}
int CFred_mission_save::save_fiction() {
int CFred_mission_save::save_fiction()
{
if (mission_has_fiction()) {
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (optional_string_fred("#Fiction Viewer"))
parse_comments(2);
else
@@ -1960,7 +1990,7 @@ int CFred_mission_save::save_fiction() {
optional_string_fred("$Formula:");
}
} else {
MessageBox(NULL, "Warning: This mission contains fiction viewer data, but you are saving in the retail mission format.", "Incompatibility with retail mission format", MB_OK);
MessageBox(nullptr, "Warning: This mission contains fiction viewer data, but you are saving in the retail mission format.", "Incompatibility with retail mission format", MB_OK);
}
}
@@ -1969,7 +1999,8 @@ int CFred_mission_save::save_fiction() {
return err;
}
int CFred_mission_save::save_goals() {
int CFred_mission_save::save_goals()
{
SCP_string sexp_out;
int i;
@@ -2048,14 +2079,16 @@ int CFred_mission_save::save_goals() {
return err;
}
int CFred_mission_save::save_matrix(matrix &m) {
int CFred_mission_save::save_matrix(matrix &m)
{
fout("\n\t%f, %f, %f,\n", m.vec.rvec.xyz.x, m.vec.rvec.xyz.y, m.vec.rvec.xyz.z);
fout("\t%f, %f, %f,\n", m.vec.uvec.xyz.x, m.vec.uvec.xyz.y, m.vec.uvec.xyz.z);
fout("\t%f, %f, %f", m.vec.fvec.xyz.x, m.vec.fvec.xyz.y, m.vec.fvec.xyz.z);
return 0;
}
int CFred_mission_save::save_messages() {
int CFred_mission_save::save_messages()
{
int i;
fred_parse_flag = 0;
@@ -2064,7 +2097,7 @@ int CFred_mission_save::save_messages() {
fout("\t\t;! %d total\n", Num_messages - Num_builtin_messages);
// Goober5000 - special Command info
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (stricmp(The_mission.command_sender, DEFAULT_COMMAND))
fout("\n$Command Sender: %s", The_mission.command_sender);
@@ -2130,7 +2163,8 @@ int CFred_mission_save::save_messages() {
return err;
}
int CFred_mission_save::save_mission_file(char *pathname) {
int CFred_mission_save::save_mission_file(char *pathname)
{
char backup_name[256], savepath[MAX_PATH_LEN], *p;
strcpy_s(savepath, "");
@@ -2160,7 +2194,8 @@ int CFred_mission_save::save_mission_file(char *pathname) {
return err;
}
int CFred_mission_save::save_mission_info() {
int CFred_mission_save::save_mission_info()
{
required_string_fred("#Mission Info");
parse_comments(0);
@@ -2235,7 +2270,7 @@ int CFred_mission_save::save_mission_info() {
}
// Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
// write out the nebula clipping multipliers
fout("\n+Fog Near Mult: %f\n", Neb2_fog_near_mult);
fout("\n+Fog Far Mult: %f\n", Neb2_fog_far_mult);
@@ -2261,7 +2296,7 @@ int CFred_mission_save::save_mission_info() {
fout(" %d", The_mission.num_respawns);
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
fso_comment_push(";;FSO 3.6.11;;");
if (optional_string_fred("+Max Respawn Time:"))
parse_comments(2);
@@ -2276,7 +2311,7 @@ int CFred_mission_save::save_mission_info() {
}
}
if (Format_fs2_open == FSO_FORMAT_RETAIL) {
if (Mission_save_format == FSO_FORMAT_RETAIL) {
if (optional_string_fred("+Red Alert:"))
parse_comments(2);
else
@@ -2285,7 +2320,7 @@ int CFred_mission_save::save_mission_info() {
fout(" %d", (The_mission.flags[Mission::Mission_Flags::Red_alert]) ? 1 : 0);
}
if (Format_fs2_open == FSO_FORMAT_RETAIL) //-V581
if (Mission_save_format == FSO_FORMAT_RETAIL) //-V581
{
if (optional_string_fred("+Scramble:"))
parse_comments(2);
@@ -2304,7 +2339,7 @@ int CFred_mission_save::save_mission_info() {
fout(" %d", (The_mission.support_ships.max_support_ships == 0) ? 1 : 0);
// here be WMCoolmon's hull and subsys repair stuff
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (optional_string_fred("+Hull Repair Ceiling:")) {
parse_comments(1);
} else {
@@ -2365,7 +2400,7 @@ int CFred_mission_save::save_mission_info() {
}
// Goober5000 - special wing info
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
int i;
fout("\n");
@@ -2499,7 +2534,8 @@ int CFred_mission_save::save_mission_info() {
return err;
}
void CFred_mission_save::save_mission_internal(const char *pathname) {
void CFred_mission_save::save_mission_internal(const char *pathname)
{
CTime t;
t = CTime::GetCurrentTime();
@@ -2570,7 +2606,8 @@ void CFred_mission_save::save_mission_internal(const char *pathname) {
mprintf(("Mission saving error code #%d\n", err));
}
int CFred_mission_save::save_music() {
int CFred_mission_save::save_music()
{
required_string_fred("#Music");
parse_comments(2);
@@ -2628,7 +2665,7 @@ int CFred_mission_save::save_music() {
}
fout(" %s", Mission_music[SCORE_DEBRIEF_SUCCESS] < 0 ? "None" : Spooled_music[Mission_music[SCORE_DEBRIEF_SUCCESS]].name);
}
if (Mission_music[SCORE_DEBRIEF_AVERAGE] != event_music_get_spooled_music_index("Average")) {
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_music[SCORE_DEBRIEF_AVERAGE] != event_music_get_spooled_music_index("Average")) {
if (optional_string_fred("$Debriefing Average Music:")) {
parse_comments(1);
} else {
@@ -2666,6 +2703,9 @@ int CFred_mission_save::save_music() {
int CFred_mission_save::save_warp_params(WarpDirection direction, ship *shipp)
{
if (Mission_save_format == FSO_FORMAT_RETAIL)
return err;
// for writing to file; c.f. parse_warp_params
const char *prefix = (direction == WarpDirection::WARP_IN) ? "$Warpin" : "$Warpout";
@@ -2760,7 +2800,8 @@ int CFred_mission_save::save_warp_params(WarpDirection direction, ship *shipp)
return err;
}
int CFred_mission_save::save_objects() {
int CFred_mission_save::save_objects()
{
SCP_string sexp_out;
int i, z;
ai_info *aip;
@@ -2791,7 +2832,7 @@ int CFred_mission_save::save_objects() {
fout(" %s\t\t;! Object #%d\n", shipp->ship_name, i);
// Display name
if (Format_fs2_open != FSO_FORMAT_RETAIL && shipp->has_display_name()) {
if (Mission_save_format != FSO_FORMAT_RETAIL && shipp->has_display_name()) {
// The display name is only written if there was one at the start to avoid introducing inconsistencies
fout("\n$Display name:");
fout_ext(" ", "%s", shipp->display_name.c_str());
@@ -2803,7 +2844,7 @@ int CFred_mission_save::save_objects() {
fout(" %s", Ship_info[shipp->ship_info_index].name);
//alt classes stuff
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
for (SCP_vector<alt_class>::iterator ii = shipp->s_alt_classes.begin(); ii != shipp->s_alt_classes.end(); ++ii) {
// is this a variable?
if (ii->variable_index != -1) {
@@ -2825,7 +2866,7 @@ int CFred_mission_save::save_objects() {
}
// optional callsign
if (strlen(Fred_callsigns[i])) {
if (Mission_save_format != FSO_FORMAT_RETAIL && strlen(Fred_callsigns[i])) {
fout("\n$Callsign: %s\n", Fred_callsigns[i]);
}
@@ -2833,7 +2874,7 @@ int CFred_mission_save::save_objects() {
parse_comments();
fout(" %s", Iff_info[shipp->team].iff_name);
if (Ship_info[shipp->ship_info_index].uses_team_colors) {
if (Mission_save_format != FSO_FORMAT_RETAIL && Ship_info[shipp->ship_info_index].uses_team_colors) {
required_string_fred("$Team Color Setting:");
parse_comments();
fout(" %s", shipp->team_name.c_str());
@@ -2847,7 +2888,7 @@ int CFred_mission_save::save_objects() {
parse_comments();
save_matrix(Objects[shipp->objnum].orient);
if (Format_fs2_retail) {
if (Mission_save_format == FSO_FORMAT_RETAIL) {
required_string_fred("$IFF:");
parse_comments();
fout(" %s", "IFF 1");
@@ -2904,7 +2945,7 @@ int CFred_mission_save::save_objects() {
if (z & SPECIAL_ARRIVAL_ANCHOR_FLAG) {
// get name
char tmp[NAME_LENGTH + 15];
stuff_special_arrival_anchor_name(tmp, z, Format_fs2_retail);
stuff_special_arrival_anchor_name(tmp, z, Mission_save_format == FSO_FORMAT_RETAIL ? 1 : 0);
// save it
fout(" %s", tmp);
@@ -2916,7 +2957,7 @@ int CFred_mission_save::save_objects() {
}
// Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if ((shipp->arrival_location == ARRIVE_FROM_DOCK_BAY) && (shipp->arrival_path_mask > 0)) {
int anchor_shipnum;
polymodel *pm;
@@ -2970,7 +3011,7 @@ int CFred_mission_save::save_objects() {
}
// Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if ((shipp->departure_location == DEPART_AT_DOCK_BAY) && (shipp->departure_path_mask > 0)) {
int anchor_shipnum;
polymodel *pm;
@@ -3071,12 +3112,12 @@ int CFred_mission_save::save_objects() {
if (shipp->flags[Ship::Ship_Flags::Dont_collide_invis])
fout(" \"don't-collide-invisible\"");
//for compatibility reasons ship locked or weapons locked are saved as both locked in retail mode
if ((Format_fs2_open == FSO_FORMAT_RETAIL) && ((shipp->flags[Ship::Ship_Flags::Ship_locked]) || (shipp->flags[Ship::Ship_Flags::Weapons_locked])))
if ((Mission_save_format == FSO_FORMAT_RETAIL) && ((shipp->flags[Ship::Ship_Flags::Ship_locked]) || (shipp->flags[Ship::Ship_Flags::Weapons_locked])))
fout(" \"locked\"");
fout(" )");
// flags2 added by Goober5000 --------------------------------
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (optional_string_fred("+Flags2:", "$Name:")) {
parse_comments();
fout(" (");
@@ -3150,7 +3191,7 @@ int CFred_mission_save::save_objects() {
}
// special explosions
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (shipp->use_special_explosion) {
fso_comment_push(";;FSO 3.6.13;;");
if (optional_string_fred("$Special Explosion:", "$Name:")) {
@@ -3240,7 +3281,7 @@ int CFred_mission_save::save_objects() {
}
// Goober5000 ------------------------------------------------
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (shipp->special_hitpoints) {
fso_comment_push(";;FSO 3.6.13;;");
if (optional_string_fred("+Special Hitpoints:", "$Name:")) {
@@ -3295,7 +3336,7 @@ int CFred_mission_save::save_objects() {
// Goober5000 - newer code to save off information about initially docked ships. ;)
if (object_is_docked(&Objects[shipp->objnum])) {
// possible incompatibility
if (Format_fs2_open == FSO_FORMAT_RETAIL && !dock_check_docked_one_on_one(&Objects[shipp->objnum])) {
if (Mission_save_format == FSO_FORMAT_RETAIL && !dock_check_docked_one_on_one(&Objects[shipp->objnum])) {
static bool warned = false;
if (!warned) {
CString text = "You are saving in the retail mission format, but \"";
@@ -3324,7 +3365,7 @@ int CFred_mission_save::save_objects() {
}
}
// check the ship flag about killing off the ship before a missino starts. Write out the appropriate
// check the ship flag about killing off the ship before a mission starts. Write out the appropriate
// variable if necessary
if (shipp->flags[Ship::Ship_Flags::Kill_before_mission]) {
if (optional_string_fred("+Destroy At:", "$Name:"))
@@ -3377,7 +3418,7 @@ int CFred_mission_save::save_objects() {
fout(" %d", shipp->score);
if (Format_fs2_open != FSO_FORMAT_RETAIL && shipp->assist_score_pct != 0) {
if (Mission_save_format != FSO_FORMAT_RETAIL && shipp->assist_score_pct != 0) {
fso_comment_push(";;FSO 3.6.10;;");
if (optional_string_fred("+Assist Score Percentage:")) {
parse_comments();
@@ -3452,7 +3493,9 @@ int CFred_mission_save::save_objects() {
return err;
}
int CFred_mission_save::save_players() {
int CFred_mission_save::save_players()
{
bool wrote_fso_data = false;
int i, j;
int var_idx;
int used_pool[MAX_WEAPON_TYPES];
@@ -3478,7 +3521,7 @@ int CFred_mission_save::save_players() {
}
// write out callsign list
if (Mission_callsign_count > 0) {
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_callsign_count > 0) {
fout("\n\n#Callsigns:\n");
// write them all out
@@ -3509,6 +3552,7 @@ int CFred_mission_save::save_players() {
if (strlen(Team_data[i].ship_list_variables[j])) {
var_idx = get_index_sexp_variable_name(Team_data[i].ship_list_variables[j]);
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
wrote_fso_data = true;
fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
} else {
@@ -3519,6 +3563,7 @@ int CFred_mission_save::save_players() {
if (strlen(Team_data[i].ship_count_variables[j])) {
var_idx = get_index_sexp_variable_name(Team_data[i].ship_count_variables[j]);
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
wrote_fso_data = true;
fout("@%s\n", Sexp_variables[var_idx].variable_name);
} else {
@@ -3536,11 +3581,13 @@ int CFred_mission_save::save_players() {
fout(" (\n");
generate_weaponry_usage_list(i, used_pool);
for (j = 0; j < Team_data[i].num_weapon_choices; j++) {
// first output the weapon name or a variable that sets it
if (strlen(Team_data[i].weaponry_pool_variable[j])) {
var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_pool_variable[j]);
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
wrote_fso_data = true;
fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
} else {
@@ -3553,6 +3600,7 @@ int CFred_mission_save::save_players() {
if (strlen(Team_data[i].weaponry_amount_variable[j])) {
var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_amount_variable[j]);
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
wrote_fso_data = true;
fout("@%s\n", Sexp_variables[var_idx].variable_name);
} else {
@@ -3574,6 +3622,12 @@ int CFred_mission_save::save_players() {
fout(")");
// sanity check
if (Mission_save_format == FSO_FORMAT_RETAIL && wrote_fso_data) {
// this is such an unlikely (and hard-to-fix) case that a warning should be sufficient
MessageBox(nullptr, "Warning: This mission contains variable-based team loadout information, but you are saving in the retail mission format. Retail FRED and FS2 will not be able to read this information.", "Incompatibility with retail mission format", MB_OK);
}
// Goober5000 - mjn.mixael's required weapon feature
bool uses_required_weapon = false;
for (j = 0; j < weapon_info_size(); j++) {
@@ -3582,7 +3636,7 @@ int CFred_mission_save::save_players() {
break;
}
}
if (uses_required_weapon) {
if (Mission_save_format != FSO_FORMAT_RETAIL && uses_required_weapon) {
if (optional_string_fred("+Required for mission:", "$Starting Shipname:"))
parse_comments(2);
else
@@ -3604,8 +3658,9 @@ int CFred_mission_save::save_players() {
return err;
}
int CFred_mission_save::save_plot_info() {
if (Format_fs2_open == FSO_FORMAT_RETAIL) {
int CFred_mission_save::save_plot_info()
{
if (Mission_save_format == FSO_FORMAT_RETAIL) {
if (optional_string_fred("#Plot Info")) {
parse_comments(2);
@@ -3654,7 +3709,8 @@ int CFred_mission_save::save_plot_info() {
return err;
}
int CFred_mission_save::save_reinforcements() {
int CFred_mission_save::save_reinforcements()
{
int i, j, type;
fred_parse_flag = 0;
@@ -3720,7 +3776,8 @@ int CFred_mission_save::save_reinforcements() {
return err;
}
void CFred_mission_save::save_single_dock_instance(ship *shipp, dock_instance *dock_ptr) {
void CFred_mission_save::save_single_dock_instance(ship *shipp, dock_instance *dock_ptr)
{
Assert(shipp && dock_ptr);
Assert(dock_ptr->docked_objp->type == OBJ_SHIP || dock_ptr->docked_objp->type == OBJ_START);
@@ -3752,7 +3809,8 @@ void CFred_mission_save::save_single_dock_instance(ship *shipp, dock_instance *d
fso_comment_pop(true);
}
void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship) {
void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship)
{
int i, z;
ship_weapon *wp = &ptr->weapons;
@@ -3833,7 +3891,8 @@ void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship) {
fso_comment_pop(true);
}
int CFred_mission_save::save_variables() {
int CFred_mission_save::save_variables()
{
char *type;
char number[] = "number";
char string[] = "string";
@@ -3847,7 +3906,7 @@ int CFred_mission_save::save_variables() {
// get count
int num_variables = sexp_variable_count();
if (Format_fs2_open == FSO_FORMAT_RETAIL) {
if (Mission_save_format == FSO_FORMAT_RETAIL) {
generate_special_explosion_block_variables();
num_block_vars = num_block_variables();
}
@@ -3875,7 +3934,7 @@ int CFred_mission_save::save_variables() {
fout("\n\t\t%d\t\t\"%s\"\t\t\"%s\"\t\t\"%s\"", i, Sexp_variables[i].variable_name, Sexp_variables[i].text, type);
// persistent and network variables
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
// Network variable - Karajorma
if (Sexp_variables[i].type & SEXP_VARIABLE_NETWORK) {
fout("\t\t\"%s\"", "network-variable");
@@ -3912,12 +3971,14 @@ int CFred_mission_save::save_variables() {
return err;
}
int CFred_mission_save::save_vector(vec3d &v) {
int CFred_mission_save::save_vector(vec3d &v)
{
fout(" %f, %f, %f", v.xyz.x, v.xyz.y, v.xyz.z);
return 0;
}
int CFred_mission_save::save_waypoints() {
int CFred_mission_save::save_waypoints()
{
//object *ptr;
fred_parse_flag = 0;
@@ -3935,39 +3996,41 @@ int CFred_mission_save::save_waypoints() {
parse_comments();
fout(" %s", jnp->GetName());
if (jnp->IsSpecialModel()) {
if (optional_string_fred("+Model File:", "$Jump Node:"))
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (jnp->IsSpecialModel()) {
if (optional_string_fred("+Model File:", "$Jump Node:"))
parse_comments();
else
fout("\n+Model File:");
int model = jnp->GetModelNumber();
polymodel *pm = model_get(model);
fout(" %s", pm->filename);
}
if (jnp->IsColored()) {
if (optional_string_fred("+Alphacolor:", "$Jump Node:"))
parse_comments();
else
fout("\n+Alphacolor:");
color jn_color = jnp->GetColor();
fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha);
}
int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
if (hidden_is_there)
parse_comments();
else
fout("\n+Model File:");
int model = jnp->GetModelNumber();
polymodel *pm = model_get(model);
fout(" %s", pm->filename);
}
if (hidden_is_there || jnp->IsHidden()) {
if (!hidden_is_there)
fout("\n+Hidden:");
if (jnp->IsColored()) {
if (optional_string_fred("+Alphacolor:", "$Jump Node:"))
parse_comments();
else
fout("\n+Alphacolor:");
color jn_color = jnp->GetColor();
fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha);
}
int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
if (hidden_is_there)
parse_comments();
if (hidden_is_there || jnp->IsHidden()) {
if (!hidden_is_there)
fout("\n+Hidden:");
if (jnp->IsHidden())
fout(" %s", "true");
else
fout(" %s", "false");
if (jnp->IsHidden())
fout(" %s", "true");
else
fout(" %s", "false");
}
}
fso_comment_pop();
@@ -3995,7 +4058,8 @@ int CFred_mission_save::save_waypoints() {
return err;
}
int CFred_mission_save::save_waypoint_list(waypoint_list *wp_list) {
int CFred_mission_save::save_waypoint_list(waypoint_list *wp_list)
{
Assert(wp_list != NULL);
SCP_vector<waypoint>::iterator ii;
@@ -4007,7 +4071,8 @@ int CFred_mission_save::save_waypoint_list(waypoint_list *wp_list) {
return 0;
}
int CFred_mission_save::save_wings() {
int CFred_mission_save::save_wings()
{
SCP_string sexp_out;
int i, j, z, ship, count = 0;
@@ -4027,7 +4092,7 @@ int CFred_mission_save::save_wings() {
fout(" %s", Wings[i].name);
// squad logo - Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if (strlen(Wings[i].wing_squad_filename) > 0) //-V805
{
if (optional_string_fred("+Squad Logo:", "$Name:"))
@@ -4072,7 +4137,7 @@ int CFred_mission_save::save_wings() {
if (z & SPECIAL_ARRIVAL_ANCHOR_FLAG) {
// get name
char tmp[NAME_LENGTH + 15];
stuff_special_arrival_anchor_name(tmp, z, Format_fs2_retail);
stuff_special_arrival_anchor_name(tmp, z, Mission_save_format == FSO_FORMAT_RETAIL ? 1 : 0);
// save it
fout(" %s", tmp);
@@ -4084,7 +4149,7 @@ int CFred_mission_save::save_wings() {
}
// Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if ((Wings[i].arrival_location == ARRIVE_FROM_DOCK_BAY) && (Wings[i].arrival_path_mask > 0)) {
int anchor_shipnum;
polymodel *pm;
@@ -4134,7 +4199,7 @@ int CFred_mission_save::save_wings() {
}
// Goober5000
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
if (Mission_save_format != FSO_FORMAT_RETAIL) {
if ((Wings[i].departure_location == DEPART_AT_DOCK_BAY) && (Wings[i].departure_path_mask > 0)) {
int anchor_shipnum;
polymodel *pm;
+2 -4
View File
@@ -1195,8 +1195,7 @@ void ShipGoalsDlg::OnSelchangeObject10()
void ShipGoalsDlg::set_object(int item)
{
char *str;
int i = 0, z, num, not_used;
int i = 0, z, num;
ship_subsys *subsys;
if (m_behavior[item] > 0) {
@@ -1213,8 +1212,7 @@ void ShipGoalsDlg::set_object(int item)
for (i=0; i<num; i++) {
Assert(Docking_bay_list[i]);
z = m_dock2_box[item] -> AddString(Docking_bay_list[i]);
str = ai_get_goal_target_name(Docking_bay_list[i], &not_used);
m_dock2_box[item] -> SetItemDataPtr(z, str);
m_dock2_box[item] -> SetItemDataPtr(z, Docking_bay_list[i]);
}
} else if ((mode == AI_GOAL_DESTROY_SUBSYSTEM) && (m_data[item] >= 0)) {
+1
View File
@@ -1902,6 +1902,7 @@ void game_init()
// Do this before the initial scripting hook runs in case that hook does something with the UI
scpui::initialize();
Script_system.RunInitFunctions();
Script_system.RunCondition(CHA_GAMEINIT);
game_title_screen_close();
+6 -5
View File
@@ -95,13 +95,13 @@ initialize(const std::string& cfilepath, int argc, char* argv[], Editor* editor,
mod_table_init();
listener(SubSystem::Locale);
// initialize localization module. Make sure this is done AFTER initialzing OS.
// NOTE : Fred should ALWAYS run in English. Otherwise it might swap in another language
// initialize localization module. Make sure this is done AFTER initializing OS.
// NOTE: Fred should ALWAYS run without localization. Otherwise it might swap in another language
// when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
// trust me on this :)
lcl_init(FS2_OPEN_DEFAULT_LANGUAGE);
lcl_init(LCL_UNTRANSLATED);
// Goober5000 - force init XSTRs (so they work, but only work in English, based on above comment)
// Goober5000 - force init XSTRs (so they work, but only work untranslated, based on above comment)
Xstr_inited = 1;
#ifndef NDEBUG
@@ -254,8 +254,9 @@ initialize(const std::string& cfilepath, int argc, char* argv[], Editor* editor,
control_config_common_init();
listener(SubSystem::ScriptingInitHook);
Script_system.RunInitFunctions();
Script_system.RunCondition(CHA_GAMEINIT);
return true;
}
+150 -81
View File
@@ -38,7 +38,8 @@
namespace fso {
namespace fred {
int CFred_mission_save::autosave_mission_file(char* pathname) {
int CFred_mission_save::autosave_mission_file(char* pathname)
{
char backup_name[256], name2[256];
int i;
@@ -60,7 +61,8 @@ int CFred_mission_save::autosave_mission_file(char* pathname) {
return err;
}
void CFred_mission_save::bypass_comment(const char* comment, const char* end) {
void CFred_mission_save::bypass_comment(const char* comment, const char* end)
{
char* ch = strstr(raw_ptr, comment);
if (ch != NULL) {
if (end != NULL) {
@@ -84,17 +86,20 @@ void CFred_mission_save::bypass_comment(const char* comment, const char* end) {
}
}
void CFred_mission_save::convert_special_tags_to_retail(char* text, int max_len) {
void CFred_mission_save::convert_special_tags_to_retail(char* text, int max_len)
{
replace_all(text, "$quote", "''", max_len);
replace_all(text, "$semicolon", ",", max_len);
}
void CFred_mission_save::convert_special_tags_to_retail(SCP_string& text) {
void CFred_mission_save::convert_special_tags_to_retail(SCP_string& text)
{
replace_all(text, "$quote", "''");
replace_all(text, "$semicolon", ",");
}
void CFred_mission_save::convert_special_tags_to_retail() {
void CFred_mission_save::convert_special_tags_to_retail()
{
int i, team, stage;
if (save_format != MissionFormat::RETAIL) {
@@ -123,7 +128,8 @@ void CFred_mission_save::convert_special_tags_to_retail() {
}
}
int CFred_mission_save::fout(const char* format, ...) {
int CFred_mission_save::fout(const char* format, ...)
{
SCP_string str;
va_list args;
@@ -139,7 +145,8 @@ int CFred_mission_save::fout(const char* format, ...) {
return 0;
}
int CFred_mission_save::fout_ext(const char* pre_str, const char* format, ...) {
int CFred_mission_save::fout_ext(const char* pre_str, const char* format, ...)
{
SCP_string str_scp;
SCP_string str_out_scp;
va_list args;
@@ -241,7 +248,8 @@ int CFred_mission_save::fout_ext(const char* pre_str, const char* format, ...) {
return 0;
}
int CFred_mission_save::fout_version(const char* format, ...) {
int CFred_mission_save::fout_version(const char* format, ...)
{
SCP_string str_scp;
char* ch = NULL;
va_list args;
@@ -250,6 +258,11 @@ int CFred_mission_save::fout_version(const char* format, ...) {
return err;
}
// if we're saving in retail format, we can completely skip this
if (save_format == MissionFormat::RETAIL) {
return 0;
}
// output the version first thing, but skip the special case where we use
// fout_version() for multiline value strings (typically indicated by an initial space)
if ((save_format == MissionFormat::COMPATIBILITY_MODE) && (*format != ' ') && !fso_ver_comment.empty()) {
@@ -330,7 +343,8 @@ int CFred_mission_save::fout_version(const char* format, ...) {
return 0;
}
void CFred_mission_save::parse_comments(int newlines) {
void CFred_mission_save::parse_comments(int newlines)
{
char* comment_start = NULL;
int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0;
bool version_added = false;
@@ -507,7 +521,8 @@ void CFred_mission_save::parse_comments(int newlines) {
return;
}
void CFred_mission_save::save_ai_goals(ai_goal* goalp, int ship) {
void CFred_mission_save::save_ai_goals(ai_goal* goalp, int ship)
{
const char* str = NULL;
char buf[80];
int i, valid, flag = 1;
@@ -668,7 +683,8 @@ void CFred_mission_save::save_ai_goals(ai_goal* goalp, int ship) {
fso_comment_pop(true);
}
int CFred_mission_save::save_asteroid_fields() {
int CFred_mission_save::save_asteroid_fields()
{
int i;
fred_parse_flag = 0;
@@ -763,7 +779,8 @@ int CFred_mission_save::save_asteroid_fields() {
return err;
}
int CFred_mission_save::save_bitmaps() {
int CFred_mission_save::save_bitmaps()
{
int i;
uint j;
@@ -909,7 +926,8 @@ int CFred_mission_save::save_bitmaps() {
return err;
}
int CFred_mission_save::save_briefing() {
int CFred_mission_save::save_briefing()
{
int i, j, k, nb;
SCP_string sexp_out;
brief_stage* bs;
@@ -1097,7 +1115,8 @@ int CFred_mission_save::save_briefing() {
return err;
}
int CFred_mission_save::save_cmd_brief() {
int CFred_mission_save::save_cmd_brief()
{
int stage;
stage = 0;
@@ -1140,7 +1159,8 @@ int CFred_mission_save::save_cmd_brief() {
return err;
}
int CFred_mission_save::save_cmd_briefs() {
int CFred_mission_save::save_cmd_briefs()
{
int i;
for (i = 0; i < Num_teams; i++) {
@@ -1151,7 +1171,8 @@ int CFred_mission_save::save_cmd_briefs() {
return err;
}
void CFred_mission_save::fso_comment_push(const char* ver) {
void CFred_mission_save::fso_comment_push(const char* ver)
{
if (fso_ver_comment.empty()) {
fso_ver_comment.push_back(SCP_string(ver));
return;
@@ -1186,7 +1207,8 @@ void CFred_mission_save::fso_comment_push(const char* ver) {
}
}
void CFred_mission_save::fso_comment_pop(bool pop_all) {
void CFred_mission_save::fso_comment_pop(bool pop_all)
{
if (fso_ver_comment.empty()) {
return;
}
@@ -1199,7 +1221,8 @@ void CFred_mission_save::fso_comment_pop(bool pop_all) {
fso_ver_comment.pop_back();
}
int CFred_mission_save::save_common_object_data(object* objp, ship* shipp) {
int CFred_mission_save::save_common_object_data(object* objp, ship* shipp)
{
int j, z;
ship_subsys* ptr = NULL;
ship_info* sip = NULL;
@@ -1219,7 +1242,7 @@ int CFred_mission_save::save_common_object_data(object* objp, ship* shipp) {
}
// Goober5000
if (save_format != MissionFormat::RETAIL && (shipp->special_hitpoints)) {
if (save_format != MissionFormat::RETAIL && (shipp->special_hitpoints > 0)) {
temp_max_hull_strength = (float) shipp->special_hitpoints;
} else {
temp_max_hull_strength = sip->max_hull_strength;
@@ -1396,7 +1419,8 @@ void CFred_mission_save::save_custom_bitmap(const char* expected_string_640,
const char* expected_string_1024,
const char* string_field_640,
const char* string_field_1024,
int blank_lines) {
int blank_lines)
{
if (save_format != MissionFormat::RETAIL) {
if ((*string_field_640 != '\0') || (*string_field_1024 != '\0')) {
while (blank_lines-- > 0) {
@@ -1414,7 +1438,8 @@ void CFred_mission_save::save_custom_bitmap(const char* expected_string_640,
}
}
int CFred_mission_save::save_cutscenes() {
int CFred_mission_save::save_cutscenes()
{
char type[NAME_LENGTH];
SCP_string sexp_out;
@@ -1481,7 +1506,8 @@ int CFred_mission_save::save_cutscenes() {
return err;
}
int CFred_mission_save::save_debriefing() {
int CFred_mission_save::save_debriefing()
{
int j, i;
SCP_string sexp_out;
@@ -1541,7 +1567,8 @@ int CFred_mission_save::save_debriefing() {
return err;
}
int CFred_mission_save::save_events() {
int CFred_mission_save::save_events()
{
SCP_string sexp_out;
int i, j, add_flag;
@@ -1696,7 +1723,8 @@ int CFred_mission_save::save_events() {
return err;
}
int CFred_mission_save::save_fiction() {
int CFred_mission_save::save_fiction()
{
if (mission_has_fiction()) {
if (save_format != MissionFormat::RETAIL) {
if (optional_string_fred("#Fiction Viewer")) {
@@ -1787,7 +1815,8 @@ int CFred_mission_save::save_fiction() {
return err;
}
int CFred_mission_save::save_goals() {
int CFred_mission_save::save_goals()
{
SCP_string sexp_out;
int i;
@@ -1871,14 +1900,16 @@ int CFred_mission_save::save_goals() {
return err;
}
int CFred_mission_save::save_matrix(matrix& m) {
int CFred_mission_save::save_matrix(matrix& m)
{
fout("\n\t%f, %f, %f,\n", m.vec.rvec.xyz.x, m.vec.rvec.xyz.y, m.vec.rvec.xyz.z);
fout("\t%f, %f, %f,\n", m.vec.uvec.xyz.x, m.vec.uvec.xyz.y, m.vec.uvec.xyz.z);
fout("\t%f, %f, %f", m.vec.fvec.xyz.x, m.vec.fvec.xyz.y, m.vec.fvec.xyz.z);
return 0;
}
int CFred_mission_save::save_messages() {
int CFred_mission_save::save_messages()
{
int i;
fred_parse_flag = 0;
@@ -1958,7 +1989,8 @@ int CFred_mission_save::save_messages() {
return err;
}
int CFred_mission_save::save_mission_file(const char* pathname_in) {
int CFred_mission_save::save_mission_file(const char* pathname_in)
{
char backup_name[256], savepath[MAX_PATH_LEN], pathname[MAX_PATH_LEN];
strcpy_s(pathname, pathname_in);
@@ -1990,7 +2022,8 @@ int CFred_mission_save::save_mission_file(const char* pathname_in) {
return err;
}
int CFred_mission_save::save_mission_info() {
int CFred_mission_save::save_mission_info()
{
required_string_fred("#Mission Info");
parse_comments(0);
@@ -2343,7 +2376,8 @@ int CFred_mission_save::save_mission_info() {
return err;
}
void CFred_mission_save::save_mission_internal(const char* pathname) {
void CFred_mission_save::save_mission_internal(const char* pathname)
{
time_t rawtime;
time(&rawtime);
@@ -2416,7 +2450,8 @@ void CFred_mission_save::save_mission_internal(const char* pathname) {
mprintf(("Mission saving error code #%d\n", err));
}
int CFred_mission_save::save_music() {
int CFred_mission_save::save_music()
{
required_string_fred("#Music");
parse_comments(2);
@@ -2478,7 +2513,7 @@ int CFred_mission_save::save_music() {
Mission_music[SCORE_DEBRIEF_SUCCESS] < 0 ? "None"
: Spooled_music[Mission_music[SCORE_DEBRIEF_SUCCESS]].name);
}
if (Mission_music[SCORE_DEBRIEF_AVERAGE] != event_music_get_spooled_music_index("Average")) {
if (save_format != MissionFormat::RETAIL && Mission_music[SCORE_DEBRIEF_AVERAGE] != event_music_get_spooled_music_index("Average")) {
if (optional_string_fred("$Debriefing Average Music:")) {
parse_comments(1);
} else {
@@ -2519,6 +2554,9 @@ int CFred_mission_save::save_music() {
int CFred_mission_save::save_warp_params(WarpDirection direction, ship *shipp)
{
if (save_format == MissionFormat::RETAIL)
return err;
// for writing to file; c.f. parse_warp_params
const char *prefix = (direction == WarpDirection::WARP_IN) ? "$Warpin" : "$Warpout";
@@ -2613,7 +2651,8 @@ int CFred_mission_save::save_warp_params(WarpDirection direction, ship *shipp)
return err;
}
int CFred_mission_save::save_objects() {
int CFred_mission_save::save_objects()
{
SCP_string sexp_out;
int i, z;
ai_info* aip;
@@ -2677,7 +2716,7 @@ int CFred_mission_save::save_objects() {
}
// optional callsign
if (strlen(Fred_callsigns[i])) {
if (save_format != MissionFormat::RETAIL && strlen(Fred_callsigns[i])) {
fout("\n$Callsign: %s\n", Fred_callsigns[i]);
}
@@ -2685,7 +2724,7 @@ int CFred_mission_save::save_objects() {
parse_comments();
fout(" %s", Iff_info[shipp->team].iff_name);
if (Ship_info[shipp->ship_info_index].uses_team_colors) {
if (save_format != MissionFormat::RETAIL && Ship_info[shipp->ship_info_index].uses_team_colors) {
required_string_fred("$Team Color Setting:");
parse_comments();
fout(" %s", shipp->team_name.c_str());
@@ -3241,7 +3280,7 @@ int CFred_mission_save::save_objects() {
}
}
// check the ship flag about killing off the ship before a missino starts. Write out the appropriate
// check the ship flag about killing off the ship before a mission starts. Write out the appropriate
// variable if necessary
if (shipp->flags[Ship::Ship_Flags::Kill_before_mission]) {
if (optional_string_fred("+Destroy At:", "$Name:")) {
@@ -3375,7 +3414,9 @@ int CFred_mission_save::save_objects() {
return err;
}
int CFred_mission_save::save_players() {
int CFred_mission_save::save_players()
{
bool wrote_fso_data = false;
int i, j;
int var_idx;
int used_pool[MAX_WEAPON_TYPES];
@@ -3401,7 +3442,7 @@ int CFred_mission_save::save_players() {
}
// write out callsign list
if (Mission_callsign_count > 0) {
if (save_format != MissionFormat::RETAIL && Mission_callsign_count > 0) {
fout("\n\n#Callsigns:\n");
// write them all out
@@ -3432,6 +3473,7 @@ int CFred_mission_save::save_players() {
if (strlen(Team_data[i].ship_list_variables[j])) {
var_idx = get_index_sexp_variable_name(Team_data[i].ship_list_variables[j]);
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
wrote_fso_data = true;
fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
} else {
@@ -3442,6 +3484,7 @@ int CFred_mission_save::save_players() {
if (strlen(Team_data[i].ship_count_variables[j])) {
var_idx = get_index_sexp_variable_name(Team_data[i].ship_count_variables[j]);
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
wrote_fso_data = true;
fout("@%s\n", Sexp_variables[var_idx].variable_name);
} else {
@@ -3459,11 +3502,13 @@ int CFred_mission_save::save_players() {
fout(" (\n");
generate_weaponry_usage_list_team(i, used_pool);
for (j = 0; j < Team_data[i].num_weapon_choices; j++) {
// first output the weapon name or a variable that sets it
if (strlen(Team_data[i].weaponry_pool_variable[j])) {
var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_pool_variable[j]);
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
wrote_fso_data = true;
fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
} else {
@@ -3476,6 +3521,7 @@ int CFred_mission_save::save_players() {
if (strlen(Team_data[i].weaponry_amount_variable[j])) {
var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_amount_variable[j]);
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
wrote_fso_data = true;
fout("@%s\n", Sexp_variables[var_idx].variable_name);
} else {
@@ -3497,6 +3543,15 @@ int CFred_mission_save::save_players() {
fout(")");
// sanity check
if (save_format == MissionFormat::RETAIL && wrote_fso_data) {
// this is such an unlikely (and hard-to-fix) case that a warning should be sufficient
_viewport->dialogProvider->showButtonDialog(DialogType::Warning,
"Incompatibility with retail mission format",
"Warning: This mission contains variable-based team loadout information, but you are saving in the retail mission format. Retail FRED and FS2 will not be able to read this information.",
{ DialogButton::Ok });
}
// Goober5000 - mjn.mixael's required weapon feature
bool uses_required_weapon = false;
for (j = 0; j < static_cast<int>(Weapon_info.size()); j++) {
@@ -3505,7 +3560,7 @@ int CFred_mission_save::save_players() {
break;
}
}
if (uses_required_weapon) {
if (save_format != MissionFormat::RETAIL && uses_required_weapon) {
if (optional_string_fred("+Required for mission:", "$Starting Shipname:")) {
parse_comments(2);
} else {
@@ -3529,7 +3584,8 @@ int CFred_mission_save::save_players() {
return err;
}
int CFred_mission_save::save_plot_info() {
int CFred_mission_save::save_plot_info()
{
if (save_format == MissionFormat::RETAIL) {
if (optional_string_fred("#Plot Info")) {
parse_comments(2);
@@ -3579,7 +3635,8 @@ int CFred_mission_save::save_plot_info() {
return err;
}
int CFred_mission_save::save_reinforcements() {
int CFred_mission_save::save_reinforcements()
{
int i, j, type;
fred_parse_flag = 0;
@@ -3652,7 +3709,8 @@ int CFred_mission_save::save_reinforcements() {
return err;
}
void CFred_mission_save::save_single_dock_instance(ship* shipp, dock_instance* dock_ptr) {
void CFred_mission_save::save_single_dock_instance(ship* shipp, dock_instance* dock_ptr)
{
Assert(shipp && dock_ptr);
Assert(dock_ptr->docked_objp->type == OBJ_SHIP || dock_ptr->docked_objp->type == OBJ_START);
@@ -3689,7 +3747,8 @@ void CFred_mission_save::save_single_dock_instance(ship* shipp, dock_instance* d
fso_comment_pop(true);
}
void CFred_mission_save::save_turret_info(ship_subsys* ptr, int ship) {
void CFred_mission_save::save_turret_info(ship_subsys* ptr, int ship)
{
int i, z;
ship_weapon* wp = &ptr->weapons;
@@ -3781,7 +3840,8 @@ void CFred_mission_save::save_turret_info(ship_subsys* ptr, int ship) {
fso_comment_pop(true);
}
int CFred_mission_save::save_variables() {
int CFred_mission_save::save_variables()
{
char* type;
char number[] = "number";
char string[] = "string";
@@ -3864,12 +3924,14 @@ int CFred_mission_save::save_variables() {
return err;
}
int CFred_mission_save::save_vector(vec3d& v) {
int CFred_mission_save::save_vector(vec3d& v)
{
fout(" %f, %f, %f", v.xyz.x, v.xyz.y, v.xyz.z);
return 0;
}
int CFred_mission_save::save_waypoints() {
int CFred_mission_save::save_waypoints()
{
//object *ptr;
fred_parse_flag = 0;
@@ -3887,43 +3949,48 @@ int CFred_mission_save::save_waypoints() {
parse_comments();
fout(" %s", jnp->GetName());
if (jnp->IsSpecialModel()) {
if (optional_string_fred("+Model File:", "$Jump Node:")) {
if (save_format != MissionFormat::RETAIL) {
if (jnp->IsSpecialModel()) {
if (optional_string_fred("+Model File:", "$Jump Node:")) {
parse_comments();
}
else {
fout("\n+Model File:");
}
int model = jnp->GetModelNumber();
polymodel* pm = model_get(model);
fout(" %s", pm->filename);
}
if (jnp->IsColored()) {
if (optional_string_fred("+Alphacolor:", "$Jump Node:")) {
parse_comments();
}
else {
fout("\n+Alphacolor:");
}
color jn_color = jnp->GetColor();
fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha);
}
int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
if (hidden_is_there) {
parse_comments();
} else {
fout("\n+Model File:");
}
int model = jnp->GetModelNumber();
polymodel* pm = model_get(model);
fout(" %s", pm->filename);
}
if (hidden_is_there || jnp->IsHidden()) {
if (!hidden_is_there) {
fout("\n+Hidden:");
}
if (jnp->IsColored()) {
if (optional_string_fred("+Alphacolor:", "$Jump Node:")) {
parse_comments();
} else {
fout("\n+Alphacolor:");
}
color jn_color = jnp->GetColor();
fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha);
}
int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
if (hidden_is_there) {
parse_comments();
}
if (hidden_is_there || jnp->IsHidden()) {
if (!hidden_is_there) {
fout("\n+Hidden:");
}
if (jnp->IsHidden()) {
fout(" %s", "true");
} else {
fout(" %s", "false");
if (jnp->IsHidden()) {
fout(" %s", "true");
}
else {
fout(" %s", "false");
}
}
}
@@ -3952,7 +4019,8 @@ int CFred_mission_save::save_waypoints() {
return err;
}
int CFred_mission_save::save_waypoint_list(waypoint_list* wp_list) {
int CFred_mission_save::save_waypoint_list(waypoint_list* wp_list)
{
Assert(wp_list != NULL);
SCP_vector<waypoint>::iterator ii;
@@ -3964,7 +4032,8 @@ int CFred_mission_save::save_waypoint_list(waypoint_list* wp_list) {
return 0;
}
int CFred_mission_save::save_wings() {
int CFred_mission_save::save_wings()
{
SCP_string sexp_out;
int i, j, z, count = 0;
+6 -6
View File
@@ -29,20 +29,20 @@ class IntelParseTest : public test::FSTestFixture {
// Commonly used expected intel data entries.
static intel_data expected_foo = {
"Foo name", // XSTR id 3000
"Foo desc", // XSTR id 3001
"Foo name default", // XSTR id 3000
"Foo desc default", // XSTR id 3001
"Foo anim",
IIF_IN_TECH_DATABASE | IIF_DEFAULT_IN_TECH_DATABASE
};
static intel_data expected_bar = {
"Bar name", // XSTR id 3002
"Bar desc", // XSTR id 3003
"Bar name default", // XSTR id 3002
"Bar desc default", // XSTR id 3003
"Bar anim",
0
};
static intel_data expected_baz = {
"Baz name", // XSTR id 3004
"Baz desc", // XSTR id 3005
"Baz name default", // XSTR id 3004
"Baz desc default", // XSTR id 3005
"Baz anim",
IIF_IN_TECH_DATABASE | IIF_DEFAULT_IN_TECH_DATABASE
};