Skip to content

Commit

Permalink
EnableHollyCourse2: add mode 3 to hide entirely until reaching rank A…
Browse files Browse the repository at this point in the history
… holly 1
  • Loading branch information
emoose committed Aug 27, 2024
1 parent c216492 commit a43403e
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 16 deletions.
3 changes: 2 additions & 1 deletion OutRun2006Tweaks.ini
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ AutoDetectResolution = true
# 0 = disable
# 1 = enable, unlocked with Holly 1 missions
# 2 = enable, unlocked after reaching rank A on Holly 1
EnableHollyCourse2 = 1
# 3 = enable, hidden until reaching rank A on Holly 1 (completion bonus!)
EnableHollyCourse2 = 3

# Skips the beginning intro logos, saving a couple seconds from startup time. (game will still show a white screen for 5-10 seconds while data loads in)
# Can also be enabled via -SkipIntros launch parameter.
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ Latest builds can be found under the releases section: https://github.com/emoose
- Restores the car base shadow from the C2C console ports, which was missing on PC for some reason

**Gameplay:**
- Built-in framelimiter to prevent speedups, framerate can be partially unlocked with game running at 60FPS internally
- Points game toward new online servers, restoring the online multiplayer modes
- Restored XInput rumble code from the Xbox release, allowing gear shifts/drifts/crashes/etc to give feedback
- Xbox Series impulse triggers are supported and can be tweaked inside INI
- Steering deadzone can be customized from the default 20%
- Horn button can be made functional during normal gameplay, outside of the "honk your horn!" girl requests
- Allows randomizing the set of highway animations to use, instead of only using the set for the game mode being played
- In-game HUD can be optionally toggled via bindable keypress
- Passing all the C2C missions might unlock something new 🐱

**Bugfixes:**
- Built-in framelimiter to prevent speedups, framerate can be partially unlocked with game running at 60FPS internally
- Prevents save corruption bug when remapping controls with many input devices connected
- Fixed C2C ranking scoreboards not updating on Steam and other releases due to faulty anti-piracy checks
- Pegasus animation's clopping sound effect will now end correctly
Expand Down
2 changes: 1 addition & 1 deletion src/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ void Plugin_Init()
}
catch (const std::exception&)
{
spdlog::error("Plugin_Init: Failed to create SaveGame folder (game might not have permissions) - game might have issues writing savegame!");
spdlog::error("Plugin_Init: Failed to create SaveGame folder (may require permissions?) - game might have issues writing savegame!");
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/game_addrs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ namespace Game
inline fn_1arg_int GetStageUniqueNum = nullptr;
inline fn_1arg_int GetMaxCsLen = nullptr;

inline fn_1arg_int Sumo_CheckRacerUnlocked = nullptr;

// 2d sprite drawing
inline fn_1arg sprSetFontPriority = nullptr;
inline fn_1arg sprSetPrintFont = nullptr;
Expand Down Expand Up @@ -174,6 +176,8 @@ namespace Game
GetStageUniqueNum = Module::fn_ptr<fn_1arg_int>(0x4DC50);
GetMaxCsLen = Module::fn_ptr<fn_1arg_int>(0x3D470);

Sumo_CheckRacerUnlocked = Module::fn_ptr<fn_1arg_int>(0xE8410);

sprSetFontPriority = Module::fn_ptr<fn_1arg>(0x2CCB0);
sprSetPrintFont = Module::fn_ptr<fn_1arg>(0x2CA60);
sprSetFontColor = Module::fn_ptr<fn_1arg>(0x2CCA0);
Expand Down
66 changes: 53 additions & 13 deletions src/hooks_misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@

class EnableHollyCourse2 : public Hook
{
// TODO: right now Sumo_IsRacerUnlocked is hooked so that 7 (holly course2) gets checked as 6 (holly course1)
// So course 2 will unlock if you have Holly unlocked, but maybe this could be improved to only unlock after reaching rank on Holly1?
// (IIRC the game shows a congrats screen once you manage to unlock a new racer though, not sure how we could reimplement that for holly2..)
// TODO: Percentage might not be affected by the holly 2 missions atm, letting it go above 100% might be neat

inline static SafetyHookMid Sumo_IsRacerUnlocked_midhook{};
inline static SafetyHookMid Sumo_IsRacerUnlocked_midhook2{};
Expand Down Expand Up @@ -95,6 +93,42 @@ class EnableHollyCourse2 : public Hook
}
}

