Decompiled source of PartyPlus English v1.0.0

PartyPlusEnglish.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace PartyPlusEnglish;

[BepInPlugin("ridvan.partyplus.english", "PartyPlus English", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	public const string Guid = "ridvan.partyplus.english";

	public const string Version = "1.0.0";

	private const string PartyPlusGuid = "com.yourname.partyplus";

	private const string ModHubGuid = "kaefchik.modhub";

	private static int _replaced;

	private void Awake()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Expected O, but got Unknown
		Harmony val = new Harmony("ridvan.partyplus.english");
		HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin).GetMethod("Transpiler", BindingFlags.Static | BindingFlags.NonPublic));
		int num = 0;
		string[] array = new string[2] { "com.yourname.partyplus", "kaefchik.modhub" };
		foreach (string text in array)
		{
			if (!Chainloader.PluginInfos.TryGetValue(text, out var value) || value.Instance == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Plugin not found: " + text));
				continue;
			}
			Assembly assembly = ((object)value.Instance).GetType().Assembly;
			foreach (MethodBase item in TargetMethods(assembly))
			{
				try
				{
					MethodBase methodBase = item;
					HarmonyMethod val3 = val2;
					val.Patch(methodBase, (HarmonyMethod)null, (HarmonyMethod)null, val3, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)string.Concat("Could not patch ", item.DeclaringType, ".", item.Name, ": ", ex.Message));
				}
			}
			TranslateConfigDescriptions(value.Instance.Config);
		}
		TranslateHubEntries();
		((BaseUnityPlugin)this).Logger.LogInfo((object)("PartyPlus English 1.0.0: patched " + num + " methods, " + _replaced + " strings."));
	}

	private static IEnumerable<MethodBase> TargetMethods(Assembly asm)
	{
		Type[] types;
		try
		{
			types = asm.GetTypes();
		}
		catch (ReflectionTypeLoadException ex)
		{
			types = ex.Types.Where((Type t) => t != null).ToArray();
		}
		Type[] array = types;
		foreach (Type type in array)
		{
			if (type.IsGenericTypeDefinition)
			{
				continue;
			}
			bool isPlugin = typeof(BaseUnityPlugin).IsAssignableFrom(type);
			IEnumerable<MethodBase> methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast<MethodBase>().Concat(type.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast<MethodBase>());
			foreach (MethodBase m in methods)
			{
				if (!m.IsAbstract && !m.ContainsGenericParameters && (!isPlugin || !(m.Name == "Awake")) && m.GetMethodBody() != null && ContainsTranslatable(m))
				{
					yield return m;
				}
			}
		}
	}

	private static bool ContainsTranslatable(MethodBase m)
	{
		try
		{
			string text = default(string);
			foreach (CodeInstruction originalInstruction in PatchProcessor.GetOriginalInstructions(m, (ILGenerator)null))
			{
				if (originalInstruction.opcode == OpCodes.Ldstr)
				{
					object obj = originalInstruction.operand as string;
					if (obj != null)
					{
						text = (string)obj;
						obj = text;
					}
					if (obj != null && Tr.Map.ContainsKey(text))
					{
						return true;
					}
				}
			}
		}
		catch
		{
		}
		return false;
	}

	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		foreach (CodeInstruction item in list)
		{
			string text = item.operand as string;
			if (item.opcode == OpCodes.Ldstr && text != null && Tr.Map.TryGetValue(text, out var value))
			{
				item.operand = value;
				_replaced++;
			}
		}
		return list;
	}

	private void TranslateConfigDescriptions(ConfigFile cfg)
	{
		FieldInfo field = typeof(ConfigDescription).GetField("<Description>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
		if (field == null)
		{
			return;
		}
		bool flag = false;
		foreach (ConfigDefinition item in cfg.Keys.ToList())
		{
			ConfigDescription description = cfg[item].Description;
			if (description != null && description.Description != null && Tr.Map.TryGetValue(description.Description, out var value))
			{
				field.SetValue(description, value);
				flag = true;
			}
		}
		if (!flag)
		{
			return;
		}
		try
		{
			cfg.Save();
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Config save failed: " + ex.Message));
		}
	}

	private void TranslateHubEntries()
	{
		try
		{
			if (!(Type.GetType("ModHub.ModHubAPI, ModHub")?.GetField("_entries", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) is IEnumerable enumerable))
			{
				return;
			}
			string text = default(string);
			foreach (object item in enumerable)
			{
				string[] array = new string[2] { "Title", "Description" };
				foreach (string name in array)
				{
					FieldInfo field = item.GetType().GetField(name);
					if (field != null)
					{
						object obj = field.GetValue(item) as string;
						if (obj != null)
						{
							text = (string)obj;
							obj = text;
						}
						if (obj != null && Tr.Map.TryGetValue(text, out var value))
						{
							field.SetValue(item, value);
						}
					}
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Hub entry translation failed: " + ex.Message));
		}
	}
}
internal static class Tr
{
	internal static readonly Dictionary<string, string> Map = new Dictionary<string, string>
	{
		{ "{0} приглашает тебя в пати\n[{1}] принять   [{2}] отклонить", "{0} invites you to the party\n[{1}] accept   [{2}] decline" },
		{ "Слишком далеко для пинга (макс. {0:0} м)", "Too far to ping (max {0:0} m)" },
		{ "Сюда", "Here" },
		{ "Враг: ", "Enemy: " },
		{ "[PartyPlus] Загружено пати: {0}", "[PartyPlus] Parties loaded: {0}" },
		{ "[PartyPlus] Не удалось загрузить пати: ", "[PartyPlus] Failed to load parties: " },
		{ "[PartyPlus] Не удалось сохранить пати: ", "[PartyPlus] Failed to save parties: " },
		{ "Игрок «", "Player \"" },
		{ "» не в сети", "\" is offline" },
		{ " уже в пати", " is already in a party" },
		{ "Пати заполнено", "Party is full" },
		{ "Приглашение отправлено: ", "Invite sent: " },
		{ "Нет активных приглашений", "No active invites" },
		{ "Приглашение истекло", "Invite expired" },
		{ "перешёл в другое пати", "joined another party" },
		{ "Пати уже заполнено", "Party is already full" },
		{ " вступил в пати", " joined the party" },
		{ " отклонил приглашение", " declined the invite" },
		{ "вышел из пати", "left the party" },
		{ "Ты не в пати", "You are not in a party" },
		{ ". Пати распущено", ". Party disbanded" },
		{ "{0:0} м", "{0:0} m" },
		{ "Панель пати — тащи мышью", "Party panel — drag with mouse" },
		{ "не в сети", "offline" },
		{ ">мёртв</color>", ">dead</color>" },
		{ "</color> приглашает тебя в пати", "</color> invites you to the party" },
		{ "[{0}] принять    [{1}] отклонить    [{2}] меню", "[{0}] accept    [{1}] decline    [{2}] menu" },
		{ "[{0}] / Esc — закрыть   ·   колесо мыши — прокрутка", "[{0}] / Esc — close   ·   mouse wheel — scroll" },
		{ "Приглашение от <color=#", "Invite from <color=#" },
		{ "Принять", "Accept" },
		{ "Отклонить", "Decline" },
		{ "Твоё пати", "Your party" },
		{ "Ты не в пати. Пригласи кого-нибудь ниже.", "You are not in a party. Invite someone below." },
		{ ">(ты)</color>", ">(you)</color>" },
		{ ">не в сети</color>", ">offline</color>" },
		{ "Покинуть пати", "Leave party" },
		{ "Игроки на сервере", "Players on server" },
		{ "Пригласить", "Invite" },
		{ "Больше никого нет", "No one else here" },
		{ "Интерфейс", "Interface" },
		{ "Закрепить панель пати на месте", "Lock party panel in place" },
		{ ">Перетащи панель мышью, потом поставь галочку обратно.</color>", ">Drag the panel with the mouse, then tick the box again.</color>" },
		{ "Размер пинга", "Ping size" },
		{ "Прозрачность пинга", "Ping opacity" },
		{ "Сбросить позицию панели", "Reset panel position" },
		{ "Цвет полосы HP", "HP bar color" },
		{ "При малом здоровье", "Low health" },
		{ "Порог малого HP", "Low HP threshold" },
		{ "Обводка тиммейтов сквозь стены", "Teammate outline through walls" },
		{ "Выключена админом сервера", "Disabled by server admin" },
		{ "Включить обводку", "Enable outline" },
		{ "Яркость", "Brightness" },
		{ "Дальность", "Range" },
		{ "Сервер", "Server" },
		{ ">Менять может только админ сервера.</color>", ">Only the server admin can change these.</color>" },
		{ "Ты админ — изменения разойдутся всем игрокам, когда закроешь меню.", "You are admin — changes are sent to all players when you close the menu." },
		{ "Размер пати", "Party size" },
		{ "Время приглашения", "Invite time" },
		{ "Частота обновления", "Update rate" },
		{ "Пинги включены", "Pings enabled" },
		{ "Длительность пинга", "Ping duration" },
		{ "Дальность пинга", "Ping range" },
		{ "Ники над тиммейтами", "Names above teammates" },
		{ "Дальность ников", "Name range" },
		{ "Разрешить обводку тиммейтов", "Allow teammate outline" },
		{ "Отправить на сервер", "Send to server" },
		{ "Отправить на сервер *", "Send to server *" },
		{ "выкл", "off" },
		{ "без огр.", "unlimited" },
		{ "{0:0} с", "{0:0} s" },
		{ "{0:0.0} с", "{0:0.0} s" },
		{ "Сколько игроков может быть в одном пати.", "How many players can be in one party." },
		{ "Как часто (в секундах) сервер рассылает позиции и HP тиммейтов.", "How often (in seconds) the server sends teammate positions and HP." },
		{ "Через сколько секунд приглашение в пати сгорает.", "After how many seconds a party invite expires." },
		{ "Разрешить пинги для пати.", "Allow pings for the party." },
		{ "Сколько секунд пинг висит в мире.", "How many seconds a ping stays in the world." },
		{ "Максимальная дальность луча пинга в метрах.", "Maximum ping ray distance in meters." },
		{ "Показывать ник, HP и дистанцию над тиммейтами (видно сквозь стены).", "Show name, HP and distance above teammates (visible through walls)." },
		{ "С какой максимальной дистанции видно ник тиммейта. 0 = без ограничений.", "Maximum distance at which a teammate's name is visible. 0 = unlimited." },
		{ "Разрешить игрокам подсвечивать тиммейтов сквозь стены. Если выключено — обводки не будет ни у кого.", "Allow players to highlight teammates through walls. If off, nobody gets an outline." },
		{ "Поставить пинг туда, куда смотришь (по умолчанию колёсико мыши).", "Place a ping where you are looking (default: mouse wheel click)." },
		{ "Быстро принять приглашение в пати.", "Quickly accept a party invite." },
		{ "Быстро отклонить приглашение в пати.", "Quickly decline a party invite." },
		{ "Панель с никами и HP тиммейтов.", "Panel with teammate names and HP." },
		{ "true = панель пати закреплена на месте. false = свободное перемещение: открой меню пати (или инвентарь) и перетащи панель мышью. Позиция сохраняется сама.", "true = party panel is locked in place. false = free movement: open the party menu (or inventory) and drag the panel with the mouse. Position is saved automatically." },
		{ "Отступ панели пати слева (пиксели).", "Party panel offset from the left (pixels)." },
		{ "Отступ панели пати сверху (пиксели).", "Party panel offset from the top (pixels)." },
		{ "Размер метки пинга на экране (1 = обычный, 0.5 = в два раза меньше).", "Ping marker size on screen (1 = normal, 0.5 = half size)." },
		{ "Непрозрачность метки пинга (1 = полностью видна, 0.2 = почти прозрачная).", "Ping marker opacity (1 = fully visible, 0.2 = almost transparent)." },
		{ "Цвет полосы здоровья тиммейтов (HEX, например B8E0F2FF). Меняется и в меню пати.", "Teammate health bar color (HEX, e.g. B8E0F2FF). Can also be changed in the party menu." },
		{ "Цвет полосы, когда у тиммейта мало здоровья.", "Bar color when a teammate has low health." },
		{ "При какой доле HP полоса меняет цвет (0.3 = 30%). 0 = никогда.", "HP fraction at which the bar changes color (0.3 = 30%). 0 = never." },
		{ "Убирает ванильную полосу HP над игроками из твоего пати — вместо неё рисуется полоса PartyPlus (ник, HP, дистанция). У мобов и чужих игроков всё как было.", "Hides the vanilla HP bar above your party members and draws the PartyPlus bar instead (name, HP, distance). Mobs and other players are unchanged." },
		{ "Размер цифры расстояния до тиммейта и до пинга.", "Size of the distance number for teammates and pings." },
		{ "Подсвечивать силуэт тиммейта, когда он за стеной, деревом или горой.", "Highlight a teammate's silhouette when they are behind a wall, tree or mountain." },
		{ "Цвет силуэта тиммейта сквозь стены (HEX). Меняется и в меню пати.", "Teammate silhouette color through walls (HEX). Can also be changed in the party menu." },
		{ "Насколько яркий силуэт (1 = сплошной, 0.2 = еле видно).", "How bright the silhouette is (1 = solid, 0.2 = barely visible)." },
		{ "До какого расстояния (м) подсвечивать тиммейтов. 0 = без ограничений.", "Up to what distance (m) teammates are highlighted. 0 = unlimited." },
		{ "Использовать шрифт Valheim. Если вместо русских букв квадратики — выключи. Применяется после перезапуска.", "Use the Valheim font. If letters show up as squares, turn this off. Applies after restart." },
		{ "Масштаб всего интерфейса мода.", "Scale of the whole mod interface." },
		{ "Пати, приглашения, HUD и пинги", "Party, invites, HUD and pings" },
		{ "PartyPlus 1.8.0 загружен", "PartyPlus 1.8.0 loaded" },
		{ "[PartyPlus] Шейдер Hidden/Internal-Colored не найден — обводка тиммейтов отключена", "[PartyPlus] Shader Hidden/Internal-Colored not found — teammate outline disabled" },
		{ "[PartyPlus] EnemyHud.UpdateHuds не найден — стандартная полоса над тиммейтами не будет скрываться", "[PartyPlus] EnemyHud.UpdateHuds not found — the vanilla bar above teammates will not be hidden" },
		{ "{0} / Esc — закрыть", "{0} / Esc — close" },
		{ "Все плитки скрыты — включи их в настройках", "All tiles are hidden — enable them in settings" },
		{ "Пока ни один мод не подключён к хабу", "No mods are connected to the hub yet" },
		{ "Основное", "General" },
		{ "Клавиша хаба", "Hub key" },
		{ "Нажми клавишу...", "Press a key..." },
		{ "Надпись окна", "Window caption" },
		{ "Плиток в ряду", "Tiles per row" },
		{ "Рядов без прокрутки", "Rows without scrolling" },
		{ "Масштаб меню", "Menu scale" },
		{ "Плитки в хабе", "Tiles in hub" },
		{ "Пока ни один мод не подключён", "No mods connected yet" },
		{ "Сбросить всё", "Reset all" },
		{ "Изменения сохраняются сразу. Esc — назад", "Changes are saved immediately. Esc — back" },
		{ "Нажми новую клавишу (можно с Ctrl/Shift/Alt). Esc — отмена", "Press a new key (Ctrl/Shift/Alt allowed). Esc — cancel" },
		{ "Клавиша, открывающая общее меню модов", "Key that opens the shared mod menu" },
		{ "Надпись внизу окна хаба", "Caption at the bottom of the hub window" },
		{ "Сколько рядов видно без прокрутки", "How many rows are visible without scrolling" },
		{ "Масштаб всех меню, использующих ModHub", "Scale of all menus that use ModHub" },
		{ "id плиток через запятую, которые скрыты в хабе (удобнее менять в самом хабе через шестерёнку)", "Comma-separated tile ids hidden in the hub (easier to change in the hub itself via the gear)" }
	};
}