Re: Combat Overhaul Alpha
Posted: Fri Aug 30, 2019 8:21 am
Hey, l3lessed, there was an issue with the old code I provided that was causing problems on reload due to SerializablePlayer's reference to playerMouseLook not being updated. I thought you should see the updated version if you decide to create this as a mod instead of as a custom build. I'm not trying to beat a dead horse here - the custom build may be the best way to go, just wanted to keep everything updated. The rest of this post is something of an in-depth rehash of what I posted the other day in this topic, for you or anyone else who might be interested.
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:
That call does slow down execution of the frame, but only by 1/10th of a millisecond (on my fairly basic laptop). I'm not sure if that's due to the Reflection call itself or because of the Update() chain that gets executed as a result of it. Reflection calls on fields are certainly faster. I haven't benchmarked the difference between the DaggerfallPopUpWindowUpdate() call and a Reflection call on a simpler function.
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.
The example code uses my ReflectionHelper extension class (here), so you may want that. If you decide to try a mod version, feel free to ask if you need any help with getting it to work.
Great work on this so far - it's really coming together!
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!