just-harry / FancyScreenPatchForRecettear

Fancy Screen Patch for Recettear is a widescreen patch for the video-game Recettear.
11 stars 0 forks source link

Increase game speed in dungeons? #4

Open jacobgruber opened 1 year ago

jacobgruber commented 1 year ago

First off, thanks for making this. Works great even on linux. This is more of an open-ended question than a real issue.

Based on the insane cheat engine spelunking you've done, how difficult do you think it would be to increase the game speed in dungeons, either by a permanent effect similar to the "Everyone moves faster" item or by increasing the speed at which all game logic runs? As far as I can tell, this isn't possible by modifying the game resources, and would require changes to the executable.

I want to adventure with Caillou but I am impatient.

just-harry commented 1 year ago

First off, thanks for making this.

Thank you :)

Works great even on linux.

Glad to hear it. May I ask—did you run the script natively under Linux? Or, via Wine or something like that? I never actually tested the script outside of Windows.

As far as I can tell, this isn't possible by modifying the game resources, and would require changes to the executable.

You'd be correct, there.


(Any addresses/offsets mentioned from hereon are for the English version of the game.)

Running the game entirely at a higher speed is easy: decrease the GameFrameTimeIntegralPart value, and update the GameFrameTimeFractionalPartNumerator and GameFrameTimeFractionalPartDenominator values accordingly, so that the game logic processes more quickly. (This can be toyed with via Cheat Engine, with the cheat-table generated when the CheatEngineTablePath parameter of the script is used.)

Doing so conditionally is also easy enough: GameStateB is 1 when the game is in a 3D area (in the shop, or in a dungeon), and then GameStateD, when dereferenced as a 32-bit integer, is zero when in the shop and non-zero when in a dungeon. So, you could change the aforementioned frame-timing values in BeforeGameFrame based on that state.

But, preventing that from interfering with the frame interpolation logic may be trickier: as you would have to update the frame-time calculations to account for the third framerate, and then you'd also have to make sure that the timing for the game frame-rate and the presentation frame-rate doesn't drift out of sync when switching between the two game frame-rates.

For reference: https://github.com/Unjust-Harry/FancyScreenPatchForRecettear/blob/1.0.2/FancyScreenPatchForRecettear/Install-FancyScreenPatchForRecettear.ps1#L2413-L2416 https://github.com/Unjust-Harry/FancyScreenPatchForRecettear/blob/1.0.2/FancyScreenPatchForRecettear/Install-FancyScreenPatchForRecettear.ps1#L6826-L6864 https://github.com/Unjust-Harry/FancyScreenPatchForRecettear/blob/1.0.2/FancyScreenPatchForRecettear/Install-FancyScreenPatchForRecettear.ps1#L7368-L7517 https://github.com/Unjust-Harry/FancyScreenPatchForRecettear/blob/1.0.2/FancyScreenPatchForRecettear/Install-FancyScreenPatchForRecettear.ps1#L8718-L8751 https://github.com/Unjust-Harry/FancyScreenPatchForRecettear/blob/1.0.2/FancyScreenPatchForRecettear/Install-FancyScreenPatchForRecettear.ps1#L8429


As for making a specific "Combat News" status take effect always, that's also possible: the game has support for multiple combat-news statuses taking effect at the same time. Four, to be precise (I think). There's an array for four 32-bit integers at 0x0438b93c, with the length of the array being stored just before it at 0x0438b938. Each integer in the array represents a combat-news status that's in effect, a value of 12 is used for the "Everyone moves faster" effect. This too could be toyed about with with Cheat Engine.

Though, the game uses a function defined at 0x0043647f to check if a combat-news status is in effect, so it be would easier to make that function return true unconditionally for a given combat-news status, rather than bothering with manipulating the array.


To satisfy my own curiosity, I looked into tweaking the movement speeds for the adventurers, and pinned down where the movement speeds for the adventurers are actually stored.

A 32-bit integer at 0x0438b7d8 is used to keep track of which adventurer is at play. Multiply that integer by sixteen, and use that value to index 0x073ae074 as an array of floats, and you'll find the normal, non-dashing movement speed for an adventurer.

For the speed when dashing, a 32-bit integer at 0x056db034 is non-zero when an adventurer is dashing, and I've yet to figure out the rest.


