Skip to content

Commit

Permalink
3.9.0 Use ghostmode to reset camera when teleporting, added custom sp…
Browse files Browse the repository at this point in the history
…awn point for rayman 2, small refactorings and bugfixes.
  • Loading branch information
rtsonneveld committed Jun 19, 2023
1 parent 51e501c commit 6b10fe1
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 272 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Threading;

namespace OpenSpaceToolbox
{
public class Rayman2CheckpointExtra : OpenspaceExtraAction
{
public Rayman2CheckpointExtra(Rayman2GameManager gameManager) : base(gameManager)
{
Name = ShortName = "Set spawn point (CAREFUL)";

ResurrectionPositionPointer = 0x500590;

AssemblyNop1Pointer = 0x406495;
AssemblyNop2Pointer = 0x4064A2;
AssemblyNop3Pointer = 0x4031CB;

RespawnPersoPointer = 0x4B7308;
RespawnPersoPointerPath = new int[] {0xC, 0, 0x4, 0x1C, 0xA90 };
}

private int RespawnPersoPointer { get; }
private int[] RespawnPersoPointerPath { get; }

private int ResurrectionPositionPointer { get; }

private int AssemblyNop1Pointer { get; }
private int AssemblyNop2Pointer { get; }
private int AssemblyNop3Pointer { get; }

public override void Action()
{
int processHandle = GameManager.GetProcessHandle();
if (processHandle < 0)
return;

// (ugly and a bit too permanent)
// nop the code that resets the resurrection point
GameManager.WriteBytes(new byte[5] { 0x90, 0x90, 0x90, 0x90, 0x90 }, AssemblyNop1Pointer);
GameManager.WriteBytes(new byte[5] { 0x90, 0x90, 0x90, 0x90, 0x90 }, AssemblyNop2Pointer);

/*
* Code that's NOPed:
* void fn_vSnifThePlayerIsDead(void)
{
-> POS_fn_vSetIdentityMatrix(&g_stEngineStructure.stMainCharacterPosition);
-> POS_fn_vSetIdentityMatrix(&g_stEngineStructure.stMainCameraPosition);
...
*/

// and nop the code that checks if resurrection is TRUE
// (so it always executes that part)
GameManager.WriteBytes(new byte[2] { 0x90, 0x90}, AssemblyNop3Pointer);

/*
* Code that's NOPed:
* void fn_vInitDeadLoop() {
* ...
* -> if(g_stEngineStructure.bResurection)
*/

var playerCoords = GameManager.PlayerCoordinates;

Thread.Sleep(100);

GameManager.WriteCoordinates(
playerCoords.Item1, playerCoords.Item2, playerCoords.Item3,
ResurrectionPositionPointer);

// GlobalActorModel.DsgVar_29, reset checkpoint object to null
GameManager.WriteDWord(0, RespawnPersoPointer, RespawnPersoPointerPath);

Thread.Sleep(50);

// Deathwarp and immediately put player back
GameManager.WriteEngineMode(8); // EM_ModePlayerDead

Thread.Sleep(150);
GameManager.PlayerCoordinates = playerCoords;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public Rayman2GameManager()
PausedStatePointer = 0x500FAA;
PlayerCoordinatesBasePointer = 0x500560;
PlayerCoordinatesOffsets = new[] { 0x224, 0x310, 0x34, 0x0, 0x1ac };

GhostModePointer = 0x500370;

PossibleProcessNames = new[] { "Rayman2", "Rayman2.exe", "Rayman2.exe.noshim" };

//Extras
Expand All @@ -33,6 +36,7 @@ public Rayman2GameManager()
new Rayman2MaxHpExtra(this),
new Rayman2GlmMonitorExtra(this),
new Rayman2ProgressArrayExtra(this),
new Rayman2CheckpointExtra(this),
};

//Levels
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public Rayman3GameManager()
EngineModePointer = EngineStructurePointer + 0x0;
LevelNamePointer = EngineStructurePointer + 0x1F;
PausedStatePointer = 0x7D848C;
GhostModePointer = 0x7D7D84;
PlayerCoordinatesBasePointer = 0x5BFAD4;
PlayerCoordinatesOffsets = new[] { 0x140, 0x258, 0x108, 0x324};
PossibleProcessNames = new[] { "Rayman3", "Rayman3.exe", "Rayman3.exe.noshim" };
Expand Down Expand Up @@ -133,6 +134,6 @@ public Rayman3GameManager()
Levels = LevelContainers.SelectMany(x => x.Levels);
}

#endregion
#endregion
}
}
243 changes: 132 additions & 111 deletions OpenSpaceToolbox/GameManager/GenericGameManager.cs
Original file line number Diff line number Diff line change
@@ -1,61 +1,71 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;

