oni-priority-ux/mod/PLibLighting/LightingPatches.cs

171 lines
6.9 KiB
C#

/*
* Copyright 2022 Peter Han
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using HarmonyLib;
using PeterHan.PLib.Core;
using PeterHan.PLib.Detours;
using System;
using System.Collections.Generic;
using UnityEngine;
using IntHandle = HandleVector<int>.Handle;
using LightGridEmitter = LightGridManager.LightGridEmitter;
using TranspiledMethod = System.Collections.Generic.IEnumerable<HarmonyLib.CodeInstruction>;
namespace PeterHan.PLib.Lighting {
/// <summary>
/// Contains all patches (many!) required by the PLib Lighting subsystem. Only applied by
/// the latest version of PLightManager.
/// </summary>
internal static class LightingPatches {
private delegate IntHandle AddToLayerDelegate(Light2D instance, Extents ext,
ScenePartitionerLayer layer);
private static readonly DetouredMethod<AddToLayerDelegate> ADD_TO_LAYER =
typeof(Light2D).DetourLazy<AddToLayerDelegate>("AddToLayer");
private static readonly IDetouredField<Light2D, int> ORIGIN = PDetours.
DetourFieldLazy<Light2D, int>("origin");
private static readonly IDetouredField<LightShapePreview, int> PREVIOUS_CELL =
PDetours.DetourFieldLazy<LightShapePreview, int>("previousCell");
private static bool ComputeExtents_Prefix(Light2D __instance, ref Extents __result) {
var lm = PLightManager.Instance;
bool cont = true;
if (lm != null && __instance != null) {
var shape = __instance.shape;
int rad = Mathf.CeilToInt(__instance.Range), cell;
lm.AddLight(__instance.emitter, __instance.gameObject);
if (shape > LightShape.Cone && rad > 0 && Grid.IsValidCell(cell = ORIGIN.Get(
__instance))) {
Grid.CellToXY(cell, out int x, out int y);
// Better safe than sorry, check whole possible radius
__result = new Extents(x - rad, y - rad, 2 * rad, 2 * rad);
cont = false;
}
}
return cont;
}
/// <summary>
/// Applies the required lighting related patches.
/// </summary>
/// <param name="plibInstance">The Harmony instance to use for patching.</param>
public static void ApplyPatches(Harmony plibInstance) {
// Light2D
plibInstance.Patch(typeof(Light2D), "ComputeExtents",
prefix: PatchMethod(nameof(ComputeExtents_Prefix)));
plibInstance.Patch(typeof(Light2D), nameof(Light2D.FullRemove), postfix:
PatchMethod(nameof(Light2D_FullRemove_Postfix)));
plibInstance.Patch(typeof(Light2D), nameof(Light2D.RefreshShapeAndPosition),
postfix: PatchMethod(nameof(Light2D_RefreshShapeAndPosition_Postfix)));
// LightBuffer
try {
plibInstance.PatchTranspile(typeof(LightBuffer), "LateUpdate", PatchMethod(
nameof(LightBuffer_LateUpdate_Transpile)));
} catch (Exception e) {
// Only visual, log as warning
PUtil.LogExcWarn(e);
}
// LightGridEmitter
plibInstance.Patch(typeof(LightGridEmitter), "ComputeLux", prefix:
PatchMethod(nameof(ComputeLux_Prefix)));
plibInstance.Patch(typeof(LightGridEmitter), nameof(LightGridEmitter.
UpdateLitCells), prefix: PatchMethod(nameof(UpdateLitCells_Prefix)));
// LightGridManager
plibInstance.Patch(typeof(LightGridManager), nameof(LightGridManager.
CreatePreview), prefix: PatchMethod(nameof(CreatePreview_Prefix)));
// LightShapePreview
plibInstance.Patch(typeof(LightShapePreview), "Update", prefix:
PatchMethod(nameof(LightShapePreview_Update_Prefix)));
// Rotatable
plibInstance.Patch(typeof(Rotatable), "OrientVisualizer", postfix:
PatchMethod(nameof(OrientVisualizer_Postfix)));
}
private static bool ComputeLux_Prefix(LightGridEmitter __instance, int cell,
LightGridEmitter.State ___state, ref int __result) {
var lm = PLightManager.Instance;
return lm == null || !lm.GetBrightness(__instance, cell, ___state, out __result);
}
private static bool CreatePreview_Prefix(int origin_cell, float radius,
LightShape shape, int lux) {
var lm = PLightManager.Instance;
return lm == null || !lm.PreviewLight(origin_cell, radius, shape, lux);
}
private static void Light2D_FullRemove_Postfix(Light2D __instance) {
var lm = PLightManager.Instance;
if (lm != null && __instance != null)
lm.DestroyLight(__instance.emitter);
}
private static void Light2D_RefreshShapeAndPosition_Postfix(Light2D __instance,
Light2D.RefreshResult __result) {
var lm = PLightManager.Instance;
if (lm != null && __instance != null && __result == Light2D.RefreshResult.Updated)
lm.AddLight(__instance.emitter, __instance.gameObject);
}
private static TranspiledMethod LightBuffer_LateUpdate_Transpile(
TranspiledMethod body) {
var target = typeof(Light2D).GetPropertySafe<LightShape>(nameof(Light2D.shape),
false)?.GetGetMethod(true);
return (target == null) ? body : PPatchTools.ReplaceMethodCallSafe(body, target,
typeof(PLightManager).GetMethodSafe(nameof(PLightManager.LightShapeToRayShape),
true, typeof(Light2D)));
}
private static void LightShapePreview_Update_Prefix(LightShapePreview __instance) {
var lm = PLightManager.Instance;
// Pass the preview object into LightGridManager
if (lm != null && __instance != null)
lm.PreviewObject = __instance.gameObject;
}
private static void OrientVisualizer_Postfix(Rotatable __instance) {
// Force regeneration on next Update()
if (__instance != null && __instance.TryGetComponent(out LightShapePreview
preview))
PREVIOUS_CELL.Set(preview, -1);
}
/// <summary>
/// Gets a HarmonyMethod instance for manual patching using a method from this class.
/// </summary>
/// <param name="name">The method name.</param>
/// <returns>A reference to that method as a HarmonyMethod for patching.</returns>
private static HarmonyMethod PatchMethod(string name) {
return new HarmonyMethod(typeof(LightingPatches), name);
}
private static bool UpdateLitCells_Prefix(LightGridEmitter __instance,
List<int> ___litCells, LightGridEmitter.State ___state) {
var lm = PLightManager.Instance;
return lm == null || !lm.UpdateLitCells(__instance, ___state, ___litCells);
}
}
}