-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathEntryInfo.cs
512 lines (434 loc) · 21.7 KB
/
EntryInfo.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.ModLoader;
using Terraria.Localization;
using Terraria.ID;
using Terraria.GameContent.ItemDropRules;
using Terraria.GameContent;
using ReLogic.Content;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using Microsoft.Xna.Framework;
using System.Text.RegularExpressions;
using Terraria.ModLoader.Config;
namespace BossChecklist
{
internal enum EntryType {
Boss,
MiniBoss,
Event
}
internal enum CollectibleType {
Relic,
MasterPet,
Trophy,
Mask,
Music,
Generic,
}
internal class EntryInfo {
// This localization-ignoring string is used for cross mod queries and networking. Each key is completely unique.
internal string Key { get; init; }
internal EntryType type;
internal string modSource;
internal LocalizedText name; // This should not be used for displaying purposes. Use 'EntryInfo.GetDisplayName' instead.
internal List<int> npcIDs;
internal Dictionary<int, LocalizedText> npcLimbs;
internal float progression;
internal Func<bool> downed;
internal Func<bool> available;
internal bool hidden;
internal Func<NPC, LocalizedText> customDespawnMessages;
internal List<string> relatedEntries;
internal List<int> spawnItem;
internal Func<LocalizedText> spawnInfo;
internal Dictionary<int, CollectibleType> collectibles;
internal List<DropRateInfo> loot;
internal List<int> lootItemTypes;
internal List<int> lootItemTypes_BagExclusives;
internal Asset<Texture2D> portraitTexture; // used for vanilla entry portrait drawing
internal Action<SpriteBatch, Rectangle, Color> customDrawing; // used for modded entry portrait drawing
internal Func<List<Asset<Texture2D>>> headIconTextures;
/*
internal ExpandoObject ConvertToExpandoObject() {
dynamic expando = new ExpandoObject();
expando.key = Key;
expando.modSource = modSource;
expando.internalName = internalName;
expando.displayName = name;
expando.progression = progression;
expando.downed = new Func<bool>(downed);
expando.isBoss = type.Equals(EntryType.Boss);
expando.isMiniboss = type.Equals(EntryType.MiniBoss);
expando.isEvent = type.Equals(EntryType.Event);
expando.npcIDs = new List<int>(npcIDs);
expando.spawnItem = new List<int>(spawnItem);
expando.loot = new List<int>(loot);
expando.collectibles = new List<int>(collectibles);
return expando;
}
*/
internal Dictionary<string, object> ConvertToDictionary(Version GetEntryInfoAPIVersion) {
// We may want to allow different returns based on api version.
//if (GetEntryInfoAPIVersion == new Version(1, 1)) {
var dict = new Dictionary<string, object> {
{ "key", Key },
{ "modSource", modSource },
{ "displayName", name },
{ "progression", progression },
{ "downed", new Func<bool>(downed) },
{ "isBoss", type.Equals(EntryType.Boss) },
{ "isMiniboss", type.Equals(EntryType.MiniBoss) },
{ "isEvent", type.Equals(EntryType.Event) },
{ "npcIDs", new List<int>(npcIDs) },
{ "spawnInfo", new Func<LocalizedText>(spawnInfo) },
{ "spawnItems", new List<int>(spawnItem) },
{ "treasureBag", TreasureBag },
{ "relic", Relic },
{ "dropRateInfo", new List<DropRateInfo>(loot) },
{ "loot", new List<int>(lootItemTypes) },
{ "collectibles", new List<int>(collectibles.Keys.ToList()) }
};
return dict;
}
internal string DisplayName => name.Value;
internal string DisplaySpawnInfo => spawnInfo().Value;
internal string ModDisplayName => ModLoader.TryGetMod(modSource, out Mod mod) ? BossUISystem.RemoveChatTags(mod) : modSource;
internal bool MarkedAsDowned => WorldAssist.MarkedEntries.Contains(this.Key);
internal bool IsAutoDownedOrMarked => (BossChecklist.BossLogConfig.AutomaticChecklist && downed()) || MarkedAsDowned;
internal bool IsUpNext => BossLogUI.FindNextEntry() == GetIndex;
internal int GetIndex => BossChecklist.bossTracker.SortedEntries.IndexOf(this);
internal int TreasureBag => loot.FirstOrDefault(drops => ItemID.Sets.BossBag[drops.itemId] && this.type != EntryType.Event).itemId;
internal int Relic => collectibles.FirstOrDefault(x => x.Value == CollectibleType.Relic).Key;
internal int ExpertItem => lootItemTypes_BagExclusives.FirstOrDefault(x => ContentSamples.ItemsByType[x].expert);
internal List<int> CollectibleDrops => lootItemTypes.Intersect(collectibles.Keys).ToList();
internal bool IsRecordIndexed(out int recordIndex) {
recordIndex = BossChecklist.bossTracker.BossRecordKeys.IndexOf(this.Key);
return BossChecklist.bossTracker.BossRecordKeys.Contains(this.Key);
}
/// <summary>
/// Determines what despawn message should be used based on client configuration and submitted entry data.
/// </summary>
/// <returns>A LocalizedText of the despawn message of the passed npc. Returns null if no message can be found.</returns>
internal LocalizedText GetDespawnMessage(NPC npc) {
// When unique despawn messages are enabled, pass the NPC for the custom message function provided by the entry
if (BossChecklist.FeatureConfig.DespawnMessageType == "Unique" && customDespawnMessages(npc) is LocalizedText message && Language.Exists(message.Key))
return message; // this will only return a unique message if the custom message function properly assigns one
// If the Unique message was empty/null or the player is using Generic despawn messages, try to find an appropriate despawn message to send
// Return a generic despawn message if any player is left alive or return a boss victory despawn message if all player's were killed
if (BossChecklist.FeatureConfig.DespawnMessageType != "Disabled")
return Language.GetText(Main.player.Any(plr => plr.active && !plr.dead) ? $"{NPCAssist.LangChat}.Despawn.Generic" : $"{NPCAssist.LangChat}.Loss.Generic");
return null; // The despawn message feature was disabled. Return an empty message.
}
/// <summary>
/// Handles the extra npc defeation messages related to boss limbs and towers.
/// These messages will not appear if the related configs are disabled.
/// </summary>
internal LocalizedText GetLimbMessage(NPC npc) {
if (BossChecklist.FeatureConfig.LimbMessages == "Unique" && npcLimbs[npc.type] is LocalizedText message)
return message;
if (BossChecklist.FeatureConfig.LimbMessages != "Disabled") {
string specialCase = (npc.type == NPCID.SkeletronHand || npc.type == NPCID.MoonLordHead) ? new NPCDefinition(npc.type).Name : "";
return Language.GetText($"{NPCAssist.LangChat}.Defeated.Generic" + specialCase);
}
return null;
}
/// <summary>
/// Determines whether or not the entry should be visible on the Table of Contents,
/// based on configurations and filter status.
/// </summary>
/// <returns>If the entry should be visible</returns>
internal bool VisibleOnChecklist() {
bool HideUnsupported = modSource == "Unknown" && BossChecklist.BossLogConfig.HideUnsupported; // entries not using the new mod calls for the Boss Log
bool HideUnavailable = !available() && !IsAutoDownedOrMarked && (BossChecklist.BossLogConfig.HideUnavailable || !BossUISystem.Instance.BossLog.HiddenEntriesMode); // entries that are labeled as not available
bool HideHidden = hidden && !BossUISystem.Instance.BossLog.HiddenEntriesMode; // entries that are labeled as hidden
bool SkipNonBosses = BossChecklist.BossLogConfig.OnlyShowBossContent && type != EntryType.Boss; // if the user has the config to only show bosses and the entry is not a boss
if (HideUnavailable || HideHidden || SkipNonBosses || HideUnsupported)
return false;
// Make sure the filters allow the entry to be visible
string bFilter = BossChecklist.BossLogConfig.FilterBosses;
string mbFilter = BossChecklist.BossLogConfig.FilterMiniBosses;
string eFilter = BossChecklist.BossLogConfig.FilterEvents;
bool FilterBoss = type == EntryType.Boss && bFilter == BossLogConfiguration.Option_HideWhenCompleted && IsAutoDownedOrMarked;
bool FilterMiniBoss = type == EntryType.MiniBoss && (mbFilter == BossLogConfiguration.Option_Hide || (mbFilter == BossLogConfiguration.Option_HideWhenCompleted && IsAutoDownedOrMarked));
bool FilterEvent = type == EntryType.Event && (eFilter == BossLogConfiguration.Option_Hide || (eFilter == BossLogConfiguration.Option_HideWhenCompleted && IsAutoDownedOrMarked));
if (FilterBoss || FilterMiniBoss || FilterEvent)
return false;
return true; // if it passes all the checks, it should be shown
}
internal bool VisibleOnPageContent() {
if (BossChecklist.BossLogConfig.ProgressiveChecklist && !IsAutoDownedOrMarked && !IsUpNext)
return false;
return VisibleOnChecklist();
}
internal EntryInfo(EntryType entryType, string modSource, string internalName, float progression, Func<bool> downed, List<int> npcIDs, Dictionary<string, object> extraData = null) {
// Add the mod source to the opted mods list of the credits page if its not already and add the entry type
if (modSource != "Terraria" && modSource != "Unknown") {
BossChecklist.bossTracker.RegisteredMods.TryAdd(modSource, new int[3]);
BossChecklist.bossTracker.RegisteredMods[modSource][(int)entryType]++;
}
// required entry data
this.Key = modSource + " " + internalName;
this.type = entryType;
this.modSource = modSource;
this.progression = progression;
this.downed = downed;
this.npcIDs = npcIDs ?? new List<int>();
// Localization checks
LocalizedText name = extraData?.ContainsKey("displayName") == true ? extraData["displayName"] as LocalizedText : null;
Func<LocalizedText> spawnInfo = null;
if (extraData?.ContainsKey("spawnInfo") == true) {
if (extraData["spawnInfo"] is Func<LocalizedText>) {
spawnInfo = extraData["spawnInfo"] as Func<LocalizedText>;
}
else if (extraData["spawnInfo"] is LocalizedText) {
spawnInfo = () => extraData["spawnInfo"] as LocalizedText;
}
}
if (name == null || spawnInfo == null) {
// Modded. Ensure that all nulls passed in autoregister a localization key.
if (type == EntryType.Event) {
name ??= Language.GetOrRegister($"Mods.{modSource}.BossChecklistIntegration.{internalName}.EntryName", () => Regex.Replace(internalName, "([A-Z])", " $1").Trim()); // Add spaces before each capital letter.
spawnInfo ??= () => Language.GetOrRegister($"Mods.{modSource}.BossChecklistIntegration.{internalName}.SpawnInfo", () => "Spawn conditions unknown");
}
else {
int primaryNPCID = npcIDs?.Count > 0 ? npcIDs[0] : 0;
if (ModContent.GetModNPC(primaryNPCID) is ModNPC modNPC) {
string prefix = modNPC.GetLocalizationKey("BossChecklistIntegration");
// For single NPC bosses, assume EntryName is DisplayName rather than registering a localization key.
if (/*internalName == modNPC.Name &&*/ npcIDs.Count == 1 && !Language.Exists($"{prefix}.EntryName"))
name ??= modNPC.DisplayName;
name ??= Language.GetOrRegister($"{prefix}.EntryName", () => Regex.Replace(internalName, "([A-Z])", " $1").Trim());
spawnInfo ??= () => Language.GetOrRegister($"{prefix}.SpawnInfo", () => "Spawn conditions unknown"); // Register English/default, not localized.
}
else {
// Mod registered boss for vanilla npc or no npcids?
name ??= Language.GetText("Mods.BossChecklist.BossSpawnInfo.Unknown");
spawnInfo ??= () => Language.GetText("Mods.BossChecklist.BossSpawnInfo.Unknown");
}
}
}
this.name = name;
this.spawnInfo = spawnInfo;
// self-initializing data
this.hidden = false; // defaults to false, hidden status can be toggled per world
this.relatedEntries = new List<string>(); /// Setup in <see cref="BossTracker.SetupEntryRelations"/>
this.loot = new List<DropRateInfo>(); /// Setup in <see cref="BossTracker.FinalizeEntryLootTables"/>
this.lootItemTypes = new List<int>(); /// Setup in <see cref="BossTracker.FinalizeEntryLootTables"/>
this.lootItemTypes_BagExclusives = new List<int>(); /// Setup in <see cref="BossTracker.FinalizeEntryLootTables"/>
this.collectibles = new Dictionary<int, CollectibleType>(); /// Setup in <see cref="BossTracker.FinalizeCollectibleTypes"/>
if (extraData?.ContainsKey("collectibles") == true) {
if (extraData["collectibles"] is Dictionary<int, CollectibleType> collection) {
this.collectibles = collection;
}
else {
InterpretObjectAsListOfInt(extraData["collectibles"]).ForEach(item => collectibles.TryAdd(item, CollectibleType.Generic));
}
}
// optional extra data
List<int> InterpretObjectAsListOfInt(object data) => data is List<int> ? data as List<int> : (data is int ? new List<int>() { Convert.ToInt32(data) } : new List<int>());
List<string> InterpretObjectAsListOfStrings(object data) => data is List<string> ? data as List<string> : (data is string ? new List<string>() { data as string } : null);
this.npcLimbs = extraData?.ContainsKey("limbs") == true ? extraData["limbs"] as Dictionary<int, LocalizedText> : new Dictionary<int, LocalizedText>();
this.available = extraData?.ContainsKey("availability") == true ? extraData["availability"] as Func<bool> : () => true;
this.spawnItem = extraData?.ContainsKey("spawnItems") == true ? InterpretObjectAsListOfInt(extraData["spawnItems"]) : new List<int>();
this.customDrawing = extraData?.ContainsKey("customPortrait") == true ? extraData["customPortrait"] as Action<SpriteBatch, Rectangle, Color> : null;
this.customDespawnMessages = (NPC npc) => null;
if (extraData?.ContainsKey("despawnMessage") == true) {
if (extraData["despawnMessage"] is Func<NPC, LocalizedText> multiMessage) {
this.customDespawnMessages = multiMessage;
}
else if (extraData["despawnMessage"] is LocalizedText singleMessage) {
this.customDespawnMessages = (NPC npc) => singleMessage;
}
}
headIconTextures = () => new List<Asset<Texture2D>> { TextureAssets.NpcHead[0] }; // If the head textures is empty, fill it with the '?' head icon so modder's see something is wrong
if (extraData?.ContainsKey("overrideHeadTextures") == true) {
object headIconData = extraData["overrideHeadTextures"];
if (headIconData is Func<List<string>>) {
headIconTextures = headIconData as Func<List<Asset<Texture2D>>>;
}
else if (InterpretObjectAsListOfStrings(headIconData) is List<string> pathList) {
List<Asset<Texture2D>> icons = new List<Asset<Texture2D>>();
foreach (string texturePath in pathList) {
if (ModContent.HasAsset(texturePath))
icons.Add(ModContent.Request<Texture2D>(texturePath, AssetRequestMode.ImmediateLoad));
}
if (pathList.Count > 0)
headIconTextures = () => icons;
}
}
else if (entryType != EntryType.Event) {
// Skip events, as they must use a custom icon to display.
List<Asset<Texture2D>> icons = new List<Asset<Texture2D>>();
foreach (int npc in npcIDs) {
if (npc == NPCID.DD2DarkMageT1 || npc == NPCID.DD2OgreT2)
continue; // skips extra Ogre and Dark Mage head drawing
if (NPCID.Sets.BossHeadTextures[npc] != -1)
icons.Add(TextureAssets.NpcHeadBoss[NPCID.Sets.BossHeadTextures[npc]]);
}
if (icons.Count > 0)
headIconTextures = () => icons;
}
}
// Workaround for vanilla events with illogical translation keys.
internal EntryInfo WithCustomTranslationKey(string translationKey) {
// EntryInfo.name should remain as a translation key.
this.name = Language.GetText(translationKey);
return this;
}
internal EntryInfo WithCustomLimbs(List<int> limbs) {
foreach (int npc in limbs) {
this.npcLimbs.TryAdd(npc, Language.GetOrRegister($"Mods.BossChecklist.ChatMessages.Defeated.{new NPCDefinition(npc).Name}"));
}
return this;
}
internal EntryInfo WithCustomAvailability(Func<bool> funcBool) {
this.available = funcBool;
return this;
}
internal EntryInfo WithCustomPortrait(string texturePath) {
if (ModContent.HasAsset(texturePath)) {
this.portraitTexture = ModContent.Request<Texture2D>(texturePath);
}
return this;
}
internal EntryInfo WithCustomHeadIcon(string texturePath) {
if (ModContent.HasAsset(texturePath)) {
this.headIconTextures = () => new List<Asset<Texture2D>>() { ModContent.Request<Texture2D>(texturePath) };
}
else {
this.headIconTextures = () => new List<Asset<Texture2D>>() { TextureAssets.NpcHead[0] };
}
return this;
}
internal EntryInfo WithCustomHeadIcon(List<string> texturePaths) {
List<Asset<Texture2D>> icons = new List<Asset<Texture2D>>();
foreach (string path in texturePaths) {
if (ModContent.HasAsset(path))
icons.Add(ModContent.Request<Texture2D>(path));
}
if (icons.Count == 0)
icons = new List<Asset<Texture2D>>() { TextureAssets.NpcHead[0] };
this.headIconTextures = () => icons;
return this;
}
internal static EntryInfo MakeVanillaBoss(EntryType type, float val, string key, int npcID, Func<bool> downed) {
string nameKey = key.Substring(key.LastIndexOf(".") + 1);
// BossChecklist only has despawn messages for vanilla Bosses
// Betsy does not have a despawn message as Old Ones Army has a message already
Func<NPC, LocalizedText> customMessages = null;
if (type == EntryType.Boss && nameKey != "DD2Betsy") {
List<int> DayDespawners = new List<int>() {
NPCID.EyeofCthulhu,
NPCID.Retinazer,
NPCID.Spazmatism,
NPCID.TheDestroyer,
};
customMessages = delegate (NPC npc) {
if (Main.player.All(plr => !plr.active || plr.dead)) {
return Language.GetText($"{NPCAssist.LangChat}.Loss.{nameKey}"); // Despawn message when all players are dead
}
else if (Main.dayTime && DayDespawners.Contains(npc.type)) {
return Language.GetText($"{NPCAssist.LangChat}.Despawn.Day"); // Despawn message when it turns to day
}
// unique despawn messages should default to the generic message when no conditions are met
return Language.GetText($"{NPCAssist.LangChat}.Despawn.Generic");
};
}
return new EntryInfo(
entryType: type,
modSource: "Terraria",
internalName: nameKey,
progression: val,
downed: downed,
npcIDs: new List<int>() { npcID },
extraData: new Dictionary<string, object>() {
{ "displayName", Language.GetText(key) },
{ "spawnInfo", Language.GetText($"Mods.BossChecklist.BossSpawnInfo.{nameKey}") },
{ "spawnItems", BossTracker.EntrySpawnItems.GetValueOrDefault($"Terraria {nameKey}") },
{ "collectibles", BossTracker.EntryCollectibles.GetValueOrDefault($"Terraria {nameKey}") },
{ "despawnMessage", customMessages },
}
);
}
internal static EntryInfo MakeVanillaBoss(EntryType type, float val, string key, List<int> ids, Func<bool> downed) {
string nameKey = key.Substring(key.LastIndexOf(".") + 1).Replace(" ", "").Replace("'", "");
if (nameKey.EndsWith("Head"))
nameKey = nameKey.Substring(0, nameKey.Length - 4);
Func<NPC, LocalizedText> customMessages = null;
if (type == EntryType.Boss) { // BossChecklist only has despawn messages for vanilla Bosses
List<int> DayDespawners = new List<int>() {
NPCID.EyeofCthulhu,
NPCID.Retinazer,
NPCID.Spazmatism,
NPCID.TheDestroyer,
};
customMessages = delegate (NPC npc) {
if (Main.player.All(plr => !plr.active || plr.dead)) {
return Language.GetText($"{NPCAssist.LangChat}.Loss.{nameKey}"); // Despawn message when all players are dead
}
else if (Main.dayTime && DayDespawners.Contains(npc.type)) {
return Language.GetText($"{NPCAssist.LangChat}.Despawn.Day"); // Despawn message when it turns to day
}
// unique despawn messages should default to the generic message when no conditions are met
return Language.GetText($"{NPCAssist.LangChat}.Despawn.Generic");
};
}
return new EntryInfo(
entryType: type,
modSource: "Terraria",
internalName: nameKey,
progression: val,
downed: downed,
npcIDs: ids,
extraData: new Dictionary<string, object>() {
{ "displayName", Language.GetText(key) },
{ "spawnInfo", Language.GetText($"Mods.BossChecklist.BossSpawnInfo.{nameKey}") },
{ "spawnItems", BossTracker.EntrySpawnItems.GetValueOrDefault($"Terraria {nameKey}") },
{ "collectibles", BossTracker.EntryCollectibles.GetValueOrDefault($"Terraria {nameKey}") },
{ "despawnMessage", customMessages },
}
);
}
internal static EntryInfo MakeVanillaEvent(float val, string key, Func<bool> downed) {
string nameKey = key.Substring(key.LastIndexOf(".") + 1).Replace(" ", "").Replace("'", "");
return new EntryInfo(
entryType: EntryType.Event,
modSource: "Terraria",
internalName: nameKey,
progression: val,
downed: downed,
npcIDs: BossTracker.EventNPCs.GetValueOrDefault($"Terraria {nameKey}"),
extraData: new Dictionary<string, object>() {
{ "displayName", Language.GetText(key) },
{ "spawnInfo", Language.GetText($"Mods.BossChecklist.BossSpawnInfo.{nameKey}") },
{ "spawnItems", BossTracker.EntrySpawnItems.GetValueOrDefault($"Terraria {nameKey}") },
{ "collectibles", BossTracker.EntryCollectibles.GetValueOrDefault($"Terraria {nameKey}") },
}
);
}
public override string ToString() => $"{progression} {Key}";
}
internal enum OrphanType {
SubmitEntryLoot,
SubmitEntryCollectibles,
SubmitEntrySpawnItems,
SubmitEventNPCs
}
internal class OrphanInfo {
internal OrphanType type;
internal string modCallerDisplayName;
internal Dictionary<string, object> values;
internal OrphanInfo(OrphanType type, string modSource, Dictionary<string, object> values) {
this.type = type; // Type is null checked before being created within ModCall
this.modCallerDisplayName = modSource;
// Sort through the data submissions to remove any invalid data
foreach (string Key in values.Keys) {
if (!Key.StartsWith("Terraria ") && !ModLoader.TryGetMod(Key.Substring(0, Key.IndexOf(" ")), out _))
values.Remove(Key); // remove submissions that use an entry key from an unloaded mod (no need to log removed entries for unloaded mods)
}
this.values = values;
}
}
}