// if Settings::EnableHollyCourse2 == 3, only draw holly2 sprite once holly1 is complete
inline static SafetyHookMid Sumo_RacerMenuDraw_NumSprites_midhook{};
static void Sumo_RacerMenuDraw_CheckHollyCompletion_dest(SafetyHookContext& ctx)
{
uint32_t edi = ctx.edi;
uint32_t cmp_value = 7;

static bool holly2unlocked_previous = false;
bool holly2unlocked = Game::Sumo_CheckRacerUnlocked(7);
if (holly2unlocked)
cmp_value = 8; // allow loop to draw holly2 sprite

// Patch selection structs to change whether holly2 can be selected, if holly2 state has changed at all
if (holly2unlocked_previous != holly2unlocked)
{
holly2unlocked_previous = holly2unlocked;

// Fix selection structs so course 2 can be selected
Memory::VP::Patch(Module::exe_ptr(0x1CE430) + 0x4, holly2unlocked ? int(7) : int(-1)); // down from flagman 4
Memory::VP::Patch(Module::exe_ptr(0x1CE460) + 0x8, holly2unlocked ? int(7) : int(4)); // right from holly 1

// Allow selections to go in reverse, why not?
Memory::VP::Patch(Module::exe_ptr(0x1CE400) + 0xC, int(3)); // left from flagman 1
Memory::VP::Patch(Module::exe_ptr(0x1CE430) + 0x8, int(0)); // right from flagman 4
Memory::VP::Patch(Module::exe_ptr(0x1CE440) + 0xC, holly2unlocked ? int(7) : int(6)); // left from clarissa
Memory::VP::Patch(Module::exe_ptr(0x1CE470) + 0x8, int(4)); // right from holly 2
}

uintptr_t eflags = 0;

// Update CF (Carry Flag) - if edi < 7 in an unsigned comparison
if (edi < cmp_value) eflags |= (1 << 0); // CF is the 0th bit

ctx.eflags = eflags;
}

public:
std::string_view description() override
{
Expand All @@ -121,24 +155,30 @@ class EnableHollyCourse2 : public Hook
Memory::VP::Patch(Module::exe_ptr(0xE815C) + 1, uint8_t(8));
Memory::VP::Patch(Module::exe_ptr(0xE8A43) + 1, int(8));
Memory::VP::Patch(Module::exe_ptr(0xE8AE5) + 1, uint8_t(8));
Memory::VP::Patch(Module::exe_ptr(0xE8D0B) + 2, uint8_t(8));

// Fix selection structs so course 2 can be selected
Memory::VP::Patch(Module::exe_ptr(0x1CE430) + 0x4, int(7)); // down from flagman 4
Memory::VP::Patch(Module::exe_ptr(0x1CE460) + 0x8, int(7)); // right from holly 1
if (Settings::EnableHollyCourse2 == 3) // 3 = only show after holly1 is completed, hide if not complete
Sumo_RacerMenuDraw_NumSprites_midhook = safetyhook::create_mid(Module::exe_ptr(0xE8D0E), Sumo_RacerMenuDraw_CheckHollyCompletion_dest);
else
{
Memory::VP::Patch(Module::exe_ptr(0xE8D0B) + 2, uint8_t(8));

// Fix selection structs so course 2 can be selected
Memory::VP::Patch(Module::exe_ptr(0x1CE430) + 0x4, int(7)); // down from flagman 4
Memory::VP::Patch(Module::exe_ptr(0x1CE460) + 0x8, int(7)); // right from holly 1

// Allow selections to go in reverse, why not?
Memory::VP::Patch(Module::exe_ptr(0x1CE400) + 0xC, int(3)); // left from flagman 1
Memory::VP::Patch(Module::exe_ptr(0x1CE430) + 0x8, int(0)); // right from flagman 4
Memory::VP::Patch(Module::exe_ptr(0x1CE440) + 0xC, int(7)); // left from clarissa
Memory::VP::Patch(Module::exe_ptr(0x1CE470) + 0x8, int(4)); // right from holly 2
// Allow selections to go in reverse, why not?
Memory::VP::Patch(Module::exe_ptr(0x1CE400) + 0xC, int(3)); // left from flagman 1
Memory::VP::Patch(Module::exe_ptr(0x1CE430) + 0x8, int(0)); // right from flagman 4
Memory::VP::Patch(Module::exe_ptr(0x1CE440) + 0xC, int(7)); // left from clarissa
Memory::VP::Patch(Module::exe_ptr(0x1CE470) + 0x8, int(4)); // right from holly 2
}

// Switch racer number from 7 to 6 to satisfy some switch cases
Sumo_IsRacerUnlocked_midhook = safetyhook::create_mid(Module::exe_ptr(0xE8414), Sumo_RacerSwitch7to6_dest);
Sumo_RacerMenuInput_midhook = safetyhook::create_mid(Module::exe_ptr(0xE87C6), Sumo_RacerSwitch7to6_dest);

// Hook IsRacerUnlocked to make it check holly1 rank before unlocking holly2
if (Settings::EnableHollyCourse2 == 2)
if (Settings::EnableHollyCourse2 == 2 || Settings::EnableHollyCourse2 == 3)
Sumo_IsRacerUnlocked_midhook2 = safetyhook::create_mid(Module::exe_ptr(0xE8537), Sumo_IsRacerUnlocked_CheckHolly1Rank_dest);

// Hook GetRacerRank to let it fetch holly2 rank
Expand Down

0 comments on commit a43403e

Please sign in to comment.