Compare commits

...
Author SHA1 Message Date
Goober5000 19ca467c41 Merge pull request #4513 from EatThePath/cleanup-eval-stack
Clean up the lua stack after EvalStringWithReturn's function creation
2022-07-27 23:29:52 -04:00
David Gibson 73736280f9 Clean up the lua stack after EvalStringWithReturn's function creation 2022-07-27 19:18:26 -05:00
Goober5000 d5546f95e8 Merge pull request #4510 from Goober5000/warn_about_too_many_sexps
prevent too many dynamic SEXPs from breaking the SEXP system
2022-07-24 17:10:10 -04:00
Goober5000 29edfeedea display category name 2022-07-24 15:08:49 -04:00
Goober5000 8cf20b0175 prevent too many dynamic SEXPs from breaking the SEXP system
SEXP categories will not support more than 256 SEXPs; this is why the CHANGE2 category had to be created when CHANGE got full.  Unfortunately, dynamic SEXPs will count toward this limit, so mysterious errors will occur for the ones whose category indexes roll over.  The problem is not easily fixable, but at least the engine can detect the situation, warn about it, and refrain from adding any problematic SEXPs.

Since too many *total* SEXPs would cause a similar problem, this adds a warning for that case too.
2022-07-24 15:06:13 -04:00
Goober5000 c85040efc4 prevent a crash on game exit (#4507)
When `mission_campaign_clear()` is called, all persistent SEXP nodes (those used by the campaign) are marked as non-persistent.  When `sexp_shutdown()` was moved in #4503, this caused `Sexp_nodes` to be nullptr when the campaign was cleared, which caused a crash.  This reorders the shutdown functions to prevent the crash.
2022-07-22 20:39:20 -05:00
MageKing17 a16be863f3 Actually grab the object's type when doing type checks. (#4505)
PR #4500 added some checks, but accidentally compared `aip->target_objnum` to an object type (although the assertion message correctly retrieved the object type, leading to the bizarre assertion that `OBJ_SHIP`, i.e. 1, was an invalid target object type). This commit makes them all access the `Objects[]` array and retrieve the `.type`, just as the assertion message does (and the pre-#4500 conditional did).

Related to #4504, in that it's the source of the failed assertions, although I can't be sure it was the cause of the crashes without testing.
2022-07-20 21:08:40 -04:00
BMagnu de80c254ae Ensure that SEXP shudown is before script shutdown (#4503)
* Ensure that SEXP shudown is before script shutdown

* Move before scpui destroy call
2022-07-19 18:56:42 -05:00
Allen A. Babb d703fcacc3 Move processing of thrust keys to be within the same block as the throttle keys (#4501)
* Remove do_thrust_keys() function
* Clean up documentation of Viewer_mode flags
2022-07-19 18:55:55 -05:00
Goober5000 ddef6ea377 Merge pull request #4498 from Baezon/clear-goals-fix
Bugfix for not present ships in `clear-goals`
2022-07-19 09:57:48 -04:00
BMagnu 2b2333f692 Ensure sufficient stack size for Lua values (#4482)
* Ensure sufficient stack size

* Fix Warning

* Fix formatting

* Rename variable and add docs
2022-07-19 10:14:41 +00:00
JohnAFernandez 4a819d7b20 Include more valid object types in this check (#4500)
* Include more valid object types in this check

The previous version of the fix only included ships because it was believed to be the object that a ship used to be guarding.  Now it's clear that this is an object that the ship is currently targeting *after* guarding something.

So remove the UNREACHABLE and manually check the valid object types with an assert.  Then only continue with the function if it is the right target type.

* typo
2022-07-19 01:47:51 -04:00
Asteroth a79dd70138 bug fix for not present ships in clear-goals 2022-07-14 16:19:37 -04:00
Goober5000 d5af6b5775 Merge pull request #4496 from Goober5000/fix_4495
check all possible extensions for audio files
2022-07-14 13:19:03 -04:00
Goober5000 e3b24ea9e2 check all possible extensions for audio files
When the code checks for the existence of an audio file, be sure to check all relevant extensions.

This fixes #4495.  Tested with both ST:R personas as well as the Vasudan persona.
2022-07-14 01:38:05 -04:00
Joshua Glatt 8645210c0a Fix type checking and FRED help for containers in special argument lists (#4486) 2022-07-13 19:52:21 -07:00
Goober5000 8c39bf54d4 Merge pull request #4491 from Goober5000/time_updates
some fixes for playing movies via script
2022-07-13 20:00:26 -04:00
Goober5000 bb43411bb1 elaborate script descriptions 2022-07-13 12:30:01 -04:00
Asteroth 622ae3ef2c Fix some bugs with maneuvers (#4483)
* split ai_control_info_check and player-not-use-ai wipes maneuvers

* remove redundant flag upkeep and clarify logic
2022-07-13 00:08:08 -04:00
Goober5000 ab7a1d8fd7 some fixes for playing movies via script
The movie player code was missing some important functionality, in particular:
1) There was only one timing method to get real-time elapsed time, which was not suitable for pausing or time compression
2) There was an improper cast for the FPS property, causing a script error
3) The `getSeconds` method lost precision during casting

This patches those deficiencies.
2022-07-12 23:35:27 -04:00
Asteroth a246ef21aa make sure to cap bank_vel (#4487) 2022-07-12 20:15:42 -04:00
JohnAFernandez e8d379a91c Fix an nprintf being in the wrong logic branch (#4485) 2022-07-11 21:29:06 -04:00
Goober5000 45862e06a9 Merge pull request #4461 from daftmugi/fix/accidental-skip-cutscene
Prevent accidental skip cutscene at loading screen
2022-07-11 11:20:00 -04:00
Goober5000 f96a27feb4 Merge pull request #4370 from JohnAFernandez/Ensure-obj_team-is_called_safely
Do our best to avoid invalid types in obj_team
2022-07-09 23:06:11 -04:00
Goober5000 f935a4ac38 Merge pull request #4475 from Goober5000/fix_erroneous_angle_modification
fix erroneous position/angle modification
2022-07-09 22:58:15 -04:00
Goober5000 601addd5c2 more feedback 2022-07-09 01:56:24 -04:00
Goober5000 7cfbdcd6cf address feedback 2022-07-08 19:29:51 -04:00
Goober5000 a009dc188c Merge pull request #4470 from jg18/issue_4405
Provide a limited partial fix for FRED keyboard shortcuts
2022-07-08 18:56:40 -04:00
Goober5000 bb6d7515df Merge pull request #4477 from notimaginative/fix_fireball_lod_assertion
fix lod assertion check (it can equal the max)
2022-07-08 18:56:26 -04:00
Goober5000 f33b8d987e Merge pull request #4478 from notimaginative/fix_psnet_memory_error
fix memset() on nullptr in psnet_get_addr()
2022-07-08 18:56:07 -04:00
Goober5000 2fd98c908b Merge pull request #4476 from Goober5000/fix_4472
use proper bounds check when bashing hull in red-alert
2022-07-08 18:53:52 -04:00
Taylor Richards bf1a9f192a fix memset() on nullptr in psnet_get_addr()
"addr" is optional and allowed to be null, so we've got to check
it before zeroing
2022-07-08 13:24:58 -04:00
Taylor Richards ec8cb1ba71 fix lod assertion check (it can equal the max) 2022-07-08 13:22:39 -04:00
John Fernandez e9e722244b Return here if not a ship 2022-07-08 12:24:19 -04:00
John Fernandez 2449dd6f70 Equals is ok here 2022-07-08 12:17:53 -04:00
John Fernandez 2748cc6f97 Return here if not a ship in release 2022-07-08 12:17:02 -04:00
John Fernandez 660f72dcc7 Clearer Assertions(TM) 2022-07-08 12:09:52 -04:00
John Fernandez f1506987eb Make assertions more readable. 2022-07-08 12:09:52 -04:00
John Fernandez e0d2d76dc2 Add an else unreachable. 2022-07-08 12:09:52 -04:00
John Fernandez 09b35edae1 More helpful assertions here 2022-07-08 12:09:52 -04:00
John Fernandez 390ddb3fc8 Do our best to avoid invalid types in obj_team 2022-07-08 12:09:52 -04:00
Goober5000 0ab523bcf4 Merge pull request #4462 from the-maddin/fix-gcc12-warnings
Fix gcc12 warnings
2022-07-07 21:41:04 -04:00
Goober5000 d72c5dbe75 Merge pull request #4471 from Goober5000/snd_aav_fix
reset the adjusted volume when the mission closes
2022-07-07 21:40:29 -04:00
Goober5000 324ccf37cb use proper bounds check when bashing hull in red-alert
This range check is to ensure that the ship's hull is in a valid range, so it should therefore check that the hull is less than the max strength, not less than the current strength.  This was inadvertently changed in 593be42e66 and a04e83ab01, but retail behavior is to set the exact hull from the previous mission.

Tested for both regular ships and delayed-arrival ships.  Fixes #4472.
2022-07-07 21:31:14 -04:00
Taylor Richards 20094df4cd address issues causing network connectivity failures on macOS (#4473) 2022-07-07 03:15:27 -04:00
Goober5000 8db280ea83 fix erroneous position/angle modification
Due to rounding and conversion approximations, it was possible for FRED to think the position and/or angle was modified when no such modification had occurred.  This could result in erroneous updates.  In the case of angles this could result in a group of ships being assigned the wrong orientation.

Follow-up to #2614.
2022-07-07 02:57:06 -04:00
Goober5000 a12e74fe9d reset the adjusted volume when the mission closes
Move `snd_aav_init` to `game_level_close` so that it is always called when the mission exits, not just in the `GS_STATE_GAME_PLAY` state.

Fixes #4468.
2022-07-06 12:08:56 -04:00
the-maddin 82e2b67ed8 remove magic number, static assert about lod level
assumption
2022-07-06 09:22:49 +02:00
Joshua Glatt 3311704253 Provide a limited partial fix for FRED keyboard shortcuts 2022-07-05 21:29:11 -07:00
the-maddin 7f7eed7a48 comment to clarify 2022-07-05 23:38:57 +02:00
the-maddin a95938a418 remove #define 2022-07-05 23:38:41 +02:00
the-maddin 557a6a989c skip LODs instead of ERRORing 2022-07-05 23:35:37 +02:00
Asteroth 0f168ecc5b warn on mismatched turret objects (#4458) 2022-07-04 21:55:55 -04:00
Joshua Glatt 1768f5bb1b Handle syntax checking and caching for nodes with dynamic values (#4460) 2022-07-03 23:57:54 -07:00
JohnAFernandez 9e6790f317 Prevent a crash by exiting here (#4449)
* Prevent a crash by exiting here

And make an assertion more explicit, just in case.

* Fix comment
2022-07-03 22:28:08 -04:00
JohnAFernandez 993a9e9f18 Merge pull request #4463 from Kiloku/bugfix/3321-clamp-values-from-pilot-file
Clamp values to their correct ranges when loading and saving pilot and campaign files
2022-07-03 10:18:30 -04:00
kiloku 516548d6be No clamping bools 2022-07-03 07:49:35 -03:00
kiloku d1800d8622 Use the labels displayed in the menus 2022-07-03 07:48:03 -03:00
Daft Mugi c9dd97d223 Prevent accidental skip cutscene at loading screen
`os_ignore_events()` deferred events instead of ignoring them. The
player could accidentally skip a cutscene at the loading screen and
not see any indication there was a cutscene.

This only removes deferred skip-cutscene key events, except ESC, right
before the cutscene plays. This makes it less likely for a player to
accidentally skip the cutscene.

This also renames `buffered_events` to `deferred_events` and
`os_ignore_events()` to `os_defer_events_on_load_screen()`, which more
accurately describes the behavior.

Fixes #4419.
2022-07-02 16:58:52 -05:00
Goober5000 c0ea9f5ffc Merge pull request #4455 from Goober5000/sexp_error_msg_tweak
refine SEXP error reporting
2022-07-02 13:22:04 -04:00
kiloku 319c8cc2a1 Clamp values before saving 2022-07-02 09:47:46 -03:00
kiloku 07b8e8d6cb Clamp settings values when loading 2022-07-01 22:56:19 -03:00
the-maddin 80e2908aa3 GCC12 warning format-overflow
Fireball filename field could potentially overflow
when adding LOD filename extensions. Added explicit
lengths and checks.
2022-07-02 01:03:32 +02:00
Daft Mugi 25a6bf2b87 Prevent accidental skip cutscene at loading screen
os_ignore_events() was buffering events instead of ignoring them. This
caused any skip-cutscene key to be buffered during the loading screen,
which would later be handled during the cutscene. From the player's
perspective, there was no cutscene.

This changes os_ignore_events() to ignore events.

Fixes #4419.
2022-07-01 14:40:10 -05:00
Joshua Glatt 1930943d2d Auto-accept completed campaign mission before exiting game from debriefing screen via options menu (#4450) 2022-06-30 19:26:25 -07:00
the-maddin ed16a81e37 GCC12 warning array compare
not comparing ship_info name addresses, but
ship_info argument address with address of
iterator element.
2022-07-01 00:59:57 +02:00
Asteroth 0121e439aa tweak to getTurretHeading() docs (#4456) 2022-06-30 15:07:14 -04:00
Goober5000 6fb72499f9 make all the error messages consistent 2022-06-30 03:15:53 -04:00
Goober5000 eb69133a57 refine SEXP error reporting
Sync the "mission save" and "error reporting" versions of the SEXP variable string.  Also use `stuff_sexp_text_string` to print the "bad node" token rather than `Sexp_nodes[bad_node].text`, so that a human-readable token is displayed instead of a magic number like 26.

Related to #4226, as the generated error message is formatted differently enough to be confusing.
2022-06-30 02:50:08 -04:00
Goober5000 2fcc3ecbf8 Merge pull request #4434 from Goober5000/sync_orientation
sync Orientation virtvars
2022-06-29 19:29:13 -04:00
JohnAFernandez 30b2b6caa7 Merge pull request #4407 from JohnAFernandez/Fix-Hud-Brackets
A comprehensive fix for the `player.target_is_dying` variable.
2022-06-29 12:31:07 -04:00
John Fernandez 4250aab601 Hud Bracket fixes
1) A comprehensive fix for the is_target_dying variable

2) Also do distance correctly for non-player target brackets by adding a check that we're dealing with the target before using the current target distance.  Then also calc distance if it is not the player target.

3) Update comments and checks to be consistent

Many of these did not reflect how the variable was actually used, so update the comments.
2022-06-29 11:40:12 -04:00
Goober5000 a9e6c328cf Merge pull request #4448 from Goober5000/async_mprintfs
add some logging to waitAsync
2022-06-28 13:55:33 -04:00
m!m 07b6c17db0 Merge pull request #4445 from the-maddin/ignore-gcc11-warning
Ignore Wshadow warnings in stale librocket headers
2022-06-28 18:11:58 +02:00
Goober5000 2ce953aee8 address feedback 2022-06-28 12:04:16 -04:00
Goober5000 05f71a30fb add some logging to waitAsync
Log when the context is created, completes, and is aborted.  Adds insight for the situation described in #4432.
2022-06-28 12:02:13 -04:00
Goober5000 5dfef48bf6 Merge pull request #4452 from Goober5000/default_enabled_nprintf_categories
set up debug_filter.cfg to have default filters that can be turned off
2022-06-28 12:01:33 -04:00
wookieejedi f280f11824 Fix AI Goal Display Strings (#4451)
* Fix AI Goal Display Strings

Okay so previously, if a target goal was not valid the code was
`strcpy(outbuf, XSTR("no orders", 495)); `

but then it was changed to
`outbuf += XSTR("no orders", 495);`

So it would instead append the "no orders" instead of properly replace the whole string. Looks just like a copy and paster oversight, since all the other text appends the name of the target with the order.

This PR correctly replaces, rather than appends that text. Fixes #4345

* appease clang

* use more simple =
2022-06-28 09:33:29 -04:00
Goober5000 e7b73da90e Merge pull request #4447 from Goober5000/fix_4412_harder
even more fixes for the skip-training-mission crash
2022-06-27 23:50:02 -04:00
Goober5000 8698993d44 set up debug_filter.cfg to have default filters that can be turned off
Small refactoring of the debug filter, needed as a prerequisite for #4448.
2022-06-27 23:36:49 -04:00
wookieejedi 7b04d401fe Fix Seg Fault on Standalones with Safer Check for Secondaries (#4446)
Tests with standalone multiplayer builds revealed that the server would seg fault if a player tried to switch secondaries if the ship did not have secondaries. Tests with standalones showed this fixed the bug. Thanks to chief for the bug IDing and the fix code.
2022-06-26 22:20:48 -04:00
Goober5000 a2b05f085b even more fixes for the skip-training-mission crash
As noted in #4412, it's possible for `Campaign.current_mission` to be -1 even in campaign mode.  This explictly checks for that.
2022-06-26 19:08:50 -04:00
the-maddin 6e612cdc97 msvc compat 2022-06-26 21:57:09 +02:00
the-maddin 9a55d90f5d Ignore Wshadow warnings in stale librocket header
GCC 11 raises a warning due to a parameter "word"
shadowing a typedef "word".
2022-06-26 21:19:35 +02:00
Tyler Blount cc2b9fcf94 Use correct needs elements for post build action (#4439) 2022-06-25 19:00:26 -05:00
Goober5000 ed8af9d197 Merge pull request #4440 from Goober5000/fix_message_source_error
better handling of invalid message sources
2022-06-25 16:44:23 -04:00
Goober5000 43f8b123a8 better handling of invalid message sources
Another bug discovered by Durandal.  The message sender was a variable containing an invalid value, and the game crashed.  This handles it more gracefully.

Tested and verified.
2022-06-25 15:56:52 -04:00
Goober5000 a930301ec1 Merge pull request #4437 from Goober5000/fix_remove_form_on_wing
fix crash when removing form-on-wing
2022-06-25 15:35:24 -04:00
Tyler Blount 112f2bb5ba Merge pull request #4438 from z64555/fix/post-build-release-autotrigger01
Fix/post build release autotrigger01
2022-06-25 09:55:11 -07:00
z64555 5b8dce1dda Try encapsulating the OR in the same brackets. 2022-06-25 10:23:57 -05:00
z64555 b6c684644a use inputs context instead of github.events.inputs context 2022-06-25 10:06:32 -05:00
Goober5000 d754f7fe0d fix crash when removing form-on-wing
This goal was missed in `ai_remove_goal_sexp_sub`, so add handling for it.  Also make the error message clearer in case another goal is missed in the future.
2022-06-25 00:41:34 -04:00
wookieejedi 2beaa886ca Fixes Team Colors not working with show-ship (#4436)
Updates the `show-ship` function to account for team colors. Also, cleans up the function a bit. Tested and works as expected. Fixes #4435
2022-06-24 22:06:13 -04:00
Goober5000 53bc7861bf sync Orientation virtvars
The two `VIRTVAR`s for Orientation in subsystems and submodel instances do the same thing, but did not have the same code - in particular, the subsystem version did not update the subsystem angle and turret angle.  This syncs them up.
2022-06-23 10:08:34 -04:00
Taylor Richards 9de4a49c42 stop non-printable keys adding text to multi msg buffer (#4433)
Arrow, function, Ctrl, Alt ... and any other non-ascii keys
would previously add a blank space to the multi msg buffer.
This prevents that from happening, making sure that it's an
ascii character before appending it to the buffer.

(Retail bug??)
2022-06-23 05:55:52 -04:00
Goober5000 6e5626722a Merge pull request #4431 from Goober5000/skybox_none
fix clearing a skybox model when it isn't the mission default
2022-06-20 20:33:16 -04:00
Goober5000 1fa5068555 Merge pull request #4428 from Goober5000/fix_4412
more fixes for the skip-training-mission crash
2022-06-20 20:33:06 -04:00
Goober5000 d7f0051394 fix clearing a skybox model when it isn't the mission default
The previous way to clear a skybox was to set it to "default", and if the mission had no skybox then it would be cleared.  The skybox code can correctly handle the lack of a skybox, but if you pass an empty string to the skybox preloader it fails with a file not found error.  This fixes the preloader, and also adds "none" as an explicit no-skybox parameter.
2022-06-20 02:33:25 -04:00
Goober5000 6b9dacb598 more fixes for the skip-training-mission crash
Certain campaign-related functions would run even when the game was not in campaign mode (e.g. when playing in the tech room).  Normally these would be benign, but with a new pilot or with a skipped mission, the current campaign mission would be -1 and a crash would occur.

Should fully fix #4412.
2022-06-19 23:09:53 -04:00
Allen A. Babb eef9a340b1 Flush the mouse state after toggling between Use_mouse_to_fly in scripting/api/libs/controls.cpp::MouseControlStatus (#4426)
See the discussion in issue #4173 for details.
2022-06-19 20:43:42 -05:00
Tyler Blount 4a9c6567cc Merge pull request #4425 from z64555/fix/post-build-release-autotrigger
Autotrigger on either workflow_call or push event.
2022-06-19 09:40:24 -07:00
z64555 ead7f10006 Autotrigger on either workflow_call or push event. 2022-06-19 07:21:50 -05:00
Taylor Richards c7abceaf30 fix control chars test that also excluded space key (#4424) 2022-06-19 06:32:53 -04:00
77 changed files with 1010 additions and 415 deletions

No files matched your search

+2 -2
View File
@@ -315,6 +315,6 @@ jobs:
needs: [linux_zip, windows_zip]
uses: ./.github/workflows/post-build-release.yaml
with:
linux_result: ${{ needs.build_linux.result }}
windows_result: ${{ needs.build_windows.result }}
linux_result: ${{ needs.linux_zip.result }}
windows_result: ${{ needs.windows_zip.result }}
releaseTag: ${{ github.ref_name }}
+4 -4
View File
@@ -30,7 +30,7 @@ env:
NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }}
HLP_API: ${{ secrets.HLP_API }}
HLP_KEY: ${{ secrets.HLP_KEY }}
RELEASE_TAG: ${{ github.event.inputs.releaseTag }}
RELEASE_TAG: ${{ inputs.releaseTag }}
jobs:
post_builds:
@@ -51,10 +51,10 @@ jobs:
run: pip install -r ci/post/requirements.txt
- name: Post Builds (Auto trigger)
if: ${{ github.event_name == 'workflow_call' }}
if: ${{ (github.event_name == 'workflow_call') || (github.event_name == 'push') }}
env:
LINUX_RESULT: ${{ github.event.inputs.linux_result }}
WINDOWS_RESULT: ${{ github.event.inputs.windows_result }}
LINUX_RESULT: ${{ inputs.linux_result }}
WINDOWS_RESULT: ${{ inputs.windows_result }}
run: python ci/post/main.py release
- name: Post Builds (Manual trigger)
+182 -134
View File
@@ -2400,6 +2400,7 @@ void ai_attack_object(object* attacker, object* attacked, int ship_info_index)
Assert(attacker != nullptr);
Assert(attacker->instance >= 0);
Assert(attacker->type == OBJ_SHIP);
Assert(Ships[attacker->instance].ai_index != -1);
// Bogus! Who tried to get me to attack myself!
if (attacker == attacked) {
@@ -10015,11 +10016,25 @@ void guard_object_was_hit(object *guard_objp, object *hitter_objp)
aip->submode_start_time = Missiontime;
aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
} else {
int num_attacking_cur, num_attacking_new;
// This section used to be a lot simpler, but some "invalid" object types were probably getting into num_ships_attacking
// below, so we have to manually return for some object types and make sure to assert for other object types
// that are symptomatic of a larger problem with the engine.
// This just checks to see that the target was a valid type. If it is not one of these, something really
// could be drastically wrong in the engine.
Assertion(Objects[aip->target_objnum].type == OBJ_SHIP || Objects[aip->target_objnum].type == OBJ_WEAPON || Objects[aip->target_objnum].type == OBJ_DEBRIS || Objects[aip->target_objnum].type == OBJ_ASTEROID || Objects[aip->target_objnum].type == OBJ_WAYPOINT,
"This function just discovered that %s has an invalid target object type of %d. This is bad. Please report!",
Ships[aip->shipnum].ship_name, Objects[aip->target_objnum].type);
if (!(Objects[aip->target_objnum].type == OBJ_SHIP || Objects[aip->target_objnum].type == OBJ_WEAPON || Objects[aip->target_objnum].type == OBJ_ASTEROID || Objects[aip->target_objnum].type == OBJ_DEBRIS)){
// it's probably a valid target, but retail would not count those cases, so just return.
return;
}
int num_attacking_cur = num_ships_attacking(aip->target_objnum);
num_attacking_cur = num_ships_attacking(aip->target_objnum);
if (num_attacking_cur > 1) {
num_attacking_new = num_ships_attacking(hitter_objnum);
int num_attacking_new = num_ships_attacking(hitter_objnum);
if (num_attacking_new < num_attacking_cur) {
@@ -14490,6 +14505,13 @@ void validate_mode_submode(ai_info *aip)
*/
void ai_frame(int objnum)
{
// Cyborg - can you believe this was added in *2022*??
Assertion (Objects[objnum].type == OBJ_SHIP, "Ai_frame was called for a non-ship of type %d. Please report!", Objects[objnum].type);
if (Objects[objnum].type != OBJ_SHIP){
return;
}
ship *shipp = &Ships[Objects[objnum].instance];
ai_info *aip = &Ai_info[shipp->ai_index];
int target_objnum;
@@ -14572,7 +14594,7 @@ void ai_frame(int objnum)
} else if (aip->resume_goal_time == -1) {
// AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
if ( Ship_info[shipp->ship_info_index].class_type > -1 && (Ship_types[Ship_info[shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::AI_auto_attacks]) ) {
target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level]); // Attack up to 25K units away.
target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level]); // Attack up to 2.5K units away.
if (target_objnum != -1) {
if (aip->target_objnum != target_objnum)
aip->aspect_locked_time = 0.0f;
@@ -14700,7 +14722,8 @@ void ai_frame(int objnum)
}
}
static void ai_control_info_check(ai_info *aip, ship_info *sip, physics_info *pi)
// set and removes any maneuver flags for ai maneuver overrides
static void ai_control_info_flags_upkeep(ai_info* aip, ship_info* sip, physics_info* pi)
{
if (aip->ai_override_flags.none_set())
return;
@@ -14731,6 +14754,37 @@ static void ai_control_info_check(ai_info *aip, ship_info *sip, physics_info *pi
}
else
{
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Dont_bank_when_turning] || sip->flags[Ship::Info_Flags::Dont_bank_when_turning])
AI_ci.control_flags |= CIF_DONT_BANK_WHEN_TURNING;
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Dont_clamp_max_velocity] || sip->flags[Ship::Info_Flags::Dont_clamp_max_velocity])
AI_ci.control_flags |= CIF_DONT_CLAMP_MAX_VELOCITY;
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Instantaneous_acceleration] || sip->flags[Ship::Info_Flags::Instantaneous_acceleration])
AI_ci.control_flags |= CIF_INSTANTANEOUS_ACCELERATION;
}
// set physics flag according to whether we are instantaneously accelerating
if (AI_ci.control_flags & CIF_INSTANTANEOUS_ACCELERATION)
pi->flags |= PF_MANEUVER_NO_DAMP;
else
pi->flags &= ~PF_MANEUVER_NO_DAMP;
}
// applies any rotational or lateral maneuver values to the AI's CI
static void ai_control_info_apply(ai_info *aip)
{
if (aip->ai_override_flags.none_set())
return;
bool lateral_maneuver = false;
bool rotational_maneuver = false;
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Lateral_never_expire] || !timestamp_elapsed(aip->ai_override_lat_timestamp))
lateral_maneuver = true;
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Rotational_never_expire] || timestamp_elapsed(aip->ai_override_rot_timestamp))
rotational_maneuver = true;
if (lateral_maneuver || rotational_maneuver) {
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Full_rot])
{
AI_ci.pitch = aip->ai_override_ci.pitch;
@@ -14740,17 +14794,11 @@ static void ai_control_info_check(ai_info *aip, ship_info *sip, physics_info *pi
else
{
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Pitch])
{
AI_ci.pitch = aip->ai_override_ci.pitch;
}
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Heading])
{
AI_ci.heading = aip->ai_override_ci.heading;
}
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Roll])
{
AI_ci.bank = aip->ai_override_ci.bank;
}
}
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Full_lat])
@@ -14762,32 +14810,13 @@ static void ai_control_info_check(ai_info *aip, ship_info *sip, physics_info *pi
else
{
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Up])
{
AI_ci.vertical = aip->ai_override_ci.vertical;
}
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Sideways])
{
AI_ci.sideways = aip->ai_override_ci.sideways;
}
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Forward])
{
AI_ci.forward = aip->ai_override_ci.forward;
}
}
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Dont_bank_when_turning] || sip->flags[Ship::Info_Flags::Dont_bank_when_turning])
AI_ci.control_flags |= CIF_DONT_BANK_WHEN_TURNING;
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Dont_clamp_max_velocity] || sip->flags[Ship::Info_Flags::Dont_clamp_max_velocity])
AI_ci.control_flags |= CIF_DONT_CLAMP_MAX_VELOCITY;
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Instantaneous_acceleration] || sip->flags[Ship::Info_Flags::Instantaneous_acceleration])
AI_ci.control_flags |= CIF_INSTANTANEOUS_ACCELERATION;
}
// set physics flag according to whether we are instantaneously accelerating
if (AI_ci.control_flags & CIF_INSTANTANEOUS_ACCELERATION)
pi->flags |= PF_NO_DAMP;
else
pi->flags &= ~PF_NO_DAMP;
}
int Last_ai_obj = -1;
@@ -14847,9 +14876,12 @@ void ai_process( object * obj, int ai_index, float frametime )
if (shipp->flags[Ship::Ship_Flags::Dying] && sip->flags[Ship::Info_Flags::Large_ship_deathroll])
rfc = false;
// regardless if we're acting on them, upkeep maneuver flags
ai_control_info_flags_upkeep(aip, sip, &obj->phys_info);
if (rfc) {
// Wanderer - sexp based override goes here - only if rfc is valid though
ai_control_info_check(aip, sip, &obj->phys_info);
ai_control_info_apply(aip);
physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
}
@@ -15109,9 +15141,9 @@ void init_aip_from_class_and_profile(ai_info *aip, ai_class *aicp, ai_profile_t
void ai_do_default_behavior(object *obj)
{
Assert(obj != NULL);
Assert(obj->instance != -1);
Assert(Ships[obj->instance].ai_index != -1);
Assertion(obj != nullptr, "Ai_do_default_behavior() was given a bad information. Specifically obj is a nullptr. This is a coder error, please report!");
Assertion((obj->type == OBJ_SHIP) && (obj->instance > -1) && (obj->instance < MAX_SHIPS), "This function that requires an object to be a ship was passed an invalid ship. Please report\n\nObject type: %d\nInstance #: %d", obj->type, obj->instance);
Assertion(Ships[obj->instance].ai_index >= 0, "A ship has been found to have an invalid ai_index of %d. This should have been assigned and is coder mistake, please report!", Ships[obj->instance].ai_index);
ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
ship_info* sip = &Ship_info[Ships[obj->instance].ship_info_index];
@@ -15209,118 +15241,125 @@ void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *o
return;
}
if ((obj_team(objp_hitter) == obj_team(objp_hit)) && (objp_hitter == Player_obj)) {
// Cyborg -- I had to switch around the logic here because it was potentially
// sending non-ships to obj_team, which would trigger an assert
if (objp_hitter == Player_obj){
// AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point. If so, bail out.
// Cyborg - So check if this is a ship first. Probably true, but you never know, with FSO
// (especially in multi)
if ( objp_hitter->type != OBJ_SHIP ) {
return;
}
Assert(objp_hitter->type == OBJ_SHIP);
// And assert that obj_hit is a ship as well.
Assert(objp_hit->type == OBJ_SHIP);
Assert((objp_weapon->type == OBJ_WEAPON) || (objp_weapon->type == OBJ_BEAM)); //beam added for traitor detection - FUBAR
ship *shipp_hitter = &Ships[objp_hitter->instance];
ship *shipp_hit = &Ships[objp_hit->instance];
if (obj_team(objp_hitter) == obj_team(objp_hit)) {
if (shipp_hitter->team != shipp_hit->team) {
return;
}
Assert((objp_weapon->type == OBJ_WEAPON) || (objp_weapon->type == OBJ_BEAM)); //beam added for traitor detection - FUBAR
// get the player
player *pp = &Players[Player_num];
ship *shipp_hitter = &Ships[objp_hitter->instance];
ship *shipp_hit = &Ships[objp_hit->instance];
// wacky stuff here
if (pp->friendly_hits != 0) {
float time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
if (time_since_last_hit > 60.0f) {
pp->friendly_hits = 0;
pp->friendly_damage = 0.0f;
} else if (time_since_last_hit > 2.0f) {
pp->friendly_hits -= (int) time_since_last_hit/2;
pp->friendly_damage -= time_since_last_hit;
}
if (pp->friendly_damage < 0.0f) {
pp->friendly_damage = 0.0f;
}
if (pp->friendly_hits < 0) {
pp->friendly_hits = 0;
}
}
}
float damage; // Damage done by weapon. Gets scaled down based on size of ship.
if (objp_weapon->type == OBJ_BEAM) // added beam for traitor detection -FUBAR
damage = beam_get_ship_damage(&Beams[objp_weapon->instance], objp_hit);
else
damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
// wacky stuff here
ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
if (shipp_hit->ship_max_hull_strength > 1000.0f) {
float factor = shipp_hit->ship_max_hull_strength / 1000.0f;
factor = MIN(100.0f, factor);
damage /= factor;
}
// Don't penalize much at all for hitting cargo
if (sip->class_type > -1) {
damage *= Ship_types[sip->class_type].ff_multiplier;
}
// Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
damage /= 5.0f;
}
pp->friendly_last_hit_time = Missiontime;
pp->friendly_hits++;
// cap damage and number of hits done this frame
float accredited_damage = MIN(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
pp->friendly_damage += accredited_damage;
pp->damage_this_burst += accredited_damage;
// Done with adjustments to damage. Evaluate based on current friendly_damage
nprintf(("AI", "Friendly damage: %.1f, threshold: %.1f, inc damage: %.1f, max burst: %d\n", pp->friendly_damage, FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f), pp->damage_this_burst, MAX_BURST_DAMAGE ));
if (is_instructor(objp_hit)) {
// it's not nice to hit your instructor
if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
pp->last_warning_message_time = Missiontime;
ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
training_fail();
// Instructor leaves.
mission_do_departure(objp_hit);
gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED ); // Force player to warp out.
} else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
// warning every 4 sec
// use NULL as the message sender here since it is the Terran Command persona
message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
pp->last_warning_message_time = Missiontime;
if (shipp_hitter->team != shipp_hit->team) {
return;
}
// not nice to hit your friends
} else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
mission_goal_fail_all();
ai_abort_rearm_request( Player_obj );
// get the player
player *pp = &Players[Player_num];
Player_ship->team = Iff_traitor;
// wacky stuff here
if (pp->friendly_hits != 0) {
float time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
if (time_since_last_hit > 60.0f) {
pp->friendly_hits = 0;
pp->friendly_damage = 0.0f;
} else if (time_since_last_hit > 2.0f) {
pp->friendly_hits -= (int) time_since_last_hit/2;
pp->friendly_damage -= time_since_last_hit;
}
} else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
// no closer than 4 sec intervals
// Note: (damage > frand()) added on 12/9/97 by MK. Since damage is now scaled down for big ships, we could get too
// many warnings. Kind of tedious. frand() returns a value in 0..1, so this won't affect legit hits.
process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
pp->last_warning_message_time = Missiontime;
if (pp->friendly_damage < 0.0f) {
pp->friendly_damage = 0.0f;
}
if (pp->friendly_hits < 0) {
pp->friendly_hits = 0;
}
}
}
float damage; // Damage done by weapon. Gets scaled down based on size of ship.
if (objp_weapon->type == OBJ_BEAM) // added beam for traitor detection -FUBAR
damage = beam_get_ship_damage(&Beams[objp_weapon->instance], objp_hit);
else
damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
// wacky stuff here
ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
if (shipp_hit->ship_max_hull_strength > 1000.0f) {
float factor = shipp_hit->ship_max_hull_strength / 1000.0f;
factor = MIN(100.0f, factor);
damage /= factor;
}
// Don't penalize much at all for hitting cargo
if (sip->class_type > -1) {
damage *= Ship_types[sip->class_type].ff_multiplier;
}
// Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
damage /= 5.0f;
}
pp->friendly_last_hit_time = Missiontime;
pp->friendly_hits++;
// cap damage and number of hits done this frame
float accredited_damage = MIN(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
pp->friendly_damage += accredited_damage;
pp->damage_this_burst += accredited_damage;
// Done with adjustments to damage. Evaluate based on current friendly_damage
nprintf(("AI", "Friendly damage: %.1f, threshold: %.1f, inc damage: %.1f, max burst: %d\n", pp->friendly_damage, FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f), pp->damage_this_burst, MAX_BURST_DAMAGE ));
if (is_instructor(objp_hit)) {
// it's not nice to hit your instructor
if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
pp->last_warning_message_time = Missiontime;
ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
training_fail();
// Instructor leaves.
mission_do_departure(objp_hit);
gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED ); // Force player to warp out.
} else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
// warning every 4 sec
// use NULL as the message sender here since it is the Terran Command persona
message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
pp->last_warning_message_time = Missiontime;
}
// not nice to hit your friends
} else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
mission_goal_fail_all();
ai_abort_rearm_request( Player_obj );
Player_ship->team = Iff_traitor;
} else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
// no closer than 4 sec intervals
// Note: (damage > frand()) added on 12/9/97 by MK. Since damage is now scaled down for big ships, we could get too
// many warnings. Kind of tedious. frand() returns a value in 0..1, so this won't affect legit hits.
process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
pp->last_warning_message_time = Missiontime;
}
}
}
}
@@ -15545,11 +15584,14 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal)
return;
hitter_objnum = hit_objp->parent;
Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
Assertion((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS), "hitter_objnum in this function is an invalid index of %d. This can cause random behavior, and is a coder mistake. Please report!", hitter_objnum);
objp_hitter = &Objects[hitter_objnum];
Assertion((objp_hitter->type == OBJ_SHIP) || (objp_hitter->type == OBJ_WEAPON) || (objp_hitter->type == OBJ_ASTEROID) || (objp_hitter->type == OBJ_BEAM) || (objp_hitter->type == OBJ_DEBRIS),
"The ai code is passing an invalid object type of %d to a function when trying to decide how to respond to something hitting it. This is a bug in the code, please report!", objp_hitter->type);
// lets not check hits by ghosts any further either
if(objp_hitter->type == OBJ_GHOST)
if(objp_hitter->type != OBJ_SHIP && objp_hitter->type != OBJ_WEAPON && objp_hitter->type != OBJ_ASTEROID && objp_hitter->type != OBJ_BEAM && objp_hitter->type != OBJ_DEBRIS)
return;
// Hit by a protected ship, don't attack it.
@@ -16147,6 +16189,12 @@ void maybe_cheat_fire_synaptic(object *objp)
ship *shipp;
int wing_index, time;
Assertion(objp->type == OBJ_SHIP, "This ai function requires a ship, but was called with a non-ship object type of %d. Please report!", objp->type);
if (objp->type != OBJ_SHIP) {
return;
}
shipp = &Ships[objp->instance];
if (!(strnicmp(shipp->ship_name, NOX("delta"), 5)))
+5 -1
View File
@@ -1235,6 +1235,10 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
goalmode = AI_GOAL_IGNORE_NEW;
break;
case OP_AI_FORM_ON_WING:
priority = 99;
goalmode = AI_GOAL_FORM_ON_WING;
break;
default:
const ai_mode_lua* luaAIMode = ai_lua_find_mode(op);
if(luaAIMode != nullptr){
@@ -1251,7 +1255,7 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
priority = localnode >= 0 ? atoi( CTEXT(localnode) ) : -1;
}
else {
UNREACHABLE("Invalid SEXP-OP number %d for an AI goal!", op);
UNREACHABLE("Invalid SEXP-OP %s (number %d) for an AI goal!", Sexp_nodes[node].text, op);
}
break;
};
+1
View File
@@ -51,6 +51,7 @@ void processEvents()
{
io::mouse::CursorManager::get()->showCursor(false);
os_remove_deferred_cutscene_key_events();
os_poll();
}
+12 -1
View File
@@ -407,10 +407,21 @@ void fireball_parse_tbl()
{
if (fi.lod_count > 1)
{
Assertion(fi.lod_count <= MAX_FIREBALL_LOD, "Fireball LOD (%d) greater than MAX_FIREBALL_LOD %d.", fi.lod_count, MAX_FIREBALL_LOD);
static_assert(MAX_FIREBALL_LOD < 10, "The fireball LOD naming scheme needs to be changed for LODs > 9");
auto lod0 = fi.lod[0].filename;
constexpr int MAX_BASENAME_LEN = MAX_FILENAME_LEN - 3; // ensure base file name + _*lod* fits in filename field
if (strlen(lod0) > MAX_BASENAME_LEN)
{
Warning(LOCATION, "A fireball base file name may not be longer than %d characters due to the LOD naming scheme.\nLODs other than LOD0 will be skipped for fireball %s", MAX_BASENAME_LEN, lod0);
fi.lod_count = 1;
continue;
}
for (int j = 1; j < fi.lod_count; ++j)
sprintf(fi.lod[j].filename, "%s_%d", lod0, j);
sprintf(fi.lod[j].filename, "%.*s_%d", MAX_BASENAME_LEN, lod0, j % MAX_FIREBALL_LOD /*to show gcc12 format string is safe*/);
}
}
+1 -1
View File
@@ -1294,7 +1294,7 @@ void parse_soundtrack()
continue;
// check for file
if (!cf_exists_full(Soundtracks[strack_idx].patterns[i].fname, CF_TYPE_MUSIC))
if (!cf_exists_full_ext(Soundtracks[strack_idx].patterns[i].fname, CF_TYPE_MUSIC, NUM_AUDIO_EXT, audio_ext_list))
return;
}
+16 -16
View File
@@ -31,23 +31,23 @@
#define GM_CAMPAIGN_MODE (1 << 10) // are we currently in a campaign.
#define GM_LAB (1 << 11) // We are currently in the F3 lab
#define VM_EXTERNAL (1 << 0) // Set if not viewing from player position.
#define VM_TRACK (1 << 1) // Set if viewer is tracking target.
#define VM_DEAD_VIEW (1 << 2) // Set if viewer is watching from dead view.
#define VM_CHASE (1 << 3) // Chase view.
#define VM_OTHER_SHIP (1 << 4) // View from another ship.
#define VM_CAMERA_LOCKED (1 << 5) // Set if player does not have control of the camera
#define VM_WARP_CHASE (1 << 6) // View while warping out (form normal view mode)
#define VM_PADLOCK_UP (1 << 7)
#define VM_PADLOCK_REAR (1 << 8)
#define VM_PADLOCK_LEFT (1 << 9)
#define VM_PADLOCK_RIGHT (1 << 10)
#define VM_WARPIN_ANCHOR (1 << 11) // special warpin camera mode
#define VM_TOPDOWN (1 << 12) //Camera is looking down on ship
#define VM_FREECAMERA (1 << 13) //Camera is not attached to any particular object, probably under SEXP control
#define VM_CENTERING (1 << 14) // View is springing to center
#define VM_EXTERNAL (1 << 0) // Set if not viewing from player position.
#define VM_TRACK (1 << 1) // Set if viewer is tracking target.
#define VM_DEAD_VIEW (1 << 2) // Set if viewer is watching from dead view.
#define VM_CHASE (1 << 3) // Chase view.
#define VM_OTHER_SHIP (1 << 4) // View from another ship.
#define VM_CAMERA_LOCKED (1 << 5) // Set if player does not have control of the camera
#define VM_WARP_CHASE (1 << 6) // View while warping out (form normal view mode)
#define VM_PADLOCK_UP (1 << 7) // Set when player is looking up
#define VM_PADLOCK_REAR (1 << 8) // Set when player is looking behind
#define VM_PADLOCK_LEFT (1 << 9) // Set when player is looking left
#define VM_PADLOCK_RIGHT (1 << 10) // Set when player is looking right
#define VM_WARPIN_ANCHOR (1 << 11) // special warpin camera mode
#define VM_TOPDOWN (1 << 12) // Camera is looking down on ship
#define VM_FREECAMERA (1 << 13) // Camera is not attached to any particular object, probably under SEXP control
#define VM_CENTERING (1 << 14) // View is springing to center
#define VM_PADLOCK_ANY (VM_PADLOCK_UP|VM_PADLOCK_REAR|VM_PADLOCK_LEFT|VM_PADLOCK_RIGHT)
#define VM_PADLOCK_ANY (VM_PADLOCK_UP | VM_PADLOCK_REAR | VM_PADLOCK_LEFT | VM_PADLOCK_RIGHT)
//-----Cutscene stuff
//No bars
+1
View File
@@ -1515,6 +1515,7 @@ void hud_update_frame(float /*frametime*/)
}
if (Player_ai->target_objnum == -1) {
Player->target_is_dying = -1; // according to comments elsewhere in the code, set to -1 when no target
hud_target_change_check();
return;
}
+13 -2
View File
@@ -395,7 +395,12 @@ void HudGaugeBrackets::renderObjectBrackets(object *targetp, color *clr, int w_c
int bound_rc;
SCP_list<CJumpNode>::iterator jnp;
if ( Player->target_is_dying <= 0 || (Player_ai->target_objnum >= 0 && targetp != &Objects[Player_ai->target_objnum])) {
bool not_player_target = (Player_ai->target_objnum < 0 || targetp != &Objects[Player_ai->target_objnum]);
// target_is_dying is set to zero when the target is not dying, -1 when no target. Only 1, aka "TRUE", is invalid.
if ( not_player_target || Player->target_is_dying <= 0) {
int modelnum;
switch ( targetp->type ) {
@@ -475,7 +480,13 @@ void HudGaugeBrackets::renderObjectBrackets(object *targetp, color *clr, int w_c
int target_objnum = -1;
if(flags & TARGET_DISPLAY_DIST) {
distance = Player_ai->current_target_distance;
// since we can have either the target, or rogue asteroids bracketed,
// check which case we are in, and then do the correct distance.
if (not_player_target) {
distance = hud_find_target_distance(targetp, &Player_obj->pos);
} else {
distance = Player_ai->current_target_distance;
}
}
if(flags & TARGET_DISPLAY_DOTS) {
+2 -1
View File
@@ -194,7 +194,8 @@ void HudGaugeLock::renderOld(float frametime)
return;
}
if (Player->target_is_dying) {
// 1 is the only value for which your target is actually dying
if (Player->target_is_dying == 1) {
return;
}
+10 -1
View File
@@ -309,12 +309,21 @@ ship_info *sip = &Ship_info[Player_ship->ship_info_index];
}
void HudGaugeReticle::getFirepointStatus() {
// allow the firepoint status to be empty when a multiplayer observer
// this is not a bug, the observer will simply *not* have any firepoints.
if (Objects[Player->objnum].type == OBJ_OBSERVER) {
// only multiplayer instances should be getting here!
Assertion((Game_mode & GM_MULTIPLAYER), "Somehow FSO thinks its player object is an observer even though it's not in Multiplayer. Please report!");
return;
}
//First, get the player ship
ship_info* sip;
ship* shipp;
polymodel* pm;
Assert(Objects[Player->objnum].type == OBJ_SHIP);
Assertion(Objects[Player->objnum].type == OBJ_SHIP, "HudGaugeReticle::getFirepointStatus was passed an invalid object type of %d. Please report!", Objects[Player->objnum].type);
if (Objects[Player->objnum].type == OBJ_SHIP) {
shipp = &Ships[Objects[Player->objnum].instance];
+3
View File
@@ -1530,6 +1530,8 @@ void hud_target_missile(object *source_obj, int next_flag)
continue;
}
Assertion(Objects[so->objnum].type == OBJ_SHIP, "hud_target_missile was about to call obj_team with a non-ship obejct with type %d. Please report!", Objects[so->objnum].type);
// only allow targeting of hostile bombs
if (!iff_x_attacks_y(Player_ship->team, obj_team(A))) {
continue;
@@ -4523,6 +4525,7 @@ void HudGaugeTargetTriangle::render(float /*frametime*/)
object *targetp = &Objects[Player_ai->target_objnum];
// draw the targeting triangle that orbits the outside of the outer circle of the reticle
// checking !target_is_dying is correct here, since you do not want -1 (no target) or 1(is actually dying)
if (!Player->target_is_dying && maybeFlashSexp() != 1) {
hud_set_iff_color(targetp, 1);
+2 -2
View File
@@ -372,7 +372,7 @@ bool CFtpGet::IssuePasv()
return false;
}
if ( connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&dataaddr), sizeof(dataaddr)) == SOCKET_ERROR ) {
if ( connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&dataaddr), psnet_get_sockaddr_len(&dataaddr)) == SOCKET_ERROR ) {
return false;
}
@@ -392,7 +392,7 @@ int CFtpGet::ConnectControlSocket()
}
// Now we will connect to the host
if ( connect(m_ControlSock, reinterpret_cast<LPSOCKADDR>(&m_ServerAddr), sizeof(m_ServerAddr)) ) {
if ( connect(m_ControlSock, reinterpret_cast<LPSOCKADDR>(&m_ServerAddr), psnet_get_sockaddr_len(&m_ServerAddr)) ) {
m_State = FTP_STATE_CANT_CONNECT;
return 0;
}
+2 -2
View File
@@ -331,7 +331,7 @@ int ChttpGet::ConnectSocket()
timeout.tv_sec = 0;
timeout.tv_usec = 0;
int serr = connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&hostaddr), sizeof(hostaddr));
int serr = connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&hostaddr), psnet_get_sockaddr_len(&hostaddr));
int cerr = WSAGetLastError();
if (serr) {
// fail after 20 seconds
@@ -360,7 +360,7 @@ int ChttpGet::ConnectSocket()
if (m_Aborting)
return 0;
serr = connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&hostaddr), sizeof(hostaddr));
serr = connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&hostaddr), psnet_get_sockaddr_len(&hostaddr));
if (serr == 0)
break;
+3 -3
View File
@@ -1924,12 +1924,12 @@ int button_function_critical(int n, net_player *p = NULL)
if(at_self)
control_used(CYCLE_NUM_MISSLES);
if ( objp == Player_obj ) {
if ( Player_ship->weapons.num_secondary_banks <= 0 ) {
if ( Ships[objp->instance].weapons.num_secondary_banks <= 0 ) {
if ( objp == Player_obj ) {
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "This ship has no secondary weapons", 33));
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
break;
}
break;
}
polymodel *pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
+5
View File
@@ -642,3 +642,8 @@ fix timestamp_get_mission_time()
return static_cast<fix>(time / MICROSECONDS_PER_SECOND);
}
uint64_t timestamp_get_mission_time_in_microseconds()
{
return timestamp_get_microseconds() - Timestamp_microseconds_at_mission_start;
}
+2
View File
@@ -106,6 +106,7 @@ extern int timer_get_seconds(); // seconds since program started... not accur
constexpr int MILLISECONDS_PER_SECOND = 1000;
constexpr uint64_t MICROSECONDS_PER_SECOND = 1000000;
constexpr uint64_t NANOSECONDS_PER_SECOND = 1000000000;
constexpr uint64_t NANOSECONDS_PER_MICROSECOND = 1000;
// use this call to get the current counter value (which represents the time at the time
// this function is called). I.e. it doesn't return a count that would be in the future,
@@ -225,5 +226,6 @@ void timestamp_start_mission();
// Calculate the current mission time using the timestamps
fix timestamp_get_mission_time();
uint64_t timestamp_get_mission_time_in_microseconds();
#endif
+1
View File
@@ -2070,6 +2070,7 @@ void vm_angular_move_forward_vec(const vec3d* goal_f, const matrix* orient, cons
// handle bank separately, simpler, since its just a target velocity
{
CLAMP(bank_vel, -vel_limit->xyz.z, vel_limit->xyz.z);
float delta_bank_vel = bank_vel - w_in->xyz.z;
float delta_bank_accel = fl_abs(delta_bank_vel) / delta_t; // the accel required to reach the target vel this frame
float accel = (delta_bank_accel > acc_limit->xyz.z) ? acc_limit->xyz.z : delta_bank_accel;
+7 -1
View File
@@ -25,6 +25,7 @@
#include "menuui/optionsmenu.h"
#include "menuui/optionsmenumulti.h"
#include "mission/missionbriefcommon.h"
#include "missionui/missiondebrief.h"
#include "missionui/missionscreencommon.h"
#include "nebula/neb.h"
#include "network/multi.h"
@@ -707,8 +708,13 @@ void options_button_pressed(int n)
case ABORT_GAME_BUTTON:
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
choice = popup( PF_NO_NETWORKING | PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR( "Exit Game?", 374));
if ( choice == 1 )
if ( choice == 1 ) {
if (gameseq_get_state(1) == GS_STATE_DEBRIEF && (Game_mode & GM_CAMPAIGN_MODE)) {
// auto-accept mission outcome before quitting
debrief_maybe_auto_accept();
}
gameseq_post_event(GS_EVENT_QUIT_GAME);
}
break;
case CONTROL_CONFIG_BUTTON:
+12 -7
View File
@@ -951,6 +951,9 @@ void mission_campaign_store_goals_and_events()
int cur, i;
cmission *mission_obj;
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.current_mission < 0))
return;
cur = Campaign.current_mission;
name = Campaign.missions[cur].name;
@@ -1027,6 +1030,9 @@ void mission_campaign_store_variables(int persistence_type, bool store_red_alert
int cur, i, j;
cmission *mission_obj;
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.current_mission < 0))
return;
cur = Campaign.current_mission;
mission_obj = &Campaign.missions[cur];
@@ -1092,6 +1098,9 @@ void mission_campaign_store_variables(int persistence_type, bool store_red_alert
// jg18 - adapted from mission_campaign_store_variables()
void mission_campaign_store_containers(ContainerType persistence_type, bool store_red_alert)
{
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.current_mission < 0))
return;
if (!sexp_container_has_persistent_non_eternal_containers()) {
// nothing to do
return;
@@ -1891,12 +1900,8 @@ void mission_campaign_save_on_close_variables()
{
int i;
// make sure we are actually playing a campaign
if (!(Game_mode & GM_CAMPAIGN_MODE))
return;
// make sure this is a single-player campaign
if (!(Campaign.type == CAMPAIGN_TYPE_SINGLE))
// make sure we are actually playing a single-player campaign
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE) || (Campaign.current_mission < 0))
return;
// now save variables
@@ -1936,7 +1941,7 @@ void mission_campaign_save_on_close_variables()
void mission_campaign_save_on_close_containers()
{
// make sure we are actually playing a single-player campaign
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE))
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE) || (Campaign.current_mission < 0))
return;
// now save containers
+2 -1
View File
@@ -34,6 +34,7 @@
#include "scripting/scripting.h"
#include "ship/ship.h"
#include "ship/subsysdamage.h"
#include "sound/audiostr.h"
#include "sound/fsspeech.h"
#include "species_defs/species_defs.h"
#include "utils/Random.h"
@@ -1735,7 +1736,7 @@ void message_queue_message( int message_num, int priority, int timing, const cha
if (message_filename_has_fs1_wingman_prefix(filename)) {
char converted[MAX_FILENAME_LEN];
message_filename_convert_to_command(converted, filename);
if (cf_exists_full(converted, CF_TYPE_VOICE_SPECIAL)) {
if (cf_exists_full_ext(converted, CF_TYPE_VOICE_SPECIAL, NUM_AUDIO_EXT, audio_ext_list)) {
convert_to_command = true;
}
}
+7 -1
View File
@@ -6247,6 +6247,7 @@ bool post_process_mission()
SCP_string location_str;
SCP_string sexp_str;
SCP_string error_msg;
SCP_string bad_node_str;
// it's helpful to point out the goal/event, so do that if we can
int index;
@@ -6258,8 +6259,13 @@ bool post_process_mission()
convert_sexp_to_string(sexp_str, i, SEXP_ERROR_CHECK_MODE);
truncate_message_lines(sexp_str, 30);
stuff_sexp_text_string(bad_node_str, bad_node, SEXP_ERROR_CHECK_MODE);
if (!bad_node_str.empty()) { // the previous function adds a space at the end
bad_node_str.pop_back();
}
sprintf(error_msg, "%s%s.\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)\n", location_str.c_str(), sexp_error_message(result), sexp_str.c_str(), Sexp_nodes[bad_node].text);
sprintf(error_msg, "%s%s.\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)\n", location_str.c_str(), sexp_error_message(result), sexp_str.c_str(), bad_node_str.c_str());
Warning(LOCATION, "%s", error_msg.c_str());
// syntax errors are recoverable in Fred but not FS
+15 -2
View File
@@ -378,6 +378,7 @@ int Debrief_should_show_popup = 1;
static int Debrief_skip_popup_already_shown = 0;
void debrief_text_init();
bool debrief_can_accept();
void debrief_accept(int ok_to_post_start_game_event = 1);
void debrief_kick_selected_player();
@@ -925,7 +926,7 @@ void debrief_choose_voice(char *voice_dest, size_t buf_size, char *voice_base, i
if (snprintf(voice_dest, buf_size, NOX("%d_%s"), persona_index, voice_base) < static_cast<int>(buf_size))
{
// if it exists, we're done
if (cf_exists_full(voice_dest, CF_TYPE_VOICE_DEBRIEFINGS))
if (cf_exists_full_ext(voice_dest, CF_TYPE_VOICE_DEBRIEFINGS, NUM_AUDIO_EXT, audio_ext_list))
return;
}
@@ -1268,12 +1269,17 @@ void debrief_assemble_optional_mission_popup_text(char *buffer, char *mission_lo
strcat(buffer, XSTR("\n\n\nDo you want to play the optional mission?", 1491));
}
bool debrief_can_accept()
{
return !(/*Cheats_enabled ||*/ Turned_traitor || Must_replay_mission);
}
// what to do when the accept button is hit
void debrief_accept(int ok_to_post_start_game_event)
{
int go_loop = 0;
if ( (/*Cheats_enabled ||*/ Turned_traitor || Must_replay_mission) && (Game_mode & GM_CAMPAIGN_MODE) ) {
if ( !debrief_can_accept() && (Game_mode & GM_CAMPAIGN_MODE) ) {
const char *str;
int z;
@@ -2556,6 +2562,13 @@ void debrief_handle_player_drop()
debrief_rebuild_player_list();
}
void debrief_maybe_auto_accept()
{
if (debrief_can_accept()) {
debrief_accept(0);
}
}
void debrief_disable_accept()
{
}
+1
View File
@@ -25,6 +25,7 @@ void debrief_close();
void debrief_rebuild_player_list();
void debrief_handle_player_drop();
void debrief_maybe_auto_accept();
void debrief_disable_accept();
void debrief_assemble_optional_mission_popup_text(char *buffer, char *mission_loop_desc);
+15 -3
View File
@@ -885,8 +885,14 @@ void red_alert_bash_wingman_status()
mprintf(("Invalid ship class specified in red alert data for ship %s. Using mission defaults.\n", shipp->ship_name));
}
float max_hull;
if (shipp->special_hitpoints)
max_hull = shipp->ship_max_hull_strength;
else
max_hull = Ship_info[shipp->ship_info_index].max_hull_strength;
// restore hull (but not shields)
if (ras->hull >= 0.0f && ras->hull <= ship_objp->hull_strength)
if (ras->hull >= 0.0f && ras->hull <= max_hull)
ship_objp->hull_strength = ras->hull;
else
mprintf(("Invalid health in red alert data for ship %s. Using mission defaults.\n", shipp->ship_name));
@@ -979,9 +985,15 @@ void red_alert_bash_wingman_status()
}
}
float max_hull;
if (pobjp->special_hitpoints)
max_hull = pobjp->ship_max_hull_strength;
else
max_hull = Ship_info[pobjp->ship_class].max_hull_strength;
// restore hull (but not shields)
if (ras->hull >= 0.0f && ras->hull <= (pobjp->initial_hull * pobjp->ship_max_hull_strength / 100.0f))
pobjp->initial_hull = (int) (ras->hull * 100.0f / pobjp->ship_max_hull_strength);
if (ras->hull >= 0.0f && ras->hull <= max_hull)
pobjp->initial_hull = (int) (ras->hull * 100.0f / max_hull);
else
mprintf(("Invalid health in red alert data for ship %s. Using mission defaults.\n", pobjp->name));
+12 -9
View File
@@ -2385,26 +2385,29 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
int n_banks = cfread_int(fp); // Number of turrets
for ( i = 0; i < n_banks; i++ ) {
int parent; // The parent subobj of the turret (the gun base)
int physical_parent; // The subobj that the firepoints are physically attached to (the gun barrel)
int n_slots; // How many firepoints the turret has
model_subsystem *subsystemp; // The actual turret subsystem
parent = cfread_int( fp );
physical_parent = cfread_int(fp);
int base_obj = cfread_int(fp); // The parent subobj of the turret (the gun base)
int gun_obj = cfread_int(fp); // The subobj that the firepoints are physically attached to (the gun barrel)
if (base_obj != gun_obj && pm->submodel[gun_obj].parent != base_obj) {
Warning(LOCATION, "Model %s turret %s has a gun submodel that is not an immediate child object of the base", pm->filename, pm->submodel[base_obj].name);
gun_obj = base_obj; // fall back to singlepart handling
}
int snum=-1;
if ( subsystems ) {
for ( snum = 0; snum < n_subsystems; snum++ ) {
subsystemp = &subsystems[snum];
if ( parent == subsystemp->subobj_num ) {
if ( base_obj == subsystemp->subobj_num ) {
cfread_vector( &temp_vec, fp );
vm_vec_normalize_safe(&temp_vec);
subsystemp->turret_norm = temp_vec;
n_slots = cfread_int( fp );
subsystemp->turret_gun_sobj = physical_parent;
subsystemp->turret_gun_sobj = gun_obj;
if(n_slots > MAX_TFP) {
Warning(LOCATION, "Model %s has %i turret firing points on subsystem %s, maximum is %i", pm->filename, n_slots, subsystemp->name, MAX_TFP);
}
@@ -2423,8 +2426,8 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
subsystemp->turret_num_firing_points = n_slots;
// copy the subsystem index that the gun base submodel should have at this point
Assertion(pm->submodel[parent].subsys_num >= 0, "Turret gun base should have a subsystem index!");
pm->submodel[physical_parent].subsys_num = pm->submodel[parent].subsys_num;
Assertion(pm->submodel[base_obj].subsys_num >= 0, "Turret gun base should have a subsystem index!");
pm->submodel[gun_obj].subsys_num = pm->submodel[base_obj].subsys_num;
break;
}
@@ -2434,7 +2437,7 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
if ( (n_subsystems == 0) || (snum == n_subsystems) ) {
vec3d bogus;
nprintf(("Warning", "Turret submodel %i not found for turret %i in model %s\n", parent, i, pm->filename));
nprintf(("Warning", "Turret submodel %i not found for turret %i in model %s\n", base_obj, i, pm->filename));
cfread_vector( &bogus, fp );
n_slots = cfread_int( fp );
for (j = 0; j < n_slots; j++ )
+2 -2
View File
@@ -152,7 +152,7 @@ int ConnectToChatServer(char *serveraddr, char *nickname, char *trackerid)
return -1;
}
if (SOCKET_ERROR==bind(Chatsock, reinterpret_cast<LPSOCKADDR>(&Chataddr), sizeof(Chataddr)))
if (SOCKET_ERROR==bind(Chatsock, reinterpret_cast<LPSOCKADDR>(&Chataddr), psnet_get_sockaddr_len(&Chataddr)))
{
return -1;
}
@@ -164,7 +164,7 @@ int ConnectToChatServer(char *serveraddr, char *nickname, char *trackerid)
return -2;
}
if(SOCKET_ERROR == connect(Chatsock, reinterpret_cast<LPSOCKADDR>(&Chataddr), sizeof(Chataddr)))
if(SOCKET_ERROR == connect(Chatsock, reinterpret_cast<LPSOCKADDR>(&Chataddr), psnet_get_sockaddr_len(&Chataddr)))
{
if(WSAEWOULDBLOCK == WSAGetLastError())
{
+1 -1
View File
@@ -1785,7 +1785,7 @@ void multi_ingame_send_ship_update(net_player *p)
// go through the list and send all ships which are mark as OF_COULD_BE_PLAYER
while(moveup!=END_OF_LIST(&Ship_obj_list)){
//Make sure the object can be a player and is on the same team as this guy
if(Objects[moveup->objnum].flags[Object::Object_Flags::Could_be_player] && obj_team(&Objects[moveup->objnum]) == p->p_info.team){
if(Objects[moveup->objnum].flags[Object::Object_Flags::Could_be_player] && Objects[moveup->objnum].type == OBJ_SHIP && obj_team(&Objects[moveup->objnum]) == p->p_info.team){
// send the update
send_ingame_ship_update_packet(p,&Ships[Objects[moveup->objnum].instance]);
}
+2 -1
View File
@@ -1368,9 +1368,10 @@ int multi_oo_pack_data(net_player *pl, object *objp, ushort oo_flags, ubyte *dat
// Check that we are not sending too much data, if so, don't actually send.
if (packet_size + ret <= OO_MAX_DATA_SIZE) {
nprintf(("Network","Had to remove subsystems section from data packet for %s\n", shipp->ship_name));
oo_flags |= OO_SUBSYSTEMS_NEW;
packet_size += ret;
} else {
nprintf(("Network","Had to remove subsystems section from data packet for %s\n", shipp->ship_name));
}
}
}
+9 -1
View File
@@ -207,6 +207,7 @@ void multi_msg_eval_text_msg()
int multi_msg_text_process(int k)
{
char str[2];
int ascii;
// keep eating keys for a short period of time
if ( Multi_msg_eat_stamp.isValid() && !ui_timestamp_elapsed(Multi_msg_eat_stamp) ) {
@@ -248,9 +249,16 @@ int multi_msg_text_process(int k)
// stick other printable characters onto the text
default :
ascii = key_to_ascii(k);
// make sure it's actually a printable character
if (ascii == 255) {
break;
}
// if we're not already at the maximum length
if(strlen(Multi_msg_text) < MULTI_MSG_MAX_LEN){
str[0] = (char)key_to_ascii(k);
str[0] = static_cast<char>(ascii);
str[1] = '\0';
strcat_s(Multi_msg_text,str);
}
+38 -5
View File
@@ -332,8 +332,14 @@ int SENDTO(SOCKET s, char * buf, int len, int flags, SOCKADDR *to, int tolen, in
outbuf[0] = static_cast<char>(psnet_type);
memcpy(&outbuf[1], buf, static_cast<size_t>(len));
SOCKLEN_T addrlen = tolen;
if (addrlen == sizeof(SOCKADDR_STORAGE)) {
addrlen = psnet_get_sockaddr_len(reinterpret_cast<SOCKADDR_STORAGE*>(to));
}
// send it
return static_cast<int>( sendto(s, outbuf, len + 1, flags, reinterpret_cast<LPSOCKADDR>(to), tolen) );
return static_cast<int>( sendto(s, outbuf, len + 1, flags, reinterpret_cast<LPSOCKADDR>(to), addrlen) );
}
/**
@@ -547,7 +553,7 @@ bool psnet_init_my_addr()
continue;
}
rval = connect(tsock, reinterpret_cast<LPSOCKADDR>(&remote_addr), sizeof(remote_addr));
rval = connect(tsock, reinterpret_cast<LPSOCKADDR>(&remote_addr), psnet_get_sockaddr_len(&remote_addr));
if (rval) {
continue;
@@ -586,7 +592,7 @@ bool psnet_init_my_addr()
continue;
}
rval = connect(tsock, reinterpret_cast<LPSOCKADDR>(&remote_addr), sizeof(remote_addr));
rval = connect(tsock, reinterpret_cast<LPSOCKADDR>(&remote_addr), psnet_get_sockaddr_len(&remote_addr));
if (rval) {
continue;
@@ -919,6 +925,25 @@ static void psnet_sockaddr_storage_to_in6(const SOCKADDR_STORAGE *addr, SOCKADDR
}
}
/**
* @brief Helper to get the exact length of a specific protocol struct from
* a generic storage struct
*
* @param addr
* @return SOCKLEN_T
*/
SOCKLEN_T psnet_get_sockaddr_len(const SOCKADDR_STORAGE *addr)
{
// internally we should always use IPv6, but cover the bases
if (addr->ss_family == AF_INET6) {
return static_cast<SOCKLEN_T>(sizeof(SOCKADDR_IN6));
} else if (addr->ss_family == AF_INET) {
return static_cast<SOCKLEN_T>(sizeof(SOCKADDR_IN));
} else {
return static_cast<SOCKLEN_T>(sizeof(SOCKADDR_STORAGE));
}
}
/**
* Helper to map IPv4 to IPv6
*/
@@ -1126,6 +1151,11 @@ bool psnet_get_addr(const char *host, const char *port, SOCKADDR_STORAGE *addr,
SOCKADDR_IN6 si4to6;
int rval;
if (addr) {
// by the spec, sockaddr_in6 must be zero'd, so just do that always
memset(addr, 0, sizeof(*addr));
}
memset(&si4to6, 0, sizeof(si4to6));
memset(&hints, 0, sizeof(hints));
@@ -2317,9 +2347,12 @@ bool psnet_init_socket()
psnet_set_socket_options();
// bind the socket
psnet_get_addr(nullptr, Psnet_default_port, &sockaddr);
if ( !psnet_get_addr(nullptr, Psnet_default_port, &sockaddr) ) {
ml_printf("Failed to get bind addr!");
return false;
}
if ( bind(Psnet_socket, reinterpret_cast<LPSOCKADDR>(&sockaddr), sizeof(sockaddr)) == SOCKET_ERROR) {
if ( bind(Psnet_socket, reinterpret_cast<LPSOCKADDR>(&sockaddr), psnet_get_sockaddr_len(&sockaddr)) == SOCKET_ERROR) {
Psnet_failure_code = WSAGetLastError();
if (Psnet_failure_code == WSAEADDRINUSE) {
+3
View File
@@ -178,6 +178,9 @@ bool psnet_get_addr(const char *host, uint16_t port, SOCKADDR_STORAGE *addr, int
#define ADDR_FLAG_NUMERIC_SERVICE (1<<0)
#define ADDR_FLAG_PREFER_IPV4 (1<<1)
// get protocol struct length from generic storage struct
SOCKLEN_T psnet_get_sockaddr_len(const SOCKADDR_STORAGE *addr);
// map an IPv4 address to IPv6
void psnet_map4to6(const in_addr *in4, in6_addr *in6);
+22 -8
View File
@@ -300,7 +300,7 @@ static char szWinTitle[128];
static char szWinClass[128];
static int Os_inited = 0;
static SCP_vector<SDL_Event> buffered_events;
static SCP_vector<SDL_Event> deferred_events;
int Os_debugger_running = 0;
@@ -713,11 +713,11 @@ namespace os
}
}
void os_ignore_events() {
void os_defer_events_on_load_screen() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
// Add event to buffer
buffered_events.push_back(event);
// Add event to be handled later
deferred_events.push_back(event);
}
}
@@ -745,14 +745,28 @@ static void handle_sdl_event(const SDL_Event& event) {
}
}
void os_remove_deferred_cutscene_key_events() {
deferred_events.erase(
std::remove_if(deferred_events.begin(), deferred_events.end(), [](const SDL_Event &event)
{
return (event.type == SDL_KEYUP) && (
event.key.keysym.sym == SDLK_KP_ENTER ||
event.key.keysym.sym == SDLK_RETURN ||
event.key.keysym.sym == SDLK_SPACE
);
}),
deferred_events.end()
);
}
void os_poll()
{
// Replay the buffered events
auto end = buffered_events.end();
for (auto it = buffered_events.begin(); it != end; ++it) {
// Replay the deferred events
auto end = deferred_events.end();
for (auto it = deferred_events.begin(); it != end; ++it) {
handle_sdl_event(*it);
}
buffered_events.clear();
deferred_events.clear();
SDL_Event event;
+5 -2
View File
@@ -64,9 +64,12 @@ int os_foreground();
// process management --------------------------------------------------------------
/**
* @brief Removes all pending events and ignores them
* @brief Defers all pending events to be handled later
*/
void os_ignore_events();
void os_defer_events_on_load_screen();
// removes key events that may unintentionally skip the cutscene
void os_remove_deferred_cutscene_key_events();
// call to process windows messages. only does something in non THREADED mode
void os_poll();
+37 -24
View File
@@ -25,11 +25,20 @@
#include "cfile/cfilesystem.h"
#include "parse/parselo.h"
static const char *FILTERS_ENABLED_BY_DEFAULT[] =
{
"error",
"warning",
"general",
"scripting"
};
struct outwnd_filter_struct {
char name[NAME_LENGTH];
bool enabled;
};
// This technique is necessary to avoid a crash in FRED. See commit e624f6c1.
static SCP_vector<outwnd_filter_struct>& filter_vector()
{
static SCP_vector<outwnd_filter_struct> vec;
@@ -58,7 +67,16 @@ void load_filter_info()
FILE* fp;
char pathname[MAX_PATH_LEN];
char inbuf[NAME_LENGTH + 4];
outwnd_filter_struct new_filter;
outwnd_filter_struct* filter_ptr;
// add default-enabled filters
for (const char *name : FILTERS_ENABLED_BY_DEFAULT) {
filter_vector().emplace_back();
filter_ptr = &filter_vector().back();
strcpy_s(filter_ptr->name, name);
filter_ptr->enabled = true;
}
outwnd_filter_loaded = 1;
@@ -69,30 +87,18 @@ void load_filter_info()
if (!fp) {
Outwnd_no_filter_file = 1;
strcpy_s( new_filter.name, "error" );
new_filter.enabled = true;
filter_vector().push_back( new_filter );
strcpy_s( new_filter.name, "general" );
new_filter.enabled = true;
filter_vector().push_back( new_filter );
strcpy_s( new_filter.name, "warning" );
new_filter.enabled = true;
filter_vector().push_back( new_filter );
return;
}
Outwnd_no_filter_file = 0;
while ( fgets(inbuf, NAME_LENGTH+3, fp) ) {
bool enabled;
if (*inbuf == '+')
new_filter.enabled = true;
enabled = true;
else if (*inbuf == '-')
new_filter.enabled = false;
enabled = false;
else
continue; // skip everything else
@@ -101,17 +107,24 @@ void load_filter_info()
inbuf[z] = 0;
Assert( strlen(inbuf+1) < NAME_LENGTH );
strcpy_s(new_filter.name, inbuf + 1);
const char *name = &inbuf[1];
if ( !stricmp(new_filter.name, "error") ) {
new_filter.enabled = true;
} else if ( !stricmp(new_filter.name, "general") ) {
new_filter.enabled = true;
} else if ( !stricmp(new_filter.name, "warning") ) {
new_filter.enabled = true;
// find it, or create it
filter_ptr = nullptr;
for (auto &filter : filter_vector()) {
if (!stricmp(filter.name, name)) {
filter_ptr = &filter;
break;
}
}
if (filter_ptr == nullptr) {
filter_vector().emplace_back();
filter_ptr = &filter_vector().back();
strcpy_s(filter_ptr->name, name);
}
filter_vector().push_back( new_filter );
// set it as enabled or not, depending on config
filter_ptr->enabled = enabled;
}
if ( ferror(fp) && !feof(fp) )
+179 -67
View File
@@ -991,8 +991,12 @@ bool ship_class_unchanged(const ship_registry_entry *ship_entry);
void multi_sexp_modify_variable();
// jg18
// container_value_index is for using one specific value from acontainer
// container_value_index is for using one specific value from a container
int copy_node_to_replacement_args(int node, int container_value_index = -1);
// checks whether the node's value can change over the course of the mission
bool is_node_value_dynamic(int node);
// returns 0 on success or a SEXP_CHECK_* value on failure
int check_dynamic_value_node_type(int node, bool is_string, bool is_number);
#define NO_OPERATOR_INDEX_DEFINED -2
#define NOT_A_SEXP_OPERATOR -1
@@ -2121,6 +2125,12 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
} else if (Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA) {
// this is an instance of "Replace Container Data"
// can't be used in special argument list
if (type == OPF_ANYTHING || type == OPF_DATA_OR_STR_CONTAINER) {
return SEXP_CHECK_TYPE_MISMATCH;
}
const int modifier_node = Sexp_nodes[node].first;
if (modifier_node == -1) {
return SEXP_CHECK_MISSING_CONTAINER_MODIFIER;
@@ -2464,9 +2474,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
}
Assert(ship_node >= 0);
// we can't check special-arg ships
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
break;
if (is_node_value_dynamic(ship_node)) {
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
if (dyn_val_check) {
return dyn_val_check;
} else {
// since the value changes at runtime
// we can check only the type
break;
}
}
auto shipname = CTEXT(ship_node);
shipnum = ship_name_lookup(shipname, 1);
@@ -2551,9 +2568,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
ship_node = CDR(op_node);
// we can't check special-arg ships
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
break;
if (is_node_value_dynamic(ship_node)) {
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
if (dyn_val_check) {
return dyn_val_check;
} else {
// since the value changes at runtime
// we can check only the type
break;
}
}
auto shipname = CTEXT(ship_node);
shipnum = ship_name_lookup(shipname, 1);
@@ -2920,9 +2944,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
auto ship_node = Sexp_nodes[op_node].rest;
Assert(ship_node >= 0);
// we can't check special-arg ships
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
break;
if (is_node_value_dynamic(ship_node)) {
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
if (dyn_val_check) {
return dyn_val_check;
} else {
// since the value changes at runtime
// we can check only the type
break;
}
}
ship_num = ship_name_lookup(CTEXT(ship_node), 1); // Goober5000 - include players
if (ship_num < 0) {
@@ -3060,9 +3091,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
z = Sexp_nodes[z].rest; // first argument of operator should be mission name
Assert(z >= 0);
// can't check special-arg
if (Sexp_nodes[z].flags & SNF_SPECIAL_ARG_IN_NODE)
break;
if (is_node_value_dynamic(z)) {
const int dyn_val_check = check_dynamic_value_node_type(z, true, false);
if (dyn_val_check) {
return dyn_val_check;
} else {
// since the value changes at runtime
// we can check only the type
break;
}
}
// look for mission
count = count_items_with_name(CTEXT(z), Campaign.missions, Campaign.num_missions);
@@ -3145,9 +3183,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
}
Assert(ship_node >= 0);
// can't check special-arg
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
break;
if (is_node_value_dynamic(ship_node)) {
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
if (dyn_val_check) {
return dyn_val_check;
} else {
// since the value changes at runtime
// we can check only the type
break;
}
}
// look for the ship that has this dockpoint
ship_num = ship_name_lookup(CTEXT(ship_node), 1);
@@ -3508,7 +3553,7 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
if ( type2 != SEXP_ATOM_STRING )
return SEXP_CHECK_TYPE_MISMATCH;
if ( stricmp(CTEXT(node), NOX("default")) != 0 && !strstr(CTEXT(node), NOX(".pof")) )
if ( stricmp(CTEXT(node), NOX("default")) != 0 && stricmp(CTEXT(node), NOX("none")) != 0 && !strstr(CTEXT(node), NOX(".pof")) )
return SEXP_CHECK_INVALID_SKYBOX_NAME;
break;
@@ -4506,36 +4551,23 @@ void stuff_sexp_text_string(SCP_string &dest, int node, int mode)
Assertion(sexp_variables_index != -1, "Couldn't find variable: %s\n", Sexp_nodes[node].text);
Assert((Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING));
// Error check - can be Fred or FreeSpace
if (mode == SEXP_ERROR_CHECK_MODE)
auto var_name = (Fred_running) ? Sexp_nodes[node].text : Sexp_variables[sexp_variables_index].variable_name;
auto var_contents = Sexp_variables[sexp_variables_index].text;
// number
if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER)
{
if (Fred_running)
sprintf(dest, "@%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
else
sprintf(dest, "@%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER);
sprintf(dest, "@%s[%s] ", var_name, var_contents);
}
// string
else if (Sexp_nodes[node].subtype == SEXP_ATOM_STRING)
{
Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING);
sprintf(dest, "\"@%s[%s]\" ", var_name, var_contents);
}
else
{
// number
if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER)
{
Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER);
// Save as string - only Fred
Assert(mode == SEXP_SAVE_MODE);
sprintf(dest, "@%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
}
// string
else
{
Assert(Sexp_nodes[node].subtype == SEXP_ATOM_STRING);
Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING);
// Save as string - only Fred
Assert(mode == SEXP_SAVE_MODE);
sprintf(dest, "\"@%s[%s]\" ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
}
}
UNREACHABLE("SEXP variable nodes must be SEXP_ATOM_NUMBER or SEXP_ATOM_STRING!");
}
// not a variable
else
@@ -4917,8 +4949,8 @@ const ship_registry_entry *eval_ship(int node)
auto ship_it = Ship_registry_map.find(CTEXT(node));
if (ship_it != Ship_registry_map.end())
{
// cache the value, unless this node is a variable or argument because the value may change
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE))
// cache the value if it can't change later
if (!is_node_value_dynamic(node))
Sexp_nodes[node].cache = new sexp_cached_data(OPF_SHIP, -1, ship_it->second);
return &Ship_registry[ship_it->second];
@@ -4960,8 +4992,8 @@ wing *eval_wing(int node)
{
auto wingp = &Wings[wing_num];
// cache the value, unless this node is a variable or argument because the value may change
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE))
// cache the value if it can't change later
if (!is_node_value_dynamic(node))
Sexp_nodes[node].cache = new sexp_cached_data(OPF_WING, wingp);
return wingp;
@@ -5002,10 +5034,8 @@ int sexp_atoi(int node)
int num = atoi(CTEXT(node));
// cache the value, unless this node is a variable, argument, or container data, because the value may change
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) &&
!(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) &&
(Sexp_nodes[node].subtype != SEXP_ATOM_CONTAINER_DATA))
// cache the value if it can't change later
if (!is_node_value_dynamic(node))
Sexp_nodes[node].cache = new sexp_cached_data(OPF_NUMBER, num, -1);
return num;
@@ -11005,6 +11035,13 @@ void sexp_change_argument_validity(int n, bool invalidate)
// first we must check if the arg_handler marks a selection. At the moment random-of is the only one that does this
arg_n = CDR(arg_handler);
while (invalidate && (arg_n != -1)) {
Assertion(Sexp_nodes[arg_n].subtype != SEXP_ATOM_CONTAINER_NAME,
"Attempt to use invalidate-argument with container %s. Please report!",
Sexp_nodes[arg_n].text);
Assertion(Sexp_nodes[arg_n].subtype != SEXP_ATOM_CONTAINER_DATA,
"Attempt to use invalidate-argument with data from container %s. Please report!",
Sexp_nodes[arg_n].text);
if (Sexp_nodes[arg_n].flags & SNF_ARGUMENT_SELECT) {
// now check if the selected argument matches the one we want to invalidate
if (!strcmp(CTEXT(n), CTEXT(arg_n))) {
@@ -11023,6 +11060,13 @@ void sexp_change_argument_validity(int n, bool invalidate)
arg_n = CDR(arg_handler);
while (arg_n != -1)
{
Assertion(Sexp_nodes[arg_n].subtype != SEXP_ATOM_CONTAINER_NAME,
"Attempt to change argument validity of container %s. Please report!",
Sexp_nodes[arg_n].text);
Assertion(Sexp_nodes[arg_n].subtype != SEXP_ATOM_CONTAINER_DATA,
"Attempt to change argument validity of data from container %s. Please report!",
Sexp_nodes[arg_n].text);
// match?
if (!strcmp(CTEXT(n), CTEXT(arg_n)))
{
@@ -11802,7 +11846,7 @@ void sexp_clear_goals(int n)
if (ship_entry)
{
if (ship_entry->status != ShipStatus::PRESENT)
return; // ship not around anymore???? then forget it!
continue; // ship not around anymore???? then forget it!
ai_clear_ship_goals(&(Ai_info[ship_entry->shipp->ai_index]));
continue;
@@ -11812,7 +11856,7 @@ void sexp_clear_goals(int n)
if (wingp)
{
if (wingp->flags[Ship::Wing_Flags::Gone])
return; // wing not around anymore???? then forget it!
continue; // wing not around anymore???? then forget it!
ai_clear_wing_goals(wingp);
}
@@ -12184,6 +12228,11 @@ void multi_sexp_hud_display_gauge()
void sexp_player_use_ai(int flag)
{
Player_use_ai = flag ? 1 : 0;
if (!flag) {
Player_ai->ai_override_flags.reset();
Player_obj->phys_info.flags &= ~PF_MANEUVER_NO_DAMP;
}
}
// Karajorma
@@ -13353,7 +13402,8 @@ void sexp_send_one_message( const char *name, const char *who_from, const char *
} else if ( !stricmp(who_from, "<none>") ) {
source = MESSAGE_SOURCE_NONE;
} else {
Assertion(false, "Curious... all message sources should have been covered");
Warning(LOCATION, "Invalid message source '%s'. Unable to send message.", who_from);
return;
}
if ( ship_index == -1 ){
@@ -19141,13 +19191,12 @@ void sexp_set_skybox_model_preload(const char *name)
{
int i;
if ( !stricmp("default", name) ) {
// if there isn't a mission skybox model then don't load one
if ( strlen(The_mission.skybox_model) ) {
i = model_load( The_mission.skybox_model, 0, nullptr );
model_page_in_textures( i );
}
} else {
if (!stricmp("default", name)) {
name = The_mission.skybox_model;
}
// if there isn't a skybox model then don't load one
if ( strlen(name) && stricmp(name, "none") != 0 ) {
i = model_load( name, 0, nullptr );
model_page_in_textures( i );
}
@@ -22069,8 +22118,8 @@ int sexp_string_to_int(int n)
int num = atoi(buf);
// cache the value, unless this node is a variable or argument because the value may change
if (!(Sexp_nodes[n].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[n].flags & SNF_SPECIAL_ARG_IN_NODE))
// cache the value if it can't change later
if (!is_node_value_dynamic(n))
Sexp_nodes[n].cache = new sexp_cached_data(OPF_NUMBER, num, -1);
return num;
@@ -31349,6 +31398,9 @@ const char *sexp_error_message(int num)
case SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE:
return "Wrong container data type";
case SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE:
return "Invalid special argument type";
default:
Warning(LOCATION, "Unhandled sexp error code %d!", num);
return "Unhandled sexp error code!";
@@ -31679,6 +31731,47 @@ int copy_node_to_replacement_args(int node, int container_value_index)
return num_args;
}
bool is_node_value_dynamic(int node)
{
Assertion(node >= 0, "Attempt to check whether a non-existent node has a dynamic value. Please report!");
return (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) || (Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) ||
(Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA);
}
int check_dynamic_value_node_type(int node, bool is_string, bool is_number)
{
Assertion(is_node_value_dynamic(node),
"Attempt to check dynamic value type of a node that isn't a dynamic value. Please report!");
if (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) {
const int var_index = sexp_get_variable_index(node);
Assertion(var_index != -1,
"Attempt to get index of invalid variable %s. Please report!",
Sexp_nodes[node].text);
const int var_type = Sexp_variables[var_index].type;
if (((var_type & SEXP_VARIABLE_STRING) && !is_string) || ((var_type & SEXP_VARIABLE_NUMBER) && !is_number)) {
return SEXP_CHECK_INVALID_VARIABLE_TYPE;
}
} else if (Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) {
if (((Sexp_nodes[node].subtype == SEXP_ATOM_STRING) && !is_string) ||
((Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) && !is_number)) {
return SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE;
}
} else if (Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA) {
const auto *p_container = get_sexp_container(Sexp_nodes[node].text);
Assertion(p_container,
"Attempt to check dynamic value node type of data of nonexistent container %s. Please report!",
Sexp_nodes[node].text);
if (!check_container_data_sexp_arg_type(p_container->type, is_string, is_number)) {
return SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE;
}
} else {
UNREACHABLE("Unhandled dynamic value node %s. Please report!", Sexp_nodes[node].text);
}
return 0;
}
void sexp_modify_variable(int n)
{
@@ -32225,6 +32318,21 @@ int get_category(int sexp_id)
return category;
}
// Goober5000
const char *get_category_name(int category_id)
{
if (category_id == OP_CATEGORY_CHANGE2)
category_id = OP_CATEGORY_CHANGE;
for (auto &category : op_menu)
{
if (category.id == category_id)
return category.name.c_str();
}
return "<unknown>";
}
// Goober5000
int category_of_subcategory(int subcategory_id)
{
@@ -33819,7 +33927,8 @@ SCP_vector<sexp_help_struct> Sexp_help = {
// Goober5000
{ OP_INVALIDATE_ARGUMENT, "Invalidate-argument (Conditional operator)\r\n"
"\tRemoves an argument from future consideration as a " SEXP_ARGUMENT_STRING " special data item.\r\n"
"\tFor argument-related SEXPs that accept containers as arguments, the retrieved arguments can't be individually invalidated.\r\n\r\n"
"\tFor argument-related SEXPs that accept containers as arguments, the retrieved arguments can't be individually invalidated.\r\n"
"\tAdditionally, you can't invalidate individual container arguments.\r\n\r\n"
"Takes 1 or more arguments...\r\n"
"\tAll:\tThe argument to remove from the preceding argument list." },
@@ -33827,19 +33936,22 @@ SCP_vector<sexp_help_struct> Sexp_help = {
{ OP_VALIDATE_ARGUMENT, "Validate-argument (Conditional operator)\r\n"
"\tRestores an argument for future consideration as a " SEXP_ARGUMENT_STRING " special data item.\r\n"
"\tIf the argument hasn't been previously invalidated, it will do nothing.\r\n"
"\tFor argument-related SEXPs that accept containers as arguments, the retrieved arguments can't be individually validated.\r\n\r\n"
"\tFor argument-related SEXPs that accept containers as arguments, the retrieved arguments can't be individually validated.\r\n"
"\tAdditionally, you can't validate individual container arguments.\r\n\r\n"
"Takes 1 or more arguments...\r\n"
"\tAll:\tThe argument to restore to the preceding argument list." },
// Karajorma
{ OP_INVALIDATE_ALL_ARGUMENTS, "Invalidate-all-arguments (Conditional operator)\r\n"
"\tRemoves all argument from future consideration as " SEXP_ARGUMENT_STRING " special data items.\r\n"
"\tFor argument-related SEXPs that accept containers as arguments, container arguments will also be invalidated, but they can't be indvidiually validated.\r\n\r\n"
"Takes no arguments." },
// Karajorma
{ OP_VALIDATE_ALL_ARGUMENTS, "Validate-all-arguments (Conditional operator)\r\n"
"\tRestores all arguments for future consideration as " SEXP_ARGUMENT_STRING " special data items.\r\n"
"\tIf the argument hasn't been previously invalidated, it will do nothing.\r\n"
"\tFor argument-related SEXPs that accept containers as arguments, container arguments will also be validated, but they can't be indvidiually invalidated.\r\n\r\n"
"Takes no arguments." },
// Goober5000 - added wing capability
@@ -36735,7 +36847,7 @@ SCP_vector<sexp_help_struct> Sexp_help = {
"\t3-8:\tSet or unset the following skyboxes flags (optional)\r\n"
"\t\t\tadd-lighting, no-transparency, add-zbuffer\r\n"
"\t\t\tadd-culling, no-glowmaps, force-clamp\r\n\r\n"
"Note: If the model filename is set to \"default\" with no extension then it will switch to the mission supplied default skybox."
"Note: If the model filename is set to \"default\" with no extension then it will switch to the mission supplied default skybox. If it is an empty string or \"none\" then it will remove any current skybox."
},
// Goober5000
+3
View File
@@ -1117,6 +1117,7 @@ const char *CTEXT(int n);
#define SEXP_CHECK_WRONG_CONTAINER_TYPE -170
#define SEXP_CHECK_INVALID_ANIMATION -171
#define SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE -172
#define SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE -173
#define TRAINING_CONTEXT_SPEED (1<<0)
@@ -1296,6 +1297,7 @@ extern int query_operator_argument_type(int op, int argnum);
extern void update_sexp_references(const char *old_name, const char *new_name);
extern void update_sexp_references(const char *old_name, const char *new_name, int format);
extern int query_referenced_in_sexp(int mode, const char *name, int *node);
extern void stuff_sexp_text_string(SCP_string &dest, int node, int mode);
extern int build_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode);
extern int sexp_query_type_match(int opf, int opr);
extern bool sexp_recoverable_error(int num);
@@ -1357,6 +1359,7 @@ void set_turret_secondary_ammo(ship_subsys *turret, int requested_bank, int requ
extern int get_category(int sexp_id);
extern int category_of_subcategory(int subcategory_id);
extern int get_subcategory(int sexp_id);
extern const char *get_category_name(int category_id);
// Goober5000
extern void sexp_music_close();
+16 -3
View File
@@ -125,8 +125,10 @@ void parse_sexp_table(const char* filename) {
luaSexp->parseTable();
int op = add_dynamic_sexp(std::move(instance), SEXP_GOAL_OPERATOR);
luaSexp->registerAIMode(op);
luaSexp->maybeRegisterPlayerOrder(op);
if (op >= 0) {
luaSexp->registerAIMode(op);
luaSexp->maybeRegisterPlayerOrder(op);
}
}
required_string("#End");
}
@@ -177,8 +179,19 @@ int add_dynamic_sexp(std::unique_ptr<DynamicSEXP>&& sexp, int type)
new_op.min = sexp->getMinimumArguments();
new_op.max = sexp->getMaximumArguments();
int free_op_index = get_next_free_operator(sexp->getCategory());
if (free_op_index >= 256) {
Warning(LOCATION, "There are too many SEXPs in the %s category. The SEXP %s will not be added.", get_category_name(sexp->getCategory()), sexp->getName().c_str());
return -1;
}
if (Operators.size() >= FIRST_OP) {
Warning(LOCATION, "There are too many total SEXPs. The SEXP %s will not be added.", sexp->getName().c_str());
return -1;
}
// For now, all dynamic SEXPS are only valid in missions
new_op.value = get_next_free_operator(sexp->getCategory()) | sexp->getCategory() | OP_NONCAMPAIGN_FLAG;
new_op.value = free_op_index | sexp->getCategory() | OP_NONCAMPAIGN_FLAG;
new_op.type = type;
global.operator_const_mapping.insert(std::make_pair(new_op.value, std::move(sexp)));
+2 -2
View File
@@ -168,7 +168,7 @@ void physics_sim_rot(matrix * orient, physics_info * pi, float sim_time )
shock_amplitude = pi->shockwave_shake_amp * shock_fraction_time_left;
}
}
else if (pi->flags & PF_NO_DAMP) {
else if (pi->flags & PF_MANEUVER_NO_DAMP) {
rotdamp = 0.0f;
}
else {
@@ -305,7 +305,7 @@ void physics_sim_vel(vec3d * position, physics_info * pi, float sim_time, matrix
}
}
// no damping at all
else if (pi->flags & PF_NO_DAMP) {
else if (pi->flags & PF_MANEUVER_NO_DAMP) {
damp = vmd_zero_vector;
}
// newtonian
+1 -1
View File
@@ -32,7 +32,7 @@
#define PF_GLIDING (1 << 14)
#define PF_FORCE_GLIDE (1 << 15)
#define PF_NEWTONIAN_DAMP (1 << 16) // SUSHI: Whether or not to use newtonian dampening
#define PF_NO_DAMP (1 << 17) // Goober5000 - don't damp velocity changes in physics; used for instantaneous acceleration
#define PF_MANEUVER_NO_DAMP (1 << 17) // Goober5000 - don't damp velocity changes in physics; used for instantaneous acceleration
//information for physics sim for an object
typedef struct physics_info {
+35 -3
View File
@@ -1134,11 +1134,20 @@ void pilotfile::csg_write_variables()
void pilotfile::csg_read_settings()
{
clamped_range_warnings.clear();
// sound/voice/music
if (!Using_in_game_options) {
snd_set_effects_volume(cfread_float(cfp));
event_music_set_volume(cfread_float(cfp));
snd_set_voice_volume(cfread_float(cfp));
float temp_volume = cfread_float(cfp);
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Effects Volume");
snd_set_effects_volume(temp_volume);
temp_volume = cfread_float(cfp);
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Music Volume");
event_music_set_volume(temp_volume);
temp_volume = cfread_float(cfp);
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Voice Volume");
snd_set_voice_volume(temp_volume);
Briefing_voice_enabled = cfread_int(cfp) != 0;
} else {
@@ -1154,13 +1163,20 @@ void pilotfile::csg_read_settings()
// skill level
Game_skill_level = cfread_int(cfp);
clamp_value_with_warn(&Game_skill_level, 0, 4, "Game Skill Level");
// input options
if (!Using_in_game_options) {
Use_mouse_to_fly = cfread_int(cfp) != 0;
Mouse_sensitivity = cfread_int(cfp);
clamp_value_with_warn(&Mouse_sensitivity, 0, 9, "Mouse Sensitivity");
Joy_sensitivity = cfread_int(cfp);
clamp_value_with_warn(&Joy_sensitivity, 0, 9, "Joystick Sensitivity");
Joy_dead_zone_size = cfread_int(cfp);
clamp_value_with_warn(&Joy_dead_zone_size, 0, 45, "Joystick Deadzone");
} else {
// The values are set by the in-game menu but we still need to read the int from the file to maintain the correct offset
cfread_int(cfp);
@@ -1184,28 +1200,44 @@ void pilotfile::csg_read_settings()
dummy = cfread_int(cfp);
dummy = cfread_int(cfp);
}
if (!clamped_range_warnings.empty()) {
ReleaseWarning(LOCATION, "The following values in the campaign save file were out of bounds and were automatically reset:\n%s\nPlease check your settings!\n", clamped_range_warnings.c_str());
clamped_range_warnings.clear();
}
}
void pilotfile::csg_write_settings()
{
startSection(Section::Settings);
clamped_range_warnings.clear();
// sound/voice/music
clamp_value_with_warn(&Master_sound_volume, 0.f, 1.f, "Effects Volume");
cfwrite_float(Master_sound_volume, cfp);
clamp_value_with_warn(&Master_event_music_volume, 0.f, 1.f, "Music Volume");
cfwrite_float(Master_event_music_volume, cfp);
clamp_value_with_warn(&Master_voice_volume, 0.f, 1.f, "Voice Volume");
cfwrite_float(Master_voice_volume, cfp);
cfwrite_int(Briefing_voice_enabled ? 1 : 0, cfp);
// skill level
clamp_value_with_warn(&Game_skill_level, 0, 4, "Game Skill Level");
cfwrite_int(Game_skill_level, cfp);
// input options
cfwrite_int(Use_mouse_to_fly, cfp);
clamp_value_with_warn(&Mouse_sensitivity, 0, 9, "Mouse Sensitivity");
cfwrite_int(Mouse_sensitivity, cfp);
clamp_value_with_warn(&Joy_sensitivity, 0, 9, "Joystick Sensitivity");
cfwrite_int(Joy_sensitivity, cfp);
clamp_value_with_warn(&Joy_dead_zone_size, 0, 45, "Joystick Deadzone");
cfwrite_int(Joy_dead_zone_size, cfp);
if (!clamped_range_warnings.empty()) {
ReleaseWarning(LOCATION, "The following values were out of bounds when saving the campaign file and were automatically reset.\n%s\nThis shouldn't be possible, please contact the FreeSpace 2 Open Source Code Project!\n", clamped_range_warnings.c_str());
clamped_range_warnings.clear();
}
endSection();
}
+11
View File
@@ -157,6 +157,17 @@ class pilotfile {
// file offset of the size value for the current section (set with startSection())
size_t m_size_offset;
SCP_string clamped_range_warnings = "";
template <typename T> void clamp_value_with_warn(T* value, T min, T max, const char* name) {
if (*value < min) {
clamped_range_warnings += SCP_string(name) + ": too low (" + std::to_string(*value) + "). Will reset to minimum (" + std::to_string(min) + ")\n";
*value = min;
} else if (*value > max) {
clamped_range_warnings += SCP_string(name) + ": too high (" + std::to_string(*value) + "). Will reset to maximum (" + std::to_string(max) + ")\n";
*value = max;
}
}
// --------------------------------------------------------------------
// PLR specific
+49 -3
View File
@@ -795,11 +795,20 @@ void pilotfile::plr_write_controls()
void pilotfile::plr_read_settings()
{
clamped_range_warnings.clear();
// sound/voice/music
if (!Using_in_game_options) {
snd_set_effects_volume(handler->readFloat("master_sound_volume"));
event_music_set_volume(handler->readFloat("master_event_music_volume"));
snd_set_voice_volume(handler->readFloat("aster_voice_volume"));
float temp_volume = handler->readFloat("master_sound_volume");
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Effects Volume");
snd_set_effects_volume(temp_volume);
temp_volume = handler->readFloat("master_event_music_volume");
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Music Volume");
event_music_set_volume(temp_volume);
temp_volume = handler->readFloat("aster_voice_volume");
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Voice Volume");
snd_set_voice_volume(temp_volume);
Briefing_voice_enabled = handler->readInt("briefing_voice_enabled") != 0;
} else {
@@ -814,24 +823,37 @@ void pilotfile::plr_read_settings()
// skill level
Game_skill_level = handler->readInt("game_skill_level");
clamp_value_with_warn(&Game_skill_level, 0, 4, "Skill Level");
// input options
if (!Using_in_game_options) {
Use_mouse_to_fly = handler->readInt("use_mouse_to_fly") != 0;
Mouse_sensitivity = handler->readInt("mouse_sensitivity");
clamp_value_with_warn(&Mouse_sensitivity, 0, 9, "Mouse Sensitivity");
Joy_sensitivity = handler->readInt("joy_sensitivity");
clamp_value_with_warn(&Joy_sensitivity, 0, 9, "Joystick Sensitivity");
Joy_dead_zone_size = handler->readInt("joy_dead_zone_size");
clamp_value_with_warn(&Joy_dead_zone_size, 0, 45, "Joystick Deadzone");
// detail
Detail.setting = handler->readInt("setting");
clamp_value_with_warn(&Detail.setting, -1, NUM_DEFAULT_DETAIL_LEVELS - 1, "Detail Level Preset");
Detail.nebula_detail = handler->readInt("nebula_detail");
clamp_value_with_warn(&Detail.nebula_detail, 0, MAX_DETAIL_LEVEL, "Nebula Detail");
Detail.detail_distance = handler->readInt("detail_distance");
clamp_value_with_warn(&Detail.detail_distance, 0, MAX_DETAIL_LEVEL, "Model Detail");
Detail.hardware_textures = handler->readInt("hardware_textures");
clamp_value_with_warn(&Detail.hardware_textures, 0, MAX_DETAIL_LEVEL, "3D Hardware Textures");
Detail.num_small_debris = handler->readInt("num_small_debris");
clamp_value_with_warn(&Detail.num_small_debris, 0, MAX_DETAIL_LEVEL, "Impact Effects");
Detail.num_particles = handler->readInt("num_particles");
clamp_value_with_warn(&Detail.num_particles, 0, MAX_DETAIL_LEVEL, "Particles");
Detail.num_stars = handler->readInt("num_stars");
clamp_value_with_warn(&Detail.num_stars, 0, MAX_DETAIL_LEVEL, "Stars");
Detail.shield_effects = handler->readInt("shield_effects");
clamp_value_with_warn(&Detail.shield_effects, 0, MAX_DETAIL_LEVEL, "Shield Hit Effects");
Detail.lighting = handler->readInt("lighting");
clamp_value_with_warn(&Detail.lighting, 0, MAX_DETAIL_LEVEL, "Lighting");
Detail.targetview_model = handler->readInt("targetview_model");
Detail.planets_suns = handler->readInt("planets_suns");
Detail.weapon_extras = handler->readInt("weapon_extras");
@@ -856,6 +878,10 @@ void pilotfile::plr_read_settings()
handler->readInt("planets_suns");
handler->readInt("weapon_extras");
}
if (!clamped_range_warnings.empty()) {
ReleaseWarning(LOCATION, "The following values in the pilot file were out of bounds and were automatically reset:\n%s\nPlease check your settings!\n", clamped_range_warnings.c_str());
clamped_range_warnings.clear();
}
}
void pilotfile::plr_write_settings()
@@ -863,35 +889,55 @@ void pilotfile::plr_write_settings()
handler->startSectionWrite(Section::Settings);
// sound/voice/music
clamp_value_with_warn(&Master_sound_volume, 0.f, 1.f, "Effects Volume");
handler->writeFloat("master_sound_volume", Master_sound_volume);
clamp_value_with_warn(&Master_event_music_volume, 0.f, 1.f, "Music Volume");
handler->writeFloat("master_event_music_volume", Master_event_music_volume);
clamp_value_with_warn(&Master_voice_volume, 0.f, 1.f, "Voice Volume");
handler->writeFloat("aster_voice_volume", Master_voice_volume);
handler->writeInt("briefing_voice_enabled", Briefing_voice_enabled ? 1 : 0);
// skill level
clamp_value_with_warn(&Game_skill_level, 0, 4, "Skill Level");
handler->writeInt("game_skill_level", Game_skill_level);
// input options
handler->writeInt("use_mouse_to_fly", Use_mouse_to_fly);
clamp_value_with_warn(&Mouse_sensitivity, 0, 9, "Mouse Sensitivity");
handler->writeInt("mouse_sensitivity", Mouse_sensitivity);
clamp_value_with_warn(&Joy_sensitivity, 0, 9, "Joystick Sensitivity");
handler->writeInt("joy_sensitivity", Joy_sensitivity);
clamp_value_with_warn(&Joy_dead_zone_size, 0, 45, "Joystick Deadzone");
handler->writeInt("joy_dead_zone_size", Joy_dead_zone_size);
// detail
clamp_value_with_warn(&Detail.setting, -1, NUM_DEFAULT_DETAIL_LEVELS - 1, "Detail Level Preset");
handler->writeInt("setting", Detail.setting);
clamp_value_with_warn(&Detail.nebula_detail, 0, MAX_DETAIL_LEVEL, "Nebula Detail");
handler->writeInt("nebula_detail", Detail.nebula_detail);
clamp_value_with_warn(&Detail.detail_distance, 0, MAX_DETAIL_LEVEL, "Model Detail");
handler->writeInt("detail_distance", Detail.detail_distance);
clamp_value_with_warn(&Detail.hardware_textures, 0, MAX_DETAIL_LEVEL, "3D Hardware Textures");
handler->writeInt("hardware_textures", Detail.hardware_textures);
clamp_value_with_warn(&Detail.num_small_debris, 0, MAX_DETAIL_LEVEL, "Impact Effects");
handler->writeInt("num_small_debris", Detail.num_small_debris);
clamp_value_with_warn(&Detail.num_particles, 0, MAX_DETAIL_LEVEL, "Particles");
handler->writeInt("num_particles", Detail.num_particles);
clamp_value_with_warn(&Detail.num_stars, 0, MAX_DETAIL_LEVEL, "Stars");
handler->writeInt("num_stars", Detail.num_stars);
clamp_value_with_warn(&Detail.shield_effects, 0, MAX_DETAIL_LEVEL, "Shield Hit Effects");
handler->writeInt("shield_effects", Detail.shield_effects);
clamp_value_with_warn(&Detail.lighting, 0, MAX_DETAIL_LEVEL, "Lighting");
handler->writeInt("lighting", Detail.lighting);
handler->writeInt("targetview_model", Detail.targetview_model);
handler->writeInt("planets_suns", Detail.planets_suns);
handler->writeInt("weapon_extras", Detail.weapon_extras);
if (!clamped_range_warnings.empty()) {
ReleaseWarning(LOCATION, "The following values were out of bounds when saving the Pilot file and were automatically reset.\n%s\nThis shouldn't be possible, please contact the FreeSpace 2 Open Source Code Project!\n", clamped_range_warnings.c_str());
clamped_range_warnings.clear();
}
handler->endSectionWrite();
}
+9 -14
View File
@@ -510,16 +510,6 @@ void do_view_external()
Viewer_external_info.angles.h = PI2 + Viewer_external_info.angles.h;
}
/**
* Separate out the reading of thrust keys, so we can call this from external view as well as from normal view
*/
void do_thrust_keys(control_info *ci)
{
ci->forward = check_control_timef(FORWARD_THRUST) - check_control_timef(REVERSE_THRUST);
ci->sideways = (check_control_timef(RIGHT_SLIDE_THRUST) - check_control_timef(LEFT_SLIDE_THRUST));//for slideing-Bobboau
ci->vertical = (check_control_timef(UP_SLIDE_THRUST) - check_control_timef(DOWN_SLIDE_THRUST));//for slideing-Bobboau
}
/**
* Called by single and multiplayer modes to reset information inside of control info structure
*/
@@ -552,12 +542,12 @@ void read_keyboard_controls( control_info * ci, float frame_time, physics_info *
// Camera & View controls
if ( Viewer_mode & VM_EXTERNAL ) {
// External mode
control_used(VIEW_EXTERNAL);
do_view_external();
do_thrust_keys(ci);
} else if ( Viewer_mode & VM_CHASE ) {
// Chase mode
do_view_chase();
} else {
@@ -570,7 +560,6 @@ void read_keyboard_controls( control_info * ci, float frame_time, physics_info *
// Ship controls
if (Viewer_mode & VM_CAMERA_LOCKED) {
// From keyboard...
do_thrust_keys(ci);
if ( check_control(BANK_WHEN_PRESSED) ) {
ci->bank = check_control_timef(BANK_LEFT) + check_control_timef(YAW_LEFT) - check_control_timef(YAW_RIGHT) - check_control_timef(BANK_RIGHT);
ci->heading = 0.0f;
@@ -609,6 +598,12 @@ void read_keyboard_controls( control_info * ci, float frame_time, physics_info *
}
if (!(Game_mode & GM_DEAD)) {
// Thrust controls
ci->forward = check_control_timef(FORWARD_THRUST) - check_control_timef(REVERSE_THRUST);
ci->sideways = (check_control_timef(RIGHT_SLIDE_THRUST) - check_control_timef(LEFT_SLIDE_THRUST)); // for slideing-Bobboau
ci->vertical = (check_control_timef(UP_SLIDE_THRUST) - check_control_timef(DOWN_SLIDE_THRUST)); // for slideing-Bobboau
// Throttle controls
if ( button_info_query(&Player->bi, ONE_THIRD_THROTTLE) ) {
control_used(ONE_THIRD_THROTTLE);
player_clear_speed_matching();
@@ -1373,7 +1368,7 @@ void player_level_init()
Player->cargo_scan_loop = sound_handle::invalid();
Player->cargo_inspect_time = 0; // time that current target's cargo has been inspected for
Player->target_is_dying = -1; // The player target is dying, set to -1 if no target
Player->target_is_dying = -1; // Whether the player target is dying, -1 if no target
Player->current_target_sx = -1; // Screen x-pos of current target (or subsystem if applicable)
Player->current_target_sy = -1; // Screen y-pos of current target (or subsystem if applicable)
Player->target_in_lock_cone = -1; // Is the current target in secondary weapon lock cone?
+9
View File
@@ -4,8 +4,17 @@
#pragma push_macro("Assert")
#undef Assert
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <Rocket/Core/XMLNodeHandler.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#pragma pop_macro("Assert")
namespace scpui {
+9
View File
@@ -6,9 +6,18 @@
#pragma push_macro("Assert")
#undef Assert
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <Rocket/Core.h>
#include <Rocket/Core/Plugin.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#pragma pop_macro("Assert")
namespace scpui {
+9
View File
@@ -7,12 +7,21 @@
#pragma push_macro("Assert")
#undef Assert
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <Rocket/Core.h>
#include <Rocket/Core/ElementDocument.h>
#include <Rocket/Core/GeometryUtilities.h>
#include <Rocket/Core/String.h>
#include <scpui/RocketRenderingInterface.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#pragma pop_macro("Assert")
namespace scpui {
+9
View File
@@ -4,10 +4,19 @@
#pragma push_macro("Assert")
#undef Assert
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <Rocket/Core/Element.h>
#include <Rocket/Core/Geometry.h>
#include <Rocket/Core/Texture.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#pragma pop_macro("Assert")
namespace scpui {
@@ -10,8 +10,17 @@
#pragma push_macro("Assert")
#undef Assert
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <Rocket/Core.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#pragma pop_macro("Assert")
namespace scpui {
@@ -4,10 +4,19 @@
#pragma push_macro("Assert")
#undef Assert
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <Rocket/Core/Element.h>
#include <Rocket/Core/Geometry.h>
#include <Rocket/Core/Texture.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#pragma pop_macro("Assert")
namespace scpui {
+1 -1
View File
@@ -92,7 +92,7 @@ LuaPromise::LuaPromise(const std::shared_ptr<resolve_context>& resolveContext) :
{
m_state->state = State::Pending;
// This executes promises eagerly since registering the callback kicks of the async operation
// This executes promises eagerly since registering the callback kicks off the async operation
m_state->registerResolveCallback(resolveContext);
}
+1 -1
View File
@@ -111,7 +111,7 @@ ADE_FUNC(promise,
"function(function(any resolveVal) => void resolve, function(any errorVal) => void reject) => void body",
"Creates a promise that resolves when the resolve function of the callback is called or errors if the reject "
"function is called. The function will be called "
"on it's own.",
"on its own.",
"promise",
"The promise or nil on error")
{
+2
View File
@@ -334,6 +334,8 @@ ADE_VIRTVAR(MouseControlStatus, l_Mouse, "boolean", "Gets and sets the retail mo
{
Use_mouse_to_fly = false;
}
mouse_flush();
}
if (Use_mouse_to_fly)
+8 -1
View File
@@ -1729,7 +1729,11 @@ ADE_FUNC(waitAsync,
class time_resolve_context : public resolve_context, public std::enable_shared_from_this<time_resolve_context> {
public:
time_resolve_context(int timestamp) : m_timestamp(timestamp) {}
time_resolve_context(int timestamp) : m_timestamp(timestamp) {
static int unique_id_counter = 0;
m_unique_id = unique_id_counter++;
nprintf(("scripting", "waitAsync: Creating asynchronous context %d.\n", m_unique_id));
}
void setResolver(Resolver resolver) override
{
// Keep checking the time until the timestamp is elapsed
@@ -1737,11 +1741,13 @@ ADE_FUNC(waitAsync,
auto cb = [this, self, resolver](
executor::IExecutionContext::State contextState) {
if (contextState == executor::IExecutionContext::State::Invalid) {
mprintf(("waitAsync: Context is invalid, possibly due to a game state change (current state is %s). Aborting asynchronous context %d.\n", GS_state_text[gameseq_get_state()], m_unique_id));
resolver(true, luacpp::LuaValueList());
return executor::Executor::CallbackResult::Done;
}
if (timestamp_elapsed(m_timestamp)) {
nprintf(("scripting", "waitAsync: Timestamp has elapsed for asynchronous context %d.\n", m_unique_id));
resolver(false, luacpp::LuaValueList());
return executor::Executor::CallbackResult::Done;
}
@@ -1756,6 +1762,7 @@ ADE_FUNC(waitAsync,
private:
int m_timestamp = -1;
int m_unique_id = -1;
};
return ade_set_args(L,
"o",
+6 -1
View File
@@ -10,10 +10,15 @@ namespace api {
ADE_LIB(l_Time, "Time", "time", "Real-Time library");
ADE_FUNC(getCurrentTime, l_Time, nullptr, "Gets the current real-time timestamp.", "timestamp", "The current time")
ADE_FUNC(getCurrentTime, l_Time, nullptr, "Gets the current real-time timestamp, i.e. the actual elapsed time, regardless of whether the game has changed time compression or been paused.", "timestamp", "The current time")
{
return ade_set_args(L, "o", l_Timestamp.Set(timer_get_nanoseconds()));
}
ADE_FUNC(getCurrentMissionTime, l_Time, nullptr, "Gets the current mission-time timestamp, which can be affected by time compression and paused.", "timestamp", "The current mission time")
{
return ade_set_args(L, "o", l_Timestamp.Set(timestamp_get_mission_time_in_microseconds() * NANOSECONDS_PER_MICROSECOND));
}
} // namespace api
} // namespace scripting
+9 -8
View File
@@ -119,32 +119,33 @@ ADE_FUNC(isValid, l_ModelInstance, nullptr, "True if valid, false or nil if not"
return ade_set_args(L, "b", mih->IsValid());
}
ADE_VIRTVAR(Orientation, l_SubmodelInstance, "orientation", "Gets or sets the submodel instance orientation (world orientation)", "orientation", "Orientation, or identity orientation if handle is not valid")
ADE_VIRTVAR(Orientation, l_SubmodelInstance, "orientation", "Gets or sets the submodel instance orientation", "orientation", "Orientation, or identity orientation if handle is not valid")
{
submodelinstance_h *smih;
matrix_h *mh = nullptr;
if (!ade_get_args(L, "o|o", l_SubmodelInstance.GetPtr(&smih), l_Matrix.GetPtr(&mh)))
return ade_set_error(L, "o", l_Matrix.Set(matrix_h(&vmd_identity_matrix)));
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
if (!smih->IsValid())
return ade_set_error(L, "o", l_Matrix.Set(matrix_h(&vmd_identity_matrix)));
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
auto smi = smih->Get();
if (smi == nullptr)
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
if (ADE_SETTING_VAR && mh != nullptr)
{
auto sm = smih->GetSubmodel();
auto smi = smih->Get();
smi->canonical_prev_orient = smi->canonical_orient;
smi->canonical_orient = *mh->GetMatrix();
float angle = 0.0f;
vm_closest_angle_to_matrix(&smi->canonical_orient, &sm->rotation_axis, &angle);
vm_closest_angle_to_matrix(&smi->canonical_orient, &smih->GetSubmodel()->rotation_axis, &angle);
smi->cur_angle = angle;
smi->turret_idle_angle = angle;
}
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&smih->Get()->canonical_orient)));
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&smi->canonical_orient)));
}
ADE_FUNC(findWorldPoint, l_SubmodelInstance, "vector", "Calculates the world coordinates of a point in a submodel's frame of reference", "vector", "Point, or empty vector if handle is not valid")
+1 -1
View File
@@ -63,7 +63,7 @@ ADE_VIRTVAR(FPS, l_MoviePlayer, "number", "Determines the frames per second of t
LuaError(L, "Tried to modify read-only member!");
}
return ade_set_args(L, "f", (int)ph->player()->getMovieProperties().fps);
return ade_set_args(L, "f", ph->player()->getMovieProperties().fps);
}
ADE_FUNC(update, l_MoviePlayer, "timespan step_time",
+13 -4
View File
@@ -101,11 +101,11 @@ ADE_VIRTVAR(AWACSRadius, l_Subsystem, "number", "Subsystem AWACS radius", "numbe
return ade_set_args(L, "f", sso->ss->awacs_radius);
}
ADE_VIRTVAR(Orientation, l_Subsystem, "orientation", "Orientation of subobject or turret base", "orientation", "Subsystem orientation, or null orientation if handle is invalid")
ADE_VIRTVAR(Orientation, l_Subsystem, "orientation", "Orientation of subobject or turret base", "orientation", "Subsystem orientation, or identity orientation if handle is invalid")
{
ship_subsys_h *sso;
matrix_h *mh = nullptr;
if(!ade_get_args(L, "o|o", l_Subsystem.GetPtr(&sso), l_Matrix.GetPtr(&mh)))
if (!ade_get_args(L, "o|o", l_Subsystem.GetPtr(&sso), l_Matrix.GetPtr(&mh)))
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
if (!sso->isSubsystemValid())
@@ -115,10 +115,19 @@ ADE_VIRTVAR(Orientation, l_Subsystem, "orientation", "Orientation of subobject o
if (smi == nullptr)
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
if(ADE_SETTING_VAR && mh)
if (ADE_SETTING_VAR && mh != nullptr)
{
auto pm = model_get(sso->ss->system_info->model_num);
auto sm = &pm->submodel[sso->ss->system_info->subobj_num];
smi->canonical_prev_orient = smi->canonical_orient;
smi->canonical_orient = *mh->GetMatrix();
float angle = 0.0f;
vm_closest_angle_to_matrix(&smi->canonical_orient, &sm->rotation_axis, &angle);
smi->cur_angle = angle;
smi->turret_idle_angle = angle;
}
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&smi->canonical_orient)));
@@ -656,7 +665,7 @@ ADE_FUNC(rotateTurret, l_Subsystem, "vector Pos, boolean reset=false", "Rotates
return ADE_RETURN_FALSE;
}
ADE_FUNC(getTurretHeading, l_Subsystem, NULL, "Returns the turrets forward vector", "vector", "Returns a normalized version of the forward vector or null vector on error")
ADE_FUNC(getTurretHeading, l_Subsystem, NULL, "Returns the turrets forward vector", "vector", "Returns a normalized version of the forward vector in the ship's reference frame or null vector on error")
{
ship_subsys_h *sso;
if(!ade_get_args(L, "o", l_Subsystem.GetPtr(&sso)))
+1 -1
View File
@@ -31,7 +31,7 @@ ADE_FUNC(getSeconds, l_TimeSpan, nullptr, "Gets the value of this timestamp in s
}
// Timestamps and spans are stored in nanoseconds
return ade_set_args(L, "f", (float)value / 1000000000.f);
return ade_set_args(L, "f", (float)((long double)value / (long double)NANOSECONDS_PER_SECOND));
}
} // namespace api
+5 -2
View File
@@ -172,12 +172,15 @@ LuaValueList LuaFunction::call(lua_State* L, const LuaValueList& args) const {
stackTop = lua_gettop(L);
}
if(!lua_checkstack(L, (int)args.size() + 1))
throw LuaException("Lua Stack Overflow!");
// Push the function onto the stack
this->pushValue(L);
this->pushValue(L, true);
// Push the arguments onto the stack
for (const auto& arg : args) {
arg.pushValue(L);
arg.pushValue(L, true);
}
// actually call the function now!
+5 -1
View File
@@ -80,10 +80,14 @@ bool UniqueLuaReference::isValid() const {
return true;
}
void UniqueLuaReference::pushValue(lua_State* thread) const
void UniqueLuaReference::pushValue(lua_State* thread, bool manualStackAllocation) const
{
Assertion(thread != nullptr, "Valid thread state must be specified!");
if (!manualStackAllocation) {
lua_checkstack(thread, 1);
}
if (this->isValid()) {
lua_rawgeti(thread, LUA_REGISTRYINDEX, this->getReference());
}
+2 -1
View File
@@ -100,8 +100,9 @@ class UniqueLuaReference {
/**
* @brief Pushes the referenced value onto the stack.
* @param thread A specific thread state to push the value to. nullptr for the default state of this reference
* @param manualStackAllocation Set to true if you manually allocate sufficient stack size before calling this function. Keep false unless you know what you are doing.
*/
void pushValue(lua_State* thread) const;
void pushValue(lua_State* thread, bool manualStackAllocation = false) const;
};
}
+2 -2
View File
@@ -88,10 +88,10 @@ bool LuaValue::isValid() const {
return _reference && _reference->isValid();
}
bool LuaValue::pushValue(lua_State* thread) const
bool LuaValue::pushValue(lua_State* thread, bool manualStackAllocation) const
{
if (this->_reference->isValid()) {
this->_reference->pushValue(thread);
this->_reference->pushValue(thread, manualStackAllocation);
return true;
} else {
return false;
+2 -1
View File
@@ -185,8 +185,9 @@ class LuaValue {
* @brief Pushes this lua value onto the stack.
* @param thread The thread stack onto which this value should be pushed. May be nullptr for the default state of
* this value
* @param manualStackAllocation Set to true if you manually allocate sufficient stack size before calling this function. Keep false unless you know what you are doing.
*/
bool pushValue(lua_State* thread) const;
bool pushValue(lua_State* thread, bool manualStackAllocation = false) const;
lua_State* getLuaState() const;
+6
View File
@@ -337,14 +337,20 @@ bool script_state::EvalStringWithReturn(const char* string, const char* format,
scripting::ade_get_args(LuaState, format, rtn);
}
} catch (const LuaException&) {
lua_pop(LuaState, 1);
return false;
}
} catch (const LuaException& e) {
LuaError(GetLuaSession(), "%s", e.what());
lua_pop(LuaState, 1);
return false;
}
lua_pop(LuaState, 1);
return true;
}
+18 -8
View File
@@ -4616,7 +4616,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
}
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
//find the index number of the current ship info type
if (it->name == sip->name) {
if (&(*it) == sip) {
Ai_tp_list[i].ship_class.push_back((int)std::distance(Ship_info.cbegin(), it));
break;
}
@@ -7419,10 +7419,20 @@ 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);
// update any replacement and/or team color textures (wookieejedi), then render
ship* shipp = &Ships[objp->instance];
ship_info* sip = &Ship_info[Ships[objp->instance].ship_info_index];
render_info.set_replacement_textures(shipp->ship_replacement_textures);
if (sip->uses_team_colors) {
render_info.set_team_color(shipp->team_name, shipp->secondary_team_name, 0, 0);
}
render_info.set_detail_level_lock(0);
model_render_immediate(&render_info, Ship_info[Ships[objp->instance].ship_info_index].model_num, &objp->orient,
model_render_immediate(&render_info, sip->model_num, &objp->orient,
&objp->pos); // Render ship model with fixed detail level 0 so its not switching LOD when
// moving away from origin
Glowpoint_override = false;
@@ -15833,7 +15843,7 @@ SCP_string ship_return_orders(ship* sp)
outbuf += target_name;
outbuf += XSTR(" wing", 494);
} else {
outbuf += XSTR("no orders", 495);
outbuf = XSTR("no orders", 495);
}
break;
@@ -15842,7 +15852,7 @@ SCP_string ship_return_orders(ship* sp)
outbuf += XSTR("any ", -1);
outbuf += target_name;
} else {
outbuf += XSTR("no orders", 495);
outbuf = XSTR("no orders", 495);
}
break;
@@ -15858,7 +15868,7 @@ SCP_string ship_return_orders(ship* sp)
if (aigp->target_name) {
outbuf += target_name;
} else {
outbuf += XSTR("no orders", 495);
outbuf = XSTR("no orders", 495);
}
break;
@@ -15869,7 +15879,7 @@ SCP_string ship_return_orders(ship* sp)
hud_targetbox_truncate_subsys_name(subsys_name);
sprintf(outbuf, XSTR("atk %s %s", 496), target_name, subsys_name);
} else {
outbuf += XSTR("no orders", 495);
outbuf = XSTR("no orders", 495);
}
break;
}
@@ -20253,4 +20263,4 @@ int ship_check_visibility(const ship* viewed, ship* viewer)
}
return ship_is_visible;
}
}
+1 -1
View File
@@ -2221,7 +2221,7 @@ void stars_set_background_model(const char *model_name, const char *texture_name
return;
}
if (model_name != nullptr && *model_name != '\0') {
if (model_name != nullptr && *model_name != '\0' && stricmp(model_name, "none") != 0) {
new_model = model_load(model_name, 0, nullptr, -1);
if (texture_name != nullptr && *texture_name != '\0') {
+1 -1
View File
@@ -424,7 +424,7 @@ bool UI_INPUTBOX::handle_textInputEvent(const SDL_Event& event) {
if (event.text.text[0] != '\0' && event.text.text[0] != '\1') {
int key_used = 0;
for (char c : event.text.text) {
if (c <= 32)
if (c < 32)
break;
add_input(c, &key_used);
+8 -2
View File
@@ -423,7 +423,7 @@ int campaign_tree_wnd::internal_error(const char *msg, ...)
int campaign_tree_wnd::fred_check_sexp(int sexp, int type, char *msg, ...)
{
SCP_string buf, sexp_buf, error_buf;
SCP_string buf, sexp_buf, error_buf, bad_node_str;
int err = 0, z, faulty_node;
va_list args;
@@ -440,7 +440,13 @@ int campaign_tree_wnd::fred_check_sexp(int sexp, int type, char *msg, ...)
convert_sexp_to_string(sexp_buf, sexp, SEXP_ERROR_CHECK_MODE);
truncate_message_lines(sexp_buf, 30);
sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), Sexp_nodes[faulty_node].text);
stuff_sexp_text_string(bad_node_str, faulty_node, SEXP_ERROR_CHECK_MODE);
if (!bad_node_str.empty()) { // the previous function adds a space at the end
bad_node_str.pop_back();
}
sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), bad_node_str.c_str());
if (z < 0 && z > -100)
err = 1;
+1 -1
View File
@@ -445,7 +445,7 @@ IDR_MENU_EDIT_SEXP_TREE MENU
BEGIN
POPUP "Edit sexp tree"
BEGIN
MENUITEM "&Delete Item\tDelete", ID_DELETE
MENUITEM "&Delete Item", ID_DELETE
MENUITEM "&Edit Data\tSpace Bar", ID_EDIT_TEXT
MENUITEM "Expand All", ID_EXPAND_ALL
MENUITEM SEPARATOR
+8 -2
View File
@@ -3376,7 +3376,7 @@ int CFREDView::internal_error(const char *msg, ...)
int CFREDView::fred_check_sexp(int sexp, int type, const char *msg, ...)
{
SCP_string buf, sexp_buf, error_buf;
SCP_string buf, sexp_buf, error_buf, bad_node_str;
int err = 0, z, faulty_node;
va_list args;
@@ -3393,7 +3393,13 @@ int CFREDView::fred_check_sexp(int sexp, int type, const char *msg, ...)
convert_sexp_to_string(sexp_buf, sexp, SEXP_ERROR_CHECK_MODE);
truncate_message_lines(sexp_buf, 30);
sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), Sexp_nodes[faulty_node].text);
stuff_sexp_text_string(bad_node_str, faulty_node, SEXP_ERROR_CHECK_MODE);
if (!bad_node_str.empty()) { // the previous function adds a space at the end
bad_node_str.pop_back();
}
sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), bad_node_str.c_str());
if (z < 0 && z > -100)
err = 1;
+58 -19
View File
@@ -21,7 +21,8 @@
static char THIS_FILE[] = __FILE__;
#endif
const float input_threshold = 0.01f; // smallest increment of input box
constexpr auto INPUT_THRESHOLD = 0.01f; // smallest increment of input box
constexpr auto INPUT_FORMAT = "%.01f";
/////////////////////////////////////////////////////////////////////////////
// orient_editor dialog
@@ -45,15 +46,15 @@ orient_editor::orient_editor(CWnd* pParent /*=NULL*/)
Assert(query_valid_object());
auto pos = &Objects[cur_object_index].pos;
m_position_x.Format("%.01f", pos->xyz.x);
m_position_y.Format("%.01f", pos->xyz.y);
m_position_z.Format("%.01f", pos->xyz.z);
m_position_x.Format(INPUT_FORMAT, pos->xyz.x);
m_position_y.Format(INPUT_FORMAT, pos->xyz.y);
m_position_z.Format(INPUT_FORMAT, pos->xyz.z);
angles ang;
vm_extract_angles_matrix(&ang, &Objects[cur_object_index].orient);
m_orientation_p.Format("%.01f", to_degrees(ang.p));
m_orientation_b.Format("%.01f", to_degrees(ang.b));
m_orientation_h.Format("%.01f", to_degrees(ang.h));
m_orientation_p.Format(INPUT_FORMAT, to_degrees(ang.p));
m_orientation_b.Format(INPUT_FORMAT, to_degrees(ang.b));
m_orientation_h.Format(INPUT_FORMAT, to_degrees(ang.h));
}
void orient_editor::DoDataExchange(CDataExchange* pDX)
@@ -182,29 +183,47 @@ BOOL orient_editor::OnInitDialog()
return TRUE;
}
bool orient_editor::close(float val, const CString &str)
/**
* Checks whether the position value is close enough to the input string value that we can assume the input has not changed.
*/
bool orient_editor::is_close(float val, const CString &input_str) const
{
float diff = val - convert(str);
return diff > -input_threshold && diff < input_threshold;
val = perform_input_rounding(val);
float input_val = convert(input_str);
float diff = val - input_val;
return abs(diff) < INPUT_THRESHOLD;
}
/**
* Checks whether the angle value is close enough to the input string value that we can assume the input has not changed.
*/
bool orient_editor::is_angle_close(float rad, const CString &input_str) const
{
float deg = perform_input_rounding(to_degrees(rad));
float input_deg = normalize_degrees(convert(input_str));
float diff = deg - input_deg;
return abs(diff) < INPUT_THRESHOLD;
}
bool orient_editor::query_modified()
{
if (!close(Objects[cur_object_index].pos.xyz.x, m_position_x))
if (!is_close(Objects[cur_object_index].pos.xyz.x, m_position_x))
return true;
if (!close(Objects[cur_object_index].pos.xyz.y, m_position_y))
if (!is_close(Objects[cur_object_index].pos.xyz.y, m_position_y))
return true;
if (!close(Objects[cur_object_index].pos.xyz.z, m_position_z))
if (!is_close(Objects[cur_object_index].pos.xyz.z, m_position_z))
return true;
angles ang;
vm_extract_angles_matrix(&ang, &Objects[cur_object_index].orient);
if (!close(to_degrees(ang.p), m_orientation_p))
if (!is_angle_close(ang.p, m_orientation_p))
return true;
if (!close(to_degrees(ang.b), m_orientation_b))
if (!is_angle_close(ang.b, m_orientation_b))
return true;
if (!close(to_degrees(ang.h), m_orientation_h))
if (!is_angle_close(ang.h, m_orientation_h))
return true;
if (((CButton *) GetDlgItem(IDC_POINT_TO_CHECKBOX))->GetCheck() == TRUE)
@@ -224,7 +243,7 @@ void orient_editor::OnOK()
// there's enough difference in our position that we're changing it
cur_pos = &Objects[cur_object_index].pos;
if (!close(cur_pos->xyz.x, m_position_x) || !close(cur_pos->xyz.y, m_position_y) || !close(cur_pos->xyz.z, m_position_z))
if (!is_close(cur_pos->xyz.x, m_position_x) || !is_close(cur_pos->xyz.y, m_position_y) || !is_close(cur_pos->xyz.z, m_position_z))
{
vec3d pos;
pos.xyz.x = convert(m_position_x);
@@ -237,7 +256,7 @@ void orient_editor::OnOK()
// there's enough difference in our orientation that we're changing it
vm_extract_angles_matrix(&ang, &Objects[cur_object_index].orient);
if (!close(to_degrees(ang.p), m_orientation_p) || !close(to_degrees(ang.b), m_orientation_b) || !close(to_degrees(ang.h), m_orientation_h))
if (!is_angle_close(ang.p, m_orientation_p) || !is_angle_close(ang.b, m_orientation_b) || !is_angle_close(ang.h, m_orientation_h))
{
ang.p = fl_radians(convert(m_orientation_p));
ang.b = fl_radians(convert(m_orientation_b));
@@ -354,6 +373,11 @@ void orient_editor::actually_point_object(object *ptr)
float orient_editor::to_degrees(float rad)
{
float deg = fl_degrees(rad);
return normalize_degrees(deg);
}
float orient_editor::normalize_degrees(float deg)
{
while (deg < -180.0f)
deg += 180.0f;
while (deg > 180.0f)
@@ -364,7 +388,10 @@ float orient_editor::to_degrees(float rad)
return deg;
}
float orient_editor::convert(const CString &str)
/**
* Extract a float from the CString, being mindful of any comma separators.
*/
float orient_editor::convert(const CString &str) const
{
char buf[256];
size_t i, j, len;
@@ -379,6 +406,18 @@ float orient_editor::convert(const CString &str)
return (float) atof(buf);
}
/**
* Perform rounding of the float value by loading it into the input box format string and parsing it again.
* This accounts for not only decimal rounding to the precision of the input box, but also floating point rounding due to inexact fractions such as 1/10.
* See also GitHub PR #4475.
*/
float orient_editor::perform_input_rounding(float val) const
{
CString str;
str.Format(INPUT_FORMAT, val);
return convert(str);
}
void orient_editor::OnPointTo()
{
UpdateData(TRUE);
+7 -2
View File
@@ -24,6 +24,7 @@ public:
orient_editor(CWnd* pParent = NULL); // standard constructor
static float to_degrees(float radians);
static float normalize_degrees(float degrees);
// Dialog Data
//{{AFX_DATA(orient_editor)
@@ -69,8 +70,12 @@ protected:
DECLARE_MESSAGE_MAP()
private:
float convert(const CString &str);
bool close(float val, const CString &str);
float convert(const CString &str) const;
float perform_input_rounding(float val) const;
bool is_close(float val, const CString &input_str) const;
bool is_angle_close(float rad, const CString &input_str) const;
int total;
int index[MAX_OBJECTS];
void actually_point_object(object *ptr);
+4 -4
View File
@@ -2509,13 +2509,13 @@ void sexp_tree::NodeCopy()
void sexp_tree::NodeReplacePaste()
{
if (item_index < 0)
if (item_index < 0 || Sexp_clipboard < 0)
return;
int i;
// the following assumptions are made..
Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
Assert(Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED);
Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
Assertion(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_CONTAINER_NAME,
"Attempt to use container name %s from SEXP clipboard. Please report!",
@@ -2587,13 +2587,13 @@ void sexp_tree::NodeReplacePaste()
void sexp_tree::NodeAddPaste()
{
if (item_index < 0)
if (item_index < 0 || Sexp_clipboard < 0)
return;
int i;
// the following assumptions are made..
Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
Assert(Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED);
Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
Assertion(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_CONTAINER_NAME,
"Attempt to use container name %s from SEXP clipboard. Please report!",
+16 -17
View File
@@ -902,6 +902,7 @@ void game_level_close()
lock_time_compression(false);
audiostream_unpause_all();
snd_aav_init();
gr_set_ambient_light(120, 120, 120);
@@ -1192,7 +1193,7 @@ void game_loading_callback(int count)
Script_system.RemHookVar("Progress");
os_ignore_events();
os_defer_events_on_load_screen();
if (do_flip)
gr_flip();
@@ -5272,7 +5273,6 @@ void game_leave_state( int old_state, int new_state )
if ( (Game_mode & GM_MULTIPLAYER) && (new_state == GS_STATE_MAIN_MENU) ){
multi_quit_game(PROMPT_NONE);
}
snd_aav_init();
freespace_stop_mission();
@@ -6683,17 +6683,7 @@ void game_shutdown(void)
gr_clear();
gr_flip();
}
// Free the scripting resources of the new UI first
scpui::shutdown_scripting();
// Everything after this should be done without scripting so we can free those resources here
Script_system.Clear();
// Deinitialize the new UI system, needs to be done after scripting shutdown to make sure the resources were
// released properly
scpui::shutdown();
// if the player has left the "player select" screen and quit the game without actually choosing
// a player, Player will be nullptr, in which case we shouldn't write the player file out!
if (!(Game_mode & GM_STANDALONE_SERVER) && (Player!=nullptr) && !Is_standalone){
@@ -6729,9 +6719,6 @@ void game_shutdown(void)
scoring_close();
// Free SEXP resources
sexp_shutdown();
stars_close(); // clean out anything used by stars code
// the menu close functions will unload the bitmaps if they were displayed during the game
@@ -6741,6 +6728,19 @@ void game_shutdown(void)
// free left over memory from table parsing
player_tips_close();
// more fundamental shutdowns begin here ----------
sexp_shutdown(); // Free SEXP resources
scpui::shutdown_scripting(); // Free the scripting resources of the new UI first
Script_system.Clear(); // Everything after this should be done without scripting so we can free those resources here.
// By this point, all things that hold Lua References must be destroyed (such as Lua SEXPs)
scpui::shutdown(); // Deinitialize the new UI system, needs to be done after scripting shutdown to make sure the resources were released properly
control_config_common_close();
io::joystick::shutdown();
@@ -6779,7 +6779,6 @@ void game_shutdown(void)
}
lcl_xstr_close();
}
// game_stop_looped_sounds()
+8 -2
View File
@@ -2571,7 +2571,7 @@ int Editor::internal_error(const char* msg, ...) {
return -1;
}
int Editor::fred_check_sexp(int sexp, int type, const char* msg, ...) {
SCP_string buf, sexp_buf, error_buf;
SCP_string buf, sexp_buf, error_buf, bad_node_str;
int err = 0, z, faulty_node;
va_list args;
@@ -2588,12 +2588,18 @@ int Editor::fred_check_sexp(int sexp, int type, const char* msg, ...) {
convert_sexp_to_string(sexp_buf, sexp, SEXP_ERROR_CHECK_MODE);
truncate_message_lines(sexp_buf, 30);
stuff_sexp_text_string(bad_node_str, faulty_node, SEXP_ERROR_CHECK_MODE);
if (!bad_node_str.empty()) { // the previous function adds a space at the end
bad_node_str.pop_back();
}
sprintf(error_buf,
"Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)",
buf.c_str(),
sexp_error_message(z),
sexp_buf.c_str(),
Sexp_nodes[faulty_node].text);
bad_node_str.c_str());
if (z < 0 && z > -100)
err = 1;