rimworld吧 关注:277,970贴子:5,147,691

回复:泰南行为学:实战篇

只看楼主收藏回复

但还是不行,依然会出现队列阻塞的问题,本着头痛医头,脚痛医脚的精神,这时我们换个想法,欸,如果每一次玩家指挥的是时候强制中断Job,在派遣我们的任务前清理队列那么是不是就没有堵塞的问题了,只需要在点击后调用StopAll()即可
那么下面我们就要分析玩家的行为如何强制Pawn行动
首先我想到了Unity的函数Input.GetMouseDown用于获取鼠标右键相关行为,没看到.GetMouse太多不想看.
然后就是游戏里面的翻译,右键Pawn可以看见 自我治疗 的相关选项,搜索中文.可以找到SelfTend标签,反汇编搜索SelfTend得到工作JobDefOf.TendPatient和WorkTypeDefOf.Doctor.再搜工作,一眼就看见FloatMenuOptionProvider_DraftedTend,有心再搜索一下FloatMenuOptionProvider_Drafted,可以看见
FloatMenuOptionProvider_DraftedAttack
FloatMenuOptionProvider_DraftedMove
FloatMenuOptionProvider_DraftedRepair
FloatMenuOptionProvider_DraftedTend
而点击Pawn自己触发的是FloatMenuOptionProvider_DraftedTend,为了实现玩家能主动灭火,我们修改这个
查看发现获得多选项的是FloatMenuOptionProvider_DraftedTend.GetOptionsFor()这个函数,

后面一堆null,0f,一看就是默认值不管它,再看一眼FloatMenuOption的构造函数,其中只要添加第一二个参数,分别为显示标签和用于添加Job的委托,添加方式是通过Jobs.TryTakeOrderdJob添加
那就立刻编写手动添加灭火的补丁并翻译一下




IP属地:广东17楼2025-12-27 12:23
回复
    完成了主动灭火的补丁,理论上我们可以把所有FloatMenuOptionProvider_Drafted都加上StopAll()防止队列阻塞(我在之前已经测试过如果Pawn没有着火时这个队列就是空的),但是考虑到前补丁相对不太好,以及无法确定清空队列的影响,所以楼主并未将这个方案定为最终方案.
    看到这就会发现有一件事我们一直没有搞明白,就是为什么队列会阻塞.队列阻塞就说明调用肯定是哪里不一样,现在我们知道如何手动触发小人行为,那就在相关行为打上patch.楼主以先攻击某一个物体,然后在点击其他地方使得Pawn运动的行为作为对比.正常征召的Pawn可以直接打断,而目前打了补丁的小人不行,所以首先我在FloatMenuOptionProvider_DraftedMove和FloatMenuOptionProvider_DraftedAttack下面打了后补丁,由于返回是IEnumerable<Class>变量,后补丁直接也当前补丁用,ai分析调用没有差异,下一个以ThinkNode_ConditionalBurning.Satisfied()后补丁打印Log,通过把log给ai发现这个节点的函数堆栈调用不同,且这个不同是在某个函数的Pawn_JobTracker.TryTakeOrderedJob()的返回值上.继续进入这个函数,再一眼就看见IsCurrentJobPlayerInterruptible()这个函数,打开,立刻明白一切.

    不知道为什么都有isBurning()这个方法判断还要写HasAttachment(ThingDefOf.Fire),可能是老代码的遗留吧.当然这也是早有征兆的,在ThinkNode_ConditionalBurning中也是这样判断是否着火.
    通过后补丁将这个值改成false就可以通过玩家命令打断Pawn着火时的行为,如果你觉得完全操控着火Pawn太正义了,就可以不禁止乱跑,但把这里改成着火时返回true.这样就可以着火时拉着Pawn往一个方向跑.我的代码只改Pawn着火且被征召时的反应,其他状况函数返回不变


    IP属地:广东19楼2025-12-27 13:03
    回复