Even More Modifiers  1.0.0.0
A mod for rolling various bonus stats on items
GraphicsModContent.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using Microsoft.Xna.Framework;
5 using Microsoft.Xna.Framework.Graphics;
6 using Terraria;
7 using Terraria.ID;
8 using Terraria.ModLoader;
9 
10 namespace Loot.Api.ModContent
11 {
16  {
17  private IDictionary<string, Texture2D> _glowmaskTextures;
18  private IDictionary<string, Texture2D> _shaderTextures;
19 
20  private IDictionary<string, string> _keyStore;
21 
22  private IDictionary<string, Texture2D> _lookupTable;
23  private IDictionary<string, Texture2D> _lookupGlowmaskTable;
24  private IDictionary<string, Texture2D> _lookupShaderTable;
25 
26  protected override void Initialize()
27  {
28  _glowmaskTextures = new Dictionary<string, Texture2D>();
29  _shaderTextures = new Dictionary<string, Texture2D>();
30 
31  _keyStore = new Dictionary<string, string>();
32 
33  _lookupTable = new Dictionary<string, Texture2D>();
34  _lookupGlowmaskTable = new Dictionary<string, Texture2D>();
35  _lookupShaderTable = new Dictionary<string, Texture2D>();
36  }
37 
38  protected override void Unload()
39  {
40  _glowmaskTextures = null;
41  _shaderTextures = null;
42 
43  _keyStore = null;
44 
45  _lookupTable = null;
46  _lookupGlowmaskTable = null;
47  _lookupShaderTable = null;
48  }
49 
50  public override string GetRegistryKey()
51  {
52  return "ModGraphics";
53  }
54 
55  internal void AddKeyPass(string key, string keyPass)
56  {
57  if (!_keyStore.ContainsKey(key))
58  {
59  _keyStore.Add(key, keyPass);
60  }
61  }
62 
63  private IDictionary<string, int> _vanillaNamesCache;
64 
65  private void FillVanillaNamesCache()
66  {
67  _vanillaNamesCache = new Dictionary<string, int>();
68  for (int i = 1; i < ItemID.Count; i++)
69  {
70  Item item = new Item();
71  item.SetDefaults(i);
72  if (_vanillaNamesCache.ContainsKey(item.Name))
73  {
74  Loot.Logger.Error("There was a problem during initialization of vanilla names cache");
75  }
76 
77  _vanillaNamesCache.Add($"{item.Name.Replace(" ", "")}_{i}", i);
78  }
79  }
80 
81  internal string GetAssetKey(string key, Mod mod)
82  {
83  if (_vanillaNamesCache == null)
84  {
85  FillVanillaNamesCache();
86  }
87 
88  string kvpName = key;
89  if (key.Contains('/'))
90  {
91  key = key.Substring(key.LastIndexOf('/') + 1);
92  }
93 
94  // is an item name
95  if (char.IsLetter(key.First()))
96  {
97  int index = key.LastIndexOf('_');
98  key = new string(key.TakeWhile((x, i) => i < index).ToArray());
99  var itemName = key;
100  Item item = mod.GetItem(key)?.item;
101  if (item == null)
102  {
103  if (!_vanillaNamesCache.Keys.Any(x => x.StartsWith(key.Replace(" ", ""))))
104  {
105  return null;
106  }
107 
108  item = new Item();
109  item.SetDefaults(_vanillaNamesCache[_vanillaNamesCache.Keys.First(x => x.StartsWith(key.Replace(" ", "")))]);
110  }
111 
112  return kvpName.Replace(itemName, item.type.ToString());
113  }
114 
115  // is an item id
116  if (char.IsNumber(key.First()))
117  {
118  int index = key.LastIndexOf('_');
119  string itemId = new string(key.TakeWhile((x, i) => i < index).ToArray());
120  if (int.Parse(itemId) >= ItemLoader.ItemCount) return null;
121  return kvpName;
122  }
123 
124  return null;
125  }
126 
127  internal bool AnyGlowmaskAssetExists(string key, Mod mod)
128  {
129  GetItemKey(key);
130  return _glowmaskTextures.Any(x => x.Key.StartsWith(mod.Name) && x.Key.Contains($"{key}_Glowmask") || x.Key.Contains($"{key}_Glow"));
131  }
132 
133  internal bool AnyShaderAssetExists(string key, Mod mod)
134  {
135  key = GetItemKey(key);
136  return _shaderTextures.Any(x => x.Key.StartsWith(mod.Name) && x.Key.Contains($"{key}_Shader") || x.Key.Contains($"{key}_Shad"));
137  }
138 
139  private string GetItemKey(string key)
140  {
141  if (key.Contains('/'))
142  {
143  key = key.Substring(key.LastIndexOf('/') + 1);
144  }
145 
146  int index = key.LastIndexOf('_');
147  return new string(key.TakeWhile((x, i) => i < index).ToArray());
148  }
149 
150  // @todo expose a global GraphicsContent instance and prepare automagically in context ?
151  public void Prepare(Mod mod)
152  {
153  if (_keyStore.TryGetValue(mod.Name, out var keyPass))
154  {
155  var glowmasks = _glowmaskTextures.Where(x => x.Key.StartsWith(keyPass)).ToArray();
156  var shaders = _glowmaskTextures.Where(x => x.Key.StartsWith(keyPass)).ToArray();
157 
158  _lookupTable =
159  _textures.Where(x => x.Key.StartsWith(keyPass))
160  .Concat(glowmasks)
161  .Concat(shaders)
162  .ToDictionary(x => SubstringLastIndex('/', x.Key), x => x.Value);
163 
164  _lookupGlowmaskTable = glowmasks.ToDictionary(x => SubstringLastIndex('/', x.Key), x => x.Value);
165  _lookupShaderTable = shaders.ToDictionary(x => SubstringLastIndex('/', x.Key), x => x.Value);
166  }
167  else
168  {
169  throw new Exception($"Tried to get keyPass from keyStore for mod {mod.Name} but key not present");
170  }
171  }
172 
173  public void Prepare(Item item)
174  {
175  string itemId = item.type.ToString();
176  var glowmasks = _glowmaskTextures.Where(x => GetItemKey(x.Key).Equals(itemId)).ToArray();
177  var shaders = _shaderTextures.Where(x => GetItemKey(x.Key).Equals(itemId)).ToArray();
178 
179  _lookupTable =
180  _textures.Where(x => GetItemKey(x.Key).Equals(itemId))
181  .Concat(glowmasks)
182  .Concat(shaders)
183  .ToDictionary(x => SubstringLastIndex('/', x.Key), x => x.Value);
184 
185  _lookupGlowmaskTable = glowmasks.ToDictionary(x => SubstringLastIndex('/', x.Key), x => x.Value);
186  _lookupShaderTable = shaders.ToDictionary(x => SubstringLastIndex('/', x.Key), x => x.Value);
187  }
188 
189  public void ClearPreparation()
190  {
191  _lookupTable.Clear();
192  _lookupGlowmaskTable.Clear();
193  _lookupShaderTable.Clear();
194  }
195 
196  public Texture2D GetPreparedTexture(string key, bool clearPreparation = true)
197  {
198  return GetPreparedAsset(_lookupTable, key, clearPreparation);
199  }
200 
201  public Texture2D GetPreparedGlowmask(string key, bool clearPreparation = true)
202  {
203  Texture2D glowmask = GetPreparedAsset(_lookupGlowmaskTable, $"{key}_Glowmask", clearPreparation);
204  if (glowmask != null) return glowmask;
205  return GetPreparedAsset(_lookupGlowmaskTable, $"{key}_Glow", clearPreparation);
206  }
207 
208  public Texture2D GetPreparedShader(string key, bool clearPreparation = true)
209  {
210  Texture2D shader = GetPreparedAsset(_lookupShaderTable, $"{key}_Shader", clearPreparation);
211  if (shader != null) return shader;
212  return GetPreparedAsset(_lookupShaderTable, $"{key}_Shad", clearPreparation);
213  }
214 
215  private Texture2D GetPreparedAsset(IDictionary<string, Texture2D> dict, string key, bool clearPreparation = true)
216  {
217  Texture2D texture = GetFrom(dict, key);
218  if (clearPreparation && texture != null)
219  {
220  ClearPreparation();
221  }
222 
223  return texture;
224  }
225 
226  public Texture2D GetGlowmaskTexture(string key)
227  {
228  return GetFrom(_glowmaskTextures, key);
229  }
230 
231  public void AddGlowmaskTexture(string key, Texture2D texture)
232  {
233  AddTo(_glowmaskTextures, key, texture);
234  }
235 
236  public Texture2D GetShaderTexture(string key)
237  {
238  return GetFrom(_shaderTextures, key);
239  }
240 
241  public void AddShaderTexture(string key, Texture2D texture)
242  {
243  AddTo(_shaderTextures, key, texture);
244  }
245 
246  protected override void ProcessTexture2D(ref Texture2D texture)
247  {
248  texture = MultiplyColorsByAlpha(texture);
249  }
250 
251  //to clarify, this code simply 'fixes' out bad saving of the file in photoshop or whatever
252  //yeah, it works for every pixel individually
253  //some art programs save the file with every pixel having an RGBA where RGB is bigger than 0 but A may be 0 , where A would usually be transparency there, in XNA's texture loading the alpha channel is independent , and with terraria's draw mode it actually can do awesome things
254  //in terraria, A channel signals additive<---> opaque
255  //a pixel with 255 alpha draws fully opaque, a pixel with 0 alpha draws fully additive
256  //so if you do say, 255,0,0,0 color, it would draw a fully additive red
257  //which can be awesome...except when your file saves the alpha channel of supposedly transparent pixels wrong
258  public static Texture2D MultiplyColorsByAlpha(Texture2D texture)
259  {
260  Color[] data = new Color[texture.Width * texture.Height];
261  texture.GetData(data);
262  for (int i = 0; i < data.Length; i++)
263  {
264  Vector4 we = data[i].ToVector4();
265  data[i] = new Color(we.X * we.W, we.Y * we.W, we.Z * we.W, we.W);
266  }
267 
268  texture.SetData(data);
269  return texture;
270  }
271 
272  private string SubstringLastIndex(char chr, string str)
273  {
274  int index = str.LastIndexOf(chr);
275  if (index != -1)
276  {
277  return str.Substring(index + 1);
278  }
279 
280  return null;
281  }
282 
283  protected override void Load()
284  {
285  //var assembly = Assembly.GetExecutingAssembly();
286  //var types = assembly.GetTypes().Where(x => !x.IsAbstract && x.IsClass).ToList();
287 
288  //IList<string> exceptKeys = new List<string>();
289 
290  // @todo
291  // @todo load entity/shader based on ItemID in Assets/Shader or Assets/Glowmask folders
292  // @todo Item_{Id} -> The usecase here is on GlobalItem, not ModItem
293 
294  // Loads regular types by having a ShaderEntity or GlowmaskEntity
295  //foreach (var type in types)
296  //{
297  // try
298  // {
299  //var shaderEntity = type.GetProperty("ShaderEntity");
300  //var getShaderEntity = type.GetMethod("GetShaderEntity");
301  //string key;
302 
303  //if (shaderEntity != null && getShaderEntity != null)
304  //{
305  // // Get the shader entity
306  // object obj = FormatterServices.GetUninitializedObject(type);
307  // ShaderEntity entity = (ShaderEntity)getShaderEntity.Invoke(obj, null);
308 
309  // // Load the base texture
310  // key = type.FullName;
311  // var texture = ModLoader.GetTexture(key.Replace('.', '/'));
312  // AddTexture(key, texture);
313  // exceptKeys.Add(key);
314 
315  // // Load the shader texture
316  // key = entity.GetEntityKey(type);
317  // texture = ModLoader.GetTexture(key.Replace('.', '/'));
318  // AddShaderTexture(key, texture);
319  // exceptKeys.Add(key);
320  //}
321 
322  //var glowmaskEntity = type.GetProperty("GlowmaskEntity");
323  //var getGlowmaskEntity = type.GetMethod("GetGlowmaskEntity");
324 
325  //if (glowmaskEntity != null && getGlowmaskEntity != null)
326  //{
327  // // Get the glowmask entity
328  // object obj = FormatterServices.GetUninitializedObject(type);
329  // GlowmaskEntity entity = (GlowmaskEntity)getGlowmaskEntity.Invoke(obj, null);
330  // Texture2D texture;
331 
332  // // Try loading the base texture
333  // key = type.FullName;
334  // if (!exceptKeys.Contains(key))
335  // {
336  // texture = ModLoader.GetTexture(key.Replace('.', '/'));
337  // AddTexture(key, texture);
338  // exceptKeys.Add(key);
339  // }
340 
341  // // Try loading the glowmask
342  // key = entity.GetEntityKey(type);
343  // texture = ModLoader.GetTexture(key.Replace('.', '/'));
344  // AddGlowmaskTexture(key, texture);
345  // exceptKeys.Add(key);
346  //}
347  // }
348  // catch (Exception e)
349  // {
350  // // fluff
351  // ErrorLogger.Log(e.ToString());
352  // }
353  //}
354 
355  // @todo support above usecase as well
356  // load all types that are marked for autoloading
357  //var autoloadTypes =
358  // from t in assembly.GetTypes()
359  // let attribute = t.GetCustomAttribute<AutoloadModContent>(true)
360  // where attribute != null
361  // select new { Type = t, Attribute = attribute };
362 
364  //FieldInfo texturesField = typeof(Mod).GetField("textures", BindingFlags.Instance | BindingFlags.NonPublic);
365  //var textures = (Dictionary<string, Texture2D>)texturesField.GetValue(Loot.Instance);
366 
367  //foreach (var autoload in autoloadTypes)
368  //{
369  // //var fullName = autoload.Type.FullName.Replace('.', '/');
370  // var name = autoload.Type.Name;
371  // //var path = autoload.Type.Namespace;
372  // var compatibleTextures = textures
373  // .Where(x =>
374  // {
375  // var sub = SubstringLastIndex('/', x.Value.Name);
376  // return sub != null && sub.Contains(name);
377  // })
378  // .Select(x => new KeyValuePair<string, Texture2D>(x.Value.Name.Replace('/', '.'), x.Value));
379 
380  // var attr = autoload.Attribute;
381 
382  // if (attr == null)
383  // throw new NullReferenceException("Attribute was null during ModGraphicsContent Load");
384 
385  // // try adding all textures
386  // foreach (var compatibleTexture in compatibleTextures.Where(x => !exceptKeys.Contains(x.Key)))
387  // {
388  // AddTexture(compatibleTexture.Key, compatibleTexture.Value);
389  // }
390  //}
391  }
392  }
393 }
IDictionary< string, Texture2D > _lookupGlowmaskTable
IDictionary< string, Texture2D > _lookupShaderTable
void AddShaderTexture(string key, Texture2D texture)
IDictionary< string, Texture2D > _lookupTable
void AddGlowmaskTexture(string key, Texture2D texture)
IDictionary< string, int > _vanillaNamesCache
Texture2D GetPreparedAsset(IDictionary< string, Texture2D > dict, string key, bool clearPreparation=true)
Texture2D GetPreparedShader(string key, bool clearPreparation=true)
IDictionary< string, Texture2D > _shaderTextures
static Texture2D MultiplyColorsByAlpha(Texture2D texture)
override void ProcessTexture2D(ref Texture2D texture)
An abstract class that contains a mapping of strings to texture instances
Texture2D GetPreparedTexture(string key, bool clearPreparation=true)
IDictionary< string, Texture2D > _glowmaskTextures
IDictionary< string, string > _keyStore
This class stores glowmask and shader textures that can be looked up during drawing ...
Texture2D GetPreparedGlowmask(string key, bool clearPreparation=true)
string SubstringLastIndex(char chr, string str)