# 写在前面
我们知道在游戏中,有两种卡牌交互方式:拖动交互和点击按钮。
想要检测这两种交互动作,就需要patch对应的函数。


# 猜函数
# 拖动交互
# 寻找
在ME中,是在cardinteraction中添加拖动交互,因此先在dnSpy中搜索cardinteraction,发现没有特别符合的函数。

在ME中,除了属性名称,还有这个属性对应的类型,可以看出来cardinteraction是一个list列表,里面存放的是CardOnCardAction类型数据。

搜索CardOnCardAction,发现了一个CardOnCardActionRoutine,翻译过来就是卡对卡行动套路,看着挺像那么回事的。

private IEnumerator CardOnCardActionRoutine(CardOnCardAction _Action, InGameCardBase _GivenCard, InGameCardBase _ReceivingCard, bool _FastMode)
经过测试,卡牌拖动交互时,会触发这个函数。
# 测试
测试过程如下:进入游戏后选择上方的Hooks按钮,在弹出来的页面中,输入GameManager,然后点击View Methods,在下方继续输入CardOnCardActionRoutine后,找到对应的函数,点击左侧的Hooks,直到上方显示如图信息。

利用Tab控制台,随便调出具有拖动交互的卡牌,例如钓鱼竿,然后拖到海上钓鱼。
可以在log控制台中看到如下信息,说明拖动交互过程中,确实执行了这个函数。

# 按钮交互
# 寻找
在ME中,是在DismantleAction中添加按钮交互,直接搜索DismantleAction或者DismantleCardAction是比较难找到对应函数的,但是我们可以根据之前拖动交互的函数曲线救国。
查看CardOnCardActionRoutine的代码,可以发现在函数结束前,调用了另一个函数ActionRoutine,根据翻译,以及查看该函数代码,可以发现执行了大量Action相关的代码。初步推断所有Action都由此函数进行结算。

# 测试
使用Unity Explorer的Hooks功能,对ActionRoutine函数进行Hook操作。然后进行任意DismantleAction动作(这里是吃了一个芒果),可以在log控制台中看到调用了ActionRoutine函数。


# 总结
在猜函数阶段,是需要一部分代码基础的,至少需要能够看懂该函数的大致行为和作用。当然,对于交互或改卡这类常用操作来说,知道之后就可以一直用。
如果实在找不到能用的函数,也可以在群里咨询大佬。
# 写代码
ActionRoutine函数和之前patch过的GameLoad函数有一些区别,可以看到GameLoad函数前面的返回值是void,而ActionRoutine函数前面的返回值是IEnumerator。


# 协程函数
-
协程函数只能使用
postfix。 -
把patch函数的返回值从
void改成IEnumerator,然后入参添加IEnumerator result(必须为第一个参数)。 -
需要添加一句
yield return result;。 -
由于协程函数只能使用
postfix,在yield return result前的代码可以近似认为是prefix,后的代码可以近似认为是postfix。
[HarmonyPostfix, HarmonyPatch("ActionRoutine")]
public static IEnumerator ActionRoutine_Postfix(IEnumerator result)//新加的
{
Debug.LogWarning("ActionRoutine1");
yield return result;//新加的
Debug.LogWarning("ActionRoutine2");
}
# 示例
之前写过一个物品耐久合并,将两把50%耐久的长矛拖到一起,可以合成为一根100%耐久的长矛。
具体实现如下:在拖动交互进行前,先记录两把长矛的耐久(prefix),然后yield return result执行原函数,将拖过去的长矛销毁,原函数执行完成后,再修改另一把长矛的耐久(postfix)。
public static IEnumerator CardOnCardActionRoutine_Postfix(IEnumerator result, InGameCardBase _GivenCard,
InGameCardBase _ReceivingCard)
{
float usage1 = _GivenCard.CurrentUsageDurability;
float usage2 = _ReceivingCard.CurrentUsageDurability;
yield return result;
_ReceivingCard.CurrentUsageDurability = usage1 + usage2;
}
# 小知识
在写代码时,如果要用到原函数的参数,就把这个参数复制过来就行,不要修改名称。

