1、前言
为了避免本人在游玩mod地图(来自深渊)坐牢,彻底解决鸟妖生成的问题,故而开发了本mod
2、设计思路
项目设计之初,想要参考原版物品“皇家凝胶”,将鸟妖变成被动型生物。从结论上来说,这个设计很不幸失败了,失败的原因在于无法彻底禁止鸟妖射出羽毛(建议博主菜就多练),这可能是没有完全理解AI机制导致的。因此改换第二个思路,直接禁止鸟妖生成
3、代码核心
以下是初版的失败代码,失败的原因在于原代码使用SpawnModificationPriority => uint.MaxValue - 1
试图确保最高优先级,这是由于尝试重写不存在的父类成员(CS0115
),为解决这个问题,我选择直接放弃属性控制,改为通过代码逻辑手动确保最后修改生成池。
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace HarpyGoAway.NPCs
{
public class HarpySpawnControl : GlobalNPC
{
private static readonly int[] HarpyIDs = {
NPCID.Harpy,
NPCID.IlluminantHarpy,
NPCID.Splinterling
};
public override uint SpawnModificationPriority => uint.MaxValue - 1;
public override void EditSpawnPool(IDictionary<int, float> pool, NPCSpawnInfo spawnInfo)
{
try
{
// 使用安全导航操作符和模式匹配
if (spawnInfo.Player?.GetModPlayer<HarpyMedallionPlayer>() is { } player
&& player.blockHarpySpawns
&& spawnInfo.SpawnTileY < Main.worldSurface * 0.35)
{
foreach (int id in HarpyIDs)
{
if (pool.TryGetValue(id, out _))
{
pool[id] = 0f; // 设置生成概率为0
}
}
}
}
catch (Exception ex)
{
Mod.Logger.Warn($"生成控制异常:{ex}");
}
}
// 增强防御:实时禁用NPC
public override bool PreAI(NPC npc)
{
if (Array.Exists(HarpyIDs, x => x == npc.type))
{
if (Main.LocalPlayer.GetModPlayer<HarpyMedallionPlayer>().blockHarpySpawns)
{
// 立即失效并清除
npc.active = false;
npc.netUpdate = true;
// 防止残留逻辑执行
return false;
}
}
return true;
}
}
}
4、项目结构
HARPYGOAWAY/
├── Items/
│ ├── HarpyMedallion.cs
│ └── HarpyMedallion.png
├── NPCs/
│ ├── HarpyCleaner.cs
│ └── HarpySpawnControl.png
├── Players/
└── HarpyMedallionPlayer.cs
5、代码展示
HarpyMedallion.cs
using HarpyGoAway.Players;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace HarpyGoAway.Items
{
public class HarpyMedallion : ModItem
{
public override void SetStaticDefaults()
{
}
public override void SetDefaults()
{
Item.width = 28;
Item.height = 28;
Item.accessory = true; // 设为饰品
Item.rare = ItemRarityID.Pink;
Item.value = Item.sellPrice(0, 0, 0, 0);
Item.value = Item.buyPrice(0, 1, 0, 0);
}
// 饰品生效逻辑
public override void UpdateAccessory(Player player, bool hideVisual)
{
player.GetModPlayer<HarpyMedallionPlayer>().blockHarpySpawns = true;
}
// 合成配方
public override void AddRecipes()
{
CreateRecipe()
.AddIngredient(ItemID.Feather, 1)
.AddTile(18)
.Register();
}
}
}
HarpyCleaner.cs
using HarpyGoAway.Players;
using Terraria;
using Terraria.ModLoader;
namespace HarpyGoAway.NPCs
{
public class HarpyCleaner : GlobalNPC
{
public override bool PreAI(NPC npc)
{
if (npc.active &&
(npc.type == 48) &&
Main.LocalPlayer.GetModPlayer<HarpyMedallionPlayer>().blockHarpySpawns)
{
// 强制清除已存在的鸟妖
npc.active = false;
npc.netUpdate = true;
return false;
}
return true;
}
}
}
HarpySpawnControl.cs
using System;
using System.Collections.Generic;
using System.Text;
using HarpyGoAway.Players;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace HarpyGoAway.NPCs
{
public class HarpySpawnControl : GlobalNPC
{
private static readonly int[] HarpyIDs = {
NPCID.Harpy
};
public override void EditSpawnPool(IDictionary<int, float> pool, NPCSpawnInfo spawnInfo)
{
StringBuilder sb = new StringBuilder("当前生成池内容:\n");
// 记录生成池条目总数
sb.AppendLine($"总条目数:{pool.Count}");
// 遍历所有生成条目
foreach (var entry in pool)
{
string npcName = Lang.GetNPCNameValue(entry.Key);
sb.AppendLine($"ID:{entry.Key} 名称:{npcName} 概率:{entry.Value:F2}");
}
//Main.NewText(sb.ToString());
// 修改鸟妖生成概率
foreach (int id in HarpyIDs)
{
if (pool.ContainsKey(id))
{
float oldValue = pool[id];
pool[id] = 0f;
//Main.NewText($"已将 {Lang.GetNPCNameValue(id)} 生成概率从 {oldValue} 修改为 0");
}
else
{
//Main.NewText($"{Lang.GetNPCNameValue(id)} 不在生成池中");
}
}
}
// 实时清除已生成的NPC
public override bool PreAI(NPC npc)
{
if (Array.Exists(HarpyIDs, x => x == npc.type) &&
Main.LocalPlayer.GetModPlayer<HarpyMedallionPlayer>().blockHarpySpawns)
{
npc.active = false;
npc.netUpdate = true;
return false;
}
return true;
}
}
}
HarpyMedallionPlayer.cs
using Terraria;
using Terraria.ModLoader;
namespace HarpyGoAway.Players
{
public class HarpyMedallionPlayer : ModPlayer
{
public bool blockHarpySpawns;
public override void ResetEffects() => blockHarpySpawns = false;
/*
public override void PostUpdateMiscEffects()
{
// 世界地表高度的35%(转换为像素)
float skyHeightThreshold = (float)(Main.worldSurface * 0.35f * 16);
bool inSkyLayer = Player.position.Y < skyHeightThreshold;
blockHarpySpawns &= inSkyLayer;
}
*/
}
}
6、问答
Q:为什么注释掉PostUpdateMiscEffects?
A:这是由于博主对该mod地图不熟悉导致的,尚不清楚为什么在原版可以生效,但在mod地图中测试无法生效,因此采用了一刀切(取消了Y高度判断),在所有的Y值上都禁止生成鸟妖。
Q:本Mod是否进行了多人联机适配?
A:没有(博主太菜导致的,绝不是太懒
Q:该Mod能否拓展并禁止其他生物生成?
A:可以,你只需要在“黑名单”HarpyIDs中添加对应生物的NPCID
Q:Mod是否会导致一辈子见不到鸟妖?
A:不会,你只需要卸下这个物品即可正常刷新,而在戴上时会立刻杀死所有鸟妖。