From my understanding, since every script is a Component, you should be able to fully override any script you want from the game as long as you replace it properly on the relevant game objects AND update any fields that reference the component (like I had to do to SerializablePlayer in my UpdateIronmanMouseLook() code). Once everything is initialized, there should be no extra Reflection cost (except for any calls where you'd have to modify private variables or skip the parent class, which would cause infinite recursion in your derived classes if you did not skip the parent).
All you need to do is derive your custom script from the original one and include your version of whichever functions you needed to modify. You'll want to skip a level above the parent class for anything that makes a function call from base. I was able to do that with a class that inherits from DaggerfallUnitySaveGameWindow (which, in turn, inherits from DaggerfallPopupWindow) as follows:
Code: Select all
public class IronmanSaveGameWindow : DaggerfallUnitySaveGameWindow
{
private readonly IntPtr DaggerfallPopupWindowUpdatePtr = IntPtr.Zero;
private readonly Action DaggerfallPopupWindowUpdate = null;
public IronmanSaveGameWindow(IUserInterfaceManager uiManager, Modes mode, DaggerfallBaseWindow previous = null, bool displayMostRecentChar = false)
: base(uiManager, mode, previous, displayMostRecentChar)
{
DaggerfallPopupWindowUpdatePtr = this.GetType().BaseType.BaseType.GetMethod("Update").MethodHandle.GetFunctionPointer();
DaggerfallPopupWindowUpdate = (Action)Activator.CreateInstance(typeof(Action), this, DaggerfallPopupWindowUpdatePtr);
}
public override void Update()
{
DaggerfallPopupWindowUpdate();
}
}
A quick search reveals that WeaponManager.cs and FPSWeapon.cs don't make any calls to their parent class via "base" at all, which means that a mod version of this may not incur any performance hit at all (excluding any cost for modifying private fields) if the other scripts you're modifying are the same way.
Code: Select all
private static IronmanPlayerMouseLook IPML;
protected static void UpdateIronmanMouseLook()
{
if (IPML == null)
{
GameObject camera = GameManager.Instance.MainCameraObject;
camera.AddComponent<IronmanPlayerMouseLook>();
Debug.Log("UpdateIronmanMouseLook(): Added IronmanPlayerMouseLook to a Camera");
PlayerMouseLook PML = camera.GetComponent<PlayerMouseLook>();
IPML = camera.GetComponent<IronmanPlayerMouseLook>();
// Copy the state of the PlayerMouseLook component to the new IronmanPlayerMouseLook component
FieldInfo[] fields = PML.GetType().GetFields();
foreach (FieldInfo field in fields)
field.SetValue(IPML, field.GetValue(PML));
Debug.Log("UpdateIronmanMouseLook(): Copied PlayerMouseLook fields to IronmanPlayerMouseLook");
foreach (Component c in camera.GetComponents<PlayerMouseLook>())
{
if (!(c is IronmanPlayerMouseLook))
{
Debug.Log("UpdateIronmanMouseLook(): Destroying <PlayerMouseLook>");
Destroy(c);
}
else
{
Debug.Log("UpdateIronmanMouseLook(): Assigning IronmanPlayerMouseLook to GameManager.Instance.PlayerMouseLook");
GameManager.Instance.PlayerMouseLook = IPML;
}
}
GameObject PA = GameObject.Find("PlayerAdvanced");
if (PA == null)
Debug.LogError("UpdateIronmanMouseLook(): Couldn't find PlayerAdvanced GameObject! This will cause problems!");
else
{
SerializablePlayer SP = PA.GetComponent<SerializablePlayer>();
if (SP == null)
Debug.LogError("UpdateIronmanMouseLook(): Couldn't find SerializablePlayer component on PlayerAdvanced! This will cause problems!");
else
{
SP.SetFieldValue("playerMouseLook", IPML);
Debug.Log("UpdateIronmanMouseLook(): Assigned IPML to SerializablePlayer's playerMouseLook field.");
}
}
}
Debug.Log("UpdateIronmanMouseLook(): Exiting function.");
}
Great work on this so far - it's really coming together!