namespace OpenSpaceToolbox
{
/// <summary>
/// Generic game manager.
/// </summary>
public abstract class GenericGameManager
{
#region Public Properties

/// <summary>
/// The full name of the game.
/// </summary>
public string Name { get; protected set; }

/// <summary>
/// The name of the executable, without extension.
/// </summary>
public string ExecName { get; protected set; }

/// <summary>
/// The name of the game window, used for determining if the game is focused.
/// </summary>
public string WindowName { get; protected set; }

/// <summary>
/// Filename used to save level bookmarks.
/// </summary>
public string BookmarkFileName { get; protected set; }

/// <summary>
/// Player coordinates tuple.
/// Retrieving and writing values to the memory should be handled in functions: ReadPlayerCoordinates and WritePlayerCoordinates.
/// </summary>
public (float, float, float) PlayerCoordinates
{
get => ReadPlayerCoordinates();
set => WritePlayerCoordinates(value.Item1, value.Item2, value.Item3);
}

/// <summary>
/// Current level name.
/// Retrieving and writing values to the memory should be handled in functions: GetCurrentLevelName and ChangeLevel.
/// </summary>
public string CurrentLevel
{
get => GetCurrentLevelName();
set => ChangeLevel(value);
}

public ObservableCollection<ExtraAction> ExtraActions { get; protected set; }

public ObservableCollection<LevelContainerViewModel> LevelContainers { get; protected set; }

public IEnumerable<LevelViewModel> Levels { get; protected set; }
/// <summary>
/// Generic game manager.
/// </summary>
public abstract class GenericGameManager
{
#region Public Properties

/// <summary>
/// The full name of the game.
/// </summary>
public string Name { get; protected set; }

/// <summary>
/// The name of the executable, without extension.
/// </summary>
public string ExecName { get; protected set; }

/// <summary>
/// The name of the game window, used for determining if the game is focused.
/// </summary>
public string WindowName { get; protected set; }

/// <summary>
/// Filename used to save level bookmarks.
/// </summary>
public string BookmarkFileName { get; protected set; }

/// <summary>
/// Player coordinates tuple.
/// Retrieving and writing values to the memory should be handled in functions: ReadPlayerCoordinates and WritePlayerCoordinates.
/// </summary>
public (float, float, float) PlayerCoordinates
{
get => ReadPlayerCoordinates();
set
{
WritePlayerCoordinates(value.Item1, value.Item2, value.Item3);

bool oldGhostMode = ReadGhostMode();
WriteGhostMode(true);
Thread.Sleep(30);
WriteGhostMode(oldGhostMode);
}
}

/// <summary>
/// Current level name.
/// Retrieving and writing values to the memory should be handled in functions: GetCurrentLevelName and ChangeLevel.
/// </summary>
public string CurrentLevel
{
get => GetCurrentLevelName();
set => ChangeLevel(value);
}

public ObservableCollection<ExtraAction> ExtraActions { get; protected set; }

public ObservableCollection<LevelContainerViewModel> LevelContainers { get; protected set; }

public IEnumerable<LevelViewModel> Levels { get; protected set; }

#endregion

Expand All @@ -82,62 +92,73 @@ public string CurrentLevel
/// <returns></returns>
protected abstract (float, float, float) ReadPlayerCoordinates();

/// <summary>
/// Writes player coordinates from the game memory.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
protected abstract void WritePlayerCoordinates(float x, float y, float z);

/// <summary>
/// Get current level name from the game memory.
/// </summary>
/// <returns></returns>
protected abstract string GetCurrentLevelName();

/// <summary>
/// Loads a new level.
/// </summary>
/// <param name="levelName"></param>
protected virtual void ChangeLevel(string levelName)
{
OnChangeLevel?.Invoke(levelName);
}

#endregion

#region Public Methods

/// <summary>
/// Get the process handle of the game.
/// </summary>
/// <param name="showMessage">Indicates if a message should be shown if the process is not found</param>
/// <returns></returns>
public abstract int GetProcessHandle(bool showMessage = true);

/// <summary>
/// Checks if the game is currently paused.
/// </summary>
/// <returns></returns>
public abstract bool IsGamePaused();

/// <summary>
/// Checks if the game window is currently focused.
/// </summary>
/// <returns></returns>
public abstract bool IsGameFocused();

/// <summary>
/// Reloads the current level.
/// </summary>
public virtual void ReloadLevel()
{
OnReloadLevel?.Invoke();
}

public abstract void LoadOffsetLevel(int offset);

#endregion
}
}
/// <summary>
/// Writes player coordinates from the game memory.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
protected abstract void WritePlayerCoordinates(float x, float y, float z);

/// <summary>
/// Checks if GhostMode (g_ucIsEdInGhostMode) is active
/// </summary>
/// <returns>g_ucIsEdInGhostMode</returns>
public abstract bool ReadGhostMode();

/// <summary>
/// Sets GhostMode (g_ucIsEdInGhostMode)
/// </summary>
public abstract void WriteGhostMode(bool value);

/// <summary>
/// Get current level name from the game memory.
/// </summary>
/// <returns></returns>
protected abstract string GetCurrentLevelName();

/// <summary>
/// Loads a new level.
/// </summary>
/// <param name="levelName"></param>
protected virtual void ChangeLevel(string levelName)
{
OnChangeLevel?.Invoke(levelName);
}

#endregion

#region Public Methods

/// <summary>
/// Get the process handle of the game.
/// </summary>
/// <param name="showMessage">Indicates if a message should be shown if the process is not found</param>
/// <returns></returns>
public abstract int GetProcessHandle(bool showMessage = true);

/// <summary>
/// Checks if the game is currently paused.
/// </summary>
/// <returns></returns>
public abstract bool IsGamePaused();

/// <summary>
/// Checks if the game window is currently focused.
/// </summary>
/// <returns></returns>
public abstract bool IsGameFocused();

/// <summary>
/// Reloads the current level.
/// </summary>
public virtual void ReloadLevel()
{
OnReloadLevel?.Invoke();
}

public abstract void LoadOffsetLevel(int offset);

#endregion
}
}
Loading

0 comments on commit 6b10fe1

Please sign in to comment.