连锁挖矿示例 - github-user-64/tPlainModLoader GitHub Wiki

自定义修补玩家类

关于自定义修补的更多信息

[HarmonyPatch(typeof(Player))]//表示修补玩家类中的方法
internal static class PPlayer
{
    public static bool Enable = true;//启用
    public static ushort Size = 10;//挖掘半径
    public static bool PickEffect = true;//挖掘效果
    private static int type = -1;

    [HarmonyPatch("PickTile")]//表示修补挖掘方块
    [HarmonyPrefix]//表示在挖掘方块前
    internal static void PickTilePr(int x, int y, int pickPower)
    {
        if (Enable == false) return;
        type = -1;

        Tile tile = Main.tile[x, y];
        if (tile == null) return;
        if (tile.active() == false) return;//没方块
        if (TileID.Sets.Ore[tile.type] == false) return;//不是矿

        type = tile.type;
    }

    [HarmonyPatch("PickTile")]//表示修补挖掘方块
    [HarmonyPostfix]//表示在挖掘方块后
    internal static void PickTilePo(int x, int y, int pickPower)
    {
        if (PPlayer.type < 0) return;

        int type = PPlayer.type;
        PPlayer.type = -1;

        //

        bool kill = Main.tile[x, y]?.active() == false;//方块是否被挖掉

        //遍历范围内的方块, 即使没相连也会被一起挖掉
        for (int i = x - Size; i < x + Size; i++)
        {
            for (int j = y - Size; j < y + Size; j++)
            {
                if (i == x && j == y) continue;
                if (WorldGen.InWorld(i, j) == false) continue;//超出世界

                Tile tile = Main.tile[i, j];
                if (tile == null) continue;
                if (tile.type != type) continue;//类型不同
                if (tile.active() == false) continue;//没方块

                if (kill)
                {
                    WorldGen.KillTile(i, j);//破坏方块
                    if (Main.netMode == 1)//如果是客户端就发送消息, 这样在多人才能用
                    {
                        NetMessage.TrySendData(MessageID.TileManipulation, -1, -1, null, 0, i, j);
                    }

                    if (PickEffect) Main.LocalPlayer.hitTile.Prune();
                }
                else if(PickEffect)
                {
                    //如果方块没被挖掉就添加碎裂效果, 可以不加
                    Main.LocalPlayer.PickTile_DetermineDamage(i, j, pickPower, tile, out int bufferIndex, out int damage);

                    Main.LocalPlayer.hitTile.AddDamage(bufferIndex, damage);
                }
            }
        }
    }
}

初始化修补

internal class PatchInit : Mod
{
    private const string patchId = "tPlainModLoader.Mod.test1.gamePatch";//修补的id, 应该写啥都行
    private Harmony harmony = null;

    public override void AddPatch(IAddPatch addPatch)//添加修补
    {
        harmony = new Harmony(patchId);
        harmony.PatchAll();//修补全部
    }

    public override void Unload()
    {
        harmony?.UnpatchAll(patchId);//卸载修补
        harmony = null;
    }
}

UI窗口

internal class PickWindow : UIWindow//继承了模板UIWindow
{
    public PickWindow(string title, int width, int height) : base(title, width, height)
    {
        UIStackPanel sp = new UIStackPanel();//竖向排列子控件
        sp.Width.Set(0, 1);
        sp.Height.Set(0, 1);
        Child.Append(sp);//将sp添加到窗口. 要向窗口添加控件就用这个

        UIItemSwitch enable = new UIItemSwitch(null, "启用");
        enable.OnValUpdate += v => PPlayer.Enable = v;
        enable.SetVal(PPlayer.Enable);
        sp.Append(enable);

        UIItemTextBox size = new UIItemTextBox(text: "半径");
        size.OnTextChanged += v =>
        {
            if (ushort.TryParse(v, out ushort val)) PPlayer.Size = val;
        };
        size.SetText(PPlayer.Size.ToString());
        sp.Append(size);

        UIItemSwitch pickEffect = new UIItemSwitch(null, "挖掘效果");
        pickEffect.OnValUpdate += v => PPlayer.PickEffect = v;
        pickEffect.SetVal(PPlayer.PickEffect);
        sp.Append(pickEffect);
    }
}

添加UI

internal class ModifyInterfaceLayers : PatchMain
{
    private UIState state = null;
    private UserInterface ui = null;
    private PickWindow window = null;

    //和tModLoader中一样在这插入ui
    public override void SetupDrawInterfaceLayersPostfix(List<GameInterfaceLayer> gameInterfaceLayers)
    {
        //"Vanilla: Inventory"是物品栏图层的名称
        int index = gameInterfaceLayers.FindIndex(i => i.Name == "Vanilla: Inventory");
        if (index == -1) return;

        window = new PickWindow("简易连锁挖矿", 260, 160);
        state = new UIState();
        ui = new UserInterface();
        //state.Append(window);控件在这里添加就行了, 由于UIWindow在打开的时候会自动添加所以不需要这句
        //state.Append(ui1);
        //state.Append(ui2);
        //state.Append(ui...);

        //在物品栏前插入
        gameInterfaceLayers.Insert(index, new LegacyGameInterfaceLayer(
            "StaticTile.test1: Inventory Prefix(属于你的图层名称, 写啥都行)",
            () =>
            {
                ui.Draw(Main.spriteBatch, Main.gameTimeCache);//绘制ui
                return true;
            },
            InterfaceScaleType.UI));
    }

    public override void UpdateUIStatesPostfix(GameTime gameTime)
    {
        if (ui == null) return;

        if (Main.gameMenu)//在游戏外
        {
            ui.SetState(null);//禁用ui
            return;
        }

        ui.SetState(state);//启用ui
        ui.Update(gameTime);//更新ui

        //快捷键'P'开关窗口
        if (Main.keyState.IsKeyDown(Keys.P) && Main.oldKeyState.IsKeyUp(Keys.P))
        {
            if (window.IsOpen) window.Close();//从state删除
            else window.Open(state);//添加到state
        }
    }
}

实际效果

https://github.com/user-attachments/assets/9a899bd3-24f6-4289-a454-d6f87630dbca