Skip to content
This repository was archived by the owner on Nov 7, 2023. It is now read-only.

Commit b1ccfbd

Browse files
committed
feat(/scanchests): add /scanchests command to search chests for items and /tpchest to teleport to them
Closes #57 Closes #55
1 parent 9464186 commit b1ccfbd

File tree

4 files changed

+173
-1
lines changed

4 files changed

+173
-1
lines changed

Implementation/ChestManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ public void DestroyChest(DPoint anyTileLocation) {
414414

415415
public IEnumerable<IChest> EnumerateAllChests() {
416416
for (int i = 0; i < Main.chest.Length; i++) {
417-
if (Main.chest[i] != null)
417+
if (Main.chest[i] != null && i != ChestManager.DummyChestIndex)
418418
yield return new ChestAdapter(i, Main.chest[i]);
419419
}
420420

Implementation/ProtectionManager.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ public ProtectionManager(
5757
this.WorldMetadata = worldMetadata;
5858
}
5959

60+
public ProtectionEntry GetProtectionAt(DPoint tileLocation) {
61+
foreach (ProtectionEntry protection in this.EnumerateProtectionEntries(tileLocation))
62+
return protection;
63+
64+
return null;
65+
}
66+
6067
public IEnumerable<ProtectionEntry> EnumerateProtectionEntries(DPoint tileLocation) {
6168
ITile tile = TerrariaUtils.Tiles[tileLocation];
6269
if (!tile.active())

Implementation/ProtectorPlugin.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class ProtectorPlugin: TerrariaPlugin, IDisposable {
3838
public const string BankChestShare_Permission = "prot.bankchestshare";
3939
public const string NoBankChestLimits_Permission = "prot.nobankchestlimits";
4040
public const string FreeTradeChests_Permission = "prot.freetradechests";
41+
public const string ScanChests_Permission = "prot.scanchests";
4142
public const string Utility_Permission = "prot.utility";
4243
public const string Cfg_Permission = "prot.cfg";
4344

Implementation/UserInteractionHandler.cs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Collections.ObjectModel;
45
using System.Diagnostics.Contracts;
56
using System.Globalization;
67
using System.IO;
78
using System.Linq;
9+
using System.Runtime.CompilerServices;
810
using System.Text;
911
using Microsoft.Xna.Framework;
1012
using OTAPI.Tile;
@@ -15,6 +17,7 @@
1517
using Terraria.Plugins.Common;
1618
using Terraria.Plugins.Common.Collections;
1719
using TShockAPI;
20+
using TShockAPI.DB;
1821

1922
namespace Terraria.Plugins.CoderCow.Protector {
2023
public class UserInteractionHandler: UserInteractionHandlerBase, IDisposable {
@@ -134,6 +137,15 @@ public UserInteractionHandler(
134137
this.TradeChestCommand_Exec, this.TradeChestCommand_HelpCallback, ProtectorPlugin.SetTradeChests_Permission,
135138
allowServer: false
136139
);
140+
base.RegisterCommand(
141+
new[] { "scanchests" },
142+
this.ScanChestsCommand_Exec, this.ScanChestsCommand_HelpCallback, ProtectorPlugin.ScanChests_Permission
143+
);
144+
base.RegisterCommand(
145+
new[] { "tpchest" },
146+
this.TpChestCommand_Exec, this.TpChestCommand_HelpCallback, ProtectorPlugin.ScanChests_Permission,
147+
allowServer: false
148+
);
137149
#endregion
138150

139151
#if DEBUG
@@ -2080,6 +2092,158 @@ private bool TradeChestCommand_HelpCallback(CommandArgs args) {
20802092
}
20812093
#endregion
20822094

2095+
private readonly ConditionalWeakTable<TSPlayer, DPoint[]> scanChestsResults = new ConditionalWeakTable<TSPlayer, DPoint[]>();
2096+
#region [Command Handling /scanchests]
2097+
private void ScanChestsCommand_Exec(CommandArgs args) {
2098+
if (args == null || this.IsDisposed)
2099+
return;
2100+
2101+
if (args.Parameters.Count == 0) {
2102+
args.Player.SendErrorMessage("Proper syntax: /scanchests <item name> [<page>]");
2103+
args.Player.SendInfoMessage("Type /scanchests help to get more information about this command.");
2104+
return;
2105+
}
2106+
2107+
string itemNamePart;
2108+
int pageNumber = 1;
2109+
if (args.Parameters.Count == 1) {
2110+
itemNamePart = args.Parameters[0];
2111+
} else {
2112+
string lastParam = args.Parameters[args.Parameters.Count - 1];
2113+
if (lastParam.Length <= 2 && int.TryParse(lastParam, out pageNumber))
2114+
itemNamePart = args.ParamsToSingleString(0, 1);
2115+
else
2116+
itemNamePart = args.ParamsToSingleString();
2117+
2118+
if (pageNumber < 1) {
2119+
args.Player.SendErrorMessage($"\"{lastParam}\" is not a valid page number.");
2120+
return;
2121+
}
2122+
}
2123+
2124+
List<Item> itemsToLookup = TShock.Utils.GetItemByIdOrName(itemNamePart);
2125+
if (itemsToLookup.Count == 0) {
2126+
args.Player.SendErrorMessage($"Unable to guess a valid item type from \"{itemNamePart}\".");
2127+
return;
2128+
}
2129+
2130+
// DPoint is the chest location.
2131+
List<Tuple<ItemData[], DPoint>> results = new List<Tuple<ItemData[], DPoint>>();
2132+
foreach (IChest chest in this.ChestManager.EnumerateAllChests()) {
2133+
List<ItemData> matchingItems = new List<ItemData>(
2134+
from item in chest.Items
2135+
where itemsToLookup.Any(li => li.netID == item.Type)
2136+
select item);
2137+
2138+
if (matchingItems.Count > 0)
2139+
results.Add(new Tuple<ItemData[], DPoint>(matchingItems.ToArray(), chest.Location));
2140+
}
2141+
2142+
DPoint[] resultsChestLocations = results.Select(r => r.Item2).ToArray();
2143+
this.scanChestsResults.Remove(args.Player);
2144+
this.scanChestsResults.Add(args.Player, resultsChestLocations);
2145+
2146+
PaginationTools.SendPage(args.Player, pageNumber, results, new PaginationTools.Settings {
2147+
HeaderFormat = $"The Following Chests Contain \"{itemNamePart}\" (Page {{0}} of {{1}})",
2148+
NothingToDisplayString = $"No chest contains items matching \"{itemNamePart}\"",
2149+
LineTextColor = Color.LightGray,
2150+
MaxLinesPerPage = 10,
2151+
LineFormatter = (lineData, dataIndex, pageNumberLocal) => {
2152+
var result = (lineData as Tuple<ItemData[], DPoint>);
2153+
if (result == null)
2154+
return null;
2155+
2156+
ItemData[] foundItems = result.Item1;
2157+
DPoint chestLocation = result.Item2;
2158+
2159+
string foundItemsString = string.Join(" ", foundItems.Select(i => TShock.Utils.ItemTag(i.ToItem())));
2160+
2161+
string chestOwner = "{not protected}";
2162+
ProtectionEntry protection = this.ProtectionManager.GetProtectionAt(chestLocation);
2163+
if (protection != null) {
2164+
User tsUser = TShock.Users.GetUserByID(protection.Owner);
2165+
chestOwner = tsUser?.Name ?? $"{{user id: {protection.Owner}}}";
2166+
}
2167+
2168+
return new Tuple<string,Color>($"{dataIndex}. Chest owned by {TShock.Utils.ColorTag(chestOwner, Color.Red)} contains {foundItemsString}", Color.LightGray);
2169+
}
2170+
});
2171+
2172+
if (results.Count > 0)
2173+
args.Player.SendSuccessMessage("Type /tpchest <result index> to teleport to the respective chest.");
2174+
}
2175+
2176+
private bool ScanChestsCommand_HelpCallback(CommandArgs args) {
2177+
if (args == null || this.IsDisposed)
2178+
return false;
2179+
2180+
int pageNumber;
2181+
if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
2182+
return false;
2183+
2184+
switch (pageNumber) {
2185+
default:
2186+
args.Player.SendMessage("Command reference for /scanchests (Page 1 of 1)", Color.Lime);
2187+
args.Player.SendMessage("/scanchests <item name> [page]", Color.White);
2188+
args.Player.SendMessage("Searches all chests in the current world for items matching the given name. The user will be able to teleport to the chests found by this command using /tpchest.", Color.LightGray);
2189+
args.Player.SendMessage(string.Empty, Color.LightGray);
2190+
args.Player.SendMessage("item name = Part of name of the item(s) to check for.", Color.LightGray);
2191+
break;
2192+
}
2193+
2194+
return true;
2195+
}
2196+
#endregion
2197+
2198+
#region [Command Handling /tpchest]
2199+
private void TpChestCommand_Exec(CommandArgs args) {
2200+
if (args == null || this.IsDisposed)
2201+
return;
2202+
2203+
if (args.Parameters.Count != 1) {
2204+
args.Player.SendErrorMessage("Proper syntax: /tpchest <result index>");
2205+
args.Player.SendInfoMessage("Type /tpchest help to get more information about this command.");
2206+
return;
2207+
}
2208+
2209+
DPoint[] chestLocations;
2210+
if (!this.scanChestsResults.TryGetValue(args.Player, out chestLocations)) {
2211+
args.Player.SendErrorMessage("You have to use /scanchests before using this command.");
2212+
return;
2213+
}
2214+
2215+
int chestIndex;
2216+
if (!int.TryParse(args.Parameters[0], out chestIndex) || chestIndex < 1 || chestIndex > chestLocations.Length) {
2217+
args.Player.SendErrorMessage($"\"{args.Parameters[0]}\" is not a valid result index.");
2218+
return;
2219+
}
2220+
2221+
DPoint chestLocation = chestLocations[chestIndex - 1];
2222+
args.Player.Teleport(chestLocation.X * 16, chestLocation.Y * 16);
2223+
}
2224+
2225+
private bool TpChestCommand_HelpCallback(CommandArgs args) {
2226+
if (args == null || this.IsDisposed)
2227+
return false;
2228+
2229+
int pageNumber;
2230+
if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
2231+
return false;
2232+
2233+
switch (pageNumber) {
2234+
default:
2235+
args.Player.SendMessage("Command reference for /tpchest (Page 1 of 1)", Color.Lime);
2236+
args.Player.SendMessage("/tpchest <result index>", Color.White);
2237+
args.Player.SendMessage("Teleports you to a chest that was found by the /scanchests command.", Color.LightGray);
2238+
args.Player.SendMessage(string.Empty, Color.LightGray);
2239+
args.Player.SendMessage("result index = The index of the search result.", Color.LightGray);
2240+
break;
2241+
}
2242+
2243+
return true;
2244+
}
2245+
#endregion
2246+
20832247
#region [Hook Handlers]
20842248
public override bool HandleTileEdit(TSPlayer player, TileEditType editType, int blockType, DPoint location, int objectStyle) {
20852249
return this.HandleTileEdit(player, editType, blockType, location, objectStyle, false);

0 commit comments

Comments
 (0)