In short, it shouldn't be too difficult to make dungeon crawling a little less tedious. I think the latter two options would make for fun options. I wouldn't want to implement the first option, as this patch makes the game's frame-timing much too sensitive already.

jacobgruber commented 1 year ago

Well this is a lot beefier on an answer than I anticiapted! Thank you!

The permanent "Combat News" effect definitely seems like the way to go. I noticed when fixing the values of the 'active statuses' array that sometimes a different news item would take effect. Not really sure why. I feel that modifying the functions would be more reliable. Sadly my assembly chops are not what they once were and it will take me a bit to actually figure out what changes are necessary.

As for only modifying adventurer speed, I think this may be possible through only editing the resources, as speed seems to be included in chara.txt. I didn't test if modifications actually changed in game speed as I fear increasing adverturer speed without increased enemy speed would make the game far too easy.

just-harry commented 1 year ago

Well this is a lot beefier on an answer than I anticiapted! Thank you!

You're very welcome :)

I noticed when fixing the values of the 'active statuses' array that sometimes a different news item would take effect. Not really sure why.

Aye, the game's logic for anything involving random chance is a bit tricksy, I've yet to figure out how it hangs together.

I feel that modifying the functions would be more reliable. Sadly my assembly chops are not what they once were and it will take me a bit to actually figure out what changes are necessary.

Agreed. And, as I said, I think it would make for a fun option, so I'll probably get 'round to implementing it proper at some point, but for the time being here's something I whipped together quickly that should get you most of the way there, for the English version of the game:

[Call]::NewLabel('CheckIfCombatNewStatusIsInEffect', [UInt32] 0x0003647f, (VirtualAddressToFileOffset ([UInt32] 0x0003647f)))
[Call]::NewLabel('SomeArrayIndex', [UInt32] 0x0438b4dc, (VirtualAddressToFileOffset ([UInt32] 0x0438b4dc)))

Hijack 'MakeSomeCombatNewsStatusesTakeEffectAlways' 'CheckIfCombatNewStatusIsInEffect' 0 5 @(
    0x83, (ModRM 0 7 5), '&GameStateB', 0x01     <# cmp [&GameStateB], 1 #>
    0x75, '1:PerformUsualCombatNewsStatusCheck'  <# jne PerformUsualCombatNewsStatusCheck #>
    0xA1, '&GameStateD'                          <# mov eax, [&GameStateD] #>
    0x83, (ModRM 0 7 0), 0x00                    <# cmp [eax], 0 #>
    0x74, '1:PerformUsualCombatNewsStatusCheck'  <# je PerformUsualCombatNewsStatusCheck #>
    0x83, (ModRM 1 7 4), (SIB 0 4 4), 0x04, 0x0C <# cmp [esp + 4], 12 #>
    0x75, '1:PerformUsualCombatNewsStatusCheck'  <# jne PerformUsualCombatNewsStatusCheck #>
    0xB8, 0x01, 0x0, 0x0, 0x0                    <# mov eax, 1 #>
    0xC3                                         <# ret #>
'PerformUsualCombatNewsStatusCheck:'
    0xA1, '&SomeArrayIndex'                      <# mov eax, [&SomeArrayIndex] #>
)

As a bonus, if you'd like to change the speed multiplier for the "Everyone move faster" combat-news effect: You can define your own speed somewhere in the PatchData block, like so:

'EveryoneMovesFasterSpeedMultiplier:4:(float)', (LE ([Float] 1.6))

And make it take effect with this:

Preassemble 'EveryoneMovesFasterMobSpeedBoostX' ([UInt32] 0x000306ec) (VirtualAddressToFileOffset ([UInt32] 0x000306ec)) @('&EveryoneMovesFasterSpeedMultiplier')
Preassemble 'EveryoneMovesFasterMobSpeedBoostZ' ([UInt32] 0x000306fb) (VirtualAddressToFileOffset ([UInt32] 0x000306fb)) @('&EveryoneMovesFasterSpeedMultiplier')
Preassemble 'EveryoneMovesFasterPlayerSpeedBoost' ([UInt32] 0x0008bed0) (VirtualAddressToFileOffset ([UInt32] 0x0008bed0)) @('&EveryoneMovesFasterSpeedMultiplier')