diff --git a/CheckBag/Ban.cs b/CheckBag/Ban.cs new file mode 100644 index 00000000..1461aa52 --- /dev/null +++ b/CheckBag/Ban.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TShockAPI; +using TShockAPI.DB; + +namespace CheckBag +{ + public class Ban + { + static string BanningUser = "CheckBag"; + static Dictionary _bans = new(); + + + /// + /// 触发规则 + /// + public static int Trigger(string name) + { + if (_bans.ContainsKey(name)) + { + _bans[name]++; + } + else + { + _bans.Add(name, 1); + } + return _bans[name]; + } + + + /// + /// 移除记录 + /// + public static void Remove(string name) + { + if (_bans.ContainsKey(name)) + { + _bans.Remove(name); + } + } + + + /// + /// 添加封禁 + /// + public static AddBanResult AddBan(string playerName, string reason, int durationSeconds) + { + DateTime expiration = DateTime.UtcNow.AddSeconds(durationSeconds); + AddBanResult banResult = TShock.Bans.InsertBan("acc:" + playerName, reason, BanningUser, DateTime.UtcNow, expiration); + if (banResult.Ban != null) + { + TShock.Log.Info($"已封禁{playerName}。输入 /ban del {banResult.Ban.TicketNumber} 解除封禁。"); + } + else + { + TShock.Log.Info($"封禁{playerName}失败!原因: {banResult.Message}。"); + } + return banResult; + } + + + /// + /// 列出封禁 + /// + public static void ListBans(CommandArgs args) + { + var bans = ( + from ban in TShock.Bans.Bans + where ban.Value.BanningUser == BanningUser + orderby ban.Value.ExpirationDateTime descending + select ban + ).ToList(); + var lines = new List(); + var flag = false; + foreach (var ban in bans) + { + if (!flag && (ban.Value.ExpirationDateTime <= DateTime.UtcNow)) + { + lines.Add("----下面的记录都已失效----"); + flag = true; + } + lines.Add($"{ban.Value.Identifier.Substring(4)}, 截止:{ban.Value.ExpirationDateTime.ToLocalTime():yyyy-dd-HH hh:mm:ss}, 原因:{ban.Value.Reason}, 解封:/ban del {ban.Key}"); + } + + if (!lines.Any()) + { + args.Player.SendInfoMessage("没有记录!看来没人作弊(*^▽^*)"); + return; + } + + if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out int pageNumber)) + { + return; + } + PaginationTools.SendPage(args.Player, pageNumber, lines, new PaginationTools.Settings + { + MaxLinesPerPage = 15, // 每页显示15行 + HeaderFormat = "封禁记录 ({0}/{1}):", + FooterFormat = "输入/cbag ban {{0}}查看更多".SFormat(Commands.Specifier), + }) ; + } + + } +} diff --git a/CheckBag/CheckBag.cs b/CheckBag/CheckBag.cs new file mode 100644 index 00000000..b8111482 --- /dev/null +++ b/CheckBag/CheckBag.cs @@ -0,0 +1,448 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using Terraria; +using TerrariaApi.Server; +using TShockAPI; +using TShockAPI.Hooks; +using 检查背包; + + +namespace CheckBag +{ + [ApiVersion(2, 1)] + public class CheckBag : TerrariaPlugin + { + public override string Name => "检查背包(超进度物品检测)"; + public override string Author => "hufang360 修改:羽学"; + public override string Description => "定时检查玩家背包,删除违禁物品,满足次数封禁对应玩家。"; + public override Version Version => new Version(2, 0, 0, 1); + + string FilePath = Path.Combine(TShock.SavePath, "检查背包"); //创建文件夹 + + internal static Configuration Config; //将Config初始化 + int Count = -1; + + public CheckBag(Main game) : base(game) + { + Config = new Configuration(); + } + + public override void Initialize() + { + if (!Directory.Exists(FilePath)) + { + Directory.CreateDirectory(FilePath); + } + + Commands.ChatCommands.Add(new Command("cbag", CBCommand, "cbag", "检查背包") { HelpText = "检查背包" }); + ServerApi.Hooks.GameUpdate.Register(this, OnGameUpdate); + LoadConfig(); + GeneralHooks.ReloadEvent += LoadConfig; + } + + private static void LoadConfig(ReloadEventArgs args = null) + { + Config = Configuration.Read(Configuration.FilePath); + Config.Write(Configuration.FilePath); + if (args != null && args.Player != null) + { + args.Player.SendSuccessMessage("[检查背包]重新加载配置完毕。"); + } + } + + + #region 指令 + void CBCommand(CommandArgs args) + { + TSPlayer op = args.Player; + void Help() + { + List lines = new() + { + "/cbag ban,列出封禁记录", + "/cbag item,列出违规物品", + }; + op.SendInfoMessage(string.Join("\n", lines)); + } + + if (args.Parameters.Count == 0) + { + op.SendErrorMessage("语法错误,输入 /cbag help 查询用法"); + return; + } + + switch (args.Parameters[0].ToLowerInvariant()) + { + // 帮助 + case "help": + case "h": + case "帮助": + Help(); + break; + + // 查看封禁 + case "ban": + case "b": + Ban.ListBans(args); + break; + + // 物品 + case "item": + case "i": + ListItems(args); + break; + } + } + #endregion + + + #region 世界更新 + void OnGameUpdate(EventArgs args) + { + // 如果计数器不为-1且计数器小于检测间隔的分钟数,则增加计数器并返回 + if (Count != -1 && Count < Config.DetectionInterval * Config.UpdateRate) + { + Count++; + return; + } + Count = 0; + + // 对每个在线并活跃的玩家执行以下操作 + TShock.Players.Where(p => p != null && p.Active).ToList().ForEach(p => + { + // 如果玩家未登录或者玩家组具有“owner”权限或者玩家组具有“免检背包”权限,则返回 + if (!p.IsLoggedIn || p.Group.HasPermission("owner") || p.Group.HasPermission("免检背包")) // 添加“免检背包”权限检查 + return; + + // 清除玩家的物品 + ClearPlayersItem(p); + }); + } + #endregion + + + #region 检查玩家背包 + + // 检查玩家背包 + /// 需要清空的物品 + /// 需要被清理的玩家 + public void ClearPlayersItem(TSPlayer op) + + { + + Player plr = op.TPlayer; + Dictionary dict = new(); + List list = new(); + list.AddRange(plr.inventory); // 背包,钱币/弹药,手持 + list.Add(plr.trashItem); // 垃圾桶 + list.AddRange(plr.armor); // 装备,时装 + list.AddRange(plr.dye); // 染料 + list.AddRange(plr.miscEquips); // 工具栏 + list.AddRange(plr.miscDyes); // 工具栏染料 + list.AddRange(plr.bank.item); // 储蓄罐 + list.AddRange(plr.bank2.item); // 保险箱 + list.AddRange(plr.bank3.item); // 护卫熔炉 + list.AddRange(plr.bank4.item); // 虚空保险箱 + for (int i = 0; i < plr.Loadouts.Length; i++) + { + // 装备1,装备2,装备3 + list.AddRange(plr.Loadouts[i].Armor); // 装备,时装 + list.AddRange(plr.Loadouts[i].Dye); // 染料 + } + list.RemoveAll(i => i.IsAir); //移除所有的空白物品 + list.Where(item => item != null && item.active).ToList().ForEach(item => + { + if (dict.ContainsKey(item.netID)) + { + dict[item.netID] += item.stack; + } + else + { + dict.Add(item.netID, item.stack); + } + }); + + bool Check(List li, bool isCurrent) + { + ItemData data = null; + foreach (var d in li) + { + var id = d.id; + var stack = d.数量; + if (dict.ContainsKey(id) && dict[id] >= stack) + { + data = d; + break; + } + } + + if (data != null) + { + var name = op.Name; + var id = data.id; + var stack = data.数量; + var max = Config.WarningCount; + var num = Ban.Trigger(name); + string itemName = Lang.GetItemNameValue(id); + string itemDesc = stack > 1 ? $"{itemName}x{stack}" : itemName; + string opDesc = isCurrent ? "拥有超进度物品" : "拥有"; + var desc = $"{opDesc}[i/s{stack}:{id}]{itemDesc}"; + if (num < max) + { + string tips = stack > 1 ? "请减少数量" : "请销毁"; + TSPlayer.All.SendSuccessMessage($"玩家[c/FFCCFF:{name}]被检测到{desc},疑似作弊请注意!"); // 发送广播消息 + Console.WriteLine($"玩家[{name}]被检测到{desc},疑似作弊请注意!"); // 控制台输出 + + string logFolderPath = Path.Combine(TShock.SavePath, "检查背包", "检查日志"); //写入日志的路径 + Directory.CreateDirectory(logFolderPath); // 创建日志文件夹 + + string logFileName = $"log {DateTime.Now.ToString("yyyy-MM-dd")}.txt"; //给日志名字加上日期 + + File.AppendAllLines(Path.Combine(logFolderPath, logFileName), new string[] { DateTime.Now.ToString("u") + $"玩家【{name}】被检测到{desc},疑似作弊请注意!" }); //写入日志log + + HashSet checkedItems = new HashSet(); // 用于存储已经检查过的物品类型 + // 清除检测到的项目 + for (int i = 0; i < plr.inventory.Length; i++) + { + if (plr.inventory[i].type == id && plr.inventory[i].stack >= stack) + { + plr.inventory[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Inventory0 + i); + } + } + for (int i = 0; i < plr.bank4.item.Length; i++) + { + if (plr.bank4.item[i].type == id && plr.bank4.item[i].stack >= stack) + { + plr.bank4.item[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Bank4_0 + i); + } + } + for (int i = 0; i < plr.bank3.item.Length; i++) + { + if (plr.bank3.item[i].type == id && plr.bank3.item[i].stack >= stack) + { + plr.bank3.item[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Bank3_0 + i); + } + } + for (int i = 0; i < plr.bank2.item.Length; i++) + { + if (plr.bank2.item[i].type == id && plr.bank2.item[i].stack >= stack) + { + plr.bank2.item[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Bank2_0 + i); + } + } + for (int i = 0; i < plr.bank.item.Length; i++) + { + if (plr.bank.item[i].type == id && plr.bank.item[i].stack >= stack) + { + plr.bank.item[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Bank1_0 + i); + } + } + for (int i = 0; i < plr.armor.Length; i++) + { + if (plr.armor[i].type == id && plr.armor[i].stack >= stack) + { + plr.armor[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Armor0 + i); + } + } + for (int i = 0; i < plr.dye.Length; i++) + { + if (plr.dye[i].type == id && plr.dye[i].stack >= stack) + { + plr.dye[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Dye0 + i); + } + } + for (int i = 0; i < plr.miscDyes.Length; i++) + { + if (plr.miscDyes[i].type == id && plr.miscDyes[i].stack >= stack) + { + plr.miscDyes[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.MiscDye0 + i); + } + } + for (int i = 0; i < plr.miscEquips.Length; i++) + { + if (plr.miscEquips[i].type == id && plr.miscEquips[i].stack >= stack) + { + plr.miscEquips[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Misc0 + i); + } + } + if (plr.trashItem.IsAir && plr.trashItem.type >= stack) + { + plr.trashItem.TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.TrashItem); + } + for (int i = 0; i < plr.Loadouts[0].Armor.Length; i++) + { + if (plr.Loadouts[0].Armor[i].type == id && plr.Loadouts[0].Armor[i].stack >= stack) + { + plr.Loadouts[0].Armor[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Loadout1_Armor_0 + i); + } + } + for (int i = 0; i < plr.Loadouts[0].Dye.Length; i++) + { + if (plr.Loadouts[0].Dye[i].type == id && plr.Loadouts[0].Dye[i].stack >= stack) + { + plr.Loadouts[0].Dye[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Loadout1_Dye_0 + i); + } + } + for (int i = 0; i < plr.Loadouts[1].Armor.Length; i++) + { + if (plr.Loadouts[1].Armor[i].type == id && plr.Loadouts[1].Armor[i].stack >= stack) + { + plr.Loadouts[1].Armor[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Loadout2_Armor_0 + i); + } + } + for (int i = 0; i < plr.Loadouts[1].Dye.Length; i++) + { + if (plr.Loadouts[1].Dye[i].type == id && plr.Loadouts[1].Dye[i].stack >= stack) + { + plr.Loadouts[1].Dye[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Loadout2_Dye_0 + i); + } + } + for (int i = 0; i < plr.Loadouts[2].Armor.Length; i++) + { + if (plr.Loadouts[2].Armor[i].type == id && plr.Loadouts[2].Armor[i].stack >= stack) + { + plr.Loadouts[2].Armor[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Loadout3_Armor_0 + i); + } + } + for (int i = 0; i < plr.Loadouts[2].Dye.Length; i++) + { + if (plr.Loadouts[2].Dye[i].type == id && plr.Loadouts[2].Dye[i].stack >= stack) + { + plr.Loadouts[2].Dye[i].TurnToAir(); + op.SendData(PacketTypes.PlayerSlot, "", op.Index, PlayerItemSlotID.Loadout3_Dye_0 + i); + } + } + + //清理BUFF + for (int i = 0; i < 22; i++) + { + op.TPlayer.buffType[i] = 0; + } + op.SendData(PacketTypes.PlayerBuff, "", op.Index, 0f, 0f, 0f, 0); + } + else + + { + Ban.Remove(name); + TSPlayer.All.SendInfoMessage($"{name}已被封禁!原因:{desc}。"); + op.Disconnect($"你已被封禁!原因:{opDesc}{itemDesc}。"); + Ban.AddBan(name, $"{opDesc}{itemDesc}", Config.BanTime * 60); + return false; + } + } + return true; + } + + if (!Check(Config.Anytime, false)) + return; + Check(Config.Current(), true); + } + #endregion + + #region 列出违规物品 + public void ListItems(CommandArgs args) + { + static string FormatData(ItemData data) + { + var id = data.id; + var stack = data.数量; + var itemName = Lang.GetItemNameValue(id); + var itemDesc = stack > 1 ? $"{itemName}x{stack}" : itemName; + return $"[i/s{stack}:{id}]{itemDesc}"; + } + + var lines = new List(); + + var datas = Config.Current(); + var lines2 = datas.Select(d => FormatData(d)).ToList(); + lines.AddRange(WarpLines(lines2)); + if (datas.Count > 0) + { + lines[0] = "[c/FFCCFF:当前进度:]" + lines[0]; + } + + if (!lines.Any()) + { + if (Config.IsEmpty()) + { + args.Player.SendInfoMessage("你未配置任何违规物品数据!"); + } + else + { + args.Player.SendInfoMessage("没有符合当前进度的物品!"); + } + return; + } + + if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out int pageNumber)) + { + return; + } + PaginationTools.SendPage(args.Player, pageNumber, lines, new PaginationTools.Settings + { + HeaderFormat = "违规物品 ({0}/{1}):", + FooterFormat = "输入/cbag i {{0}}查看更多".SFormat(Commands.Specifier) + }); + } + #endregion + + + #region 字符串换行 + /// + /// 列数,1行显示多个 + /// + static List WarpLines(List lines, int column = 15) + { + List li1 = new(); + List li2 = new(); + foreach (var line in lines) + { + if (li2.Count % column == 0) + { + if (li2.Count > 0) + { + li1.Add(string.Join("\n", li2)); + li2.Clear(); + } + } + li2.Add(line); + } + if (li2.Any()) + { + li1.Add(string.Join("\n", li2)); + } + return li1; + } + #endregion + + + #region 销毁钩子 + protected override void Dispose(bool disposing) + { + if (disposing) + { + ServerApi.Hooks.GameUpdate.Deregister(this, OnGameUpdate); + } + base.Dispose(disposing); + } + #endregion + } +} \ No newline at end of file diff --git a/CheckBag/CheckBag.csproj b/CheckBag/CheckBag.csproj new file mode 100644 index 00000000..04c81dc3 --- /dev/null +++ b/CheckBag/CheckBag.csproj @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/CheckBag/Configuration.cs b/CheckBag/Configuration.cs new file mode 100644 index 00000000..6d30667c --- /dev/null +++ b/CheckBag/Configuration.cs @@ -0,0 +1,936 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Security.Cryptography; +using Terraria; +using TShockAPI; + +namespace CheckBag +{ + public class Configuration + { + [JsonProperty("配置说明")] + public string README = "重载配置请输入:/reload"; + [JsonProperty("物品查询")] + public string Wiki_ID = "https://terraria.wiki.gg/zh/wiki/Item_IDs"; + [JsonProperty("检测间隔(秒)")] + public int DetectionInterval = 5; // 秒 + [JsonProperty("更新频率(越小越快)")] + public int UpdateRate = 60; // 次 + [JsonProperty("封禁时长(分钟)")] + public int BanTime = 10; // 分钟 + [JsonProperty("警告次数(封禁)")] + public int WarningCount = 15; // 次 + + public static readonly string FilePath = Path.Combine(TShock.SavePath, "检查背包", "检查背包.json"); + [JsonProperty("全时期")] + public List Anytime = new(); + [JsonProperty("哥布林入侵")] + public List Goblins = new(); + [JsonProperty("史王前")] + public List SlimeKing = new(); + [JsonProperty("克眼前")] + public List Boss1 = new(); + [JsonProperty("鹿角怪前")] + public List Deerclops = new(); + [JsonProperty("世吞克脑前")] + public List Boss2 = new(); + [JsonProperty("蜂王前")] + public List QueenBee = new(); + [JsonProperty("骷髅王前")] + public List Boss3 = new(); + [JsonProperty("肉前")] + public List hardMode = new(); + [JsonProperty("皇后前")] + public List QueenSlime = new(); + [JsonProperty("一王前")] + public List MechBossAny = new(); + [JsonProperty("三王前")] + public List MechBoss = new(); + [JsonProperty("猪鲨前")] + public List Fishron = new(); + [JsonProperty("光女前")] + public List EmpressOfLight = new(); + [JsonProperty("花前")] + public List PlantBoss = new(); + [JsonProperty("石前")] + public List GolemBoss = new(); + [JsonProperty("拜月前")] + public List AncientCultist = new(); + [JsonProperty("月前")] + public List Moonlord = new(); + + + #region 默认配置 + public void Init() + { + #region 全时期 + Anytime = new List { + new ItemData(74, 500, "铂金币"), + new ItemData(75, 999, "坠落之星"), + new ItemData(3617, 1, "广播盒") + }; + #endregion + + #region 哥布林前 + Goblins = new List { + new ItemData(128, 1,"火箭靴"), + new ItemData(405, 1,"幽灵靴"), + new ItemData(3993, 1,"仙灵靴"), + new ItemData(908, 1,"熔岩靴"), + new ItemData(898, 1,"闪电靴"), + new ItemData(1862, 1,"霜花靴"), + new ItemData(5000, 1,"泰拉闪耀靴"), + new ItemData(1163, 1,"暴雪气球"), + new ItemData(983, 1,"沙暴气球"), + new ItemData(399, 1,"云朵气球"), + new ItemData(1863, 1,"臭屁气球"), + new ItemData(1252, 1,"黄马掌气球"), + new ItemData(1251, 1,"白马掌气球"), + new ItemData(1250, 1,"蓝马掌气球"), + new ItemData(3250, 1,"绿马掌气球"), + new ItemData(3251, 1,"琥珀马掌气球"), + new ItemData(3241, 1,"鲨鱼龙气球"), + new ItemData(3252, 1,"粉马掌气球"), + new ItemData(1164, 1,"气球束"), + new ItemData(3990, 1,"马掌气球束"), + new ItemData(3990, 1,"水陆两用靴"), + new ItemData(1860, 1,"水母潜水装备"), + new ItemData(1861, 1,"北极潜水装备"), + new ItemData(3995, 1,"青蛙装备"), + new ItemData(407, 1,"工具腰带"), + new ItemData(395, 1,"全球定位系统"), + new ItemData(3122, 1,"R.E.K.3000"), + new ItemData(3121, 1,"哥布林数据仪"), + new ItemData(3036, 1,"探鱼器"), + new ItemData(3123, 1,"个人数字助手"), + new ItemData(555, 1,"魔力花"), + new ItemData(4000, 1,"磁花"), + new ItemData(2221, 1,"天界手铐"), + new ItemData(1595, 1,"魔法手铐"), + new ItemData(3061, 1,"建筑师发明背包"), + new ItemData(5126, 1,"创造之手"), + new ItemData(5358, 1, "贝壳电话(家)"), + new ItemData(5359, 1, "贝壳电话(出生点)"), + new ItemData(5360, 1, "贝壳电话(海洋)"), + new ItemData(5361, 1, "贝壳电话(地狱)") + }; + #endregion + + #region 史莱姆王前 + SlimeKing = new List{ + new ItemData(3318, 1, "宝藏袋(史莱姆王)"), + new ItemData(2430, 1, "粘鞍"), + new ItemData(256, 1, "忍者兜帽"), + new ItemData(257, 1, "忍者衣"), + new ItemData(258, 1, "忍者裤"), + new ItemData(3090, 1, "皇家凝胶"), + }; + #endregion + + #region 克眼前 + Boss1 = new List { + new ItemData(3319, 1, "宝藏袋(克苏鲁之眼)"), + new ItemData(3262, 1, "代码1球"), + new ItemData(3097, 1, "克苏鲁护盾"), + }; + #endregion + + #region 鹿角怪前 + Deerclops = new List { + new ItemData(5111, 1, "宝藏袋(独眼巨鹿)"), + new ItemData(5098, 1, "眼骨"), + new ItemData(5095, 1, "露西斧"), + new ItemData(5117, 1, "气喇叭"), + new ItemData(5118, 1, "天候棒"), + new ItemData(5119, 1, "眼球激光塔"), + }; + #endregion + + #region 世吞克脑前 + Boss2 = new List { + new ItemData(3320, 1, "宝藏袋(世界吞噬怪)"), + new ItemData(3321, 1, "宝藏袋(克苏鲁之脑)"), + new ItemData(174, 1, "狱石"), + new ItemData(175, 1, "狱石锭"), + new ItemData(122, 1, "熔岩镐"), + new ItemData(120, 1, "熔火之怒"), + new ItemData(119, 1, "烈焰回旋镖"), + new ItemData(231, 1, "熔岩头盔"), + new ItemData(232, 1, "熔岩胸甲"), + new ItemData(233, 1, "熔岩护胫"), + new ItemData(2365, 1, "小鬼法杖"), + new ItemData(4821, 1, "防熔岩虫网"), + new ItemData(121, 1, "火山"), + new ItemData(3223, 1, "混乱之脑"), + new ItemData(3224, 1, "蠕虫围巾"), + new ItemData(3266, 1, "黑曜石逃犯帽"), + new ItemData(3267, 1, "黑曜石风衣"), + new ItemData(3268, 1, "黑曜石裤"), + new ItemData(102, 1, "暗影头盔"), + new ItemData(101, 1, "暗影鳞甲"), + new ItemData(100, 1, "暗影护颈"), + new ItemData(103, 1, "梦魇镐"), + new ItemData(792, 1, "猩红头盔"), + new ItemData(793, 1, "猩红鳞甲"), + new ItemData(794, 1, "猩红护颈"), + new ItemData(798, 1, "死亡使者镐"), + new ItemData(3817, 1, "护卫奖章"), + new ItemData(3813, 1, "护卫熔炉"), + new ItemData(3809, 1, "学徒围巾"), + new ItemData(3810, 1, "侍卫护盾"), + new ItemData(197, 1, "星星炮"), + new ItemData(123, 1, "流星头盔"), + new ItemData(124, 1, "流星护甲"), + new ItemData(125, 1, "流星护腿"), + new ItemData(127, 1, "太空枪"), + new ItemData(116, 1, "陨石"), + new ItemData(117, 1, "陨石锭"), + new ItemData(4076, 1, "虚空保险库"), + new ItemData(4131, 1, "虚空袋"), + new ItemData(5325, 1, "闭合的虚空袋") + }; + #endregion + + #region 蜂王前 + QueenBee = new List { + new ItemData(3322, 1, "宝藏袋(蜂王)"), + new ItemData(1123, 1, "养蜂人"), + new ItemData(2888, 1, "蜂膝弓"), + new ItemData(1121, 1, "蜜蜂枪"), + new ItemData(1132, 1, "蜂窝"), + new ItemData(1130, 1, "蜜蜂手榴弹"), + new ItemData(2431, 1, "蜂蜡"), + new ItemData(2502, 1, "涂蜜护目镜"), + new ItemData(1249, 1, "蜂蜜气球"), + new ItemData(4007, 1, "毒刺项链"), + new ItemData(5294, 1, "蜂巢球"), + new ItemData(1158, 1, "矮人项链"), + new ItemData(1430, 1, "灌注站"), + }; + #endregion + + #region 骷髅王前 + Boss3 = new List { + new ItemData(3323, 1, "宝藏袋(骷髅王)"), + new ItemData(346, 1, "保险箱"), + new ItemData(273, 1, "永夜刃"), + new ItemData(329, 1, "暗影钥匙"), + new ItemData(113, 1, "魔法导弹"), + new ItemData(683, 1, "邪恶三叉戟"), + new ItemData(157, 1, "海蓝权杖"), + new ItemData(3019, 1, "地狱之翼弓"), + new ItemData(219, 1, "凤凰爆破枪"), + new ItemData(218, 1, "烈焰火鞭"), + new ItemData(220, 1, "阳炎之怒"), + new ItemData(3317, 1, "英勇球"), + new ItemData(3282, 1, "喷流球"), + new ItemData(155, 1, "村正"), + new ItemData(156, 1, "钴护盾"), + new ItemData(397, 1, "黑曜石护盾"), + new ItemData(163, 1, "蓝月"), + new ItemData(164, 1, "手枪"), + new ItemData(151, 1, "死灵头盔"), + new ItemData(152, 1, "死灵胸甲"), + new ItemData(153, 1, "死灵护颈"), + new ItemData(5074, 1, "脊柱骨鞭"), + new ItemData(1313, 1, "骷髅头法书"), + new ItemData(2999, 1, "施法桌"), + new ItemData(3000, 1, "炼药桌"), + new ItemData(890, 1, "扩音器"), + new ItemData(891, 1, "邪眼"), + new ItemData(904, 1, "反诅咒咒语"), + new ItemData(2623, 1, "泡泡枪"), + new ItemData(327, 1, "金钥匙"), + }; + #endregion + + #region 肉山前 + hardMode = new List { + new ItemData(3324, 1, "宝藏袋(血肉墙)"), + new ItemData(2673, 1, "松露虫"), + new ItemData(3991, 1, "奥术花"), + new ItemData(3366, 1, "悠悠球袋"), + new ItemData(400, 1, "精金头饰"), + new ItemData(401, 1, "精金头盔"), + new ItemData(402, 1, "精金面具"), + new ItemData(403, 1, "精金胸甲"), + new ItemData(404, 1, "精金护腿"), + new ItemData(391, 1, "精金锭"), + new ItemData(778, 1, "精金镐"), + new ItemData(481, 1, "精金连弩"), + new ItemData(524, 1, "精金熔炉"), + new ItemData(376, 1, "秘银兜帽"), + new ItemData(377, 1, "秘银头盔"), + new ItemData(378, 1, "秘银帽"), + new ItemData(379, 1, "秘银链甲"), + new ItemData(380, 1, "秘银护胫"), + new ItemData(382, 1, "秘银锭"), + new ItemData(777, 1, "秘银镐"), + new ItemData(436, 1, "秘银连弩"), + new ItemData(525, 1, "秘银砧"), + new ItemData(371, 1, "钴帽"), + new ItemData(372, 1, "钴头盔"), + new ItemData(373, 1, "钴面具"), + new ItemData(374, 1, "钴胸甲"), + new ItemData(375, 1, "钴护腿"), + new ItemData(381, 1, "钴锭"), + new ItemData(776, 1, "钴镐"), + new ItemData(435, 1, "钴连弩"), + new ItemData(1205, 1, "钯金面具"), + new ItemData(1206, 1, "钯金头盔"), + new ItemData(1207, 1, "钯金头饰"), + new ItemData(1208, 1, "钯金胸甲"), + new ItemData(1209, 1, "钯金护腿"), + new ItemData(1184, 1, "钯金锭"), + new ItemData(1187, 1, "钯金连弩"), + new ItemData(1188, 1, "钯金镐"), + new ItemData(1189, 1, "钯金钻头"), + new ItemData(1210, 1, "山铜面具"), + new ItemData(1211, 1, "山铜头盔"), + new ItemData(1212, 1, "山铜头饰"), + new ItemData(1213, 1, "山铜胸甲"), + new ItemData(1214, 1, "山铜护腿"), + new ItemData(1191, 1, "山铜锭"), + new ItemData(1194, 1, "山铜连弩"), + new ItemData(1195, 1, "山铜镐"), + new ItemData(1196, 1, "山铜钻头"), + new ItemData(1220, 1, "山铜砧"), + new ItemData(1215, 1, "钛金面具"), + new ItemData(1216, 1, "钛金头盔"), + new ItemData(1217, 1, "钛金头饰"), + new ItemData(1218, 1, "钛金胸甲"), + new ItemData(1219, 1, "钛金护腿"), + new ItemData(1198, 1, "钛金锭"), + new ItemData(1201, 1, "钛金连弩"), + new ItemData(1202, 1, "钛金镐"), + new ItemData(1203, 1, "钛金钻头"), + new ItemData(1221, 1, "钛金熔炉"), + new ItemData(1328, 1, "海龟壳"), + new ItemData(2161, 1, "寒霜核"), + new ItemData(684, 1, "寒霜头盔"), + new ItemData(685, 1, "寒霜胸甲"), + new ItemData(686, 1, "寒霜护腿"), + new ItemData(726, 1, "寒霜法杖"), + new ItemData(1264, 1, "寒霜之花"), + new ItemData(676, 1, "寒霜剑"), + new ItemData(4911, 1, "冷鞭"), + new ItemData(1306, 1, "冰雪镰刀"), + new ItemData(3783, 1, "禁戒碎片"), + new ItemData(3776, 1, "禁戒面具"), + new ItemData(3777, 1, "禁戒长袍"), + new ItemData(3778, 1, "禁戒裤"), + new ItemData(2607, 1, "蜘蛛牙"), + new ItemData(2370, 1, "蜘蛛面具"), + new ItemData(2371, 1, "蜘蛛胸甲"), + new ItemData(2372, 1, "蜘蛛护胫"), + new ItemData(2551, 1, "蜘蛛法杖"), + new ItemData(2366, 1, "蜘蛛女王法杖"), + new ItemData(1308, 1, "剧毒法杖"), + new ItemData(389, 1, "太极连枷"), + new ItemData(426, 1, "毁灭刃"), + new ItemData(3051, 1, "魔晶碎块"), + new ItemData(422, 1, "圣水"), + new ItemData(2998, 1, "召唤师徽章"), + new ItemData(489, 1, "巫士徽章"), + new ItemData(490, 1, "战士徽章"), + new ItemData(491, 1, "游侠徽章"), + new ItemData(492, 1, "恶魔之翼"), + new ItemData(493, 1, "天使之翼"), + new ItemData(785, 1, "鸟妖之翼"), + new ItemData(1165, 1, "蝙蝠之翼"), + new ItemData(761, 1, "仙灵之翼"), + new ItemData(822, 1, "冰冻之翼"), + new ItemData(485, 1, "月光护身符"), + new ItemData(900, 1, "月亮石"), + new ItemData(497, 1, "海神贝壳"), + new ItemData(861, 1, "月亮贝壳"), + new ItemData(3013, 1, "臭虎爪"), + new ItemData(3014, 1, "爬藤怪法杖"), + new ItemData(3015, 1, "腐香囊"), + new ItemData(3016, 1, "血肉指虎"), + new ItemData(3992, 1, "狂战士手套"), + new ItemData(536, 1, "泰坦手套"), + new ItemData(897, 1, "强力手套"), + new ItemData(527, 1, "暗黑碎块"), + new ItemData(528, 1, "光明碎块"), + new ItemData(520, 1, "光明之魂"), + new ItemData(521, 1, "暗影之魂"), + new ItemData(575, 1, "飞翔之魂"), + new ItemData(535, 1, "点金石"), + new ItemData(860, 1, "神话护身符"), + new ItemData(554, 1, "十字项链"), + new ItemData(862, 1, "星星面纱"), + new ItemData(1613, 1, "十字章护盾"), + new ItemData(1612, 1, "十字章护符"), + new ItemData(892, 1, "维生素"), + new ItemData(886, 1, "盔甲抛光剂"), + new ItemData(901, 1, "盔甲背带"), + new ItemData(893, 1, "三折地图"), + new ItemData(889, 1, "快走时钟"), + new ItemData(903, 1, "计划书"), + new ItemData(888, 1, "蒙眼布"), + new ItemData(3781, 1, "袖珍镜"), + new ItemData(5354, 1, "反光墨镜"), + new ItemData(1253, 1, "冰冻海龟壳"), + new ItemData(3290, 1, "狱火球"), + new ItemData(3289, 1, "冰雪悠悠球"), + new ItemData(3316, 1, "渐变球"), + new ItemData(3315, 1, "好胜球"), + new ItemData(3283, 1, "吉克球"), + new ItemData(3054, 1, "暗影焰刀"), + new ItemData(532, 1, "星星斗篷"), + new ItemData(1247, 1, "蜜蜂斗篷"), + new ItemData(1244, 1, "雨云魔杖"), + new ItemData(1326, 1, "混沌传送杖"), + new ItemData(522, 1, "诅咒焰"), + new ItemData(519, 1, "诅咒焰"), + new ItemData(3010, 1, "诅咒镖"), + new ItemData(545, 1, "诅咒箭"), + new ItemData(546, 1, "诅咒弹"), + new ItemData(1332, 1, "灵液"), + new ItemData(1334, 1, "灵液箭"), + new ItemData(1335, 1, "灵液弹"), + new ItemData(3011, 1, "灵液镖"), + new ItemData(1356, 1, "灵液瓶"), + new ItemData(1336, 1, "黄金雨"), + new ItemData(1346, 1, "纳米机器人"), + new ItemData(1350, 1, "纳米弹"), + new ItemData(1357, 1, "纳米机器人瓶"), + new ItemData(1347, 1, "爆炸粉"), + new ItemData(1351, 1, "爆破弹"), + new ItemData(526, 1, "独角兽角"), + new ItemData(501, 1, "妖精尘"), + new ItemData(516, 1, "圣箭"), + new ItemData(502, 1, "水晶碎块"), + new ItemData(518, 1, "水晶风暴"), + new ItemData(515, 1, "水晶子弹"), + new ItemData(3009, 1, "水晶镖"), + new ItemData(534, 1, "霰弹枪"), + new ItemData(3211, 1, "舌锋剑"), + new ItemData(723, 1, "光束剑"), + new ItemData(514, 1, "激光步枪"), + new ItemData(1265, 1, "乌兹冲锋枪"), + new ItemData(3788, 1, "玛瑙爆破枪"), + new ItemData(3210, 1, "毒弹枪"), + new ItemData(2270, 1, "鳄鱼机关枪"), + new ItemData(434, 1, "发条式突击步枪"), + new ItemData(496, 1, "冰雪魔杖"), + new ItemData(3006, 1, "夺命杖"), + new ItemData(3007, 1, "飞镖手枪"), + new ItemData(3008, 1, "飞镖步枪"), + new ItemData(3029, 1, "代达罗斯风暴弓"), + new ItemData(3052, 1, "暗影焰弓"), + new ItemData(5065, 1, "共鸣权杖"), + new ItemData(4269, 1, "血红法杖"), + new ItemData(4270, 1, "血荆棘"), + new ItemData(4272, 1, "滴滴怪致残者"), + new ItemData(3787, 1, "裂天剑"), + new ItemData(1321, 1, "魔法箭袋"), + new ItemData(4006, 1, "潜行者箭袋"), + new ItemData(4002, 1, "熔火箭袋"), + new ItemData(3103, 1, "无尽箭袋"), + new ItemData(3104, 1, "无尽火枪袋"), + new ItemData(2750, 1, "流星法杖"), + new ItemData(905, 1, "钱币枪"), + new ItemData(2584, 1, "海盗法杖"), + new ItemData(854, 1, "优惠卡"), + new ItemData(855, 1, "幸运币"), + new ItemData(3034, 1, "钱币戒指"), + new ItemData(3035, 1, "贪婪戒指"), + new ItemData(1324, 1, "香蕉回旋镖"), + new ItemData(3012, 1, "铁链血滴子"), + new ItemData(4912, 1, "鞭炮"), + new ItemData(544, 1, "机械魔眼"), + new ItemData(556, 1, "机械蠕虫"), + new ItemData(557, 1, "机械骷髅头"), + new ItemData(3779, 1, "神灯烈焰") + }; + #endregion + + #region 史莱姆女皇前 + QueenSlime = new List { + new ItemData(4957, 1, "宝藏袋(史莱姆皇后)"), + new ItemData(4987, 1, "挥发明胶"), + new ItemData(4980, 1, "失谐钩爪"), + new ItemData(4981, 1, "明胶女式鞍"), + new ItemData(4982, 1, "水晶刺客兜帽"), + new ItemData(4983, 1, "水晶刺客上衣"), + new ItemData(4984, 1, "水晶刺客裤"), + new ItemData(4758, 1, "刃杖"), + }; + #endregion + + #region 一王前 + MechBossAny = new List { + new ItemData(3325, 1, "宝藏袋(毁灭者"), + new ItemData(3326, 1, "宝藏袋(双子魔眼)"), + new ItemData(3327, 1, "宝藏袋(机械骷髅王)"), + new ItemData(1291, 1, "生命果"), + new ItemData(5338, 1, "神盾果"), + new ItemData(533, 1, "巨兽鲨"), + new ItemData(4060, 1, "超级星星炮"), + new ItemData(561, 1, "光辉飞盘"), + new ItemData(494, 1, "魔法竖琴"), + new ItemData(495, 1, "彩虹魔杖"), + new ItemData(4760, 1, "中士联盾"), + new ItemData(506, 1, "火焰喷射器"), + new ItemData(3284, 1, "代码2球"), + new ItemData(3287, 1, "Red的抛球"), + new ItemData(3288, 1, "女武神悠悠球"), + new ItemData(3286, 1, "叶列茨球"), + new ItemData(1515, 1, "蜜蜂之翼"), + new ItemData(821, 1, "烈焰之翼"), + new ItemData(748, 1, "喷气背包"), + new ItemData(4896, 1, "远古神圣面具"), + new ItemData(4897, 1, "远古神圣头盔"), + new ItemData(4898, 1, "远古神圣头饰"), + new ItemData(4899, 1, "远古神圣兜帽"), + new ItemData(4900, 1, "远古神圣板甲"), + new ItemData(4901, 1, "远古神圣护胫"), + new ItemData(558, 1, "神圣头饰"), + new ItemData(559, 1, "神圣面具"), + new ItemData(553, 1, "神圣头盔"), + new ItemData(4873, 1, "神圣兜帽"), + new ItemData(551, 1, "神圣板甲"), + new ItemData(552, 1, "神圣护胫"), + new ItemData(1225, 1, "神圣锭"), + new ItemData(578, 1, "神圣连弩"), + new ItemData(4678, 1, "迪朗达尔"), + new ItemData(550, 1, "永恒之枪"), + new ItemData(2535, 1, "魔眼法杖"), + new ItemData(3353, 1, "机械矿车"), + new ItemData(547, 1, "恐惧之魂"), + new ItemData(548, 1, "力量之魂"), + new ItemData(549, 1, "视域之魂"), + new ItemData(3856, 1, "飞眼怪蛋"), + new ItemData(3835, 1, "瞌睡章鱼"), + new ItemData(3836, 1, "恐怖关刀"), + new ItemData(3854, 1, "幽灵凤凰"), + new ItemData(3823, 1, "地狱烙印"), + new ItemData(3852, 1, "无限智慧巨著"), + new ItemData(3797, 1, "学徒帽"), + new ItemData(3798, 1, "学徒长袍"), + new ItemData(3799, 1, "学徒裤"), + new ItemData(3800, 1, "侍卫大头盔"), + new ItemData(3801, 1, "侍卫板甲"), + new ItemData(3802, 1, "侍卫护胫"), + new ItemData(3803, 1, "女猎人假发"), + new ItemData(3804, 1, "女猎人上衣"), + new ItemData(3805, 1, "女猎人裤"), + new ItemData(3811, 1, "女猎人圆盾"), + new ItemData(3806, 1, "武僧浓眉秃头帽"), + new ItemData(3807, 1, "武僧衣"), + new ItemData(3808, 1, "武僧裤"), + new ItemData(3812, 1, "武僧腰带"), + }; + #endregion + + #region 三王前 + MechBoss = new List { + new ItemData(935, 1, "复仇者徽章"), + new ItemData(2220, 1, "天界徽章"), + new ItemData(936, 1, "机械手套"), + new ItemData(1343, 1, "烈火手套"), + new ItemData(674, 1, "原版断钢剑"), + new ItemData(675, 1, "原版永夜刃"), + new ItemData(990, 1, "镐斧"), + new ItemData(579, 1, "斧钻"), + new ItemData(947, 1, "叶绿矿"), + new ItemData(1006, 1, "叶绿锭"), + new ItemData(1001, 1, "叶绿面具"), + new ItemData(1002, 1, "叶绿头盔"), + new ItemData(1003, 1, "叶绿头饰"), + new ItemData(1004, 1, "叶绿板甲"), + new ItemData(1005, 1, "叶绿护胫"), + new ItemData(1229, 1, "叶绿连弩"), + new ItemData(1230, 1, "叶绿镐"), + new ItemData(1231, 1, "叶绿钻头"), + new ItemData(1235, 1, "叶绿箭"), + new ItemData(1179, 1, "叶绿弹"), + new ItemData(1316, 1, "海龟头盔"), + new ItemData(1317, 1, "海龟铠甲"), + new ItemData(1318, 1, "海龟护腿"), + new ItemData(5382, 1, "华夫饼烘烤模"), + }; + #endregion + + #region 世纪之花前 + PlantBoss = new List { + new ItemData(3328, 1, "宝藏袋(世纪之花)"), + new ItemData(1958, 1, "调皮礼物"), + new ItemData(1844, 1, "南瓜月勋章"), + new ItemData(4961, 1, "七彩草蛉"), + new ItemData(3336, 1, "孢子囊"), + new ItemData(963, 1, "黑腰带"), + new ItemData(984, 1, "忍者大师装备"), + new ItemData(977, 1, "分趾厚底袜"), + new ItemData(3292, 1, "克苏鲁之眼"), + new ItemData(3291, 1, "克拉肯球"), + new ItemData(1178, 1, "吹叶机"), + new ItemData(3105, 1, "毒气瓶"), + new ItemData(3106, 1, "变态人的刀"), + new ItemData(2770, 1, "蛾怪之翼"), + new ItemData(1871, 1, "喜庆之翼"), + new ItemData(1183, 1, "妖灵瓶"), + new ItemData(4679, 1, "晨星"), + new ItemData(4680, 1, "暗黑收割"), + new ItemData(1444, 1, "暗影束法杖"), + new ItemData(1445, 1, "狱火叉"), + new ItemData(1446, 1, "幽灵法杖"), + new ItemData(3249, 1, "致命球法杖"), + new ItemData(1305, 1, "斧"), + new ItemData(757, 1, "泰拉刃"), + new ItemData(1569, 1, "吸血鬼刀"), + new ItemData(1571, 1, "腐化者之戟"), + new ItemData(1572, 1, "寒霜九头蛇法杖"), + new ItemData(1156, 1, "食人鱼枪"), + new ItemData(1260, 1, "彩虹枪"), + new ItemData(4607, 1, "沙漠虎杖"), + new ItemData(1508, 1, "灵气"), + new ItemData(1946, 1, "雪人炮"), + new ItemData(1947, 1, "北极"), + new ItemData(1931, 1, "暴雪法杖"), + new ItemData(1928, 1, "圣诞树剑"), + new ItemData(1929, 1, "链式机枪"), + new ItemData(1930, 1, "剃刀松"), + new ItemData(3997, 1, "冰冻护盾"), + new ItemData(1552, 1, "蘑菇矿锭"), + new ItemData(1546, 1, "蘑菇矿头饰"), + new ItemData(1547, 1, "蘑菇矿面具"), + new ItemData(1548, 1, "蘑菇矿头盔"), + new ItemData(1549, 1, "蘑菇矿胸甲"), + new ItemData(1550, 1, "蘑菇矿护腿"), + new ItemData(1866, 1, "悬浮板"), + new ItemData(1570, 1, "断裂英雄剑"), + new ItemData(823, 1, "幽灵之翼"), + new ItemData(1503, 1, "幽灵兜帽"), + new ItemData(1504, 1, "幽灵长袍"), + new ItemData(1505, 1, "幽灵裤"), + new ItemData(1506, 1, "幽灵镐"), + new ItemData(3261, 1, "幽灵锭"), + new ItemData(1729, 1, "阴森木"), + new ItemData(1830, 1, "阴森之翼"), + new ItemData(1832, 1, "阴森头盔"), + new ItemData(1833, 1, "阴森胸甲"), + new ItemData(1834, 1, "阴森护腿"), + new ItemData(1802, 1, "乌鸦法杖"), + new ItemData(1801, 1, "蝙蝠权杖"), + new ItemData(4444, 1, "女巫扫帚"), + new ItemData(1157, 1, "矮人法杖"), + new ItemData(1159, 1, "提基面具"), + new ItemData(1160, 1, "提基衣"), + new ItemData(1161, 1, "提基裤"), + new ItemData(1162, 1, "叶之翼"), + new ItemData(1845, 1, "死灵卷轴"), + new ItemData(1864, 1, "甲虫莎草纸"), + new ItemData(1339, 1, "小毒液瓶"), + new ItemData(1342, 1, "毒液弹"), + new ItemData(1341, 1, "毒液箭"), + new ItemData(1340, 1, "毒液瓶"), + new ItemData(1255, 1, "维纳斯万能枪"), + new ItemData(3107, 1, "钉枪"), + new ItemData(3108, 1, "钉子"), + new ItemData(1782, 1, "玉米糖步枪"), + new ItemData(1783, 1, "玉米糖"), + new ItemData(1910, 1, "精灵熔枪"), + new ItemData(1300, 1, "步枪瞄准镜"), + new ItemData(1254, 1, "狙击步枪"), + new ItemData(760, 1, "感应雷发射器"), + new ItemData(759, 1, "火箭发射器"), + new ItemData(758, 1, "榴弹发射器"), + new ItemData(1784, 1, "杰克南瓜灯发射器"), + new ItemData(1785, 1, "爆炸杰克南瓜灯"), + new ItemData(1835, 1, "尖桩发射器"), + new ItemData(1836, 1, "尖桩"), + new ItemData(4457, 1, "初级迷你核弹"), + new ItemData(4458, 1, "二级迷你核弹"), + new ItemData(771, 1, "初级火箭"), + new ItemData(772, 1, "二级火箭"), + new ItemData(773, 1, "三级火箭"), + new ItemData(774, 1, "四级火箭"), + new ItemData(4445, 1, "初级集束火箭"), + new ItemData(4446, 1, "二级集束火箭"), + new ItemData(4447, 1, "湿火箭"), + new ItemData(4448, 1, "熔岩火箭"), + new ItemData(4449, 1, "蜂蜜火箭"), + new ItemData(4459, 1, "干火箭"), + new ItemData(1259, 1, "花冠"), + new ItemData(3018, 1, "种子弯刀"), + new ItemData(1826, 1, "无头骑士剑"), + new ItemData(1513, 1, "圣骑士锤"), + new ItemData(938, 1, "圣骑士护盾"), + new ItemData(3998, 1, "英雄护盾"), + new ItemData(1327, 1, "死神镰刀"), + new ItemData(4812, 1, "南瓜香薰蜡烛"), + new ItemData(1301, 1, "毁灭者徽章"), + new ItemData(4005, 1, "侦察镜"), + }; + #endregion + + #region 猪鲨前 + Fishron = new List { + new ItemData(3330, 1, "宝藏袋(猪龙鱼公爵)"), + new ItemData(2609, 1, "猪龙鱼之翼"), + new ItemData(2622, 1, "利刃台风"), + new ItemData(2624, 1, "海啸"), + new ItemData(2621, 1, "暴风雨法杖"), + new ItemData(2611, 1, "猪鲨链球"), + new ItemData(3367, 1, "虾松露") + }; + #endregion + + #region 光女前 + EmpressOfLight = new List { + new ItemData(4782, 1, "宝藏袋(光之女皇)"), + new ItemData(4823, 1, "女皇之翼"), + new ItemData(4715, 1, "星星吉他"), + new ItemData(4914, 1, "万花筒"), + new ItemData(5005, 1, "泰拉棱镜"), + new ItemData(4989, 1, "翱翔徽章"), + new ItemData(4923, 1, "星光"), + new ItemData(4952, 1, "夜光"), + new ItemData(4953, 1, "日暮"), + new ItemData(4811, 1, "光之珠宝"), + }; + #endregion + + #region 石巨人前 + GolemBoss = new List { + new ItemData(3329, 1, "宝藏袋(石巨人)"), + new ItemData(3860, 1, "宝藏袋(双足翼龙)"), + new ItemData(4807, 1, "石巨人守卫"), + new ItemData(1248, 1, "石巨人之眼"), + new ItemData(3337, 1, "闪亮石"), + new ItemData(1294, 1, "锯刃镐"), + new ItemData(1122, 1, "疯狂飞斧"), + new ItemData(1295, 1, "高温射线枪"), + new ItemData(1296, 1, "大地法杖"), + new ItemData(1297, 1, "石巨人之拳"), + new ItemData(3110, 1, "天界壳"), + new ItemData(1865, 1, "天界石"), + new ItemData(899, 1, "太阳石"), + new ItemData(2218, 1, "甲虫外壳"), + new ItemData(2199, 1, "甲虫头盔"), + new ItemData(2200, 1, "甲虫铠甲"), + new ItemData(2201, 1, "甲虫壳"), + new ItemData(2202, 1, "甲虫护腿"), + new ItemData(2280, 1, "甲虫之翼"), + new ItemData(948, 1, "蒸汽朋克之翼"), + new ItemData(3871, 1, "英灵殿骑士头盔"), + new ItemData(3872, 1, "英灵殿骑士胸甲"), + new ItemData(3873, 1, "英灵殿骑士护胫"), + new ItemData(3874, 1, "暗黑艺术家帽子"), + new ItemData(3875, 1, "暗黑艺术家长袍"), + new ItemData(3876, 1, "暗黑艺术家护腿"), + new ItemData(3877, 1, "红色骑术兜帽"), + new ItemData(3878, 1, "红色骑术服"), + new ItemData(3879, 1, "红色骑术护腿"), + new ItemData(3880, 1, "渗透忍者头盔"), + new ItemData(3882, 1, "渗透忍者裤装"), + new ItemData(1258, 1, "毒刺发射器"), + new ItemData(2797, 1, "外星霰弹枪"), + new ItemData(2882, 1, "带电爆破炮"), + new ItemData(2769, 1, "宇宙车钥匙"), + new ItemData(2880, 1, "波涌之刃"), + new ItemData(2795, 1, "激光机枪"), + new ItemData(2749, 1, "外星法杖"), + new ItemData(2796, 1, "电圈发射器"), + new ItemData(3883, 1, "双足翼龙之翼"), + new ItemData(3870, 1, "双足翼龙怒气"), + new ItemData(3859, 1, "空中祸害"), + new ItemData(3858, 1, "天龙之怒"), + new ItemData(3827, 1, "飞龙"), + new ItemData(1261, 1, "毒刺矢"), + }; + #endregion + + #region 拜月前 + AncientCultist = new List { + new ItemData(3456, 1, "星璇碎片"), + new ItemData(3457, 1, "星云碎片"), + new ItemData(3458, 1, "日耀碎片"), + new ItemData(3459, 1, "星尘碎片"), + new ItemData(3473, 1, "日耀喷发剑"), + new ItemData(3543, 1, "破晓之光"), + new ItemData(3474, 1, "星尘细胞法杖"), + new ItemData(3531, 1, "星尘之龙法杖"), + new ItemData(3475, 1, "星旋机枪"), + new ItemData(3540, 1, "幻影弓"), + new ItemData(3542, 1, "星云烈焰"), + new ItemData(3476, 1, "星云奥秘"), + new ItemData(3549, 1, "远古操纵机"), + new ItemData(3544, 1, "超级治疗药水"), + }; + #endregion + + #region 月前 + Moonlord = new List { + new ItemData(3332, 1, "宝藏袋(月亮领主)"), + new ItemData(3601, 1, "天界符"), + new ItemData(3460, 1, "夜明矿"), + new ItemData(3467, 1, "夜明锭"), + new ItemData(3567, 1, "夜明弹"), + new ItemData(3568, 1, "夜明箭"), + new ItemData(2757, 1, "星旋头盔"), + new ItemData(2758, 1, "星旋胸甲"), + new ItemData(2759, 1, "星旋护腿"), + new ItemData(3469, 1, "星旋强化翼"), + new ItemData(3381, 1, "星尘头盔"), + new ItemData(3382, 1, "星尘板甲"), + new ItemData(3383, 1, "星尘护腿"), + new ItemData(3470, 1, "星云斗篷"), + new ItemData(2760, 1, "星云头盔"), + new ItemData(2761, 1, "星云胸甲"), + new ItemData(2762, 1, "星云护腿"), + new ItemData(3471, 1, "星尘之翼"), + new ItemData(2763, 1, "耀斑头盔"), + new ItemData(2764, 1, "耀斑胸甲"), + new ItemData(2765, 1, "耀斑护腿"), + new ItemData(3468, 1, "日耀之翼"), + new ItemData(3466, 1, "星尘镐"), + new ItemData(2776, 1, "星旋镐"), + new ItemData(2781, 1, "星云镐"), + new ItemData(2786, 1, "耀斑镐斧"), + new ItemData(2774, 1, "星旋钻头"), + new ItemData(2779, 1, "星云钻头"), + new ItemData(2784, 1, "耀斑钻头"), + new ItemData(3464, 1, "星尘钻头"), + new ItemData(2768, 1, "钻头控制装置"), + new ItemData(3384, 1, "传送枪"), + new ItemData(1131, 1, "重力球"), + new ItemData(4954, 1, "天界星盘"), + new ItemData(4956, 1, "天顶剑"), + new ItemData(3065, 1, "狂星之怒"), + new ItemData(3063, 1, "彩虹猫之刃"), + new ItemData(1553, 1, "太空海豚机枪"), + new ItemData(3930, 1, "喜庆弹射器Mk2"), + new ItemData(3570, 1, "月耀"), + new ItemData(3389, 1, "泰拉悠悠球"), + new ItemData(3541, 1, "终极棱镜"), + new ItemData(3569, 1, "月亮传送门法杖"), + new ItemData(3571, 1, "七彩水晶法杖"), + new ItemData(5335, 1, "和谐传送杖"), + new ItemData(5364, 1, "无底微光桶"), + }; + #endregion + } + #endregion + + #region 超进度物品 + public List Current() + { + List list = new(); + if (!NPC.downedSlimeKing) list.AddRange(SlimeKing); + if (!NPC.downedGoblins) list.AddRange(Goblins); + if (!NPC.downedBoss1) list.AddRange(Boss1); + if (!NPC.downedDeerclops) list.AddRange(Deerclops); + if (!NPC.downedBoss2) list.AddRange(Boss2); + if (!NPC.downedQueenBee) list.AddRange(QueenBee); + if (!NPC.downedBoss3) list.AddRange(Boss3); + if (!Main.hardMode) list.AddRange(hardMode); + if (!NPC.downedQueenSlime) list.AddRange(QueenSlime); + if (!NPC.downedMechBossAny) list.AddRange(MechBossAny); + if (!NPC.downedMechBoss1 || !NPC.downedMechBoss2 || !NPC.downedMechBoss3) list.AddRange(MechBoss); + if (!NPC.downedFishron) list.AddRange(Fishron); + if (!NPC.downedEmpressOfLight) list.AddRange(EmpressOfLight); + if (!NPC.downedPlantBoss) list.AddRange(PlantBoss); + if (!NPC.downedGolemBoss) list.AddRange(GolemBoss); + if (!NPC.downedAncientCultist) list.AddRange(AncientCultist); + if (!NPC.downedMoonlord) list.AddRange(Moonlord); + return list; + } + #endregion + + #region 违规物品是否为空 + public bool IsEmpty() + { + if ( + Goblins.Count + + SlimeKing.Count + + Boss1.Count + + Deerclops.Count + + Boss2.Count + + QueenBee.Count + + Boss3.Count + + hardMode.Count + + QueenSlime.Count + + MechBossAny.Count + + MechBoss.Count + + Fishron.Count + + EmpressOfLight.Count + + PlantBoss.Count + + GolemBoss.Count + + AncientCultist.Count + + Moonlord.Count > 0) + { + return false; + } + return true; + } + #endregion + + + + #region 加载配置文件方法 + public void Write(string path) + { + using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write)) + { + var str = JsonConvert.SerializeObject(this, Formatting.Indented); + using (var sw = new StreamWriter(fs)) + { + sw.Write(str); + } + } + } + + public static Configuration Read(string path) + { + if (!File.Exists(path)) + { + var defaultConfig = new Configuration(); + defaultConfig.Init(); // 在写入之前初始化新配置对象 + defaultConfig.Write(path); + return defaultConfig; + } + else + { + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var sr = new StreamReader(fs)) + { + var json = sr.ReadToEnd(); + var cf = JsonConvert.DeserializeObject(json); + return cf; + } + } + } + + #endregion + + + } + + #region 物品数据 + public class ItemData + { + public int id = 0; + + public int 数量 = 1; + + public string 名称 = ""; + + public ItemData() + { + } + + public ItemData(int _id, int stack, string name = "") + { + id = _id; + 数量 = stack; + 名称 = name; + } + } + #endregion +} \ No newline at end of file diff --git a/CheckBag/PlayerItemSlotID.cs b/CheckBag/PlayerItemSlotID.cs new file mode 100644 index 00000000..840b9f2c --- /dev/null +++ b/CheckBag/PlayerItemSlotID.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace 检查背包 +{ + public static class PlayerItemSlotID + { + public static readonly int Inventory0; + + public static readonly int InventoryMouseItem; + + public static readonly int Armor0; + + public static readonly int Dye0; + + public static readonly int Misc0; + + public static readonly int MiscDye0; + + public static readonly int Bank1_0; + + public static readonly int Bank2_0; + + public static readonly int TrashItem; + + public static readonly int Bank3_0; + + public static readonly int Bank4_0; + + public static readonly int Loadout1_Armor_0; + + public static readonly int Loadout1_Dye_0; + + public static readonly int Loadout2_Armor_0; + + public static readonly int Loadout2_Dye_0; + + public static readonly int Loadout3_Armor_0; + + public static readonly int Loadout3_Dye_0; + + public static bool[] CanRelay; + + public static int _nextSlotId; + + static PlayerItemSlotID() + { + CanRelay = new bool[0]; + Inventory0 = AllocateSlots(58, canNetRelay: true); + InventoryMouseItem = AllocateSlots(1, canNetRelay: true); + Armor0 = AllocateSlots(20, canNetRelay: true); + Dye0 = AllocateSlots(10, canNetRelay: true); + Misc0 = AllocateSlots(5, canNetRelay: true); + MiscDye0 = AllocateSlots(5, canNetRelay: true); + Bank1_0 = AllocateSlots(40, canNetRelay: false); + Bank2_0 = AllocateSlots(40, canNetRelay: false); + TrashItem = AllocateSlots(1, canNetRelay: false); + Bank3_0 = AllocateSlots(40, canNetRelay: false); + Bank4_0 = AllocateSlots(40, canNetRelay: false); + Loadout1_Armor_0 = AllocateSlots(20, canNetRelay: true); + Loadout1_Dye_0 = AllocateSlots(10, canNetRelay: true); + Loadout2_Armor_0 = AllocateSlots(20, canNetRelay: true); + Loadout2_Dye_0 = AllocateSlots(10, canNetRelay: true); + Loadout3_Armor_0 = AllocateSlots(20, canNetRelay: true); + Loadout3_Dye_0 = AllocateSlots(10, canNetRelay: true); + } + + public static int AllocateSlots(int amount, bool canNetRelay) + { + int nextSlotId = _nextSlotId; + _nextSlotId += amount; + int num = CanRelay.Length; + Array.Resize(ref CanRelay, num + amount); + for (int i = num; i < _nextSlotId; i++) + { + CanRelay[i] = canNetRelay; + } + + return nextSlotId; + } + } +} diff --git a/CheckBag/README.md b/CheckBag/README.md new file mode 100644 index 00000000..aaebce20 --- /dev/null +++ b/CheckBag/README.md @@ -0,0 +1,95 @@ +# CheckBag 检查背包 + +- 作者: hufang360,羽学 +- 出处: [github](https://github.com/hufang360/TShockCheckBag) +- 本插件可通过修改【检查背包。json】配置文件来控制: +- 检测间隔、警告次数、封禁时间、更新频率 +- 并加入了常见的进度项,默认生成的配置文件完善了大部分进度物品项. +- +## 更新日志 + +``` +- 2.0.0 +- 经玩家反馈,检测背包的速度太慢特此更新:在配置文件加入了个【每秒更新频率】,数字越少检测速度越快 +- +- 1.9.0 +- 修复了日志会额外写一个.txt文件在服务器根目录的问题 +- 【检查日志】记录换行间隔不再那么长 + +- 1.8.0 +- 更新将违规信息写入【检查背包】文件夹里的【检查日志】 +- 修改了默认生成的配置文件 +- 【新一王后】→【肉后】:神灯烈焰 +- 考虑tshock醉酒世界BUG骷髅王前掉骨头因素 +- 【骷髅王后】→【世吞克脑后】:虚空保险库、虚空袋、闭合的虚空袋 + +- 1.7.0 +- 全时期封禁加入:广播盒 +- 优化了/cbag i的显示方式为:每行1个物品,每页15行,只显示当前进度会检测的物品 +- 超进度物品警告方法从私聊玩家改为了发包给所有在线玩家 + +- 1.6.0 +- 根据Dont dig up地图种子重调了默认配置文件以下物品进度项: +- 移除:魔法飞刀 +- 骷髅王后进度:海蓝权杖、邪恶三叉戟、泡泡枪、金钥匙 +- 世吞克脑后:陨石锭、星星炮、流星套、太空枪 + +- 1.5.0 +- 加入了【免检背包】权限名,不再对【未登录】用户进行装备检查 + +- 1.4.0 +- 将默认生成的配置里:抑郁球、血脉球从【世吞前】进度移除, +- 根据wiki把各种悠悠球产生时期明确排列了对应时期的进度锁。 + +- 1.3.0 +- 去除所有重复的.json文件内的超进度物品ID。 + +- 1.2.0 +- 修复服主超管免检权,将config配置文件补充完整的进度ID + +- 1.1.0 +- 完善了checkbag插件的配置文件, +- 将原有的/checkbag指令与权限重新缩短为:/cbag +- 将watcher插件的清理违禁物品方式加入到了【检查背包】插件中 +- 配置加入了【哥布林前】、【克眼】、【猪鲨】等更多进度限制 +``` +## 指令 + +| 语法 | 权限 | 说明 | +| -------------- | :-----------------: | :------: | +| reload | 无 | 无| +| cbag b | cbag | 列出封禁记录 | +| cbag i | cbag | 列出违规物品 | +| 无 | 免检背包 | 不对其检查 | + +## 配置 + +```json + "配置说明": "重载配置请输入:/reload", + "物品查询": "https://terraria.wiki.gg/zh/wiki/Item_IDs", + "检测间隔(秒)": 5, + "更新频率(越小越快)": 60, + "封禁时长(分钟)": 10, + "警告次数(封禁)": 15, + "全时期": [ + { + "id": 74, + "数量": 500, + "名称": "铂金币" + }, + { + "id": 75, + "数量": 999, + "名称": "坠落之星" + }, + { + "id": 3617, + "数量": 1, + "名称": "广播盒" + } + ] + … +``` +## 反馈 +- 共同维护的插件库:https://github.com/THEXN/TShockPlugin/ +- 国内社区trhub.cn 或 TShock官方群等 \ No newline at end of file diff --git a/DisableGodMod/DisableGodMod.cs b/DisableGodMod/DisableGodMod.cs new file mode 100644 index 00000000..af8b15dc --- /dev/null +++ b/DisableGodMod/DisableGodMod.cs @@ -0,0 +1,429 @@ +using System; +using System.IO; +using System.Timers; +using Terraria; +using TerrariaApi.Server; +using static 阻止玩家无敌; +using TShockAPI; +using TShockAPI.DB; +using TShockAPI.Hooks; + +[ApiVersion(2, 1)] +public class 阻止玩家无敌 : TerrariaPlugin +{ + public class LPlayer + { + public int Index { get; set; } + + public int Tr { get; set; } + + public int Dm { get; set; } + + public int LHp { get; set; } + + public int LMaxHp { get; set; } + + public bool Heal { get; set; } + + public bool Skip { get; set; } + + public bool BAA { get; set; } + + public int Mis { get; set; } + + public DateTime LastTiem { get; set; } + + public DateTime LCheckTiem { get; set; } + + public int KickL { get; set; } + + public DateTime LastTiemKickL { get; set; } + + public LPlayer(int index) + { + Tr = 0; + Dm = 0; + Mis = 0; + LHp = 0; + LMaxHp = 0; + Skip = true; + Heal = false; + BAA = false; + Index = index; + LastTiem = DateTime.UtcNow; + KickL = 0; + LastTiemKickL = DateTime.UtcNow; + LCheckTiem = DateTime.UtcNow; + } + } + + private static readonly System.Timers.Timer Update = new System.Timers.Timer(3000.0); + + public static bool ULock = false; + + public override string Author => "GK 修改:羽学"; + + public override string Description => "如果玩家无敌那么就断开它!"; + + public override string Name => "阻止玩家无敌"; + + public override Version Version => new Version(1, 0, 2, 1); + + private LPlayer[] LPlayers { get; set; } + + public 阻止玩家无敌(Main game) + : base(game) + { + LPlayers = new LPlayer[256]; + base.Order = 1000; + } + + public override void Initialize() + { + ServerApi.Hooks.GameInitialize.Register(this, OnInitialize); + ServerApi.Hooks.NetGetData.Register(this, GetData); + ServerApi.Hooks.NpcStrike.Register(this, NpcStrike); + ServerApi.Hooks.NetSendData.Register(this, SendData); + ServerApi.Hooks.NetGreetPlayer.Register(this, OnGreetPlayer); + ServerApi.Hooks.ServerLeave.Register(this, OnLeave); + ServerApi.Hooks.NpcSpawn.Register(this, OnSpawn); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + ServerApi.Hooks.GameInitialize.Deregister(this, OnInitialize); + ServerApi.Hooks.NetGetData.Deregister(this, GetData); + ServerApi.Hooks.NetSendData.Deregister(this, SendData); + ServerApi.Hooks.NpcStrike.Deregister(this, NpcStrike); + ServerApi.Hooks.NetGreetPlayer.Deregister(this, OnGreetPlayer); + ServerApi.Hooks.ServerLeave.Deregister(this, OnLeave); + ServerApi.Hooks.NpcSpawn.Deregister(this, OnSpawn); + Update.Elapsed -= OnUpdate; + Update.Stop(); + } + base.Dispose(disposing); + } + + private void OnInitialize(EventArgs args) + { + Update.Elapsed += OnUpdate; + Update.Start(); + } + + public void OnUpdate(object sender, ElapsedEventArgs e) + { + } + + public static bool Timeout(DateTime Start, bool warn = true, int ms = 500) + { + bool flag = (DateTime.Now - Start).TotalMilliseconds >= (double)ms; + if (flag) + { + ULock = false; + } + if (warn && flag) + { + TShock.Log.Error("阻止无敌超时,已抛弃部分提示"); + } + return flag; + } + + private void OnGreetPlayer(GreetPlayerEventArgs e) + { + lock (LPlayers) + { + LPlayers[e.Who] = new LPlayer(e.Who); + } + } + + private void OnLeave(LeaveEventArgs e) + { + lock (LPlayers) + { + if (LPlayers[e.Who] != null) + { + LPlayers[e.Who] = null; + } + } + } + + private void OnSpawn(NpcSpawnEventArgs args) + { + if (args.Handled || !Main.npc[args.NpcId].boss || !((Entity)Main.npc[args.NpcId]).active) + { + return; + } + lock (LPlayers) + { + for (int i = 0; i < LPlayers.Length; i++) + { + if (LPlayers[i] != null) + { + LPlayers[i].LCheckTiem = DateTime.UtcNow; + LPlayers[i].Tr = 0; + LPlayers[i].Mis = 0; + } + } + } + } + + private void SendData(SendDataEventArgs args) + { + if (args.Handled || args.MsgId != PacketTypes.PlayerHealOther) + { + return; + } + int number = args.number; + if (number < 0) + { + return; + } + lock (LPlayers) + { + if (LPlayers[number] != null && LPlayers[number].Tr == 3) + { + LPlayers[number].Heal = true; + } + } + } + + private void NpcStrike(NpcStrikeEventArgs args) + { + if (args.Handled) + { + return; + } + lock (LPlayers) + { + if (LPlayers[((Entity)args.Player).whoAmI] != null && (DateTime.UtcNow - LPlayers[((Entity)args.Player).whoAmI].LCheckTiem).TotalMilliseconds > 600000.0) + { + LPlayers[((Entity)args.Player).whoAmI].LCheckTiem = DateTime.UtcNow; + LPlayers[((Entity)args.Player).whoAmI].Tr = 0; + LPlayers[((Entity)args.Player).whoAmI].Mis = 0; + } + } + } + // 定义方法GetData用于获取并处理游戏数据 + private void GetData(GetDataEventArgs args) + { + // 获取当前处理的TSPlayer对象实例 + TSPlayer tSPlayer = TShock.Players[args.Msg.whoAmI]; + // 如果玩家不存在、已断开连接、事件已被处理、或者该玩家在LPlayers数组中不存在,则直接返回 + if (tSPlayer == null || !tSPlayer.ConnectionAlive || args.Handled || LPlayers[args.Msg.whoAmI] == null || tSPlayer.Group.HasPermission("免检无敌") || tSPlayer.Group.Name == "owner") + { + return; + } + // 对LPlayers加锁以确保线程安全 + lock (LPlayers) + { + // 判断消息类型是否为玩家血量更新 + if (args.MsgID == PacketTypes.PlayerHp) + { + // 如果玩家当前正处于禁用状态,则直接返回 + if (tSPlayer.IsBeingDisabled()) + { + return; + } + // 从二进制流中读取相关数据 + using BinaryReader binaryReader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)); + byte b = binaryReader.ReadByte(); + short num = binaryReader.ReadInt16(); + short num2 = binaryReader.ReadInt16(); + + // 如果玩家的检测状态未初始化 + if (LPlayers[args.Msg.whoAmI].Tr == 0) + { + // 如果玩家还未进行首次判断,则进行初始化 + if (!LPlayers[args.Msg.whoAmI].BAA) + { + // 检查所有在线玩家,如果有任何玩家正在进行无敌检测,则跳过此次检查 + for (int i = 0; i < LPlayers.Length; i++) + { + if (LPlayers[i] != null && LPlayers[i].BAA) + { + return; + } + } + LPlayers[args.Msg.whoAmI].BAA = true; + } + // 如果玩家已跳过此次伤害检测,并且超过1.5秒,则重置跳过状态 + if (LPlayers[args.Msg.whoAmI].Skip && (DateTime.UtcNow - LPlayers[args.Msg.whoAmI].LastTiem).TotalMilliseconds > 1500.0) + { + LPlayers[args.Msg.whoAmI].Skip = false; + } + // 如果血量大于1且未跳过此次伤害检测,则开始无敌检测流程 + if (num > 1 && !LPlayers[args.Msg.whoAmI].Skip) + { + LPlayers[args.Msg.whoAmI].Tr = 1; + LPlayers[args.Msg.whoAmI].LHp = num; + tSPlayer.DamagePlayer(1); + } + } + // 如果玩家正处于无敌检测流程中 + else if (LPlayers[args.Msg.whoAmI].Tr == 1) + { + if (LPlayers[args.Msg.whoAmI].LHp != num) + { + LPlayers[args.Msg.whoAmI].Tr = 2; + LPlayers[args.Msg.whoAmI].BAA = false; + } + // 否则继续无敌检测流程 + else + { + // 如果已累计错误次数达到1次及以上 + if (LPlayers[args.Msg.whoAmI].Mis >= 1) + { + // 踢出玩家,并显示无敌违规提示 + tSPlayer.Kick($"玩家 {tSPlayer.Name} 因无敌被踢出.", force: true, silent: false, "Server"); + return; + } + LPlayers[args.Msg.whoAmI].Mis++; + LPlayers[args.Msg.whoAmI].Tr = 0; + // 重置跳过状态(同上) + if (LPlayers[args.Msg.whoAmI].Skip && (DateTime.UtcNow - LPlayers[args.Msg.whoAmI].LastTiem).TotalMilliseconds > 1500.0) + { + LPlayers[args.Msg.whoAmI].Skip = false; + } + // 继续无敌检测流程(同上) + if (num > 1 && !LPlayers[args.Msg.whoAmI].Skip) + { + LPlayers[args.Msg.whoAmI].Tr = 1; + LPlayers[args.Msg.whoAmI].LHp = num; + tSPlayer.DamagePlayer(1); + } + } + } + //血量溢出检测... + + //else if (LPlayers[args.Msg.whoAmI].Tr == 2 && num > tSPlayer.TPlayer.statLifeMax2 && num > tSPlayer.TPlayer.statLifeMax2 + (num2 - tSPlayer.TPlayer.statLifeMax)) + //{ + // if ((DateTime.UtcNow - LPlayers[args.Msg.whoAmI].LastTiemKickL).TotalMilliseconds > 30000.0) + // { + // LPlayers[args.Msg.whoAmI].KickL = 0; + // LPlayers[args.Msg.whoAmI].LastTiemKickL = DateTime.UtcNow; + // } + // LPlayers[args.Msg.whoAmI].KickL++; + // if (LPlayers[args.Msg.whoAmI].KickL > 3) + // { + // tSPlayer.Kick($"玩家 {tSPlayer.Name} 血量溢出被踢出.", force: true, silent: false, "Server"); + // } + // return; + //} + + // 更新玩家当前和最大血量 + if (LPlayers[args.Msg.whoAmI].LMaxHp != 0 && num2 != LPlayers[args.Msg.whoAmI].LMaxHp) + { + // 检测玩家是否非法修改血量上限,如有异常将踢出玩家 + // 若玩家原血量上限在400至500之间,且新血量上限不是增加5点或10点,则视为异常 + if (LPlayers[args.Msg.whoAmI].LMaxHp >= 400 && LPlayers[args.Msg.whoAmI].LMaxHp < 500) + { + if (num2 != LPlayers[args.Msg.whoAmI].LMaxHp + 5 && num2 != LPlayers[args.Msg.whoAmI].LMaxHp + 10) + { + string text = $"玩家 {tSPlayer.Name} 修改血量上限({LPlayers[args.Msg.whoAmI].LMaxHp}>{num2 - LPlayers[args.Msg.whoAmI].LMaxHp}>{num2})"; + // 封禁玩家账号,并给出封禁原因,此处为模拟注释,实际操作请替换对应API调用 + //TShock.Bans.InsertBan($"{Identifier.Account}{tSPlayer.Account.Name}", text + "被封号.", "阻止玩家无敌", DateTime.UtcNow, DateTime.MaxValue); + tSPlayer.Kick(text + "被踢出.", force: true, silent: false, "Server"); + return; + } + } + // 若玩家原血量上限小于400,且新血量上限不是增加20点或40点,则视为异常 + else if (LPlayers[args.Msg.whoAmI].LMaxHp < 400) + { + if (num2 != LPlayers[args.Msg.whoAmI].LMaxHp + 20 && num2 != LPlayers[args.Msg.whoAmI].LMaxHp + 40) + { + string text2 = $"玩家 {tSPlayer.Name} 修改血量上限({LPlayers[args.Msg.whoAmI].LMaxHp}>{num2 - LPlayers[args.Msg.whoAmI].LMaxHp}>{num2})"; + // 封禁玩家账号,并给出封禁原因,此处为模拟注释,实际操作请替换对应API调用 + //TShock.Bans.InsertBan($"{Identifier.Account}{tSPlayer.Account.Name}", text2 + "被封号.", "阻止玩家无敌", DateTime.UtcNow, DateTime.MaxValue); + tSPlayer.Kick(text2 + "被踢出.", force: true, silent: false, "Server"); + return; + } + } + // 若玩家的新血量上限大于原血量上限,且不在上述合法范围内,则视为异常 + else if (num2 > LPlayers[args.Msg.whoAmI].LMaxHp) + { + string text3 = $"玩家 {tSPlayer.Name} 修改血量上限({LPlayers[args.Msg.whoAmI].LMaxHp}>{num2 - LPlayers[args.Msg.whoAmI].LMaxHp}>{num2})"; + // 封禁玩家账号,并给出封禁原因,此处为模拟注释,实际操作请替换对应API调用 + //TShock.Bans.InsertBan($"{Identifier.Account}{tSPlayer.Account.Name}", text3 + "被封号.", "阻止玩家无敌", DateTime.UtcNow, DateTime.MaxValue); + tSPlayer.Kick(text3 + "被踢出.", force: true, silent: false, "Server"); + return; + } + } + // 更新玩家当前血量和最大血量数值 + LPlayers[args.Msg.whoAmI].LHp = num; + LPlayers[args.Msg.whoAmI].LMaxHp = num2; + return; + } + // 检查不同消息类型并作出相应处理 + if (args.MsgID == PacketTypes.PlayerHurtV2) + { + // 若玩家防御力低于200且处于特定检测状态 + if (LPlayers[args.Msg.whoAmI].Tr == 2 && tSPlayer.TPlayer.statDefense <= 199) + { + // 此处可能需要添加针对玩家防御力低的具体处理逻辑,目前为空 + } + } + else if (args.MsgID == PacketTypes.PlayerStealth) + { + // 若玩家处于未检测状态(Tr=0),则标记跳过下次伤害检测,并记录当前时间 + if (LPlayers[args.Msg.whoAmI].Tr == 0) + { + LPlayers[args.Msg.whoAmI].Skip = true; + LPlayers[args.Msg.whoAmI].LastTiem = DateTime.UtcNow; + } + // 若玩家处于特定检测状态(Tr=1),则改变检测状态,并关闭当前检测标志位 + else if (LPlayers[args.Msg.whoAmI].Tr == 1) + { + LPlayers[args.Msg.whoAmI].Tr = 2; + LPlayers[args.Msg.whoAmI].BAA = false; + } + } + // 若玩家处于未检测状态(Tr=0),则标记跳过下次伤害检测,并记录当前时间 + else if (args.MsgID == PacketTypes.PlayerDodge) + { + if (LPlayers[args.Msg.whoAmI].Tr == 0) + { + LPlayers[args.Msg.whoAmI].Skip = true; + LPlayers[args.Msg.whoAmI].LastTiem = DateTime.UtcNow; + } + // 若玩家处于特定检测状态(Tr=1),则改变检测状态,并关闭当前检测标志位 + else if (LPlayers[args.Msg.whoAmI].Tr == 1) + { + LPlayers[args.Msg.whoAmI].Tr = 2; + LPlayers[args.Msg.whoAmI].BAA = false; + } + } + else if (args.MsgID == PacketTypes.EffectHeal) + { + // 若玩家处于特定恢复状态(Tr=3),则标记为正在接受治疗 + if (LPlayers[args.Msg.whoAmI].Tr == 3) + { + LPlayers[args.Msg.whoAmI].Heal = true; + } + } + else if (args.MsgID == PacketTypes.PlayerHealOther) + { + // 若玩家处于特定恢复状态(Tr=3),则标记为正在对他方进行治疗 + if (LPlayers[args.Msg.whoAmI].Tr == 3) + { + LPlayers[args.Msg.whoAmI].Heal = true; + } + } + else if (args.MsgID == PacketTypes.PlayerSpawn) + { + // 若玩家处于未检测状态(Tr=0),则标记跳过下次伤害检测,并记录当前时间 + if (LPlayers[args.Msg.whoAmI].Tr == 0) + { + LPlayers[args.Msg.whoAmI].Skip = true; + LPlayers[args.Msg.whoAmI].LastTiem = DateTime.UtcNow; + } + // 若玩家处于特定恢复状态(Tr=3),则标记为正在接受治疗 + else if (LPlayers[args.Msg.whoAmI].Tr == 3) + { + LPlayers[args.Msg.whoAmI].Heal = true; + } + } + } + } +} diff --git a/DisableGodMod/DisableGodMod.csproj b/DisableGodMod/DisableGodMod.csproj new file mode 100644 index 00000000..04c81dc3 --- /dev/null +++ b/DisableGodMod/DisableGodMod.csproj @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/DisableGodMod/README.md b/DisableGodMod/README.md new file mode 100644 index 00000000..3878b5e8 --- /dev/null +++ b/DisableGodMod/README.md @@ -0,0 +1,30 @@ +# DisableGodMod 阻止玩家无敌 + +- 作者: GK、羽学 +- 出处: QQ群(232109072) +- 插件的主要目的是监控并防止玩家在游戏中使用无敌或其他作弊行为。 +- 通过监听玩家的生命值、防御值、隐身状态、闪避状态以及治疗效果等信息的变化, +- 会对违规玩家执行踢出操作。同时,为了避免并发问题, +- 对玩家数据容器进行了同步锁处理。 +- 对玩家的行为进行分析判断,当发现疑似无敌或非法修改生命值上限等情况时, +## 更新日志 + +``` +- 给插件添加了一个权限名, +- 避免它对服主、超管等用户组也会实施同样的检测惩罚, +- 并注释掉了它原本可能的封禁手段,带来的玩家不满。 +``` +## 指令 + +| 语法 | 权限 | 说明 | +| -------------- | :-----------------: | :------: | +| 无 | 免检无敌 |插件不对其检测| + +## 配置 + +```json +暂无 +``` +## 反馈 +- 共同维护的插件库:https://github.com/THEXN/TShockPlugin/ +- 国内社区trhub.cn 或 TShock官方群等 \ No newline at end of file diff --git a/DisableMonsCoin/DisableMonsCoin.csproj b/DisableMonsCoin/DisableMonsCoin.csproj index 639adc02..04c81dc3 100644 --- a/DisableMonsCoin/DisableMonsCoin.csproj +++ b/DisableMonsCoin/DisableMonsCoin.csproj @@ -1,5 +1,3 @@ - - - + \ No newline at end of file diff --git a/DisableMonsCoin/README.md b/DisableMonsCoin/README.md index 060a00d3..8aa29dc3 100644 --- a/DisableMonsCoin/README.md +++ b/DisableMonsCoin/README.md @@ -1,12 +1,28 @@ -## 怪物不掉钱插件说明 +# DisableMonsCoin 怪物不掉钱插件说明 + +- 作者: 豆沙、羽学 +- 出处: [github](https://gitee.com/Crafty/bean-points) +- 将其BPNPC.cs中的ClearCoins方法单独提取出来作为一个独立插件让 +- 怪物不再掉落钱币 +- +- +## 更新日志 +- 1.0.0 +- 适配.net 6.0 ``` -作者:豆沙 -修改:羽学 -插件来源:https://gitee.com/Crafty/bean-points -将其BPNPC.cs中的ClearCoins方法单独提取出来作为一个独立插件 +暂无 ``` +## 指令 -## 使用方法 -``` -把.dll文件放入ServerPlugins文件夹,重启服务器即可 +| 语法 | 权限 | 说明 | +| -------------- | :-----------------: | :------: | +| 无 | 无 | 无| + +## 配置 + +```json +暂无 ``` +## 反馈 +- 共同维护的插件库:https://github.com/THEXN/TShockPlugin/ +- 国内社区trhub.cn 或 TShock官方群等 \ No newline at end of file diff --git a/DisableSurfaceProjectiles/Configuration.cs b/DisableSurfaceProjectiles/Configuration.cs new file mode 100644 index 00000000..fe93cd36 --- /dev/null +++ b/DisableSurfaceProjectiles/Configuration.cs @@ -0,0 +1,79 @@ +using Newtonsoft.Json; +using Terraria; +using TShockAPI; + +namespace 禁地表弹幕 +{ + public class Configuration + { + public static readonly string FilePath = Path.Combine(TShock.SavePath, "禁地表弹幕表.json"); + // 新增配置项:地表下禁止弹幕的高度阈值(单位:游戏格子) + [JsonProperty("配置说明1")] + public string Text1 = "(注意:颠倒和正常地表只能开启一个,高度阈值数值649为1倍 正常种子:大世界10384(16倍)"; + [JsonProperty("配置说明2")] + public string Text2 = "(颠倒地图种子:小世界25960(40倍)中世界31476(48.5倍) 大世界35370(54.5倍)"; + [JsonProperty("启用")] + public bool Enabled { get; set; } = true; + [JsonProperty("开启正常高度限制")] + public bool NormalHeightLimit { get; set; } = true; + [JsonProperty("正常限制高度阈值")] + public int NormalValue { get; set; } = (int)(Main.worldSurface * 16); + [JsonProperty("开启颠倒高度限制")] + public bool HellHeightLimit { get; set; } = false; + [JsonProperty("颠倒限制高度阈值")] + public int HellValue { get; set; } = (int)(Main.worldSurface * 40); + [JsonProperty("禁用地表弹幕id")] + public int[] projectile_ID = new int[] { 28, 29, 37, 65, 68, 99, 108, 136, 137, 138, 139, 142, 143, 144, 146, 147, 149, 164, 339, 341, 354, 453, 516, 519, 637, 716, 718, 727, 773, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 796, 797, 798, 799, 800, 801, 804, 805, 806, 807, 809, 810, 863, 868, 869, 904, 905, 906, 910, 911, 949, 1013, 1014 }; + + // 添加一个新的方法来获取带有名称的列表 + public Dictionary GetIdsWithNames() + { + var dict = new Dictionary(); + + foreach (var id in projectile_ID) + { + string name = (string)Terraria.Lang.GetProjectileName(id); + + // 检查出现名字为ProjectileName. 时替换为“未知” + if (name.StartsWith("ProjectileName.")) + { + name = "未知"; + } + + dict[id] = name; + } + return dict; + } + + public void Write(string path) + { + using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write)) + { + var str = JsonConvert.SerializeObject(this, Formatting.Indented); + using (var sw = new StreamWriter(fs)) + { + sw.Write(str); + } + } + } + + public static Configuration ReadOrCreateDefault(string path) + { + if (!File.Exists(path)) + { + var defaultConfig = new Configuration(); + defaultConfig.Write(path); + return defaultConfig; + } + + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + using (var sr = new StreamReader(fs)) + { + var cf = JsonConvert.DeserializeObject(sr.ReadToEnd()); + return cf; + } + } + } + } +} \ No newline at end of file diff --git a/DisableSurfaceProjectiles/DisableSurfaceProjectiles.cs b/DisableSurfaceProjectiles/DisableSurfaceProjectiles.cs new file mode 100644 index 00000000..993f3854 --- /dev/null +++ b/DisableSurfaceProjectiles/DisableSurfaceProjectiles.cs @@ -0,0 +1,192 @@ +using Terraria; +using System.Timers; +using TerrariaApi.Server; +using TShockAPI; +using TShockAPI.Hooks; + +namespace 禁地表弹幕 +{ + [ApiVersion(2, 1)] + public class DisableSurfaceProjectiles : TerrariaPlugin + { + public override string Author => "羽学 感谢Cai 西江小子 熙恩"; + public override string Description => "禁止特定弹幕在地表产生"; + public override string Name => "禁地表弹幕"; + public override Version Version => new(1, 0, 0, 5); + internal static Configuration Config; + public static bool _isEnabled; // 存储插件是否启用的状态,默认为false + public DisableSurfaceProjectiles(Main game) : base(game) + { + Order = 40; + _isEnabled = false; // 初始化为关闭状态 + + // 添加一个定时器,20秒后执行ReloadConfig方法 + /* + System.Timers.Timer timer = new System.Timers.Timer(20000); // 设置定时器间隔为20000毫秒(即20秒) + timer.Elapsed += (sender, eventArgs) => ReloadConfig(null); // 注册Elapsed事件处理器 + timer.AutoReset = false; // 设置定时器只执行一次 + timer.Start(); // 开始计时器 + */ + } + public override void Initialize() + { + GetDataHandlers.NewProjectile += OnProjectileNew; + GeneralHooks.ReloadEvent += ReloadConfig; + ServerApi.Hooks.GamePostInitialize.Register(this, OnWorldload); + Commands.ChatCommands.Add(new Command("禁地表弹幕", Command, "禁地表弹幕")); //添加一个指令权限 + } + + private static void OnWorldload(EventArgs args) + { + LoadConfig(); + } + + private static void ReloadConfig(ReloadEventArgs args = null) + { + LoadConfig(); + // 如果 args 不为空,则发送重载成功的消息 + if (args != null && args.Player != null) + { + args.Player.SendSuccessMessage("[禁地表弹幕]重新加载配置完毕。"); + } + + _isEnabled = Config.Enabled; // 重新加载后同步插件启用状态 + } + + private static void LoadConfig() + { + Config = Configuration.ReadOrCreateDefault(Configuration.FilePath); + _isEnabled = Config.Enabled; // 更新全局启用状态 + Config.Write(Configuration.FilePath); + } + + // 定义Command方法处理玩家执行的相关命令 + public void Command(CommandArgs args) + { + // 更改_isEnabled并同步到配置 + LoadConfig(); // 先加载配置文件,确保_isEnabled反映最新的配置状态 + bool previousState = _isEnabled; // 记录之前的启用状态 + Config.Write(Configuration.FilePath); // 将新状态写入配置文件 + + // 检查玩家是否具有“禁地表弹幕”权限 + if (!args.Player.HasPermission("禁地表弹幕")) + { + // 如果没有权限,则向该玩家发送错误消息并退出方法 + args.Player.SendErrorMessage("你没有使用禁地表弹幕指令的权限"); + return; + } + + // 切换插件启用状态(布尔值(!_isEnabled)意味着翻转当前状态) + _isEnabled = !_isEnabled; + Config.Enabled = _isEnabled; + Config.Write(Configuration.FilePath); + + // 根据previousState而不是_isEnabled来构建消息,因为_isEnabled已经被切换了 + string stateChangeVerb = previousState ? "关闭" : "开启"; + + // 当插件启用时的操作 + if (_isEnabled) + { + // 从配置中获取带有名称的弹幕ID列表 + var projectileIdsWithNames = Config.GetIdsWithNames(); + + // 如果列表不为空,则进行后续操作 + if (projectileIdsWithNames != null) + { + // 创建用于格式化输出弹幕项目的方法 + string FormatItem(int index, KeyValuePair kv) + { + string itemNumber = $"{index + 1}. "; + string separator = (index + 1) % 5 == 0 && index < projectileIdsWithNames.Count - 1 ? "\n" : ""; + return $" {itemNumber}[c/EEF992:{kv.Value}]([c/73DCE6:{kv.Key}]){separator}"; + } + + // 构造一个格式化的弹幕列表,供玩家查看,仅在开启时显示:不想给它加命令,所以干脆写在里面了 + int count = projectileIdsWithNames.Count; + string formattedIdsForPlayers = string.Join("", Enumerable.Range(0, count).Select(index => FormatItem(index, projectileIdsWithNames.ElementAt(index)))); + + // 移除列表末尾的多余换行符(如果存在) + if (formattedIdsForPlayers.EndsWith("\n") && formattedIdsForPlayers.Length > 1) + { + formattedIdsForPlayers = formattedIdsForPlayers.Substring(0, formattedIdsForPlayers.Length - 1); + } + + // 构建并发送给玩家成功消息,包含启用/关闭状态及弹幕列表 + string playerMessage = $"{args.Player.Name}[c/FFAE80:{stateChangeVerb}]禁止地表弹幕功能,\n[c/FD7E83:禁止地表生成弹幕表]:\n{formattedIdsForPlayers}\n"; + TSPlayer.All.SendSuccessMessage(playerMessage); + + // 构建控制台消息前缀,并根据启用状态决定是否展示弹幕列表 之所以写2个是因为tshock控制台不能输出带颜色的代码 + string consoleMessagePrefix = $"{args.Player.Name} {stateChangeVerb}了禁止地表弹幕功能,\n"; + string consoleProjectilesList = _isEnabled + ? $"禁止地表生成弹幕表:\n{string.Join(", ", projectileIdsWithNames.Select(kv => $"{kv.Value}({kv.Key})"))}\n" + : string.Empty; + + // 组合完整控制台消息并记录到TShock日志 + string fullConsoleMessage = $"{consoleMessagePrefix}{consoleProjectilesList}"; + TShock.Log.ConsoleInfo(fullConsoleMessage); + } + } + + // 当插件关闭时的操作 + if (!_isEnabled) + { + // 向所有玩家发送消息,通知已关闭禁地表弹幕功能,避免再次把弹幕ID发出来所以额外写了个关闭消息,同样写2个:1个给玩家看(带颜色),1个给控制台看。 + string playerMessage = $"{args.Player.Name}[c/FFAE80:关闭了]禁止地表弹幕功能."; + TSPlayer.All.SendSuccessMessage(playerMessage); + + // 记录到控制台,只显示关闭通知,避免再次把弹幕ID发出来所以额外写了个关闭消息 + string consoleMessagePrefix = $"{args.Player.Name} 关闭了禁止地表弹幕功能."; + string fullConsoleMessage = $"{consoleMessagePrefix}\n"; + TShock.Log.ConsoleInfo(fullConsoleMessage); + } + } + //禁止生成的弹幕 + private static readonly HashSet restrictedProjectiles = new HashSet(); + + // 在事件处理方法中使用自定义高度 + private void CheckAndHandleNormalHeightRestriction(GetDataHandlers.NewProjectileEventArgs e) + { + if (_isEnabled && Config.NormalHeightLimit) + { + if (e.Position.Y < Config.NormalValue && (Config.projectile_ID.Contains(e.Type) || restrictedProjectiles.Contains(e.Type))) + { + e.Player.RemoveProjectile(e.Identity, e.Owner); + e.Handled = true; + + // 确保在开启正常高度限制时,颠倒高度限制自动关闭 + Config.HellHeightLimit = false; // 可能需要同步到配置的写入操作,取决于具体实现 + Config.Write(Configuration.FilePath); + } + } + } + + private void CheckAndHandleInvertedHeightRestriction(GetDataHandlers.NewProjectileEventArgs e) + { + if (_isEnabled && Config.HellHeightLimit) + { + // 检查是否开启了正常高度限制,如果开启则结束本次方法调用 + if (Config.NormalHeightLimit) + return; + + if (e.Position.Y > Config.HellValue && (Config.projectile_ID.Contains(e.Type) || restrictedProjectiles.Contains(e.Type))) + { + e.Player.RemoveProjectile(e.Identity, e.Owner); + e.Handled = true; + + // 确保在开启颠倒高度限制时,正常高度限制自动关闭 + Config.NormalHeightLimit = false; // 可能需要同步到配置的写入操作,取决于具体实现 + Config.Write(Configuration.FilePath); + } + } + } + + private void OnProjectileNew(object sender, GetDataHandlers.NewProjectileEventArgs e) + { + if (e.Player.HasPermission("免检地表弹幕")) + return; + + CheckAndHandleNormalHeightRestriction(e); + CheckAndHandleInvertedHeightRestriction(e); + } + } +} \ No newline at end of file diff --git a/DisableSurfaceProjectiles/DisableSurfaceProjectiles.csproj b/DisableSurfaceProjectiles/DisableSurfaceProjectiles.csproj new file mode 100644 index 00000000..04c81dc3 --- /dev/null +++ b/DisableSurfaceProjectiles/DisableSurfaceProjectiles.csproj @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/DisableSurfaceProjectiles/README.md b/DisableSurfaceProjectiles/README.md new file mode 100644 index 00000000..45281077 --- /dev/null +++ b/DisableSurfaceProjectiles/README.md @@ -0,0 +1,114 @@ +## Prohibit surface projectiles 禁止地表弹幕 + +- 作者: 羽学 +- 出处: [github](https://github.com/1242509682/ProhibitSurfaceProjectiles) +- 这是一个Tshock服务器插件主要用于禁止生成对服务器内玩家处于世界地表时产生的弹幕, +- 尤其是针对恶意使用爆炸物破坏服务器地图的人。 + +## 更新日志 + +``` +- 1.0.5 +- 移除了计时器,使用OnWorldload方法实现加载完地图后再创建配置文件, +- 方便计算出准确的Main.worldSurface地表值 +- 1.0.4 +- 添加了个计时器,20秒后再创建计算好Main.worldSurface世界地表值的配置文件, +- 配置文件可支持正常地图与颠倒世界,将指令同步到配置文件总开关。 +- 1.0.3 +- 给插件加了个指令开关与权限,并在开启时获取所有ID带有名称的列表, +- 名字显示不全的改为“未知”。 +- 开关指令名:/禁地表弹幕 (该指令的权限同名) +- 1.0.2 +- 对config预设了更多的弹幕类型,涵盖了主要破坏地图的手段 +- 1.0.1 +- 加入了Config配置文件,玩家可通过Config设置拦截的弹幕ID +``` +## 指令 + +| 语法 | 权限 | 说明 | +| -------------- | :-----------------: | :------: | +| /reload | 无 | 重载配置文件 | +| /禁地表弹幕 | 禁地表弹幕 | 功能开关 | +| 无 | 免检地表弹幕 | 不对其检测 | + +## 配置 + +```json + "配置说明1": "(注意:颠倒和正常地表只能开启一个,高度阈值数值649为1倍 正常种子:大世界10384(16倍)", + "配置说明2": "(颠倒地图种子:小世界25960(40倍)中世界31476(48.5倍) 大世界35370(54.5倍)", + "启用": true, + "开启正常高度限制": true, + "正常限制高度阈值": 10384, + "开启颠倒高度限制": false, + "颠倒限制高度阈值": 25960 + "禁用地表弹幕id": [ + 28, + 29, + 37, + 65, + 68, + 99, + 108, + 136, + 137, + 138, + 139, + 142, + 143, + 144, + 146, + 147, + 149, + 164, + 339, + 341, + 354, + 453, + 516, + 519, + 637, + 716, + 718, + 727, + 773, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 796, + 797, + 798, + 799, + 800, + 801, + 804, + 805, + 806, + 807, + 809, + 810, + 863, + 868, + 869, + 904, + 905, + 906, + 910, + 911, + 949, + 1013, + 1014 + ] +``` +## 反馈 +- 共同维护的插件库:https://github.com/THEXN/TShockPlugin/ +- 国内社区trhub.cn 或 TShock官方群等 \ No newline at end of file diff --git a/NormalDropsBags/NormalDropsBags.cs b/NormalDropsBags/NormalDropsBags.cs new file mode 100644 index 00000000..689773a7 --- /dev/null +++ b/NormalDropsBags/NormalDropsBags.cs @@ -0,0 +1,122 @@ +using Terraria; +using Terraria.DataStructures; +using TerrariaApi.Server; + +namespace 普通难度宝藏袋 +{ + [ApiVersion(2, 1)] + public class 普通难度宝藏袋 : TerrariaPlugin + { + // 同时请参阅TShock的插件开发骨架示例:TShock文档-Hello World + + public override string Author => "Quinci 羽学适配"; + + public override string Description => "让原本在普通难度下不掉落宝物袋的Boss开始掉落此类稀有战利品。"; + + public override string Name => "普通难度宝藏袋"; + + public override Version Version => new Version(1, 1, 3, 0); + + public 普通难度宝藏袋(Main game) : base(game) + { + + } + + public override void Initialize() + { + //从terria服务器api注册钩子(tshock的服务器补丁版本,打开Terraria API/OTAPI) + ServerApi.Hooks.NpcKilled.Register(this, OnNpcKill); + ServerApi.Hooks.NpcLootDrop.Register(this, OnDropLoot); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + //从它们各自的钩子集合中移除钩子以进行垃圾收集(IDisposable)。目前不太有用,因为这是在服务器关闭时使用的 + ServerApi.Hooks.NpcKilled.Deregister(this, OnNpcKill); + ServerApi.Hooks.NpcLootDrop.Deregister(this, OnDropLoot); + } + base.Dispose(disposing); + } + + private void OnNpcKill(NpcKilledEventArgs eventArgs) + { + + // 检查是否为Boss或Betsy,且当前游戏模式为普通模式;同时确认目标不是疯狂教徒(在任何情况下它都不应掉落宝物袋,这是无法获得的) + if ((eventArgs.npc.boss || eventArgs.npc.netID == Terraria.ID.NPCID.DD2Betsy) && Terraria.Main.GameMode == 0 && eventArgs.npc.netID != Terraria.ID.NPCID.CultistBoss) + { + switch (eventArgs.npc.netID) // 检查NPC类型。EOL(可能是“End of Level”的缩写,意为阶段结束)和史莱姆女皇拥有特殊的掉落机制,所以我打算替换它。 + { + case Terraria.ID.NPCID.KingSlime: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.KingSlimeBossBag); + return; + case Terraria.ID.NPCID.EyeofCthulhu: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.EyeOfCthulhuBossBag); + return; + case Terraria.ID.NPCID.EaterofWorldsHead: + case Terraria.ID.NPCID.EaterofWorldsBody: + case Terraria.ID.NPCID.EaterofWorldsTail: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.EaterOfWorldsBossBag); + return; + case Terraria.ID.NPCID.BrainofCthulhu: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.BrainOfCthulhuBossBag); + return; + case Terraria.ID.NPCID.QueenBee: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.QueenBeeBossBag); + return; + case Terraria.ID.NPCID.SkeletronHead: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.SkeletronBossBag); + return; + case Terraria.ID.NPCID.Deerclops: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.DeerclopsBossBag); + return; + case Terraria.ID.NPCID.WallofFlesh: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.WallOfFleshBossBag); + return; + case Terraria.ID.NPCID.DukeFishron: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.FishronBossBag); + return; + case Terraria.ID.NPCID.QueenSlimeBoss: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.QueenSlimeBossBag); + return; + case Terraria.ID.NPCID.Retinazer: + case Terraria.ID.NPCID.Spazmatism: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.TwinsBossBag); + return; + case Terraria.ID.NPCID.TheDestroyer: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.DestroyerBossBag); + return; + case Terraria.ID.NPCID.SkeletronPrime: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.SkeletronPrimeBossBag); + return; + case Terraria.ID.NPCID.Plantera: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.PlanteraBossBag); + return; + case Terraria.ID.NPCID.Golem: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.GolemBossBag); + return; + case Terraria.ID.NPCID.MoonLordCore: + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.MoonLordBossBag); + return; + case Terraria.ID.NPCID.HallowBoss: //eol 636 + if (eventArgs.npc.AI_120_HallowBoss_IsGenuinelyEnraged()) // 检查当前是否处于白天末期(eol: end of the day),如果是的话,则丢弃Terraprisma + { + Terraria.Item.NewItem(new EntitySource_DebugCommand(), (int)eventArgs.npc.position.X, (int)eventArgs.npc.position.Y, (int)eventArgs.npc.Size.X, (int)eventArgs.npc.Size.Y, Terraria.ID.ItemID.EmpressBlade, 1);//5005 TerraPrisma + } // DropItemInstanced() 方法将通知每个客户端存在一个物品,但在服务器端不会占用一个活跃的物品槽位,因此该物品不会被覆盖,这样每个客户端都可以收集这个物品。 + eventArgs.npc.DropItemInstanced(eventArgs.npc.position, eventArgs.npc.Size, Terraria.ID.ItemID.FairyQueenBossBag); //4782 eol Boss宝藏袋 + return; + } + + } + } + private void OnDropLoot(NpcLootDropEventArgs eventArgs) + { + // 与上述相同的条件 + if ((Terraria.Main.npc[eventArgs.NpcArrayIndex].boss || eventArgs.NpcId == Terraria.ID.NPCID.DD2Betsy) && Terraria.Main.GameMode == 0 && eventArgs.NpcId != Terraria.ID.NPCID.CultistBoss) + { + eventArgs.Handled = true; // 阻止游戏处理该事件。此举可防止NPC通过NPCLootOld()方法掉落战利品。金币、心形、法力之星以及治疗药水使用不同的方法,因此它们仍能保持正常行为。由于我们将用宝物袋替代战利品,所以取消了战利品掉落。 + } + } + } +} diff --git a/NormalDropsBags/NormalDropsBags.csproj b/NormalDropsBags/NormalDropsBags.csproj new file mode 100644 index 00000000..04c81dc3 --- /dev/null +++ b/NormalDropsBags/NormalDropsBags.csproj @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/NormalDropsBags/README.md b/NormalDropsBags/README.md new file mode 100644 index 00000000..cfbc66cd --- /dev/null +++ b/NormalDropsBags/README.md @@ -0,0 +1,27 @@ +# NormalDropsBags 普通难度掉落宝藏袋 + +- 作者:beerik94,羽学 +- 出处: https://github.com/beerik94/NormalBossBags +- 这是一个Tshock服务器插件主要用于: +- 让普通模式的BOSS掉宝藏袋 +- 仅影响普通难度地图,如果启用了专家或大师,则不执行任何操作。 + +## 更新日志 + +``` +适配.net 6.0 +``` +## 指令 + +| 语法 | 权限 | 说明 | +| -------------- | :-----------------: | :------: | +| 无 | 无 | 无| + +## 配置 + +```json +暂无 +``` +## 反馈 +- 共同维护的插件库:https://github.com/THEXN/TShockPlugin/ +- 国内社区trhub.cn 或 TShock官方群等 \ No newline at end of file diff --git a/OnlineGiftPackage/OnlineGiftPackage.csproj b/OnlineGiftPackage/OnlineGiftPackage.csproj index c864b061..91316f37 100644 --- a/OnlineGiftPackage/OnlineGiftPackage.csproj +++ b/OnlineGiftPackage/OnlineGiftPackage.csproj @@ -1,5 +1,3 @@  - - - - + + \ No newline at end of file diff --git a/OnlineGiftPackage/README.md b/OnlineGiftPackage/README.md index a37df315..8a3e1c3b 100644 --- a/OnlineGiftPackage/README.md +++ b/OnlineGiftPackage/README.md @@ -1,89 +1,79 @@ -## Online Gift Package 在线礼包 -作者:星夜神花 -修改:羽学 -插件来源:https://gitee.com/star-night-flower/tshock-gift - -根据config里的【DistributionInterval】与【TriggerSequence】随机发放【礼包列表】里的物品 +# Online Gift Package 在线礼包 +- 作者: 羽学 +- 出处: [github](https://gitee.com/star-night-flower/tshock-gift) +- 这是一个Tshock服务器插件主要用于对服务器内的在线玩家进行发放随机的在线奖励 +- 在【在线礼包.json】文件中添加完物品,游戏发送/Reload可自动计算获取总概率 +## 更新日志 -## 命令 -显示礼包奖励概率表的指令为:/在线礼包 - -## 版本 -v1.1.1 - -## 更新内容 ``` -v1.1.1 -1.完善了对“总概率”的Reload重载同步 -2.优化了命令的显示排版 -3.给命令加了个权限名 -4.移除了配置文件里的“将未符合条件者记录后台” - -v1.1.0 -1.修复了使用/reload或重启服务器时,配置文件被原配置覆盖,无法正常读取修改过的变量问题。 -2.把计算总概率的显示加入到了指令里:/在线礼包 -3.删除配置文件使用/reload可得到个计算过的“总概率”值在配置文件里 -4.配置文件加入了“跳过生命上限”的检测标准,可以决定高血量玩家无法获取在线礼包。 -5.加入了每次发放礼包是否记录后台 -6.“将未符合条件者记录后台”是羽学调试时观察用的,不建议开,后期更新会移除。 -7.赠送礼包后会提醒玩家下次发放时间 +- 1.1.1 +- 1.完善了对“总概率”的Reload重载同步 +- 2.优化了命令的显示排版 +- 3.给命令加了个权限名 +- 4.移除了配置文件里的“将未符合条件者记录后台” +- +- 1.1.0 +- 1.修复了使用/reload或重启服务器时,配置文件被原配置覆盖,无法正常读取修改过的变量问题。 +- 2.把计算总概率的显示加入到了指令里:/在线礼包 +- 3.删除配置文件使用/reload可得到个计算过的“总概率”值在配置文件里 +- 4.配置文件加入了“跳过生命上限”的检测标准,可以决定高血量玩家无法获取在线礼包。 +- 5.加入了每次发放礼包是否记录后台 +- 6.“将未符合条件者记录后台”是羽学调试时观察用的,不建议开,后期更新会移除。 +- 7.赠送礼包后会提醒玩家下次发放时间 +- +- 1.0.9 +- 1.修复了不按时送物品的问题, +- 2.移除了广播在线时长的方法, +- 3.配置文件加入了大量预设物品方案, +- 4.修复了/在线礼包指令不显示概率的问题, +- 5.不再根据序列数量来发放物品而是发过一次自动重置为0+1从第一个序列开始发, +- 6.解决了初始化配置文件时自动序列化问题 +- 7.加了个计算总概率的新方法。 +- +- 1.0.8 +- 1.补充了配置文件缺失的变量名称 +- 2.增加了总概率选项 +- 3.玩家可自定义广播间隔时间,方便与触发时间同步 +- 4.再次尝试优化定时器 +- 5.适配了.net 6.0 +- +- 1.0.7 +- 1.优化了定时器 +- 2.config文件改名为【在线礼包.json】,并对其修改项合理汉化 +``` +## 指令 -v1.0.9 -1.修复了不按时送物品的问题, -2.移除了广播在线时长的方法, -3.配置文件加入了大量预设物品方案, -4.修复了/在线礼包指令不显示概率的问题, -5.不再根据序列数量来发放物品而是发过一次自动重置为0+1从第一个序列开始发, -6.解决了初始化配置文件时自动序列化问题 -7.加了个计算总概率的新方法。 +| 语法 | 权限 | 说明 | +| -------------- | :-----------------: | :------: | +| /在线礼包 | OnlineGiftPackage | 显示礼包内所有物品的概率表 | +| /reload | 无 | 自动计算总概率 | -v1.0.8 -1.补充了配置文件缺失的变量名称 -2.增加了总概率选项 -3.玩家可自定义广播间隔时间,方便与触发时间同步 -4.再次尝试优化定时器 -5.适配了.net 6.0 +## 配置 -v1.0.7 -1.优化了定时器 -2.config文件改名为【在线礼包.json】,并对其修改项合理汉化 -``` -## 配置文件示例 -```(json) +```json { - "总概率": 60, "启用": true, - "DistributionInterval/秒": 1800, + "总概率(自动更新)": 60, + "发放间隔/秒": 1800, "跳过生命上限": 500, - "OutputConsole": false, - "将未符合条件者记录后台": false, + "每次发放礼包记录后台": false, "礼包列表": [ { "物品名称": "铂金币", "物品ID": 74, - "ItemAmount": [ - 2, - 5 - ], - "Probability": 1 - }, - { - "物品名称": "蠕虫罐头", - "物品ID": 4345, - "ItemAmount": [ + "所占概率": 1, + "物品数量": [ 2, 5 - ], - "Probability": 1 + ] } -], -"TriggerSequence": { - "1": "[c/55CDFF:服主]送了你1个礼包", - "2": "[c/55CDFF:服主]送了你1个礼包", - "3": "[c/55CDFF:服主]送了你1个礼包", - "4": "[c/55CDFF:服主]送了你1个礼包", - "5": "[c/55CDFF:服主]送了你1个礼包" + ], + "触发序列": { + "1": "[c/55CDFF:服主]送了你1个礼包" } } ``` +## 反馈 +- 共同维护的插件库:https://github.com/THEXN/TShockPlugin/ +- 国内社区trhub.cn 或 TShock官方群等 \ No newline at end of file diff --git a/Plugin.sln b/Plugin.sln index 49718684..8daf9530 100644 --- a/Plugin.sln +++ b/Plugin.sln @@ -44,6 +44,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RainbowChat", "RainbowChat\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CGive", "CGive\CGive.csproj", "{7A15E6B2-F299-4F07-8429-129119F4553C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CheckBag", "CheckBag\CheckBag.csproj", "{1564799E-66D1-4C9F-9333-2DBA24FF9499}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisableGodMod", "DisableGodMod\DisableGodMod.csproj", "{5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisableMonsCoin", "DisableMonsCoin\DisableMonsCoin.csproj", "{C8B631CB-F176-4921-B313-6E49A4AAC3AC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisableSurfaceProjectiles", "DisableSurfaceProjectiles\DisableSurfaceProjectiles.csproj", "{CD5A7945-D416-4DEE-8079-DF0B9B3652ED}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NormalDropsBags", "NormalDropsBags\NormalDropsBags.csproj", "{D169A104-9372-4DB4-AF39-B511087885A0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OnlineGiftPackage", "OnlineGiftPackage\OnlineGiftPackage.csproj", "{4348C222-7ADC-443D-BF67-CA86C5880A67}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RecipesBrowser", "RecipesBrowser\RecipesBrowser.csproj", "{EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TownNPCHomes", "TownNPCHomes\TownNPCHomes.csproj", "{6FAF2ED2-38B1-46C7-83F1-B909D260D70E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -196,6 +212,70 @@ Global {7A15E6B2-F299-4F07-8429-129119F4553C}.Release|Any CPU.Build.0 = Release|Any CPU {7A15E6B2-F299-4F07-8429-129119F4553C}.Release|x64.ActiveCfg = Release|Any CPU {7A15E6B2-F299-4F07-8429-129119F4553C}.Release|x64.Build.0 = Release|Any CPU + {1564799E-66D1-4C9F-9333-2DBA24FF9499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1564799E-66D1-4C9F-9333-2DBA24FF9499}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1564799E-66D1-4C9F-9333-2DBA24FF9499}.Debug|x64.ActiveCfg = Debug|Any CPU + {1564799E-66D1-4C9F-9333-2DBA24FF9499}.Debug|x64.Build.0 = Debug|Any CPU + {1564799E-66D1-4C9F-9333-2DBA24FF9499}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1564799E-66D1-4C9F-9333-2DBA24FF9499}.Release|Any CPU.Build.0 = Release|Any CPU + {1564799E-66D1-4C9F-9333-2DBA24FF9499}.Release|x64.ActiveCfg = Release|Any CPU + {1564799E-66D1-4C9F-9333-2DBA24FF9499}.Release|x64.Build.0 = Release|Any CPU + {5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}.Debug|x64.ActiveCfg = Debug|Any CPU + {5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}.Debug|x64.Build.0 = Debug|Any CPU + {5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}.Release|Any CPU.Build.0 = Release|Any CPU + {5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}.Release|x64.ActiveCfg = Release|Any CPU + {5C7C65FD-E104-4BDA-B7E5-4FC1BF0D2F16}.Release|x64.Build.0 = Release|Any CPU + {C8B631CB-F176-4921-B313-6E49A4AAC3AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8B631CB-F176-4921-B313-6E49A4AAC3AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8B631CB-F176-4921-B313-6E49A4AAC3AC}.Debug|x64.ActiveCfg = Debug|Any CPU + {C8B631CB-F176-4921-B313-6E49A4AAC3AC}.Debug|x64.Build.0 = Debug|Any CPU + {C8B631CB-F176-4921-B313-6E49A4AAC3AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8B631CB-F176-4921-B313-6E49A4AAC3AC}.Release|Any CPU.Build.0 = Release|Any CPU + {C8B631CB-F176-4921-B313-6E49A4AAC3AC}.Release|x64.ActiveCfg = Release|Any CPU + {C8B631CB-F176-4921-B313-6E49A4AAC3AC}.Release|x64.Build.0 = Release|Any CPU + {CD5A7945-D416-4DEE-8079-DF0B9B3652ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD5A7945-D416-4DEE-8079-DF0B9B3652ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD5A7945-D416-4DEE-8079-DF0B9B3652ED}.Debug|x64.ActiveCfg = Debug|Any CPU + {CD5A7945-D416-4DEE-8079-DF0B9B3652ED}.Debug|x64.Build.0 = Debug|Any CPU + {CD5A7945-D416-4DEE-8079-DF0B9B3652ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD5A7945-D416-4DEE-8079-DF0B9B3652ED}.Release|Any CPU.Build.0 = Release|Any CPU + {CD5A7945-D416-4DEE-8079-DF0B9B3652ED}.Release|x64.ActiveCfg = Release|Any CPU + {CD5A7945-D416-4DEE-8079-DF0B9B3652ED}.Release|x64.Build.0 = Release|Any CPU + {D169A104-9372-4DB4-AF39-B511087885A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D169A104-9372-4DB4-AF39-B511087885A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D169A104-9372-4DB4-AF39-B511087885A0}.Debug|x64.ActiveCfg = Debug|Any CPU + {D169A104-9372-4DB4-AF39-B511087885A0}.Debug|x64.Build.0 = Debug|Any CPU + {D169A104-9372-4DB4-AF39-B511087885A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D169A104-9372-4DB4-AF39-B511087885A0}.Release|Any CPU.Build.0 = Release|Any CPU + {D169A104-9372-4DB4-AF39-B511087885A0}.Release|x64.ActiveCfg = Release|Any CPU + {D169A104-9372-4DB4-AF39-B511087885A0}.Release|x64.Build.0 = Release|Any CPU + {4348C222-7ADC-443D-BF67-CA86C5880A67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4348C222-7ADC-443D-BF67-CA86C5880A67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4348C222-7ADC-443D-BF67-CA86C5880A67}.Debug|x64.ActiveCfg = Debug|Any CPU + {4348C222-7ADC-443D-BF67-CA86C5880A67}.Debug|x64.Build.0 = Debug|Any CPU + {4348C222-7ADC-443D-BF67-CA86C5880A67}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4348C222-7ADC-443D-BF67-CA86C5880A67}.Release|Any CPU.Build.0 = Release|Any CPU + {4348C222-7ADC-443D-BF67-CA86C5880A67}.Release|x64.ActiveCfg = Release|Any CPU + {4348C222-7ADC-443D-BF67-CA86C5880A67}.Release|x64.Build.0 = Release|Any CPU + {EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}.Debug|x64.ActiveCfg = Debug|Any CPU + {EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}.Debug|x64.Build.0 = Debug|Any CPU + {EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}.Release|Any CPU.Build.0 = Release|Any CPU + {EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}.Release|x64.ActiveCfg = Release|Any CPU + {EE9F711D-7F07-4B0A-BE1B-9331D6DF2CB0}.Release|x64.Build.0 = Release|Any CPU + {6FAF2ED2-38B1-46C7-83F1-B909D260D70E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FAF2ED2-38B1-46C7-83F1-B909D260D70E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FAF2ED2-38B1-46C7-83F1-B909D260D70E}.Debug|x64.ActiveCfg = Debug|Any CPU + {6FAF2ED2-38B1-46C7-83F1-B909D260D70E}.Debug|x64.Build.0 = Debug|Any CPU + {6FAF2ED2-38B1-46C7-83F1-B909D260D70E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FAF2ED2-38B1-46C7-83F1-B909D260D70E}.Release|Any CPU.Build.0 = Release|Any CPU + {6FAF2ED2-38B1-46C7-83F1-B909D260D70E}.Release|x64.ActiveCfg = Release|Any CPU + {6FAF2ED2-38B1-46C7-83F1-B909D260D70E}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/RecipesBrowser/README.md b/RecipesBrowser/README.md new file mode 100644 index 00000000..6bbed917 --- /dev/null +++ b/RecipesBrowser/README.md @@ -0,0 +1,31 @@ +# 插件名 + +- 作者: 棱镜、羽学 +- 出处: [github](https://github.com/1242509682/RecipesBrowser) +- 由于PE的Terraria的向导存在一些恶性bug, +- 导致在大多数服务器中向导被禁用,这样一来想要查合成表就非常麻烦, +- 所以写了这样一个插件,支持查找“此物品的配方”和“此物品可以合成什么” +## 更新日志 + +``` +- 0.5 +- 修复未释放钩子导致关闭服务器时的报错 +- 0.4 +- 适配.net 6.0 +- 加了中文命令,并添加了一个权限名 +``` +## 指令 + +| 语法 | 权限 | 说明 | +| -------------- | :-----------------: | :------: | +| /fd、/find | RecipesBrowser | /fd <物品ID> 查询合成所需材料与工作站| +| /查 | RecipesBrowser | /fd <物品ID> r 查询该材料可合成的物品| + +## 配置 + +```json +暂无 +``` +## 反馈 +- 共同维护的插件库:https://github.com/THEXN/TShockPlugin/ +- 国内社区trhub.cn 或 TShock官方群等 \ No newline at end of file diff --git a/RecipesBrowser/RecipesBrowser.cs b/RecipesBrowser/RecipesBrowser.cs new file mode 100644 index 00000000..c2450240 --- /dev/null +++ b/RecipesBrowser/RecipesBrowser.cs @@ -0,0 +1,403 @@ +using Terraria; +using Terraria.Localization; +using Terraria.Map; +using TerrariaApi.Server; +using TShockAPI; + +[ApiVersion(2, 1)] +public class MainPlugin : TerrariaPlugin +{ + public override string Name => "RecipesBrowser"; + + public override Version Version => new Version(0, 5); + + public override string Author => "棱镜 羽学适配"; + + public override string Description => "通过指令获取物品合成表"; + + public MainPlugin(Main game) + : base(game) + { + } + + public override void Initialize() + { + Commands.ChatCommands.Add(new Command(FindRecipe, "查", "find", "fd")); + MapHelper.Initialize(); + BuildMapAtlas(); + } + + public static void BuildMapAtlas() + { + Lang._mapLegendCache = (LocalizedText[])(object)new LocalizedText[MapHelper.LookupCount()]; + for (int i = 0; i < Lang._mapLegendCache.Length; i++) + { + Lang._mapLegendCache[i] = LocalizedText.Empty; + } + Lang._mapLegendCache[MapHelper.TileToLookup(4, 0)] = Lang._itemNameCache[8]; + Lang._mapLegendCache[MapHelper.TileToLookup(4, 1)] = Lang._itemNameCache[8]; + Lang._mapLegendCache[MapHelper.TileToLookup(5, 0)] = Language.GetText("MapObject.Tree"); + Lang._mapLegendCache[MapHelper.TileToLookup(6, 0)] = Language.GetText("MapObject.Iron"); + Lang._mapLegendCache[MapHelper.TileToLookup(7, 0)] = Language.GetText("MapObject.Copper"); + Lang._mapLegendCache[MapHelper.TileToLookup(8, 0)] = Language.GetText("MapObject.Gold"); + Lang._mapLegendCache[MapHelper.TileToLookup(9, 0)] = Language.GetText("MapObject.Silver"); + Lang._mapLegendCache[MapHelper.TileToLookup(10, 0)] = Language.GetText("MapObject.Door"); + Lang._mapLegendCache[MapHelper.TileToLookup(11, 0)] = Language.GetText("MapObject.Door"); + Lang._mapLegendCache[MapHelper.TileToLookup(12, 0)] = Lang._itemNameCache[29]; + Lang._mapLegendCache[MapHelper.TileToLookup(13, 0)] = Lang._itemNameCache[31]; + Lang._mapLegendCache[MapHelper.TileToLookup(14, 0)] = Language.GetText("MapObject.Table"); + Lang._mapLegendCache[MapHelper.TileToLookup(15, 0)] = Language.GetText("MapObject.Chair"); + Lang._mapLegendCache[MapHelper.TileToLookup(16, 0)] = Language.GetText("MapObject.Anvil"); + Lang._mapLegendCache[MapHelper.TileToLookup(17, 0)] = Lang._itemNameCache[33]; + Lang._mapLegendCache[MapHelper.TileToLookup(18, 0)] = Lang._itemNameCache[36]; + Lang._mapLegendCache[MapHelper.TileToLookup(20, 0)] = Language.GetText("MapObject.Sapling"); + Lang._mapLegendCache[MapHelper.TileToLookup(21, 0)] = Lang._itemNameCache[48]; + Lang._mapLegendCache[MapHelper.TileToLookup(22, 0)] = Language.GetText("MapObject.Demonite"); + Lang._mapLegendCache[MapHelper.TileToLookup(26, 0)] = Language.GetText("MapObject.DemonAltar"); + Lang._mapLegendCache[MapHelper.TileToLookup(26, 1)] = Language.GetText("MapObject.CrimsonAltar"); + Lang._mapLegendCache[MapHelper.TileToLookup(27, 0)] = Lang._itemNameCache[63]; + Lang._mapLegendCache[MapHelper.TileToLookup(407, 0)] = Language.GetText("MapObject.Fossil"); + Lang._mapLegendCache[MapHelper.TileToLookup(412, 0)] = Lang._itemNameCache[3549]; + for (int j = 0; j < 9; j++) + { + Lang._mapLegendCache[MapHelper.TileToLookup(28, j)] = Language.GetText("MapObject.Pot"); + } + Lang._mapLegendCache[MapHelper.TileToLookup(37, 0)] = Lang._itemNameCache[116]; + Lang._mapLegendCache[MapHelper.TileToLookup(29, 0)] = Lang._itemNameCache[87]; + Lang._mapLegendCache[MapHelper.TileToLookup(31, 0)] = Lang._itemNameCache[115]; + Lang._mapLegendCache[MapHelper.TileToLookup(31, 1)] = Lang._itemNameCache[3062]; + Lang._mapLegendCache[MapHelper.TileToLookup(32, 0)] = Language.GetText("MapObject.Thorns"); + Lang._mapLegendCache[MapHelper.TileToLookup(33, 0)] = Lang._itemNameCache[105]; + Lang._mapLegendCache[MapHelper.TileToLookup(34, 0)] = Language.GetText("MapObject.Chandelier"); + Lang._mapLegendCache[MapHelper.TileToLookup(35, 0)] = Lang._itemNameCache[1813]; + Lang._mapLegendCache[MapHelper.TileToLookup(36, 0)] = Lang._itemNameCache[1869]; + Lang._mapLegendCache[MapHelper.TileToLookup(42, 0)] = Language.GetText("MapObject.Lantern"); + Lang._mapLegendCache[MapHelper.TileToLookup(48, 0)] = Lang._itemNameCache[147]; + Lang._mapLegendCache[MapHelper.TileToLookup(49, 0)] = Lang._itemNameCache[148]; + Lang._mapLegendCache[MapHelper.TileToLookup(50, 0)] = Lang._itemNameCache[149]; + Lang._mapLegendCache[MapHelper.TileToLookup(51, 0)] = Language.GetText("MapObject.Web"); + Lang._mapLegendCache[MapHelper.TileToLookup(55, 0)] = Lang._itemNameCache[171]; + Lang._mapLegendCache[MapHelper.TileToLookup(63, 0)] = Lang._itemNameCache[177]; + Lang._mapLegendCache[MapHelper.TileToLookup(64, 0)] = Lang._itemNameCache[178]; + Lang._mapLegendCache[MapHelper.TileToLookup(65, 0)] = Lang._itemNameCache[179]; + Lang._mapLegendCache[MapHelper.TileToLookup(66, 0)] = Lang._itemNameCache[180]; + Lang._mapLegendCache[MapHelper.TileToLookup(67, 0)] = Lang._itemNameCache[181]; + Lang._mapLegendCache[MapHelper.TileToLookup(68, 0)] = Lang._itemNameCache[182]; + Lang._mapLegendCache[MapHelper.TileToLookup(69, 0)] = Language.GetText("MapObject.Thorn"); + Lang._mapLegendCache[MapHelper.TileToLookup(72, 0)] = Language.GetText("MapObject.GiantMushroom"); + Lang._mapLegendCache[MapHelper.TileToLookup(77, 0)] = Lang._itemNameCache[221]; + Lang._mapLegendCache[MapHelper.TileToLookup(78, 0)] = Lang._itemNameCache[222]; + Lang._mapLegendCache[MapHelper.TileToLookup(79, 0)] = Lang._itemNameCache[224]; + Lang._mapLegendCache[MapHelper.TileToLookup(80, 0)] = Lang._itemNameCache[276]; + Lang._mapLegendCache[MapHelper.TileToLookup(81, 0)] = Lang._itemNameCache[275]; + Lang._mapLegendCache[MapHelper.TileToLookup(82, 0)] = Lang._itemNameCache[313]; + Lang._mapLegendCache[MapHelper.TileToLookup(82, 1)] = Lang._itemNameCache[314]; + Lang._mapLegendCache[MapHelper.TileToLookup(82, 2)] = Lang._itemNameCache[315]; + Lang._mapLegendCache[MapHelper.TileToLookup(82, 3)] = Lang._itemNameCache[316]; + Lang._mapLegendCache[MapHelper.TileToLookup(82, 4)] = Lang._itemNameCache[317]; + Lang._mapLegendCache[MapHelper.TileToLookup(82, 5)] = Lang._itemNameCache[318]; + Lang._mapLegendCache[MapHelper.TileToLookup(82, 6)] = Lang._itemNameCache[2358]; + Lang._mapLegendCache[MapHelper.TileToLookup(83, 0)] = Lang._itemNameCache[313]; + Lang._mapLegendCache[MapHelper.TileToLookup(83, 1)] = Lang._itemNameCache[314]; + Lang._mapLegendCache[MapHelper.TileToLookup(83, 2)] = Lang._itemNameCache[315]; + Lang._mapLegendCache[MapHelper.TileToLookup(83, 3)] = Lang._itemNameCache[316]; + Lang._mapLegendCache[MapHelper.TileToLookup(83, 4)] = Lang._itemNameCache[317]; + Lang._mapLegendCache[MapHelper.TileToLookup(83, 5)] = Lang._itemNameCache[318]; + Lang._mapLegendCache[MapHelper.TileToLookup(83, 6)] = Lang._itemNameCache[2358]; + Lang._mapLegendCache[MapHelper.TileToLookup(84, 0)] = Lang._itemNameCache[313]; + Lang._mapLegendCache[MapHelper.TileToLookup(84, 1)] = Lang._itemNameCache[314]; + Lang._mapLegendCache[MapHelper.TileToLookup(84, 2)] = Lang._itemNameCache[315]; + Lang._mapLegendCache[MapHelper.TileToLookup(84, 3)] = Lang._itemNameCache[316]; + Lang._mapLegendCache[MapHelper.TileToLookup(84, 4)] = Lang._itemNameCache[317]; + Lang._mapLegendCache[MapHelper.TileToLookup(84, 5)] = Lang._itemNameCache[318]; + Lang._mapLegendCache[MapHelper.TileToLookup(84, 6)] = Lang._itemNameCache[2358]; + Lang._mapLegendCache[MapHelper.TileToLookup(85, 0)] = Lang._itemNameCache[321]; + Lang._mapLegendCache[MapHelper.TileToLookup(86, 0)] = Lang._itemNameCache[332]; + Lang._mapLegendCache[MapHelper.TileToLookup(87, 0)] = Lang._itemNameCache[333]; + Lang._mapLegendCache[MapHelper.TileToLookup(88, 0)] = Lang._itemNameCache[334]; + Lang._mapLegendCache[MapHelper.TileToLookup(89, 0)] = Lang._itemNameCache[335]; + Lang._mapLegendCache[MapHelper.TileToLookup(90, 0)] = Lang._itemNameCache[336]; + Lang._mapLegendCache[MapHelper.TileToLookup(91, 0)] = Language.GetText("MapObject.Banner"); + Lang._mapLegendCache[MapHelper.TileToLookup(92, 0)] = Lang._itemNameCache[341]; + Lang._mapLegendCache[MapHelper.TileToLookup(93, 0)] = Language.GetText("MapObject.FloorLamp"); + Lang._mapLegendCache[MapHelper.TileToLookup(94, 0)] = Lang._itemNameCache[352]; + Lang._mapLegendCache[MapHelper.TileToLookup(95, 0)] = Lang._itemNameCache[344]; + Lang._mapLegendCache[MapHelper.TileToLookup(96, 0)] = Lang._itemNameCache[345]; + Lang._mapLegendCache[MapHelper.TileToLookup(97, 0)] = Lang._itemNameCache[346]; + Lang._mapLegendCache[MapHelper.TileToLookup(98, 0)] = Lang._itemNameCache[347]; + Lang._mapLegendCache[MapHelper.TileToLookup(100, 0)] = Lang._itemNameCache[349]; + Lang._mapLegendCache[MapHelper.TileToLookup(101, 0)] = Lang._itemNameCache[354]; + Lang._mapLegendCache[MapHelper.TileToLookup(102, 0)] = Lang._itemNameCache[355]; + Lang._mapLegendCache[MapHelper.TileToLookup(103, 0)] = Lang._itemNameCache[356]; + Lang._mapLegendCache[MapHelper.TileToLookup(104, 0)] = Lang._itemNameCache[359]; + Lang._mapLegendCache[MapHelper.TileToLookup(105, 0)] = Language.GetText("MapObject.Statue"); + Lang._mapLegendCache[MapHelper.TileToLookup(105, 2)] = Language.GetText("MapObject.Vase"); + Lang._mapLegendCache[MapHelper.TileToLookup(106, 0)] = Lang._itemNameCache[363]; + Lang._mapLegendCache[MapHelper.TileToLookup(107, 0)] = Language.GetText("MapObject.Cobalt"); + Lang._mapLegendCache[MapHelper.TileToLookup(108, 0)] = Language.GetText("MapObject.Mythril"); + Lang._mapLegendCache[MapHelper.TileToLookup(111, 0)] = Language.GetText("MapObject.Adamantite"); + Lang._mapLegendCache[MapHelper.TileToLookup(114, 0)] = Lang._itemNameCache[398]; + Lang._mapLegendCache[MapHelper.TileToLookup(125, 0)] = Lang._itemNameCache[487]; + Lang._mapLegendCache[MapHelper.TileToLookup(128, 0)] = Lang._itemNameCache[498]; + Lang._mapLegendCache[MapHelper.TileToLookup(129, 0)] = Lang._itemNameCache[502]; + Lang._mapLegendCache[MapHelper.TileToLookup(132, 0)] = Lang._itemNameCache[513]; + Lang._mapLegendCache[MapHelper.TileToLookup(411, 0)] = Lang._itemNameCache[3545]; + Lang._mapLegendCache[MapHelper.TileToLookup(133, 0)] = Lang._itemNameCache[524]; + Lang._mapLegendCache[MapHelper.TileToLookup(133, 1)] = Lang._itemNameCache[1221]; + Lang._mapLegendCache[MapHelper.TileToLookup(134, 0)] = Lang._itemNameCache[525]; + Lang._mapLegendCache[MapHelper.TileToLookup(134, 1)] = Lang._itemNameCache[1220]; + Lang._mapLegendCache[MapHelper.TileToLookup(136, 0)] = Lang._itemNameCache[538]; + Lang._mapLegendCache[MapHelper.TileToLookup(137, 0)] = Language.GetText("MapObject.Trap"); + Lang._mapLegendCache[MapHelper.TileToLookup(138, 0)] = Lang._itemNameCache[540]; + Lang._mapLegendCache[MapHelper.TileToLookup(139, 0)] = Lang._itemNameCache[576]; + Lang._mapLegendCache[MapHelper.TileToLookup(142, 0)] = Lang._itemNameCache[581]; + Lang._mapLegendCache[MapHelper.TileToLookup(143, 0)] = Lang._itemNameCache[582]; + Lang._mapLegendCache[MapHelper.TileToLookup(144, 0)] = Language.GetText("MapObject.Timer"); + Lang._mapLegendCache[MapHelper.TileToLookup(149, 0)] = Language.GetText("MapObject.ChristmasLight"); + Lang._mapLegendCache[MapHelper.TileToLookup(166, 0)] = Language.GetText("MapObject.Tin"); + Lang._mapLegendCache[MapHelper.TileToLookup(167, 0)] = Language.GetText("MapObject.Lead"); + Lang._mapLegendCache[MapHelper.TileToLookup(168, 0)] = Language.GetText("MapObject.Tungsten"); + Lang._mapLegendCache[MapHelper.TileToLookup(169, 0)] = Language.GetText("MapObject.Platinum"); + Lang._mapLegendCache[MapHelper.TileToLookup(170, 0)] = Language.GetText("MapObject.PineTree"); + Lang._mapLegendCache[MapHelper.TileToLookup(171, 0)] = Lang._itemNameCache[1873]; + Lang._mapLegendCache[MapHelper.TileToLookup(172, 0)] = Language.GetText("MapObject.Sink"); + Lang._mapLegendCache[MapHelper.TileToLookup(173, 0)] = Lang._itemNameCache[349]; + Lang._mapLegendCache[MapHelper.TileToLookup(174, 0)] = Lang._itemNameCache[713]; + Lang._mapLegendCache[MapHelper.TileToLookup(178, 0)] = Lang._itemNameCache[181]; + Lang._mapLegendCache[MapHelper.TileToLookup(178, 1)] = Lang._itemNameCache[180]; + Lang._mapLegendCache[MapHelper.TileToLookup(178, 2)] = Lang._itemNameCache[177]; + Lang._mapLegendCache[MapHelper.TileToLookup(178, 3)] = Lang._itemNameCache[179]; + Lang._mapLegendCache[MapHelper.TileToLookup(178, 4)] = Lang._itemNameCache[178]; + Lang._mapLegendCache[MapHelper.TileToLookup(178, 5)] = Lang._itemNameCache[182]; + Lang._mapLegendCache[MapHelper.TileToLookup(178, 6)] = Lang._itemNameCache[999]; + Lang._mapLegendCache[MapHelper.TileToLookup(191, 0)] = Language.GetText("MapObject.LivingWood"); + Lang._mapLegendCache[MapHelper.TileToLookup(204, 0)] = Language.GetText("MapObject.Crimtane"); + Lang._mapLegendCache[MapHelper.TileToLookup(207, 0)] = Language.GetText("MapObject.WaterFountain"); + Lang._mapLegendCache[MapHelper.TileToLookup(209, 0)] = Lang._itemNameCache[928]; + Lang._mapLegendCache[MapHelper.TileToLookup(211, 0)] = Language.GetText("MapObject.Chlorophyte"); + Lang._mapLegendCache[MapHelper.TileToLookup(212, 0)] = Language.GetText("MapObject.Turret"); + Lang._mapLegendCache[MapHelper.TileToLookup(213, 0)] = Lang._itemNameCache[965]; + Lang._mapLegendCache[MapHelper.TileToLookup(214, 0)] = Lang._itemNameCache[85]; + Lang._mapLegendCache[MapHelper.TileToLookup(215, 0)] = Lang._itemNameCache[966]; + Lang._mapLegendCache[MapHelper.TileToLookup(216, 0)] = Language.GetText("MapObject.Rocket"); + Lang._mapLegendCache[MapHelper.TileToLookup(217, 0)] = Lang._itemNameCache[995]; + Lang._mapLegendCache[MapHelper.TileToLookup(218, 0)] = Lang._itemNameCache[996]; + Lang._mapLegendCache[MapHelper.TileToLookup(219, 0)] = Language.GetText("MapObject.SiltExtractinator"); + Lang._mapLegendCache[MapHelper.TileToLookup(220, 0)] = Lang._itemNameCache[998]; + Lang._mapLegendCache[MapHelper.TileToLookup(221, 0)] = Language.GetText("MapObject.Palladium"); + Lang._mapLegendCache[MapHelper.TileToLookup(222, 0)] = Language.GetText("MapObject.Orichalcum"); + Lang._mapLegendCache[MapHelper.TileToLookup(223, 0)] = Language.GetText("MapObject.Titanium"); + Lang._mapLegendCache[MapHelper.TileToLookup(227, 0)] = Lang._itemNameCache[1107]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 1)] = Lang._itemNameCache[1108]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 2)] = Lang._itemNameCache[1109]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 3)] = Lang._itemNameCache[1110]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 4)] = Lang._itemNameCache[1111]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 5)] = Lang._itemNameCache[1112]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 6)] = Lang._itemNameCache[1113]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 7)] = Lang._itemNameCache[1114]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 8)] = Lang._itemNameCache[3385]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 9)] = Lang._itemNameCache[3386]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 10)] = Lang._itemNameCache[3387]; + Lang._mapLegendCache[MapHelper.TileToLookup(227, 11)] = Lang._itemNameCache[3388]; + Lang._mapLegendCache[MapHelper.TileToLookup(228, 0)] = Lang._itemNameCache[1120]; + Lang._mapLegendCache[MapHelper.TileToLookup(231, 0)] = Language.GetText("MapObject.Larva"); + Lang._mapLegendCache[MapHelper.TileToLookup(232, 0)] = Lang._itemNameCache[1150]; + Lang._mapLegendCache[MapHelper.TileToLookup(235, 0)] = Lang._itemNameCache[1263]; + Lang._mapLegendCache[MapHelper.TileToLookup(236, 0)] = Lang._itemNameCache[1291]; + Lang._mapLegendCache[MapHelper.TileToLookup(237, 0)] = Lang._itemNameCache[1292]; + Lang._mapLegendCache[MapHelper.TileToLookup(238, 0)] = Language.GetText("MapObject.PlanterasBulb"); + Lang._mapLegendCache[MapHelper.TileToLookup(239, 0)] = Language.GetText("MapObject.MetalBar"); + Lang._mapLegendCache[MapHelper.TileToLookup(240, 0)] = Language.GetText("MapObject.Trophy"); + Lang._mapLegendCache[MapHelper.TileToLookup(240, 2)] = Lang._npcNameCache[21]; + Lang._mapLegendCache[MapHelper.TileToLookup(240, 3)] = Language.GetText("MapObject.ItemRack"); + Lang._mapLegendCache[MapHelper.TileToLookup(240, 4)] = Lang._itemNameCache[2442]; + Lang._mapLegendCache[MapHelper.TileToLookup(241, 0)] = Lang._itemNameCache[1417]; + Lang._mapLegendCache[MapHelper.TileToLookup(242, 0)] = Language.GetText("MapObject.Painting"); + Lang._mapLegendCache[MapHelper.TileToLookup(242, 1)] = Language.GetText("MapObject.AnimalSkin"); + Lang._mapLegendCache[MapHelper.TileToLookup(243, 0)] = Lang._itemNameCache[1430]; + Lang._mapLegendCache[MapHelper.TileToLookup(244, 0)] = Lang._itemNameCache[1449]; + Lang._mapLegendCache[MapHelper.TileToLookup(245, 0)] = Language.GetText("MapObject.Picture"); + Lang._mapLegendCache[MapHelper.TileToLookup(246, 0)] = Language.GetText("MapObject.Picture"); + Lang._mapLegendCache[MapHelper.TileToLookup(247, 0)] = Lang._itemNameCache[1551]; + Lang._mapLegendCache[MapHelper.TileToLookup(254, 0)] = Lang._itemNameCache[1725]; + Lang._mapLegendCache[MapHelper.TileToLookup(269, 0)] = Lang._itemNameCache[1989]; + Lang._mapLegendCache[MapHelper.TileToLookup(270, 0)] = Lang._itemNameCache[1993]; + Lang._mapLegendCache[MapHelper.TileToLookup(271, 0)] = Lang._itemNameCache[2005]; + Lang._mapLegendCache[MapHelper.TileToLookup(275, 0)] = Lang._itemNameCache[2162]; + Lang._mapLegendCache[MapHelper.TileToLookup(276, 0)] = Lang._itemNameCache[2163]; + Lang._mapLegendCache[MapHelper.TileToLookup(277, 0)] = Lang._itemNameCache[2164]; + Lang._mapLegendCache[MapHelper.TileToLookup(278, 0)] = Lang._itemNameCache[2165]; + Lang._mapLegendCache[MapHelper.TileToLookup(279, 0)] = Lang._itemNameCache[2166]; + Lang._mapLegendCache[MapHelper.TileToLookup(280, 0)] = Lang._itemNameCache[2167]; + Lang._mapLegendCache[MapHelper.TileToLookup(281, 0)] = Lang._itemNameCache[2168]; + Lang._mapLegendCache[MapHelper.TileToLookup(282, 0)] = Lang._itemNameCache[250]; + Lang._mapLegendCache[MapHelper.TileToLookup(413, 0)] = Language.GetText("MapObject.OrangeSquirrelCage"); + Lang._mapLegendCache[MapHelper.TileToLookup(283, 0)] = Lang._itemNameCache[2172]; + Lang._mapLegendCache[MapHelper.TileToLookup(285, 0)] = Lang._itemNameCache[2174]; + Lang._mapLegendCache[MapHelper.TileToLookup(286, 0)] = Lang._itemNameCache[2175]; + Lang._mapLegendCache[MapHelper.TileToLookup(287, 0)] = Lang._itemNameCache[2177]; + Lang._mapLegendCache[MapHelper.TileToLookup(288, 0)] = Lang._itemNameCache[2178]; + Lang._mapLegendCache[MapHelper.TileToLookup(289, 0)] = Lang._itemNameCache[2179]; + Lang._mapLegendCache[MapHelper.TileToLookup(290, 0)] = Lang._itemNameCache[2180]; + Lang._mapLegendCache[MapHelper.TileToLookup(291, 0)] = Lang._itemNameCache[2181]; + Lang._mapLegendCache[MapHelper.TileToLookup(292, 0)] = Lang._itemNameCache[2182]; + Lang._mapLegendCache[MapHelper.TileToLookup(293, 0)] = Lang._itemNameCache[2183]; + Lang._mapLegendCache[MapHelper.TileToLookup(294, 0)] = Lang._itemNameCache[2184]; + Lang._mapLegendCache[MapHelper.TileToLookup(295, 0)] = Lang._itemNameCache[2185]; + Lang._mapLegendCache[MapHelper.TileToLookup(296, 0)] = Lang._itemNameCache[2186]; + Lang._mapLegendCache[MapHelper.TileToLookup(297, 0)] = Lang._itemNameCache[2187]; + Lang._mapLegendCache[MapHelper.TileToLookup(298, 0)] = Lang._itemNameCache[2190]; + Lang._mapLegendCache[MapHelper.TileToLookup(299, 0)] = Lang._itemNameCache[2191]; + Lang._mapLegendCache[MapHelper.TileToLookup(300, 0)] = Lang._itemNameCache[2192]; + Lang._mapLegendCache[MapHelper.TileToLookup(301, 0)] = Lang._itemNameCache[2193]; + Lang._mapLegendCache[MapHelper.TileToLookup(302, 0)] = Lang._itemNameCache[2194]; + Lang._mapLegendCache[MapHelper.TileToLookup(303, 0)] = Lang._itemNameCache[2195]; + Lang._mapLegendCache[MapHelper.TileToLookup(304, 0)] = Lang._itemNameCache[2196]; + Lang._mapLegendCache[MapHelper.TileToLookup(305, 0)] = Lang._itemNameCache[2197]; + Lang._mapLegendCache[MapHelper.TileToLookup(306, 0)] = Lang._itemNameCache[2198]; + Lang._mapLegendCache[MapHelper.TileToLookup(307, 0)] = Lang._itemNameCache[2203]; + Lang._mapLegendCache[MapHelper.TileToLookup(308, 0)] = Lang._itemNameCache[2204]; + Lang._mapLegendCache[MapHelper.TileToLookup(309, 0)] = Lang._itemNameCache[2206]; + Lang._mapLegendCache[MapHelper.TileToLookup(310, 0)] = Lang._itemNameCache[2207]; + Lang._mapLegendCache[MapHelper.TileToLookup(316, 0)] = Lang._itemNameCache[2439]; + Lang._mapLegendCache[MapHelper.TileToLookup(317, 0)] = Lang._itemNameCache[2440]; + Lang._mapLegendCache[MapHelper.TileToLookup(318, 0)] = Lang._itemNameCache[2441]; + Lang._mapLegendCache[MapHelper.TileToLookup(319, 0)] = Lang._itemNameCache[2490]; + Lang._mapLegendCache[MapHelper.TileToLookup(320, 0)] = Lang._itemNameCache[2496]; + Lang._mapLegendCache[MapHelper.TileToLookup(323, 0)] = Language.GetText("MapObject.PalmTree"); + Lang._mapLegendCache[MapHelper.TileToLookup(314, 0)] = Lang._itemNameCache[2340]; + Lang._mapLegendCache[MapHelper.TileToLookup(353, 0)] = Lang._itemNameCache[2996]; + Lang._mapLegendCache[MapHelper.TileToLookup(354, 0)] = Lang._itemNameCache[2999]; + Lang._mapLegendCache[MapHelper.TileToLookup(355, 0)] = Lang._itemNameCache[3000]; + Lang._mapLegendCache[MapHelper.TileToLookup(356, 0)] = Lang._itemNameCache[3064]; + Lang._mapLegendCache[MapHelper.TileToLookup(365, 0)] = Lang._itemNameCache[3077]; + Lang._mapLegendCache[MapHelper.TileToLookup(366, 0)] = Lang._itemNameCache[3078]; + Lang._mapLegendCache[MapHelper.TileToLookup(373, 0)] = Language.GetText("MapObject.DrippingWater"); + Lang._mapLegendCache[MapHelper.TileToLookup(374, 0)] = Language.GetText("MapObject.DrippingLava"); + Lang._mapLegendCache[MapHelper.TileToLookup(375, 0)] = Language.GetText("MapObject.DrippingHoney"); + Lang._mapLegendCache[MapHelper.TileToLookup(377, 0)] = Lang._itemNameCache[3198]; + Lang._mapLegendCache[MapHelper.TileToLookup(372, 0)] = Lang._itemNameCache[3117]; + } + + private void FindRecipe(CommandArgs args) + { + + // 检查玩家是否具有“查合成表”权限 + if (!args.Player.HasPermission("RecipesBrowser")) + { + // 如果没有权限,则向该玩家发送错误消息并退出方法 + args.Player.SendErrorMessage("你没有使用合成表指令的权限"); + return; + } + + List itemByIdOrName = TShock.Utils.GetItemByIdOrName(args.Parameters[0]); + if (itemByIdOrName.Count > 1) + { + args.Player.SendInfoMessage("检索到多个物品匹配"); + string text = ""; + for (int i = 0; i < itemByIdOrName.Count; i++) + { + text = text + Lang.GetItemNameValue(itemByIdOrName[i].type) + ","; + if ((i + 1) % 5 == 0) + { + text += "\n"; + } + } + text = text.Trim(','); + args.Player.SendInfoMessage(text); + return; + } + if (itemByIdOrName.Count < 1) + { + args.Player.SendErrorMessage("未找到物品"); + return; + } + Item item = itemByIdOrName[0]; + string text2 = ((args.Parameters.Count > 1) ? args.Parameters[1] : "c"); + switch (text2.ToLower()) + { + case "r": + args.Player.SendInfoMessage(GetRecipeStringByRequired(item)); + return; + } + List list = Main.recipe.ToList().FindAll((Recipe r) => r.createItem.type == item.type); + string text3 = $"物品:{Lang.GetItemNameValue(item.type)}[i:{item.type}]\n"; + if (list.Count > 1) + { + for (int j = 0; j < list.Count; j++) + { + text3 += $"配方{j + 1}:\n"; + text3 += GetRecipeStringByResult(list[j]); + } + } + else + { + if (list.Count != 1) + { + args.Player.SendErrorMessage("此物品无配方"); + return; + } + text3 += GetRecipeStringByResult(list[0]); + } + args.Player.SendInfoMessage(text3); + } + + //释放 + protected override void Dispose(bool disposing) + { + if (disposing) + { + MapHelper.Initialize(); + BuildMapAtlas(); + } + base.Dispose(disposing); + } + + private string GetRecipeStringByResult(Recipe recipe) + { + string text = "材料:"; + foreach (Item item in recipe.requiredItem.Where((Item r) => r.stack > 0)) + { + text += string.Format("{0}{1}[i/s{2}:{3}] ", Lang.GetItemNameValue(item.type), (item.maxStack == 1 || item.stack == 0) ? "" : ("*" + item.stack), item.stack, item.type); + } + text += "\n"; + text += "需要的合成站:"; + foreach (int item2 in recipe.requiredTile.Where((int i) => i >= 0)) + { + text += $"{Lang._mapLegendCache[MapHelper.tileLookup[item2]]} "; + } + if (text.Last() == ':') + { + text += "徒手"; + } + text += "\n"; + if (recipe.needHoney) + { + text += "需要蜂蜜\n"; + } + if (recipe.needWater) + { + text += "需要水\n"; + } + if (recipe.needLava) + { + text += "需要岩浆\n"; + } + return text + "\n"; + } + + private string GetRecipeStringByRequired(Item item) + { + string text = "可合成的物品:\n"; + IEnumerable source = from r in Main.recipe + where r.requiredItem.Select((Item i) => i.type).Contains(item.type) + select r.createItem; + for (int j = 1; j <= source.Count(); j++) + { + Item val = source.ElementAt(j - 1); + text += string.Format("{0}{1}[i/s{2}:{3}],{4}", Lang.GetItemNameValue(val.type), (val.maxStack > 1) ? ("*" + val.stack) : "", val.stack, val.type, (j % 5 == 0) ? "\n" : ""); + } + text.Trim(','); + return text + "\n"; + } +} diff --git a/RecipesBrowser/RecipesBrowser.csproj b/RecipesBrowser/RecipesBrowser.csproj new file mode 100644 index 00000000..04c81dc3 --- /dev/null +++ b/RecipesBrowser/RecipesBrowser.csproj @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/TownNPCHomes/README.md b/TownNPCHomes/README.md new file mode 100644 index 00000000..45f1f8df --- /dev/null +++ b/TownNPCHomes/README.md @@ -0,0 +1,28 @@ +# 插件名 + +- 作者: 棱镜、羽学 +- 出处: [github](https://www.bbstr.net/r/72/) +- 这是一个Tshock服务器插件主要用于NPC能在允许回家的情况下快速回家, +- 如果搭配西江小子的VBY.GameContentModify插件可以不输入指令直接分配房屋即可传送NPC回家。 +- VBY.GameContentModify:[github](https://github.com/xuyuwtu/MyPlugin/tree/master/src/VBY) +## 更新日志 + +``` +- 1.1.0 +- 修复了原插件分配住所后城镇NPC会人间蒸发的BUG +- 现在即使不输入命令只要npc处于晚上或雨天都会秒传回家 +``` +## 指令 + +| 语法 | 权限 | 说明 | +| -------------- | :-----------------: | :------: | +| /npchome | tshock.world.movenpc | 该权限为原版Tshock分配NPC入住权限| + +## 配置 + +```json +暂无 +``` +## 反馈 +- 共同维护的插件库:https://github.com/THEXN/TShockPlugin/ +- 国内社区trhub.cn 或 TShock官方群等 \ No newline at end of file diff --git a/TownNPCHomes/TownNPCHomes.cs b/TownNPCHomes/TownNPCHomes.cs new file mode 100644 index 00000000..779337e4 --- /dev/null +++ b/TownNPCHomes/TownNPCHomes.cs @@ -0,0 +1,104 @@ +using System.Collections.Concurrent; +using System.Reflection; +using Microsoft.Xna.Framework; +using Terraria; +using TerrariaApi.Server; +using TShockAPI; +using static TShockAPI.GetDataHandlers; + +[ApiVersion(2, 1)] +public class MainPlugin : TerrariaPlugin +{ + public override string Author => "棱镜 羽学优化"; + + public override string Name => "TownNPCHomes"; + + public override Version Version => new Version(1, 1, 0); + + private ConcurrentDictionary npcHomePositions = new ConcurrentDictionary(); + + public MainPlugin(Main game) + : base(game) + { + } + + public override void Initialize() + { + Commands.ChatCommands.Add(new Command("tshock.world.movenpc", new CommandDelegate(TeleportNpcToTheirHomesCmd), new string[1] { "npchome" })); + NPCHome += OnNpcHome; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + NPCHome -= OnNpcHome; + } + base.Dispose(disposing); + } + + private void TeleportNpcToTheirHomesCmd(CommandArgs args) + { + ConcurrentBag npcIdsToUpdate = new ConcurrentBag(); + NPC[] npc = Main.npc; + foreach (NPC val in npc) + { + if (val != null && !(!val.active || !val.townNPC) && !val.homeless) + { + Vector2 position = new Vector2((float)(val.homeTileX * 16), (float)(val.homeTileY * 16)); + npcHomePositions.TryAdd(val.whoAmI, position); + npcIdsToUpdate.Add(val.whoAmI); + } + } + + foreach (int id in npcIdsToUpdate) + { + Vector2 position; + if (npcHomePositions.TryGetValue(id, out position)) + { + TrySendNPCHomePosition(id, position); + } + } + + args.Player.SendSuccessMessage("已将所有城镇npc传送回家!"); + } + + private void TrySendNPCHomePosition(int npcId, Vector2 position) + { + try + { + TSPlayer.All.SendData((PacketTypes)23, "", npcId, position.X, position.Y, 0f, 0); + } + catch (Exception ex) + { + TShock.Log.ConsoleError(ex.Message); + Console.WriteLine($"发送NPC ({npcId}) 回家位置数据包时发生错误: {ex.Message}"); + + } + } + + private void OnNpcHome(object sender, NPCHomeChangeEventArgs args) + { + try + { + NPC val = Main.npc[args.ID]; + + if (val == null) + { + args.Player.SendSuccessMessage($"无法找到ID为{args.ID}的NPC,无法为其设置家的位置。"); + return; + } + + if (!val.homeless && !val.Bottom.Equals(new Vector2((float)args.X * 16, (float)args.Y * 16))) + { + val.Bottom = new Vector2((float)args.X * 16, (float)args.Y * 16); + TrySendNPCHomePosition(args.ID, new Vector2((float)args.X * 16, (float)args.Y * 16)); + } + } + catch (Exception ex) + { + TShock.Log.ConsoleError(ex.Message); + args.Player.SendSuccessMessage($"处理NPC回家事件时发生错误: {ex.Message}"); + } + } +} diff --git a/TownNPCHomes/TownNPCHomes.csproj b/TownNPCHomes/TownNPCHomes.csproj new file mode 100644 index 00000000..04c81dc3 --- /dev/null +++ b/TownNPCHomes/TownNPCHomes.csproj @@ -0,0 +1,3 @@ + + + \ No newline at end of file