2using System.Collections;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.Diagnostics.CodeAnalysis;
8using System.Reflection;
10using System.Text.RegularExpressions;
11using System.Threading;
12using NPOI.SS.UserModel;
13using NPOI.XSSF.UserModel;
16using ReflexCLI.Attributes;
18using UnityEngine.Networking;
20[ConsoleCommandClassCustomizer(
"Mod")]
21public class ModUtil :
EClass
23 public static Dictionary<string, string> fallbackTypes =
new Dictionary<string, string>();
25 private static readonly Dictionary<string, ICustomContent> _customContent =
new Dictionary<string, ICustomContent>();
27 private static readonly List<Func<string, object, int>> _customCalcEvaluator =
new List<Func<string, object, int>>();
29 private static readonly HashSet<Type> _checkedAttributedTypes =
new HashSet<Type>();
31 private static readonly Dictionary<string, Texture2D> _cachedTextures =
new Dictionary<string, Texture2D>();
33 private static Effect _rodTemplate;
37 public static readonly List<ContextMenuProxy> contextMenuProxies =
new List<ContextMenuProxy>();
39 public static IReadOnlyDictionary<string, SourceData> SourceMapping => (from f in typeof(
SourceManager).GetFields()
40 where typeof(
SourceData).IsAssignableFrom(f.FieldType)
41 select f).ToDictionary((FieldInfo f) => f.FieldType.Name, (FieldInfo f) => f.GetValue(
EClass.
sources) as
SourceData);
43 public static void LoadTypeFallback()
45 string text =
"type_resolver.txt";
46 string[] array = Array.Empty<
string>();
53 array =
new string[2] {
"TrueArena,ArenaWaveEvent,ZoneEvent",
"Elin-GeneRecombinator,Elin_GeneRecombinator.IncubationSacrifice,Chara" };
56 string[] array2 = array;
57 for (
int i = 0; i < array2.Length; i++)
59 string[] array3 = array2[i].Split(
',');
60 if (array3.Length >= 2)
62 RegisterSerializedTypeFallback(array3[0], array3[1], array3[2]);
67 public static void RegisterSerializedTypeFallback(
string nameAssembly,
string nameType,
string nameFallbackType)
69 fallbackTypes[nameType] = nameFallbackType;
72 public static void LogModError(
string message,
BaseModPackage package =
null)
74 string text =
"#mod/" + package?.title +
" (" + package?.id +
")\n" + message;
75 UnityEngine.Debug.LogWarning(text.RemoveAllTags());
76 if ((package !=
null && package.isInPackages && !package.builtin) || Application.isEditor)
78 EGui.CreatePopup(text);
84 LogModError(message, FindSourceRowPackage(row));
87 public static void LogModError(
string message, Type type)
89 LogModError(message, FindFileProviderPackage(
new FileInfo(type.Assembly.Location)));
92 public static void LogModError(
string message, FileInfo file)
94 LogModError(message, FindFileProviderPackage(file));
97 public static void LogModError(
string message, DirectoryInfo dir)
99 LogModError(message, FindDirectoryProviderPackage(dir));
102 public static ModPackage GetModPackage(
string modId)
104 return ModManagerCore.Instance.MappedPackages.GetValueOrDefault(modId) as
ModPackage;
107 public static ModPackage FindFileProviderPackage(FileInfo file)
109 string path = file.FullName.NormalizePath();
113 public static ModPackage FindDirectoryProviderPackage(DirectoryInfo dir)
115 string path = dir.FullName.NormalizePath();
121 return ModManagerCore.Instance.packages.OfType<
ModPackage>().LastOrDefault((
ModPackage p) => p.sourceRows.Contains(row));
124 public static Zone FindZoneByFullName(
string zoneFullName =
"ntyris/0",
bool useRandomFallback =
false)
126 if (zoneFullName.IsEmpty())
131 if (zoneId.IsEmpty())
136 if (zone ==
null && (zoneId ==
"*" || useRandomFallback))
140 select z).RandomItem();
145 public static void OnModsActivated()
147 CommandRegistry.assemblies.Add(typeof(EScript).Assembly);
148 SoundManager.current.soundLoaders.Add(LoadSoundData);
152 LoadEffectTemplate();
159 RegisterElinEventAttributes();
160 CoroutineHelper.Deferred(RegisterElinEventAttributes);
163 private static void OnSourceImporting()
165 ExcelParser.allowTrimming =
true;
166 ImportAllModSourceSheets();
169 private static void OnSourceImported()
171 _customContent.Clear();
176 activatedUserMod.GenerateCustomContentProfiles();
177 foreach (ICustomContent
item in activatedUserMod.customContent)
184 LogModError(
"exception while generating custom source profiles\n" + ex.Message, activatedUserMod);
185 UnityEngine.Debug.LogException(ex);
188 ImportAllGunEffectSettings();
189 CustomReligionContent.managed.Clear();
190 foreach (CustomReligionContent item2
in _customContent.Values.OfType<CustomReligionContent>())
192 item2.RegisterCustomReligion();
194 CustomReligionContent.Init();
195 SourceCache.FinalizeCache();
196 SourceCache.InvalidateCacheBlobs();
197 SourceCache.ClearDetail();
202 ExportAllSourceDataCsv(text);
203 UnityEngine.Debug.Log(
"#source exported current version to " + text);
208 private static void OnPreLoadInit(GameIOContext context)
213 private static void OnPostLoadInit(GameIOContext context)
217 for (
int num = 0; num < array.Length; num++)
219 KeyValuePair<int, Spatial> keyValuePair = array[num];
220 var (num3, spatial2) = (KeyValuePair<int, Spatial>)(ref keyValuePair);
221 if (spatial2.source ==
null)
224 UnityEngine.Debug.LogWarning(
$"#mod-content removed invalid zone '{num3}', '{spatial2.id}', '{(spatial2 as Zone)?.ZoneFullName}'");
227 List<ICustomContent> contents =
new List<ICustomContent>();
230 CustomReligionContent.LoadReligionData(context);
234 LogModError(
"exception while loading custom religion data\n" + ex.Message);
235 UnityEngine.Debug.LogException(ex);
237 LoadCustomContent<CustomZoneContent>();
238 LoadCustomContent<CustomCharaContent>();
239 LoadOtherCustomContent();
240 void LoadCustomContent<T>() where T : class, ICustomContent
242 UnityEngine.Debug.Log(
"#mod-content loading " + typeof(T).Name +
"...");
243 foreach (T
item in _customContent.Values.OfType<T>())
248 item.OnGameLoad(context);
250 catch (Exception ex2)
252 LogModError(
"exception while loading custom content '" +
item.ContentId +
"'\n" + ex2.Message,
item.Owner);
253 UnityEngine.Debug.LogException(ex2);
257 void LoadOtherCustomContent()
259 foreach (ICustomContent item2
in _customContent.Values.Except(contents))
263 item2.OnGameLoad(context);
265 catch (Exception ex2)
267 LogModError(
"exception while loading custom content '" + item2.ContentId +
"'\n" + ex2.Message, item2.Owner);
268 UnityEngine.Debug.LogException(ex2);
275 private static void OnPreSaveInit(GameIOContext context)
280 private static void OnPostSaveInit(GameIOContext context)
284 CustomReligionContent.SaveReligionData(context);
288 LogModError(
"exception while saving custom religion data\n" + ex.Message);
289 UnityEngine.Debug.LogException(ex);
291 foreach (ICustomContent value
in _customContent.Values)
295 value.OnGameSave(context);
297 catch (Exception ex2)
299 LogModError(
"exception while saving custom content '" + value.ContentId +
"'\n" + ex2.Message, value.Owner);
300 UnityEngine.Debug.LogException(ex2);
305 private static void OnReligionImporting(List<Religion> list)
307 list.AddRange(CustomReligionContent.GetCustomReligions());
311 private static void OnCharaCreated(
Chara chara)
313 if (TryGetContent<CustomCharaContent>(
"Chara/" + chara.
id, out var content))
315 content.OnCharaCreated(chara);
317 if (TryGetContent<CustomBiographyContent>(
"Biography/" + chara.
id, out var content2))
319 content2.RefreshCharaBio(chara);
324 private static void OnThingCreated(
Thing thing)
326 if (TryGetContent<CustomThingContent>(
"Thing/" + thing.
id, out var content))
328 content.OnThingCreated(thing);
333 private static void OnActPerformed(
Act act)
335 if (act.
HasTag(
"godAbility"))
337 CustomReligionContent.managed.Values.FirstOrDefault((
ReligionCustom r) => r.
content.godAbilities.Contains(act.
ID))?.Talk(
"ability");
341 private static void OnSetLang(
string lang)
343 PackageIterator.ClearCache();
344 PackageIterator.RebuildAllMappings(lang);
345 SourceLocalization.SetLang(lang);
348 if (activatedUserMod.IsSourceLocalizable && (activatedUserMod.isInPackages || Application.isEditor))
350 activatedUserMod.UpdateSourceLocalizationFile(lang);
353 AliasGen.list =
null;
354 AliasGen.listMix.Clear();
355 AliasGen.listBuiltin.Clear();
356 WordGen.listMix.Clear();
357 WordGen.listBuiltin.Clear();
358 foreach (
string item in LoadAlias())
363 foreach (
string item2
in LoadName())
367 foreach (
string item3
in LoadGodTalk())
371 foreach (
string item4
in LoadCharaTalk())
375 foreach (
string item5
in LoadCharaTone())
379 BookList.dict =
null;
380 BottleMessageList.list =
null;
381 Lang.excelDialog =
null;
388 public static bool TryGetContent<T>(
string contentId, [NotNullWhen(
true)] out T content) where T :
class, ICustomContent
390 ICustomContent value;
391 bool num = _customContent.TryGetValue(contentId, out value);
392 content = value as T;
395 return content !=
null;
400 public static bool HasContent(
string contentId)
402 return _customContent.ContainsKey(contentId);
405 public static void AddContent(ICustomContent content)
407 UnityEngine.Debug.Log(_customContent.Remove(content.ContentId, out var value) ? (
"#mod-content override '" + content.ContentId +
"' from '" + value.Owner.id +
"' to '" + content.Owner.id +
"'") : (
"#mod-content added '" + content.ContentId +
"' from '" + content.Owner.id +
"'"));
408 _customContent[content.ContentId] = content;
411 public static bool FixDefaultRenderRowPref(
RenderRow r,
string id)
413 FileInfo fileInfo = PackageIterator.GetFiles(
"Texture/" +
id +
".pref").LastOrDefault();
414 if (fileInfo ==
null)
424 if (!r.
tag.Contains(
"usePref") || !FixDefaultRenderRowPref(r, r.id))
435 if (!r.
tag.Contains(
"usePref") || !FixDefaultRenderRowPref(r, r.id))
444 public static List<Thing> GenerateMerchantStock(
Card owner,
string stockId =
null,
bool forceRestock =
false)
446 List<Thing> list =
new List<Thing>();
447 if (stockId.IsEmpty())
449 stockId = owner.
GetStr(
"merchant_override");
451 if (stockId.IsEmpty())
455 string[] array = stockId.Split(
'|');
457 string[] array2 = array;
458 foreach (
string text
in array2)
460 if (!TryGetContent<CustomMerchantStock>(
"MerchantStock/" + text, out var content))
464 foreach (
Thing item in content.Generate(owner))
466 bool flag =
item.GetBool(101);
467 if (!(!forceRestock && flag) || !orCreate.Contains(
item.trait.IdNoRestock))
471 orCreate.Add(
item.trait.IdNoRestock);
480 public static void RegisterElinEventAttributes()
486 MemberInfo
item = item3.member;
492 if (!(
item is PropertyInfo property))
494 if (
item is MethodInfo method)
496 elinEventBaseAttribute.
Register(method);
501 elinEventBaseAttribute.
Register(property);
506 LogModError(
"exception while registering attribute '" + elinEventBaseAttribute.GetType().Name +
"' from '" +
item.TryToString() +
"'\n" + ex.Message,
item.DeclaringType);
507 UnityEngine.Debug.LogException(ex);
514 public static List<string> LoadBookList()
516 List<string> list =
new List<string>();
517 DirectoryInfo[] directories = PackageIterator.GetDirectories(
"Text");
518 foreach (DirectoryInfo directoryInfo
in directories)
520 list.AddRange(Directory.GetDirectories(directoryInfo.FullName));
521 UnityEngine.Debug.Log(
"#mod-content loaded book list " + directoryInfo.ShortPath());
526 public static List<string> LoadTopicFiles()
528 List<string> list =
new List<string>();
529 FileInfo[] files = PackageIterator.GetFiles(
"Text/Help/_topics.txt");
530 foreach (FileInfo fileInfo
in files)
533 UnityEngine.Debug.Log(
"#mod-content loaded book topics " + fileInfo.ShortPath());
538 public static List<string> LoadExcelDialog()
540 List<string> list =
new List<string>();
541 FileInfo[] files = PackageIterator.GetFiles(
"Dialog/dialog.xlsx");
542 foreach (FileInfo fileInfo
in files)
544 list.Add(fileInfo.FullName);
545 UnityEngine.Debug.Log(
"#mod-content loaded dialog " + fileInfo.ShortPath());
550 public static List<string> LoadAlias()
552 List<string> list =
new List<string>();
553 FileInfo[] files = PackageIterator.GetFiles(
Lang.
langCode +
"/Data/Alias.xlsx");
554 foreach (FileInfo fileInfo
in files)
556 list.Add(fileInfo.FullName);
557 UnityEngine.Debug.Log(
"#mod-content loaded Alias " + fileInfo.ShortPath());
562 public static List<string> LoadName()
564 List<string> list =
new List<string>();
565 FileInfo[] files = PackageIterator.GetFiles(
Lang.
langCode +
"/Data/Name.xlsx");
566 foreach (FileInfo fileInfo
in files)
568 list.Add(fileInfo.FullName);
569 UnityEngine.Debug.Log(
"#mod-content loaded Name " + fileInfo.ShortPath());
574 public static List<string> LoadCharaTalk()
576 List<string> list =
new List<string>();
577 FileInfo[] files = PackageIterator.GetFiles(
"Data/chara_talk.xlsx");
578 foreach (FileInfo fileInfo
in files)
580 list.Add(fileInfo.FullName);
581 UnityEngine.Debug.Log(
"#mod-content loaded chara talk " + fileInfo.ShortPath());
586 public static List<string> LoadCharaTone()
588 List<string> list =
new List<string>();
589 FileInfo[] files = PackageIterator.GetFiles(
"Data/chara_tone.xlsx");
590 foreach (FileInfo fileInfo
in files)
592 list.Add(fileInfo.FullName);
593 UnityEngine.Debug.Log(
"#mod-content loaded chara tone " + fileInfo.ShortPath());
598 public static Sprite LoadSprite(
string spritePath, Vector2? pivot =
null,
string name =
null,
int resizeWidth = 0,
int resizeHeight = 0)
600 if (spritePath.IsEmpty())
604 if (!spritePath.EndsWith(
".png"))
610 spritePath +=
".png";
612 if (!File.Exists(spritePath))
614 FileInfo[] files = PackageIterator.GetFiles(Path.Combine(
"Texture", spritePath));
619 spritePath = files[^1].FullName;
621 Vector2 valueOrDefault = pivot.GetValueOrDefault();
624 valueOrDefault =
new Vector2(0.5f, 0.5f);
625 pivot = valueOrDefault;
627 string text =
$"{spritePath}/{pivot}/{resizeWidth}/{resizeHeight}";
634 if (!_cachedTextures.TryGetValue(text, out var value2))
641 if (resizeWidth != 0 && resizeHeight != 0 && (value2.width != resizeWidth || value2.height != resizeHeight))
643 Texture2D texture2D = value2.Rescale(resizeWidth, resizeHeight);
644 UnityEngine.Object.Destroy(value2);
649 _cachedTextures[text] = value2;
651 catch (Exception arg)
653 UnityEngine.Debug.LogError(
$"#mod-content failed to load sprite {spritePath.ShortPath()}\n{arg}");
656 Texture2D texture2D2 = _cachedTextures[text];
657 Sprite sprite = Sprite.Create(texture2D2,
new Rect(0f, 0f, texture2D2.width, texture2D2.height), pivot.Value, 100f, 0u, SpriteMeshType.FullRect);
662 public static Sprite AppendSpriteSheet(
string id,
int resizeWidth = 0,
int resizeHeight = 0,
string pattern =
"@")
665 if (!dictModItems.TryGetValue(
id, out var value) && pattern !=
"")
667 value = dictModItems.Where((KeyValuePair<string, string> kv) => kv.Key.StartsWith(pattern)).FirstOrDefault((KeyValuePair<string, string> kv) =>
id.StartsWith(kv.Key[pattern.Length..])).Value;
669 string spritePath = value;
671 Sprite sprite = LoadSprite(spritePath,
null, name, resizeWidth, resizeHeight);
676 if (
SpriteSheet.
dict.TryGetValue(
id, out var value2) && value2.texture.width == sprite.texture.width && value2.texture.height == sprite.texture.height)
683 public static List<string> LoadGodTalk()
685 List<string> list =
new List<string>();
686 FileInfo[] files = PackageIterator.GetFiles(
"Data/god_talk.xlsx");
687 foreach (FileInfo fileInfo
in files)
689 list.Add(fileInfo.FullName);
690 UnityEngine.Debug.Log(
"#mod-content loaded god talk " + fileInfo.ShortPath());
695 public static Dictionary<string, CustomGunEffectData> LoadGunEffects()
697 Dictionary<string, CustomGunEffectData> dictionary =
new Dictionary<string, CustomGunEffectData>();
698 (FileInfo, EMod)[] filesEx = PackageIterator.GetFilesEx(
"Data/EffectSetting.guns.json");
699 for (
int i = 0; i < filesEx.Length; i++)
701 (FileInfo, EMod) tuple = filesEx[i];
702 FileInfo
item = tuple.Item1;
703 EMod item2 = tuple.Item2;
705 _customContent[customGunEffectSetting.
ContentId] = customGunEffectSetting;
710 dictionary.Merge(item3.
items);
711 UnityEngine.Debug.Log(
$"#mod-content loaded {item3}");
716 public static void ImportAllGunEffectSettings()
718 foreach (var (key, customGunEffectData2) in LoadGunEffects())
720 customGunEffectData2.ResolveSprite();
725 public static string ExportAllGunEffectSettings()
727 string text = CorePath.rootExe +
"/guns.json";
731 gun.Deconstruct(out var key, out var value);
737 effectData2.idSprite = effectData.
sprite.name;
739 dictionary[key2] = effectData2;
742 return $"dumped {dictionary.Count} guns data to {text}";
745 private static Effect LoadEffect(
string id)
755 string effectId =
id.Split(
'/')[^1];
756 Sprite sprite = LoadSprite(effectId);
761 Effect effect = UnityEngine.Object.Instantiate(_rodTemplate);
762 effect.name = effectId;
763 effect.sprites = Slice().ToArray();
764 UnityEngine.Object.DontDestroyOnLoad(effect);
765 UnityEngine.Debug.Log(
"#mod-content loaded custom effect '" + effectId +
"'");
767 IEnumerable<Sprite> Slice()
769 int height = (int)sprite.rect.height;
770 float frames = sprite.rect.width / (
float)height;
774 while ((
float)i < frames)
776 Sprite sprite2 = Sprite.Create(rect:
new Rect(i * height, 0f, height, height), texture: sprite.texture, pivot:
new Vector2(0.5f, 0.5f * (128f / (
float)height)), pixelsPerUnit: 100f, extrude: 0u, meshType: SpriteMeshType.FullRect);
777 sprite2.name =
$"{effectId}{i:D4}";
778 yield
return sprite2;
786 private static void LoadEffectTemplate()
788 _rodTemplate = Resources.Load<
Effect>(
"Media/Effect/General/rod");
791 UnityEngine.Debug.LogWarning(
"#mod-content cannot initialize rod effect template");
795 public static DynamicInvoker Call(
object instance, params
string[] names)
797 return new DynamicInvoker(instance?.GetType(), instance, names);
800 public static DynamicInvoker Call(Type type, params
string[] names)
802 return new DynamicInvoker(type,
null, names);
805 public static DynamicInvoker Call<T>(params
string[] names)
807 return new DynamicInvoker(typeof(T),
null, names);
810 public static DynamicInvoker CallType(
string typeName, params
string[] names)
812 return new DynamicInvoker(DynamicReflection.FindType(typeName),
null, names);
815 public static DynamicInvoker Construct(Type type)
817 return DynamicInvoker.Constructor(type);
820 public static DynamicInvoker Construct<T>()
822 return DynamicInvoker.Constructor(typeof(T));
825 public static DynamicInvoker ConstructType(
string typeName)
827 return DynamicInvoker.Constructor(DynamicReflection.FindType(typeName));
830 public static DynamicAccessor Access(
object instance, params
string[] names)
832 return new DynamicAccessor(instance?.GetType(), instance, names);
835 public static DynamicAccessor Access(Type type, params
string[] names)
837 return new DynamicAccessor(type,
null, names);
840 public static DynamicAccessor Access<T>(params
string[] names)
842 return new DynamicAccessor(typeof(T),
null, names);
845 public static DynamicAccessor AccessType(
string typeName, params
string[] names)
847 return new DynamicAccessor(DynamicReflection.FindType(typeName),
null, names);
850 public static DynamicTarget Target(Type type, params
string[] names)
852 return new DynamicTarget(type, names);
855 public static DynamicTarget Target<T>(params
string[] names)
857 return new DynamicTarget(typeof(T), names);
860 public static DynamicTarget
TargetType(
string typeName, params
string[] names)
862 return new DynamicTarget(DynamicReflection.FindType(typeName), names);
865 public static MethodInfo FindMethod(Type type, params
string[] names)
867 return new DynamicTarget(type, names).Get();
870 public static MethodInfo FindMethod<T>(params
string[] names)
872 return new DynamicTarget(typeof(T), names).Get();
875 public static MethodInfo FindGetter(Type type, params
string[] names)
877 return DynamicTarget.Accessor(type, names, setter:
false);
880 public static MethodInfo FindGetter<T>(params
string[] names)
882 return DynamicTarget.Accessor(typeof(T), names, setter:
false);
885 public static MethodInfo FindSetter(Type type, params
string[] names)
887 return DynamicTarget.Accessor(type, names, setter:
true);
890 public static MethodInfo FindSetter<T>(params
string[] names)
892 return DynamicTarget.Accessor(typeof(T), names, setter:
true);
895 public static ConstructorInfo FindConstructor(Type type, params Type[] paramTypes)
897 return DynamicTarget.Constructor(type, paramTypes);
900 public static ConstructorInfo FindConstructor<T>(params Type[] paramTypes)
902 return DynamicTarget.Constructor(typeof(T), paramTypes);
905 public static Type FindType(
string typeName)
907 return DynamicReflection.FindType(typeName);
910 public static T Coerce<T>(
object value)
912 return DynamicReflection.Coerce<T>(value);
915 public static SerializableSoundData GetSoundMeta(
string soundPath)
917 string path = Path.ChangeExtension(soundPath,
".json");
918 SerializableSoundData serializableSoundData;
919 if (File.Exists(path))
923 serializableSoundData =
IO.LoadFile<SerializableSoundData>(path);
924 if (serializableSoundData.dataVersion == SerializableSoundData.SoundDataMetaVersion.V1)
926 return serializableSoundData;
933 serializableSoundData =
new SerializableSoundData();
934 if (soundPath.NormalizePath().Contains(
"/Sound/BGM/"))
936 serializableSoundData.type = SoundData.Type.BGM;
937 serializableSoundData.bgmDataOptional =
new SerializableBGMData
939 parts =
new List<BGMData.Part>
946 return serializableSoundData;
949 public static AudioType GetAudioType(
string extension)
951 return extension.ToLowerInvariant().Trim()
switch
953 ".acc" => AudioType.ACC,
954 ".mp3" => AudioType.MPEG,
955 ".ogg" => AudioType.OGGVORBIS,
956 ".wav" => AudioType.WAV,
957 _ => AudioType.UNKNOWN,
961 public static SoundData LoadSoundData(
string soundId)
963 if (!
MOD.
sounds.TryGetValue(soundId, out var value) || !value.Exists)
967 return LoadSoundData(value);
970 public static SoundData LoadSoundData(FileInfo soundFile)
972 string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(soundFile.FullName);
973 string fullName = soundFile.FullName;
974 AudioType audioType = GetAudioType(soundFile.Extension);
975 bool stream = fullName.NormalizePath().Contains(
"/BGM/") && audioType == AudioType.OGGVORBIS;
976 using UnityWebRequest unityWebRequest = AudioClipStream.GetAudioClip(
"file://" + fullName, audioType, compressed:
false, stream);
977 unityWebRequest.SendWebRequest();
978 Stopwatch stopwatch = Stopwatch.StartNew();
979 while (!unityWebRequest.isDone && stopwatch.ElapsedMilliseconds < 5000)
983 if (unityWebRequest.result != UnityWebRequest.Result.Success)
985 UnityEngine.Debug.LogError(
"#sound '" + fileNameWithoutExtension +
"' failed to load: " + unityWebRequest.error.IsEmpty(
"timeout"));
988 AudioClip content = DownloadHandlerAudioClip.GetContent(unityWebRequest);
989 int? num = content?.samples;
990 if (!num.HasValue || num.GetValueOrDefault() <= 0)
992 UnityEngine.Debug.LogError(
$"#sound '{fileNameWithoutExtension}' sample is null: {audioType}");
995 content.name = fileNameWithoutExtension;
996 SoundData soundData = GetSoundMeta(fullName).ToSoundData();
997 if (soundData is BGMData bGMData)
999 bGMData._name = Path.GetFileNameWithoutExtension(fullName);
1000 if (bGMData.song ==
null)
1002 bGMData.song =
new BGMData.SongData();
1003 bGMData.song.parts.Add(
new BGMData.Part());
1006 soundData.clip = content;
1007 soundData.name = fileNameWithoutExtension;
1008 UnityEngine.Debug.Log(
$"#sound '{fileNameWithoutExtension}' loaded: {audioType}/{content.length}s");
1009 SoundManager.current.dictData[fileNameWithoutExtension] = soundData;
1013 public static void AddOrReplaceBGM(
string bgmId)
1017 BGMData bGMData = LoadSoundData(bgmId) as BGMData;
1018 if (bGMData ==
null)
1022 bGMData.name = bgmId.Replace(
"BGM/",
"");
1023 if (bGMData.id <= 0)
1025 Match match = Regex.Match(bGMData.name,
"^(\\d+)(?:_(.*))?$");
1028 int id = match.Groups[1].Value.ToInt();
1029 string text = (match.Groups[2].Success ? match.Groups[2].Value :
null);
1031 if (!text.IsEmpty())
1033 bGMData.name = text;
1038 bGMData.id = bgms.Count + 1;
1039 UnityEngine.Debug.Log(
$"#sound bgm unassigned/{bGMData.id}/{bGMData.name}");
1042 if (dictBGM.TryGetValue(bGMData.id, out var value))
1044 value.clip = bGMData.clip;
1045 UnityEngine.Debug.Log(
$"#sound bgm replace/{bGMData.id}/{value.name}/>/{bGMData.name}");
1050 dictBGM[bGMData.id] = bGMData;
1051 UnityEngine.Debug.Log(
$"#sound bgm addon/{bGMData.id}/{bGMData.name}");
1055 public static ModPackage FindSoundPackage(
string soundId)
1057 return ModManagerCore.Instance.packages.OfType<
ModPackage>().LastOrDefault((
ModPackage p) => p.sounds.Keys.Contains(soundId));
1060 public static Playlist CreatePlaylist(ref List<int> bgmIds, Playlist mold =
null)
1062 Playlist playlist =
EClass.
Sound.plBlank.Instantiate();
1063 if (bgmIds.Count == 0 && (
bool)mold)
1065 bgmIds = mold.ToInts();
1066 playlist.shuffle = mold.shuffle;
1067 playlist.minSwitchTime = mold.minSwitchTime;
1068 playlist.nextBGMOnSwitch = mold.nextBGMOnSwitch;
1069 playlist.ignoreLoop = mold.ignoreLoop;
1070 playlist.keepBGMifSamePlaylist = mold.keepBGMifSamePlaylist;
1071 playlist.name = mold.name;
1073 foreach (
int bgmId
in bgmIds)
1077 playlist.list.Add(
new Playlist.Item
1086 public static void SetBGMKnown(
int bgmId,
bool known =
true)
1099 [Obsolete(
"use ImportModSourceSheets for proper caching and localizaiton support")]
1100 public static void ImportExcel(
string pathToExcelFile,
string sheetName,
SourceData source)
1102 UnityEngine.Debug.Log(
"ImportExcel source:" + source?.ToString() +
" Path:" + pathToExcelFile);
1103 using FileStream @is = File.Open(pathToExcelFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
1104 XSSFWorkbook xSSFWorkbook =
new XSSFWorkbook((Stream)@is);
1105 for (
int i = 0; i < xSSFWorkbook.NumberOfSheets; i++)
1107 ISheet sheetAt = xSSFWorkbook.GetSheetAt(i);
1108 if (sheetAt.SheetName != sheetName)
1112 UnityEngine.Debug.Log(
"Importing Sheet:" + sheetName);
1115 ExcelParser.path = pathToExcelFile;
1116 if (!source.
ImportData(sheetAt,
new FileInfo(pathToExcelFile).Name, overwrite:
true))
1118 UnityEngine.Debug.LogError(
ERROR.
msg);
1121 UnityEngine.Debug.Log(
"Imported " + sheetAt.SheetName);
1124 catch (Exception ex)
1126 UnityEngine.Debug.LogError(
"[Error] Skipping import " + sheetAt.SheetName +
" :" + ex.Message +
"/" + ex.Source +
"/" + ex.StackTrace);
1132 public static SourceData FindSourceByName(
string sourceData)
1137 public static void ImportModSourceSheets(
string modId)
1141 SourceCache.InvalidateCacheVersion();
1143 if (valueOrDefault ==
null)
1147 List<string> list =
new List<string>();
1148 foreach (FileInfo sourceSheet
in valueOrDefault.Mapping.SourceSheets)
1150 sourceImporter.
fileProviders[sourceSheet.FullName] = valueOrDefault;
1151 list.Add(sourceSheet.FullName);
1154 UnityEngine.Debug.Log(
"#source finished importing workbooks from " + modId);
1156 catch (Exception exception)
1158 UnityEngine.Debug.LogException(exception);
1162 public static void ImportAllModSourceSheets()
1166 SourceCache.InvalidateCacheVersion();
1170 EClass.sources.materials
1172 List<string> list =
new List<string>();
1179 foreach (FileInfo sourceSheet
in modPackage.Mapping.SourceSheets)
1181 if (!sourceSheet.Name.StartsWith(
".") && !sourceSheet.Name.Contains(
'~'))
1183 sourceImporter.
fileProviders[sourceSheet.FullName] = modPackage;
1184 list.Add(sourceSheet.FullName);
1190 catch (Exception exception)
1192 UnityEngine.Debug.LogException(exception);
1194 UnityEngine.Debug.Log(
"#source finished importing workbooks");
1197 public static string ExportSourceDataCsv(
string sourceData,
string delimiter =
",")
1199 SourceData sourceData2 = FindSourceByName(sourceData);
1200 if (sourceData2 ==
null)
1204 IReadOnlyDictionary<string, string> typeMapping = sourceData2.
GetTypeMapping();
1205 (string, string)[] array = (from kv in sourceData2.
GetRowMapping()
1207 select (column: kv.Key, type: typeMapping[kv.Key])).ToArray();
1208 StringBuilder stringBuilder =
new StringBuilder();
1209 stringBuilder.AppendLine(
string.Join(delimiter, array.Select(((
string column,
string type) p) => p.column)));
1210 stringBuilder.AppendLine(
string.Join(delimiter, array.Select(((
string column,
string type) p) => p.type)));
1214 List<string> list =
new List<string>();
1215 (string, string)[] array3 = array;
1216 for (
int num2 = 0; num2 < array3.Length; num2++)
1218 (string, string) tuple = array3[num2];
1219 string item = tuple.Item1;
1220 string item2 = tuple.Item2;
1221 object value = baseRow.GetRowFields()[
item].GetValue(baseRow);
1223 IEnumerable enumerable;
1224 if (!(obj is
int[] array4))
1226 if (!(obj is
int key))
1228 if (obj is
string item3)
1233 enumerable = obj as IEnumerable;
1234 if (enumerable !=
null)
1239 else if (item2 ==
"element_id")
1244 list.Add(value.ToString());
1247 if (!(item2 ==
"elements"))
1249 enumerable = (IEnumerable)obj;
1252 List<string> list2 =
new List<string>();
1253 for (
int num3 = 0; num3 < array4.Length - 1; num3 += 2)
1256 string text = array4[num3 + 1].ToString();
1257 list2.Add(alias +
"/" + text);
1259 string item4 =
string.Join(
',', list2);
1263 string item5 =
string.Join(
',', enumerable.OfType<
object>());
1266 IEnumerable<string> values = list.Select(delegate(
string f)
1268 string text2 = f.Replace(
"\r",
"").Replace(
"\n",
"\\n").Replace(
"\"",
"\"\"");
1269 return (!text2.IsEmpty()) ? (
"\"" + text2 +
"\"") :
"";
1271 stringBuilder.AppendLine(
string.Join(delimiter, values));
1273 return stringBuilder.ToString();
1276 public static void ExportAllSourceDataCsv(
string dir)
1279 foreach (
string key
in SourceMapping.Keys)
1281 File.WriteAllText(Path.Combine(dir, key +
".csv"), ExportSourceDataCsv(key));
1285 public static string ExportSourceDataJson(
string sourceData, Formatting format = Formatting.Indented)
1287 SourceData sourceData2 = FindSourceByName(sourceData);
1288 if (sourceData2 ==
null)
1292 return JsonConvert.SerializeObject(sourceData2.
ExportRows(), format, GameIOContext.Settings);
1295 public static void ExportAllSourceDataJson(
string dir)
1298 foreach (
string key
in SourceMapping.Keys)
1300 File.WriteAllText(Path.Combine(dir, key +
".json"), ExportSourceDataJson(key));
1304 public static void AddContextMenuEntry(Action onClick,
string menuEntry,
string displayName =
"")
1306 if (onClick ==
null || menuEntry.IsEmpty())
1310 string[] array = menuEntry.Split(
'/', StringSplitOptions.RemoveEmptyEntries);
1311 List<ContextMenuProxy> children = contextMenuProxies;
1312 for (
int i = 0; i < array.Length; i++)
1314 bool flag = i < array.Length - 1;
1315 string part = array[i];
1317 if (contextMenuProxy ==
null)
1321 onClick = ((i == array.Length - 1 && flag) ?
null :
new Action(SafeInvoke)),
1324 children.Add(contextMenuProxy);
1326 else if (contextMenuProxy.
isMenu != flag)
1328 UnityEngine.Debug.LogWarning(
"#mod-content attempt to add context menu entry with same name but different types\n" + part +
" -> " + (contextMenuProxy.
isMenu ?
"submenu" :
"button"));
1331 children = contextMenuProxy.
children;
1333 UnityEngine.Debug.Log(
"#mod-content added context menu entry '" + menuEntry +
"' from '" + onClick.Method.TryToString() +
"'");
1340 catch (Exception exception)
1342 UnityEngine.Debug.LogException(exception);
1347 public static void RemoveContextMenuEntry(
string entry)
1349 string[] array = entry.Split(
'/', StringSplitOptions.RemoveEmptyEntries);
1350 if (array.Length == 0)
1354 List<ContextMenuProxy> children = contextMenuProxies;
1356 List<ContextMenuProxy> list =
null;
1357 string[] array2 = array;
1358 foreach (
string part
in array2)
1361 if (contextMenuProxy ==
null)
1366 children = contextMenuProxy.
children;
1368 list?.Remove(contextMenuProxy);
static void PublishEvent(string eventId, object data=null)
static void SubscribeEvent(string eventId, Action< object > handler)
static List< Func< List< string > > > booklistLoaders
string GetStr(string id, string defaultStr=null)
static HashSet< Type > modTypes
Dictionary< int, BGMData > dictBGM
virtual void OnSetLang(string lang)
static readonly JsonSerializerSettings JsonSettings
Dictionary< string, CustomGunEffectData > items
static CustomGunEffectSetting CreateFromFile(FileInfo file, ModPackage owner=null)
static List< Func< string, T > > assetLoaders
static SourceManager sources
static SoundManager Sound
static GameSetting setting
virtual void Register(MethodInfo method)
static void ClearStaticRows()
UD_String_EffectData guns
static string[] LoadTextArray(string _path)
static void SaveTextArray(string path, string[] text)
static void CreateDirectory(string path)
static void SaveFile(string path, object obj, bool compress=false, JsonSerializerSettings setting=null)
static Texture2D LoadPNG(string _path, FilterMode filter=FilterMode.Point)
static List< Func< List< string > > > excelDialogLoaders
static TalkDataList listTalk
static GodTalkDataList listGodTalk
static ToneDataList tones
static ExcelDataList listAlias
static Dictionary< string, FileInfo > sounds
static ExcelDataList listName
static new ModManager Instance
Dictionary< string, HashSet< string > > noRestocks
CustomReligionContent content
override bool ImportData(ISheet sheet, string bookname, bool overwrite=false)
virtual IReadOnlyDictionary< string, int > GetRowMapping()
override BaseRow[] ExportRows()
virtual IReadOnlyDictionary< string, string > GetTypeMapping()
IEnumerable< SourceData > ImportFilesCached(IEnumerable< string > imports, bool resetData=true)
static void HotInit(IEnumerable< SourceData > sourceData)
Dictionary< string, EMod > fileProviders
SourceData FindSourceByName(string name)
static SourcePref ReadFromIni(string path)
static Dictionary< string, string > dictModItems
static Dictionary< string, Sprite > dict
virtual string IdNoRestock
static List< Func< List< string > > > topicLoaders
static string string int zoneLv ParseZoneFullName(string zoneFullName)
Zone FindOrCreateLevel(int destLv, string subId="")