SagaSixVikings.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using SixVikings;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SagaSixVikings")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+647996836758923c0de9c8ad4e96c94fa372f426")]
[assembly: AssemblyProduct("SagaSixVikings")]
[assembly: AssemblyTitle("SagaSixVikings")]
[assembly: AssemblyVersion("1.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SixVikings
{
	internal static class Langue
	{
		internal static bool Francais
		{
			get
			{
				try
				{
					return Localization.instance.GetSelectedLanguage() == "French";
				}
				catch (Exception)
				{
					return false;
				}
			}
		}

		internal static string T(string francais, string anglais)
		{
			if (!Francais)
			{
				return anglais;
			}
			return francais;
		}

		internal static string Reglage(string anglais, string francais)
		{
			return anglais + "\nFR : " + francais;
		}
	}
}
namespace SagaSixVikings
{
	[BepInPlugin("sixvikings.saga", "Saga des Six Vikings", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static class MortDuJoueur
		{
			private static void Prefix(Player __instance, out int __state)
			{
				__state = (((Object)(object)__instance == (Object)(object)Player.m_localPlayer) ? Stat((PlayerStatType)0) : (-1));
			}

			private static void Postfix(Player __instance, HitData ___m_lastHit, int __state)
			{
				try
				{
					int num = Stat((PlayerStatType)0);
					if (__state >= 0 && num > __state)
					{
						instance.AnnoncerMort(__instance, ___m_lastHit, num);
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)instance).Logger.LogError((object)ex);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Damage", new Type[] { typeof(HitData) })]
		private static class CoupPorte
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				try
				{
					instance.NoterTouche(__instance, hit);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)instance).Logger.LogError((object)ex);
				}
			}
		}

		[HarmonyPatch(typeof(Game), "RPC_RegisterKill")]
		private static class MonstreTue
		{
			private static void Postfix(string enemyName, int bossNumber)
			{
				try
				{
					instance.NoterMort(enemyName, bossNumber);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)instance).Logger.LogError((object)ex);
				}
			}
		}

		private class CommandeSaga : ConsoleCommand
		{
			public override string Name => "saga";

			public override string Help => Langue.T("Affiche la saga de mon personnage : morts, boss vaincus, monstres étoilés tués.", "Shows my character's saga: deaths, bosses defeated, starred monsters killed.");

			public override void Run(string[] args, Terminal context)
			{
				try
				{
					instance.AfficherSaga(context);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)instance).Logger.LogError((object)ex);
				}
			}
		}

		public const string ModGuid = "sixvikings.saga";

		public const string ModNom = "Saga des Six Vikings";

		public const string ModVersion = "1.1.0";

		private const string CleEtoiles1 = "SagaSixVikings.Etoiles1";

		private const string CleEtoiles2 = "SagaSixVikings.Etoiles2";

		private const string AideJetons = "Jetons : {nom}, {n} (ex. 14e), {lieu} (ex. dans les Marais), {tueur} ; {e} ajoute un e au féminin ; {il/elle} choisit selon le genre du personnage.";

		private const string AideJetonsAnglais = "Tokens: {name}, {n} (e.g. 14th), {place} (e.g. in the Swamp), {killer}; {he/she}, {his/her}, {him/her} pick by the character's gender.";

		private const string SectionAnglaise = "Messages (English)";

		private static Plugin instance;

		private ConfigEntry<bool> annonces;

		private ConfigEntry<bool> sagaPourTous;

		private readonly Dictionary<string, ConfigEntry<string>> repliques = new Dictionary<string, ConfigEntry<string>>();

		private readonly Dictionary<string, ConfigEntry<string>> repliquesAnglaises = new Dictionary<string, ConfigEntry<string>>();

		private readonly Random hasard = new Random();

		private string derniereReplique;

		private readonly SuiviEtoiles<ZDOID> etoiles = new SuiviEtoiles<ZDOID>(5f);

		private readonly int[] etoilesDues = new int[2];

		private ZDOMan partieConnue;

		private void Awake()
		{
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			instance = this;
			annonces = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Annonces", true, Langue.Reglage("Announce my deaths in chat, with a shout every player sees.", "Annoncer mes morts dans le chat, par un cri que tous les joueurs voient."));
			sagaPourTous = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SagaVisibleParTous", true, Langue.Reglage("true: /saga shouts my stats to everyone. false: only I see them.", "true : /saga crie mes stats à tout le monde. false : je suis seul à les voir."));
			string[] causes = SagaTexte.Causes;
			foreach (string text in causes)
			{
				repliques[text] = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", text, SagaMessages.Francais[text], Langue.Reglage("French lines for the cause \"" + SagaTexte.Libelle(text, francais: false) + "\", separated by |. Used when the game is in French.", "Répliques pour la cause « " + SagaTexte.Libelle(text) + " », séparées par |. Jetons : {nom}, {n} (ex. 14e), {lieu} (ex. dans les Marais), {tueur} ; {e} ajoute un e au féminin ; {il/elle} choisit selon le genre du personnage."));
				string text2 = SagaTexte.Migrer(repliques[text].Value, SagaMessages.Anciennes[text], SagaMessages.Francais[text]);
				if (text2 != repliques[text].Value)
				{
					repliques[text].Value = text2;
				}
				repliquesAnglaises[text] = ((BaseUnityPlugin)this).Config.Bind<string>("Messages (English)", text, SagaMessages.Anglais[text], Langue.Reglage("Lines for the cause \"" + SagaTexte.Libelle(text, francais: false) + "\", separated by |. Used when the game is in any language but French. Tokens: {name}, {n} (e.g. 14th), {place} (e.g. in the Swamp), {killer}; {he/she}, {his/her}, {him/her} pick by the character's gender.", "Répliques anglaises pour la cause « " + SagaTexte.Libelle(text) + " », quand le jeu n'est pas en français."));
			}
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new CommandeSaga());
			new Harmony("sixvikings.saga").PatchAll(typeof(Plugin).Assembly);
		}

		private void AnnoncerMort(Player joueur, HitData coup, int morts)
		{
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			if (annonces.Value && !((Object)(object)Chat.instance == (Object)null))
			{
				bool francais = Langue.Francais;
				Dictionary<string, ConfigEntry<string>> dictionary = (francais ? repliques : repliquesAnglaises);
				string text = SagaTexte.Cause(((object)Unsafe.As<HitType, HitType>(ref coup?.m_hitType)/*cast due to .constrained prefix*/).ToString());
				Character val = ((coup != null) ? coup.GetAttacker() : null);
				string text2 = "";
				if ((Object)(object)val == (Object)(object)joueur)
				{
					text = "SoiMeme";
				}
				else if ((Object)(object)val != (Object)null)
				{
					Player val2 = (Player)(object)((val is Player) ? val : null);
					text2 = (((Object)(object)val2 != (Object)null) ? val2.GetPlayerName() : SagaTexte.NomAvecEtoiles(Localization.instance.Localize(val.GetHoverName()), val.GetLevel(), francais));
				}
				if (text2.Length == 0 && (text == "Monstre" || text == "Joueur"))
				{
					text = "Defaut";
				}
				List<string> list = SagaTexte.Decouper(dictionary[text].Value);
				if (list.Count == 0)
				{
					list = SagaTexte.Decouper(dictionary["Defaut"].Value);
				}
				string text3 = SagaTexte.Tirer(list, hasard, derniereReplique);
				if (text3 != null)
				{
					derniereReplique = text3;
					Dictionary<string, string> valeurs = SagaTexte.Valeurs(NomDe(joueur), SagaTexte.Ordinal(morts, francais), text2, SagaTexte.Lieu(((object)joueur.GetCurrentBiome()/*cast due to .constrained prefix*/).ToString(), francais));
					Crier(SagaTexte.Rendre(text3, valeurs, joueur.GetPlayerModel() == 1));
				}
			}
		}

		private static void Crier(string texte)
		{
			Chat.instance.SendText((Type)2, texte.Replace("$", ""));
		}

		private static string NomDe(Player joueur)
		{
			string text = joueur.GetPlayerName();
			if (string.IsNullOrEmpty(text) || text == "...")
			{
				PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
				if (val != null)
				{
					text = val.GetName();
				}
			}
			return text;
		}

		private void NoterTouche(Character cible, HitData coup)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (cible.GetLevel() < 2 || cible.IsPlayer() || cible.IsTamed())
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && coup != null && !((Object)(object)coup.GetAttacker() != (Object)(object)localPlayer))
			{
				OublierSiNouvellePartie();
				ZDOID zDOID = cible.GetZDOID();
				if (zDOID != ZDOID.None)
				{
					etoiles.Noter(zDOID, cible.m_name, cible.GetLevel());
				}
			}
		}

		private void NoterMort(string nomEnnemi, int numeroBoss)
		{
			OublierSiNouvellePartie();
			if (numeroBoss == 0 && ZDOMan.instance != null)
			{
				Devoir(etoiles.Annoncer(nomEnnemi, Existe, Time.time));
			}
		}

		private void Update()
		{
			if (!etoiles.EnAttente && etoilesDues[0] + etoilesDues[1] == 0)
			{
				return;
			}
			try
			{
				if ((Object)(object)Game.instance == (Object)null || Game.instance.IsShuttingDown() || ZDOMan.instance == null)
				{
					EffacerArdoise();
					return;
				}
				OublierSiNouvellePartie();
				Devoir(etoiles.Regler(Existe, Time.time));
				Crediter();
			}
			catch (Exception ex)
			{
				EffacerArdoise();
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
			}
		}

		private static bool Existe(ZDOID id)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return ZDOMan.instance.GetZDO(id) != null;
		}

		private void Devoir(List<int> niveaux)
		{
			foreach (int item in niveaux)
			{
				etoilesDues[(item >= 3) ? 1u : 0u]++;
			}
		}

		private void Crediter()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				Ajouter(localPlayer.m_customData, "SagaSixVikings.Etoiles1", etoilesDues[0]);
				Ajouter(localPlayer.m_customData, "SagaSixVikings.Etoiles2", etoilesDues[1]);
				etoilesDues[0] = (etoilesDues[1] = 0);
			}
		}

		private static void Ajouter(Dictionary<string, string> donnees, string cle, int combien)
		{
			if (combien > 0)
			{
				donnees[cle] = (Compteur(donnees, cle) + combien).ToString(CultureInfo.InvariantCulture);
			}
		}

		private void EffacerArdoise()
		{
			etoiles.Oublier();
			etoilesDues[0] = (etoilesDues[1] = 0);
		}

		private void OublierSiNouvellePartie()
		{
			if (ZDOMan.instance != partieConnue)
			{
				partieConnue = ZDOMan.instance;
				etoiles.Oublier();
			}
		}

		private void AfficherSaga(Terminal terminal)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				if ((Object)(object)terminal != (Object)null)
				{
					terminal.AddString(Langue.T("La saga se consulte en jeu, avec un personnage.", "The saga can only be read in game, with a character."));
				}
				return;
			}
			bool francais = Langue.Francais;
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			string[] names = Enum.GetNames(typeof(HitType));
			foreach (string text in names)
			{
				if (Enum.TryParse<PlayerStatType>("DeathBy" + text, out PlayerStatType result))
				{
					string key = SagaTexte.Cause(text);
					dictionary.TryGetValue(key, out var value);
					dictionary[key] = value + Stat(result);
				}
			}
			string text2 = SagaTexte.Resume(NomDe(localPlayer), Stat((PlayerStatType)0), dictionary, Stat((PlayerStatType)85), Compteur(localPlayer.m_customData, "SagaSixVikings.Etoiles1"), Compteur(localPlayer.m_customData, "SagaSixVikings.Etoiles2"), francais);
			if (sagaPourTous.Value && (Object)(object)Chat.instance != (Object)null)
			{
				Crier(text2);
			}
			else if ((Object)(object)terminal != (Object)null)
			{
				terminal.AddString(text2);
			}
		}

		private static int Stat(PlayerStatType type)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if (val == null || !val.m_playerStats[0].m_stats.TryGetValue(type, out var value))
			{
				return 0;
			}
			return (int)value;
		}

		private static int Compteur(Dictionary<string, string> donnees, string cle)
		{
			if (!donnees.TryGetValue(cle, out var value))
			{
				return 0;
			}
			return SagaTexte.LireCompteur(value);
		}
	}
	public class SuiviEtoiles<TId>
	{
		private struct Touche
		{
			public string Nom;

			public int Niveau;
		}

		private class Attente
		{
			public List<TId> Candidats;

			public float Limite;
		}

		private readonly Dictionary<TId, Touche> touches = new Dictionary<TId, Touche>();

		private readonly List<Attente> attentes = new List<Attente>();

		private readonly float delai;

		public bool EnAttente => attentes.Count > 0;

		public SuiviEtoiles(float delai)
		{
			this.delai = delai;
		}

		public void Noter(TId id, string nom, int niveau)
		{
			touches[id] = new Touche
			{
				Nom = nom,
				Niveau = niveau
			};
		}

		public List<int> Annoncer(string nom, Func<TId, bool> existe, float maintenant)
		{
			List<int> result = Regler(existe, maintenant);
			List<TId> list = (from t in touches
				where t.Value.Nom == nom && existe(t.Key)
				select t.Key).ToList();
			if (list.Count > 0)
			{
				attentes.Add(new Attente
				{
					Candidats = list,
					Limite = maintenant + delai
				});
			}
			return result;
		}

		public List<int> Regler(Func<TId, bool> existe, float maintenant)
		{
			List<int> list = new List<int>();
			int num = 0;
			while (num < attentes.Count)
			{
				Attente attente = attentes[num];
				int num2 = attente.Candidats.FindIndex((TId id) => touches.ContainsKey(id) && !existe(id));
				if (num2 >= 0)
				{
					TId key = attente.Candidats[num2];
					list.Add(touches[key].Niveau);
					touches.Remove(key);
					attentes.RemoveAt(num);
				}
				else if (maintenant > attente.Limite)
				{
					attentes.RemoveAt(num);
				}
				else
				{
					num++;
				}
			}
			return list;
		}

		public void Oublier()
		{
			touches.Clear();
			attentes.Clear();
		}
	}
	public static class SagaMessages
	{
		public static readonly Dictionary<string, string> Francais = new Dictionary<string, string>
		{
			{
				"Monstre",
				Liste("{nom} voulait juste dire bonjour. {tueur} n'était pas d'humeur.", "Cause officielle de la {n} mort : {tueur}. Cause réelle : {nom} voulait voir de plus près.", "{nom} a livré un combat digne des dieux {lieu}. {tueur} l'a gagné.", "{nom} contre {tueur} : c'était serré pendant deux secondes.", "{tueur} entre dans la légende. {nom} en sort pour la {n} fois.", "Colis {nom} bien réceptionné {lieu}. Signé : {tueur}.", "{n} strophe de la saga : {nom} rencontre {tueur}. Fin de la strophe.", "{nom} avait un plan {lieu}. {tueur} en avait un meilleur.", "{tueur} ajoute {nom} à son tableau de chasse {lieu}. Un trophée sans grande valeur.", "{nom} a tenté d'apprivoiser {tueur}. {tueur} a décliné l'offre.", "{n} mort pour {nom}. {tueur} réclame sa part du butin.", "Les scaldes chanteront les exploits de {tueur}. {nom} fera les chœurs, depuis le sol.")
			},
			{
				"Joueur",
				Liste("{nom} et {tueur} avaient juré de mourir ensemble. {tueur} a pris du retard.", "Règlement de comptes entre amis {lieu}. {tueur} 1, {nom} 0.", "{tueur} visait un monstre. Le monstre va bien, {nom} en est à sa {n} mort.", "Rappel à {tueur} : {nom} figure sur la liste des alliés. Enfin, figurait.", "{nom} meurt pour la {n} fois {lieu}. {tueur} a tenu à participer.", "Tir ami {lieu}. {tueur} a bien visé, mais pas le bon viking. Toutes nos excuses, {nom}.", "{tueur} jure que c'était un accident. {nom} prépare déjà sa vengeance.", "Le conseil du village examinera le cas de {tueur}. {nom} ne pourra pas témoigner.")
			},
			{
				"Chute",
				Liste("Odin a vu {nom} tomber {lieu}. Il a compté les rebonds.", "La descente, {nom} maîtrise. C'est l'arrivée qui pose problème.", "Contrôle de la gravité effectué par {nom} {lieu}. Résultat : conforme.", "{nom} n'est toujours pas un corbeau. {n} mort pour le vérifier.", "Les Valkyries emportent les braves au ciel. Pour sa {n} mort, {nom} a pris l'autre sens.", "{nom} a sauté {lieu}. Le sol, prévenu trop tard, n'a pas pu se pousser.", "{n} mort : {nom} regarde toujours où {il/elle} met les pieds. Juste après.", "{nom} a essayé de planer sans planeur {lieu}. Conclusion : il faut le planeur.", "Saut de l'ange signé {nom} {lieu}. L'ange, lui, avait des ailes.", "{nom} descend toujours plus vite que prévu. {n} mort, record personnel battu.")
			},
			{
				"Noyade",
				Liste("{nom} pensait avoir pied {lieu}. {Il/Elle} n'avait pas pied.", "Niveau de l'eau : stable. {nom} : nettement en dessous.", "{n} mort pour {nom} {lieu}. Sa pierre tombale flotte, elle.", "Pour la {n} fois, {nom} part pour le Valhalla. En passant par le fond.", "Un radeau, c'est vingt bouts de bois. {nom} a préféré une {n} mort.", "Les corbeaux ont cherché {nom} {lieu}. Ils n'ont trouvé que des bulles.", "Le service des eaux confirme : {nom} sait nager. Pas longtemps, mais {il/elle} sait.", "{nom} voulait attraper un poisson à mains nues {lieu}. Le poisson a gagné.", "Les Nornes avaient prévu une mort glorieuse. {nom} a préféré la baignade.", "{nom} vient de vérifier qu'une armure de fer ne flotte pas {lieu}.")
			},
			{
				"Arbre",
				Liste("{nom} a abattu un arbre {lieu}. L'arbre a rendu la politesse.", "Rappel de sécurité : un arbre tombe toujours du côté où se tient {nom}.", "{n} mort pour {nom}. Le bois de chauffage se défend mieux que les trolls.", "Pour la {n} fois, {nom} tombe au combat {lieu}. L'ennemi était un arbre.", "Bilan de coupe : du bois, de la résine, et {nom} en dessous.", "{nom} repose sous sa dernière victime. Les scaldes trouvent ça poétique.", "{nom} y reste pour la {n} fois. Et c'est {lui/elle} qui tenait la hache.", "On dit que les arbres ne bougent pas. {nom} ne le croira plus jamais.", "{nom} a crié « Attention ! » une seconde trop tard {lieu}.")
			},
			{
				"Defaut",
				Liste("{nom} est mort{e} {lieu}. Personne n'a vu, personne n'est surpris.", "{nom} est décédé{e}. Affaires à retirer sur place, compétences non remboursées.", "{nom} mourut en brave pour la {n} fois, dit la saga. La saga est polie.", "{nom} avait une seule consigne : rester en vie. {n} mort.", "Ici repose {nom} {lieu}. Enfin, surtout ses affaires.", "{n} mort pour {nom}. Cause inconnue, responsable bien connu{e}.", "Les Nornes avaient tissé un grand destin pour {nom}. Le fil a lâché {lieu}.", "La saga de {nom} gagne un chapitre {lieu}. Il est court.", "{nom} est mort{e}. Les dieux haussent les épaules.", "Odin inscrit une {n} mort au nom de {nom}. Il va manquer de parchemin.")
			},
			{
				"Feu",
				Liste("Les dieux ont offert le feu aux mortels. {nom} confirme : mortels.", "Alerte incendie {lieu}. Départ de feu identifié : {nom}.", "On brûlait jadis les héros une fois morts. Pour sa {n} mort, {nom} a inversé l'ordre.", "{nom} s'est approché{e} du feu pour se réchauffer. Objectif largement dépassé.", "{nom}, {n} mort, servi{e} bien cuit{e} {lieu}. Personne n'avait rien commandé.", "{nom} voulait un feu de camp. {Il/Elle} est devenu{e} le feu de camp.", "Pour sa {n} mort, {nom} a confondu se réchauffer et griller.")
			},
			{
				"Poison",
				Liste("{nom} gardait son hydromel antipoison pour une grande occasion. C'était sa {n} mort.", "Le poison a agi lentement {lieu}. {nom} a réagi encore plus lentement.", "{nom} rend l'âme pour la {n} fois. Par prudence, les corbeaux ne toucheront à rien.", "{nom} attendait que le poison passe. C'est {lui/elle} qui y est passé{e}.", "Les grands guerriers meurent debout. {nom} est mort{e} en se tenant le ventre.", "{nom} a goûté le champignon douteux {lieu}. Il était douteux.", "Une odeur de marais, un teint verdâtre : {nom} y passe pour la {n} fois.")
			},
			{
				"Froid",
				Liste("Températures en baisse {lieu}. {nom} a suivi la tendance.", "{nom} disait qu'il ne faisait pas si froid. {Il/Elle} ne dit plus rien.", "{n} mort : {nom} a claqué des dents, puis claqué tout court.", "{nom} a gelé {lieu}, figé{e} dans une pose héroïque. C'est déjà ça.", "Pour sa {n} mort, {nom} est sorti{e} sans cape. La cape est restée au chaud, dans un coffre.", "{nom} est désormais une statue de glace {lieu}. Très ressemblante.", "Pour sa {n} mort, {nom} a découvert que l'hydromel ne tient pas chaud.")
			},
			{
				"SoiMeme",
				Liste("{nom} porte plainte contre {nom}. Les deux parties ne se parlent plus.", "Odin demande qui a tué {nom} pour la {n} fois. {nom} lève la main.", "{nom} s'est occupé{e} de tout {lieu}. Les monstres n'ont rien eu à faire.", "Pour sa {n} mort, {nom} a enfin trouvé un adversaire à sa mesure : {lui/elle}-même.", "{nom} a enfin vaincu son pire ennemi {lieu}. Malheureusement, c'était {lui/elle}.", "Enquête close : {nom} est à la fois la victime et le coupable.")
			},
			{
				"Fumee",
				Liste("{nom} a construit quatre murs, un toit et un feu. Il manquait un trou.", "Pour la {n} fois, {nom} rend son dernier souffle. Il fallait le rendre dehors.", "{n} mort pour {nom}, fumé{e} comme un saumon, à domicile.", "Qualité de l'air chez {nom} : médiocre. État de l'occupant{e} : pire.", "{nom} a fumé du poisson toute la nuit. Le poisson va bien.", "{n} mort pour {nom}, terrassé{e} par sa propre maison. La cheminée était en option.")
			},
			{
				"Lave",
				Liste("{nom} a mis un pied dans la lave pour voir. On a vu.", "Pour la {n} fois, {nom} se présente devant Odin. Encore fumant{e}.", "Précision utile : le sol orange n'est pas du sol. {nom} y laisse sa {n} vie.", "{nom} a coupé par la lave pour gagner du temps. Du temps, {il/elle} en a maintenant.", "{nom} voulait vérifier la température de la lave {lieu}. Elle est chaude.", "Il reste de {nom} une odeur de grillé et une paire de bottes fondues.")
			},
			{
				"OceanBouillant",
				Liste("{nom} voulait entrer dans l'histoire. {Il/Elle} est entré{e} dans la soupe.", "Avis aux navigateurs : la mer bout. {nom} a tenu à s'en assurer.", "Pour sa {n} mort, {nom} a choisi le bain chaud. Version définitive.", "{nom} a plongé dans la mer bouillante {lieu}. Ça sentait le homard.", "Pour sa {n} mort, {nom} a été servi{e} en bouillon.")
			},
			{
				"BordDuMonde",
				Liste("La carte s'arrêtait là, {nom} non. {n} mort, la plus lointaine.", "Découverte du jour : le monde a un bord. {nom} ne pourra pas présenter ses conclusions.", "Le drakkar a passé le bord du monde. {nom} tient peut-être encore la barre.", "{nom} voulait voir ce qu'il y avait au bout du monde. Rien, et c'était mortel.")
			},
			{
				"Chariot",
				Liste("Thor traverse le ciel sur son char. {nom} a pris le sien sur la tête.", "{nom} tirait le chariot. Dans la descente, le chariot a pris les devants.", "{n} mort pour {nom}, passé{e} sous un chariot. Vitesse au moment des faits : au pas.", "{nom} vient d'apprendre qu'un chariot n'a pas de frein. Ni de klaxon.")
			},
			{
				"Bateau",
				Liste("Les grands rois reposent dans leur drakkar. {nom} repose sous le sien.", "{n} mort : {nom} s'est pris un bateau {lieu}. Oui, un bateau.", "Collision entre un bateau et {nom}. Seul le bateau est encore à flot.", "{nom} voulait monter à bord. Le bateau voulait passer. Le bateau est passé.")
			},
			{
				"Ecrasement",
				Liste("Les dieux ont envoyé un signe à {nom} {lieu}. Lourd, le signe.", "{nom} se tenait exactement là où ça tombait. {n} mort.", "{nom} n'a rien vu venir {lieu}. C'était pourtant gros et bruyant.", "{nom} a été aplati{e} {lieu}. Les scaldes l'appellent désormais la galette.")
			},
			{
				"Stalactite",
				Liste("La stalactite, c'est celle qui tombe. {nom} révise avec sa {n} mort.", "{nom} est entré{e} dans la grotte la tête haute. C'était précisément le problème.", "Inspection du plafond par {nom} : une stalactite manquante, retrouvée depuis.", "{nom} cherchait du fer. {Il/Elle} a trouvé du calcaire, de haut et très vite.")
			},
			{
				"Baliste",
				Liste("Notice de la baliste, page 1 : elle tire sur tout ce qui bouge. {nom} bougeait.", "{n} mort pour {nom} {lieu}. La baliste, elle, sait viser.", "{nom} a bâti une baliste pour garder son foyer. Elle le garde bien, même contre {nom}.", "{nom} a oublié de dire à sa baliste qu'{il/elle} était de la famille.")
			},
			{
				"Catapulte",
				Liste("{nom} a levé les yeux vers les dieux. C'était un rocher, et il arrivait vite.", "Bulletin météo : chutes de pierres isolées {lieu}, exclusivement sur {nom}.", "La catapulte visait ailleurs. Pour sa {n} mort, {nom} a fait le déplacement.", "{nom} a reçu un rocher {lieu}. Envoi recommandé, sans accusé de réception.")
			},
			{
				"PontLevis",
				Liste("Un pont-levis, ça monte et ça redescend. {nom} n'avait retenu que la moitié.", "{n} mort pour {nom} {lieu}. Arme du crime : la porte d'entrée.", "Le pont-levis devait arrêter les envahisseurs. Il a arrêté {nom}, net.", "{nom} attendait sous le pont-levis. Le pont-levis n'attend personne.")
			},
			{
				"Incinerateur",
				Liste("{n} mort pour {nom}, foudroyé{e} en sortant les poubelles.", "La saga dira que Thor en personne a foudroyé {nom}. C'était l'Oblitérateur.", "Distance de sécurité de l'Oblitérateur : trois pas. Distance retenue par {nom} : zéro.", "{nom} a voulu récupérer un objet jeté dans l'Oblitérateur. Mauvaise idée.")
			}
		};

		public static readonly Dictionary<string, string> Anglais = new Dictionary<string, string>
		{
			{
				"Monstre",
				Liste("{name} just wanted to say hello. {killer} was not in the mood.", "Official cause of the {n} death: {killer}. Real cause: {name} wanted a closer look.", "{name} fought a battle worthy of the gods {place}. {killer} won it.", "{name} versus {killer}: it was close for about two seconds.", "{killer} enters the legend. {name} leaves the world for the {n} time.", "Parcel {name} delivered {place}. Signed for by {killer}.", "The {n} verse of the saga: {name} meets {killer}. The end.", "{name} had a plan {place}. {killer} had a better one.", "{killer} adds {name} to the trophy wall {place}. Not a very valuable trophy.", "{name} tried to tame {killer}. {killer} declined the offer.", "{n} death for {name}. {killer} would like a share of the loot.", "The skalds will sing of {killer}. {name} will hum along, from the ground.")
			},
			{
				"Joueur",
				Liste("{name} and {killer} swore to die together. {killer} is running late.", "A friendly disagreement {place}. {killer} 1, {name} 0.", "{killer} was aiming at a monster. The monster is fine. {name} is on {his/her} {n} death.", "Reminder to {killer}: {name} is on the list of allies. Well, was.", "{name} dies for the {n} time {place}. {killer} insisted on helping.", "Friendly fire {place}. {killer} hit the right spot on the wrong viking. Sorry, {name}.", "{killer} swears it was an accident. {name} is already planning revenge.", "The village council will hear the case of {killer}. {name} will not be testifying.")
			},
			{
				"Chute",
				Liste("Odin watched {name} fall {place}. He counted the bounces.", "The way down, {name} has mastered. The landing still needs work.", "Gravity inspection carried out by {name} {place}. Result: working as intended.", "{name} is still not a raven. {His/Her} {n} death proves it.", "Valkyries carry the brave up to the sky. For {his/her} {n} death, {name} went the other way.", "{name} jumped {place}. The ground, warned too late, could not get out of the way.", "{n} death: {name} always looks where {he/she} puts {his/her} feet. Right after.", "{name} tried gliding without a glider {place}. Conclusion: you need the glider.", "Swan dive by {name} {place}. The swan, at least, had wings.", "{name} always comes down faster than planned. {n} death, a new personal best.")
			},
			{
				"Noyade",
				Liste("{name} thought {he/she} could touch the bottom {place}. {He/She} could not.", "Water level: stable. {name}: well below it.", "{n} death for {name} {place}. At least the gravestone floats.", "For the {n} time, {name} sets off for Valhalla. By way of the seabed.", "A raft is twenty sticks of wood. {name} chose death instead, for the {n} time.", "The ravens searched for {name} {place}. They only found bubbles.", "The harbour master confirms: {name} can swim. Not for long, but {he/she} can.", "{name} tried to catch a fish bare-handed {place}. The fish won.", "The Norns had planned a glorious death. {name} went for a swim instead.", "{name} learned the hard way that iron armour does not float {place}.")
			},
			{
				"Arbre",
				Liste("{name} cut down a tree {place}. The tree returned the favour.", "Safety reminder: a tree always falls on the side where {name} is standing.", "{n} death for {name}. Firewood fights back harder than trolls.", "For the {n} time, {name} falls in battle {place}. The enemy was a tree.", "Logging report: wood, resin, and {name} underneath.", "{name} rests beneath {his/her} last victim. The skalds find it poetic.", "{name} goes down for the {n} time. And {he/she} was the one holding the axe.", "They say trees don't move. {name} will never believe that again.", "{name} yelled timber one second too late {place}.")
			},
			{
				"Defaut",
				Liste("{name} died {place}. Nobody saw it, nobody is surprised.", "{name} has passed away. Belongings can be collected on site, skills not refunded.", "{name} died bravely for the {n} time, says the saga. The saga is being polite.", "{name} had one job: stay alive. {n} death.", "Here lies {name} {place}. Well, mostly {his/her} stuff.", "{n} death for {name}. Cause unknown, culprit well known.", "The Norns wove a great destiny for {name}. The thread snapped {place}.", "The saga of {name} gains a new chapter {place}. A short one.", "{name} died. The gods shrug.", "Odin writes down {name}'s {n} death. He is running out of parchment.")
			},
			{
				"Feu",
				Liste("The gods gave fire to mortals. {name} confirms: mortal.", "Fire alarm {place}. Source of the blaze identified: {name}.", "Heroes used to be burned after they died. For {his/her} {n} death, {name} swapped the order.", "{name} got close to the fire to warm up. Goal massively exceeded.", "{name}, {n} death, served well done {place}. Nobody ordered anything.", "{name} wanted a campfire. {He/She} became the campfire.", "For {his/her} {n} death, {name} mixed up warming up and grilling.")
			},
			{
				"Poison",
				Liste("{name} was saving {his/her} poison resistance mead for a special occasion. It was {his/her} {n} death.", "The poison acted slowly {place}. {name} reacted even more slowly.", "{name} gives up the ghost for the {n} time. To be safe, the ravens won't touch anything.", "{name} was waiting for the poison to wear off. {He/She} wore off first.", "Great warriors die standing. {name} died clutching {his/her} stomach.", "{name} tasted the suspicious mushroom {place}. It was suspicious.", "A swampy smell, a greenish glow: {name} goes down for the {n} time.")
			},
			{
				"Froid",
				Liste("Temperatures dropping {place}. {name} followed the trend.", "{name} said it wasn't that cold. {He/She} says nothing now.", "{n} death: {name}'s teeth chattered, then everything stopped.", "{name} froze {place}, locked in a heroic pose. That's something.", "For {his/her} {n} death, {name} went out without a cape. The cape stayed warm, in a chest.", "{name} is now an ice statue {place}. A very good likeness.", "For {his/her} {n} death, {name} learned that mead does not keep you warm.")
			},
			{
				"SoiMeme",
				Liste("{name} is suing {name}. Both parties have stopped speaking.", "Odin asks who killed {name} for the {n} time. {name} raises a hand.", "{name} took care of everything {place}. The monsters had nothing left to do.", "For {his/her} {n} death, {name} finally found a worthy opponent: {himself/herself}.", "{name} finally defeated {his/her} worst enemy {place}. Unfortunately, it was {himself/herself}.", "Case closed: {name} is both the victim and the culprit.")
			},
			{
				"Fumee",
				Liste("{name} built four walls, a roof and a fire. {He/She} forgot the hole.", "For the {n} time, {name} breathes {his/her} last. It should have been done outside.", "{n} death for {name}, smoked like a salmon, at home.", "Air quality at {name}'s place: poor. Condition of the occupant: worse.", "{name} smoked fish all night. The fish are fine.", "{n} death for {name}, done in by {his/her} own house. The chimney was optional.")
			},
			{
				"Lave",
				Liste("{name} put a foot in the lava, just to see. We saw.", "For the {n} time, {name} stands before Odin. Still smoking.", "Useful tip: orange ground is not ground. {name} leaves {his/her} {n} life there.", "{name} took a shortcut through the lava to save time. {He/She} has plenty of time now.", "{name} wanted to check the temperature of the lava {place}. It is hot.", "All that is left of {name} is a burnt smell and a pair of melted boots.")
			},
			{
				"OceanBouillant",
				Liste("{name} wanted to make history. {He/She} made soup.", "Notice to sailors: the sea is boiling. {name} insisted on checking.", "For {his/her} {n} death, {name} chose a hot bath. The permanent kind.", "{name} dived into the boiling sea {place}. It smelled like lobster.", "For {his/her} {n} death, {name} was served as broth.")
			},
			{
				"BordDuMonde",
				Liste("The map ended there, {name} did not. {n} death, the farthest one yet.", "Discovery of the day: the world has an edge. {name} won't be presenting the findings.", "The longship went over the edge of the world. {name} may still be at the helm.", "{name} wanted to see what was at the end of the world. Nothing, and it was deadly.")
			},
			{
				"Chariot",
				Liste("Thor crosses the sky in his chariot. {name} took a cart to the head.", "{name} was pulling the cart. On the way down, the cart took the lead.", "{n} death for {name}, run over by a cart. Speed at the time: walking pace.", "{name} just learned that carts have no brakes. Or horns.")
			},
			{
				"Bateau",
				Liste("Great kings rest in their longships. {name} rests under {his/hers}.", "{n} death: {name} got hit by a boat {place}. Yes, a boat.", "Collision between a boat and {name}. Only the boat is still afloat.", "{name} wanted to get on board. The boat wanted to get past. The boat got past.")
			},
			{
				"Ecrasement",
				Liste("The gods sent {name} a sign {place}. A heavy one.", "{name} stood exactly where it was falling. {n} death.", "{name} never saw it coming {place}. It was big and loud, though.", "{name} was flattened {place}. The skalds now call {him/her} the pancake.")
			},
			{
				"Stalactite",
				Liste("Stalactites hang from the ceiling, until they don't. {name} learns it for the {n} time.", "{name} walked into the cave head held high. That was precisely the problem.", "Ceiling inspection by {name}: one stalactite missing, since found.", "{name} was looking for iron. {He/She} found limestone instead, from above and very fast.")
			},
			{
				"Baliste",
				Liste("Ballista manual, page 1: it shoots anything that moves. {name} was moving.", "{n} death for {name} {place}. The ballista, at least, knows how to aim.", "{name} built a ballista to guard {his/her} home. It guards it well, even against {name}.", "{name} forgot to tell {his/her} ballista that {he/she} was family.")
			},
			{
				"Catapulte",
				Liste("{name} looked up to the gods. It was a rock, and it was coming fast.", "Weather report: scattered falling rocks {place}, exclusively on {name}.", "The catapult was aiming elsewhere. For {his/her} {n} death, {name} made the trip.", "{name} received a rock {place}. Express delivery, no signature required.")
			},
			{
				"PontLevis",
				Liste("A drawbridge goes up and comes down. {name} only remembered the first half.", "{n} death for {name} {place}. Murder weapon: the front door.", "The drawbridge was meant to stop invaders. It stopped {name}, dead.", "{name} was waiting under the drawbridge. The drawbridge waits for no one.")
			},
			{
				"Incinerateur",
				Liste("{n} death for {name}, struck by lightning while taking out the trash.", "The saga will say Thor himself struck down {name}. It was the Obliterator.", "Obliterator safety distance: three steps. Distance kept by {name}: zero.", "{name} tried to get something back from the Obliterator. Bad idea.")
			}
		};

		public static readonly Dictionary<string, string> Anciennes = new Dictionary<string, string>
		{
			{
				"Monstre",
				Liste("{nom} voulait juste dire bonjour. {tueur} n'était pas d'humeur.", "Cause de la {n} mort : {tueur}. Cause réelle : {nom} voulait voir de plus près.", "{nom} a livré un combat digne des dieux {lieu}. {tueur} l'a gagné.", "{nom} contre {tueur} : c'était serré pendant deux secondes.", "{tueur} entre dans la légende. {nom} en sort pour la {n} fois.", "Colis {nom} bien réceptionné {lieu}. Signé : {tueur}.", "{n} strophe de la saga : {nom} rencontre {tueur}. Fin de la strophe.", "{nom} avait un plan {lieu}. {tueur} en avait un meilleur.")
			},
			{
				"Joueur",
				Liste("{nom} et {tueur} avaient juré de mourir ensemble. {tueur} a pris du retard.", "Règlement de comptes entre amis {lieu}. {tueur} 1, {nom} 0.", "{tueur} visait un monstre. Le monstre va bien, {nom} en est à sa {n} mort.", "Rappel à {tueur} : {nom} figure sur la liste des alliés. Enfin, figurait.", "{nom} meurt pour la {n} fois {lieu}. {tueur} a tenu à participer.")
			},
			{
				"Chute",
				Liste("Odin a vu {nom} tomber {lieu}. Il a compté les rebonds.", "La descente, {nom} maîtrise. C'est l'arrivée qui pose problème.", "Contrôle de la gravité effectué par {nom} {lieu}. Résultat : conforme.", "{nom} n'est toujours pas un corbeau. {n} mort pour le vérifier.", "Les Valkyries emportent les braves au ciel. Pour sa {n} mort, {nom} a pris l'autre sens.", "{nom} a sauté {lieu}. Le sol, prévenu trop tard, n'a pas pu se déplacer.", "{n} mort : {nom} regarde toujours où {il/elle} met les pieds. Juste après.")
			},
			{
				"Noyade",
				Liste("{nom} pensait avoir pied {lieu}. {Il/Elle} n'avait pas pied.", "Niveau de l'eau : stable. {nom} : nettement en dessous.", "{n} mort pour {nom} {lieu}. Sa pierre tombale flotte, elle.", "Pour la {n} fois, {nom} part pour le Valhalla. En passant par le fond.", "Un radeau, c'est vingt bouts de bois. {nom} a préféré une {n} mort.", "Les corbeaux ont cherché {nom} {lieu}. Ils n'ont trouvé que des bulles.", "Le service des eaux confirme : {nom} sait nager. Pas longtemps, mais {il/elle} sait.")
			},
			{
				"Arbre",
				Liste("{nom} a abattu un arbre {lieu}. L'arbre a rendu la politesse.", "Rappel de sécurité : un arbre tombe toujours du côté où se tient {nom}.", "{n} mort pour {nom}. Le bois de chauffage se défend mieux que les trolls.", "Pour la {n} fois, {nom} tombe au combat {lieu}. L'ennemi était un arbre.", "Bilan de coupe : du bois, de la résine, et {nom} en dessous.", "{nom} repose sous sa dernière victime. Les scaldes trouvent ça poétique.", "{nom} y reste pour la {n} fois. Et c'est {lui/elle} qui tenait la hache.")
			},
			{
				"Defaut",
				Liste("{nom} est mort{e} {lieu}. Personne n'a vu, personne n'est surpris.", "{nom} est décédé{e}. Affaires à retirer sur place, compétences non remboursées.", "{nom} mourut en brave pour la {n} fois, dit la saga. La saga est polie.", "{nom} avait une seule consigne : rester en vie. {n} mort.", "Ici repose {nom} {lieu}. Enfin, surtout ses affaires.", "{n} mort pour {nom}. Cause inconnue, responsable bien connu{e}.", "Les Nornes avaient tissé un grand destin pour {nom}. Le fil a lâché {lieu}.")
			},
			{
				"Feu",
				Liste("Les dieux ont offert le feu aux mortels. {nom} confirme : mortels.", "Alerte incendie {lieu}. Départ de feu identifié : {nom}.", "On brûlait jadis les héros une fois morts. Pour sa {n} mort, {nom} a inversé l'ordre.", "{nom} s'est approché{e} du feu pour se réchauffer. Objectif largement dépassé.", "{nom}, {n} mort, servi{e} bien cuit{e} {lieu}. Personne n'avait rien commandé.")
			},
			{
				"Poison",
				Liste("{nom} gardait son hydromel antipoison pour une grande occasion. C'était sa {n} mort.", "Le poison a agi lentement {lieu}. {nom} a réagi encore plus lentement.", "{nom} rend l'âme pour la {n} fois. Par prudence, les corbeaux ne toucheront à rien.", "{nom} attendait que le poison passe. C'est {lui/elle} qui y est passé{e}.", "Les grands guerriers meurent debout. {nom} est mort{e} en se tenant le ventre.")
			},
			{
				"Froid",
				Liste("Températures en baisse {lieu}. {nom} a suivi la tendance.", "{nom} disait qu'il ne faisait pas si froid. {Il/Elle} ne dit plus rien.", "{n} mort : {nom} a claqué des dents, puis claqué tout court.", "{nom} a gelé {lieu}, figé{e} dans une pose héroïque. C'est déjà ça.", "Pour sa {n} mort, {nom} est sorti{e} sans cape. La cape est restée au chaud, dans un coffre.")
			},
			{
				"SoiMeme",
				Liste("{nom} porte plainte contre {nom}. Les deux parties ne se parlent plus.", "Odin demande qui a tué {nom} pour la {n} fois. {nom} lève la main.", "{nom} s'est occupé{e} de tout {lieu}. Les monstres n'ont rien eu à faire.", "Pour sa {n} mort, {nom} a enfin trouvé un adversaire à sa mesure : {lui/elle}-même.")
			},
			{
				"Fumee",
				Liste("{nom} a construit quatre murs, un toit et un feu. Il manquait un trou.", "Pour la {n} fois, {nom} rend son dernier souffle. Il fallait le rendre dehors.", "{n} mort pour {nom}, fumé{e} comme un saumon, à domicile.", "Qualité de l'air chez {nom} : médiocre. État de l'occupant{e} : pire.")
			},
			{
				"Lave",
				Liste("{nom} a mis un pied dans la lave pour voir. On a vu.", "Pour la {n} fois, {nom} se présente devant Odin. Encore fumant{e}.", "Précision utile : le sol orange n'est pas du sol. {nom} y laisse sa {n} vie.", "{nom} a coupé par la lave pour gagner du temps. Du temps, {il/elle} en a maintenant.")
			},
			{
				"OceanBouillant",
				Liste("{nom} voulait entrer dans l'histoire. {Il/Elle} est entré{e} dans la soupe.", "Avis aux navigateurs : la mer bout. {nom} a tenu à s'en assurer.", "Pour sa {n} mort, {nom} a choisi le bain chaud. Version définitive.")
			},
			{
				"BordDuMonde",
				Liste("La carte s'arrêtait là, {nom} non. {n} mort, la plus lointaine.", "Découverte du jour : le monde a un bord. {nom} ne pourra pas présenter ses conclusions.", "Le drakkar a passé le bord du monde. {nom} tient peut-être encore la barre.")
			},
			{
				"Chariot",
				Liste("Thor traverse le ciel sur son char. {nom} a pris le sien sur la tête.", "{nom} tirait le chariot. Dans la descente, le chariot a pris les devants.", "{n} mort pour {nom}, passé{e} sous un chariot. Vitesse au moment des faits : au pas.")
			},
			{
				"Bateau",
				Liste("Les grands rois reposent dans leur drakkar. {nom} repose sous le sien.", "{n} mort : {nom} s'est pris un bateau {lieu}. Oui, un bateau.", "Collision entre un bateau et {nom}. Seul le bateau est encore à flot.")
			},
			{
				"Ecrasement",
				Liste("Les dieux ont envoyé un signe à {nom} {lieu}. Lourd, le signe.", "{nom} se tenait exactement là où ça tombait. {n} mort.", "{nom} n'a rien vu venir {lieu}. C'était pourtant gros et bruyant.")
			},
			{
				"Stalactite",
				Liste("La stalactite, c'est celle qui tombe. {nom} révise avec sa {n} mort.", "{nom} est entré{e} dans la grotte la tête haute. C'était précisément le problème.", "Inspection du plafond par {nom} : une stalactite manquante, retrouvée depuis.")
			},
			{
				"Baliste",
				Liste("Notice de la baliste, page 1 : elle tire sur tout ce qui bouge. {nom} bougeait.", "{n} mort pour {nom} {lieu}. La baliste, elle, sait viser.", "{nom} a bâti une baliste pour garder son foyer. Elle le garde bien, même contre {nom}.")
			},
			{
				"Catapulte",
				Liste("{nom} a levé les yeux vers les dieux. C'était un rocher, et il arrivait vite.", "Bulletin météo : chutes de pierres isolées {lieu}, exclusivement sur {nom}.", "La catapulte visait ailleurs. Pour sa {n} mort, {nom} a fait le déplacement.")
			},
			{
				"PontLevis",
				Liste("Un pont-levis, ça monte et ça redescend. {nom} n'avait retenu que la moitié.", "{n} mort pour {nom} {lieu}. Arme du crime : la porte d'entrée.", "Le pont-levis devait arrêter les envahisseurs. Il a arrêté {nom}, net.")
			},
			{
				"Incinerateur",
				Liste("{n} mort pour {nom}, foudroyé{e} en sortant les poubelles.", "La saga dira que Thor en personne a foudroyé {nom}. C'était l'Oblitérateur.", "Distance de sécurité de l'Oblitérateur : trois pas. Distance retenue par {nom} : zéro.")
			}
		};

		public static Dictionary<string, string> Pour(bool francais)
		{
			if (!francais)
			{
				return Anglais;
			}
			return Francais;
		}

		private static string Liste(params string[] repliques)
		{
			return string.Join(" | ", repliques);
		}
	}
	public static class SagaTexte
	{
		public const string CauseDefaut = "Defaut";

		public const string CauseMonstre = "Monstre";

		public const string CauseJoueur = "Joueur";

		public const string CauseSoiMeme = "SoiMeme";

		private static readonly Dictionary<string, string> Libelles = new Dictionary<string, string>
		{
			{ "Monstre", "monstre" },
			{ "Joueur", "joueur" },
			{ "Chute", "chute" },
			{ "Noyade", "noyade" },
			{ "Feu", "feu" },
			{ "Lave", "lave" },
			{ "OceanBouillant", "océan bouillant" },
			{ "Froid", "froid" },
			{ "Poison", "poison" },
			{ "Fumee", "fumée" },
			{ "Arbre", "arbre" },
			{ "BordDuMonde", "bord du monde" },
			{ "Chariot", "chariot" },
			{ "Bateau", "bateau" },
			{ "Ecrasement", "écrasement" },
			{ "Stalactite", "stalactite" },
			{ "Baliste", "baliste" },
			{ "Catapulte", "catapulte" },
			{ "PontLevis", "pont-levis" },
			{ "Incinerateur", "incinérateur" },
			{ "SoiMeme", "soi-même" },
			{ "Defaut", "mystère" }
		};

		private static readonly Dictionary<string, string> LibellesAnglais = new Dictionary<string, string>
		{
			{ "Monstre", "monster" },
			{ "Joueur", "player" },
			{ "Chute", "fall" },
			{ "Noyade", "drowning" },
			{ "Feu", "fire" },
			{ "Lave", "lava" },
			{ "OceanBouillant", "boiling sea" },
			{ "Froid", "cold" },
			{ "Poison", "poison" },
			{ "Fumee", "smoke" },
			{ "Arbre", "tree" },
			{ "BordDuMonde", "edge of the world" },
			{ "Chariot", "cart" },
			{ "Bateau", "boat" },
			{ "Ecrasement", "crushed" },
			{ "Stalactite", "stalactite" },
			{ "Baliste", "ballista" },
			{ "Catapulte", "catapult" },
			{ "PontLevis", "drawbridge" },
			{ "Incinerateur", "obliterator" },
			{ "SoiMeme", "self" },
			{ "Defaut", "mystery" }
		};

		public static readonly string[] Causes = Libelles.Keys.ToArray();

		private static readonly Dictionary<string, string> CauseParHitType = new Dictionary<string, string>
		{
			{ "EnemyHit", "Monstre" },
			{ "PlayerHit", "Joueur" },
			{ "Fall", "Chute" },
			{ "Drowning", "Noyade" },
			{ "Burning", "Feu" },
			{ "CinderFire", "Feu" },
			{ "AshlandsLava", "Lave" },
			{ "AshlandsOcean", "OceanBouillant" },
			{ "Freezing", "Froid" },
			{ "Poisoned", "Poison" },
			{ "Smoke", "Fumee" },
			{ "Tree", "Arbre" },
			{ "EdgeOfWorld", "BordDuMonde" },
			{ "Cart", "Chariot" },
			{ "Boat", "Bateau" },
			{ "Impact", "Ecrasement" },
			{ "Structural", "Ecrasement" },
			{ "Stalagtite", "Stalactite" },
			{ "Turret", "Baliste" },
			{ "Catapult", "Catapulte" },
			{ "DrawBridge", "PontLevis" },
			{ "Incinerator", "Incinerateur" },
			{ "Self", "SoiMeme" }
		};

		private static readonly Dictionary<string, string> Lieux = new Dictionary<string, string>
		{
			{ "Meadows", "dans les Prairies" },
			{ "Swamp", "dans les Marais" },
			{ "Mountain", "en Montagne" },
			{ "BlackForest", "dans la Forêt noire" },
			{ "Plains", "dans les Plaines" },
			{ "AshLands", "dans les Terres de cendres" },
			{ "DeepNorth", "dans le Grand Nord" },
			{ "Ocean", "en pleine mer" },
			{ "Mistlands", "dans les Terres brumeuses" }
		};

		private static readonly Dictionary<string, string> LieuxAnglais = new Dictionary<string, string>
		{
			{ "Meadows", "in the Meadows" },
			{ "Swamp", "in the Swamp" },
			{ "Mountain", "in the Mountains" },
			{ "BlackForest", "in the Black Forest" },
			{ "Plains", "on the Plains" },
			{ "AshLands", "in the Ashlands" },
			{ "DeepNorth", "in the Deep North" },
			{ "Ocean", "out at sea" },
			{ "Mistlands", "in the Mistlands" }
		};

		private static readonly Regex Jeton = new Regex("\\{([^{}]*)\\}");

		private const string Voyelles = "aeiouàáâäåæéèêëíîïóôöøœúùûüý";

		public static string Ordinal(int n, bool francais = true)
		{
			if (n < 1)
			{
				n = 1;
			}
			if (francais)
			{
				if (n != 1)
				{
					return n + "e";
				}
				return "1re";
			}
			int num = n % 100;
			if (num >= 11 && num <= 13)
			{
				return n + "th";
			}
			return (n % 10) switch
			{
				1 => n + "st", 
				2 => n + "nd", 
				3 => n + "rd", 
				_ => n + "th", 
			};
		}

		public static string Cause(string hitType)
		{
			if (hitType == null || !CauseParHitType.TryGetValue(hitType, out var value))
			{
				return "Defaut";
			}
			return value;
		}

		public static string Libelle(string cause, bool francais = true)
		{
			Dictionary<string, string> dictionary = (francais ? Libelles : LibellesAnglais);
			if (cause == null || !dictionary.TryGetValue(cause, out var value))
			{
				return dictionary["Defaut"];
			}
			return value;
		}

		public static string NomAvecEtoiles(string nom, int niveau, bool francais = true)
		{
			if (niveau < 2)
			{
				return nom;
			}
			if (francais)
			{
				return nom + " (" + (niveau - 1) + ((niveau == 2) ? " étoile)" : " étoiles)");
			}
			return nom + " (" + (niveau - 1) + ((niveau == 2) ? " star)" : " stars)");
		}

		public static string Lieu(string biome, bool francais = true)
		{
			if (biome == null || !(francais ? Lieux : LieuxAnglais).TryGetValue(biome, out var value))
			{
				return "";
			}
			return value;
		}

		public static Dictionary<string, string> Valeurs(string nom, string rang, string tueur, string lieu)
		{
			return new Dictionary<string, string>
			{
				{ "nom", nom },
				{ "name", nom },
				{ "n", rang },
				{ "tueur", tueur },
				{ "killer", tueur },
				{ "lieu", lieu },
				{ "place", lieu }
			};
		}

		public static string Migrer(string valeur, string ancienne, string nouvelle)
		{
			if (!(valeur == ancienne))
			{
				return valeur;
			}
			return nouvelle;
		}

		public static List<string> Decouper(string liste)
		{
			List<string> list = new List<string>();
			if (liste == null)
			{
				return list;
			}
			string[] array = liste.Split(new char[1] { '|' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					list.Add(text);
				}
			}
			return list;
		}

		public static string Tirer(IList<string> messages, Random hasard, string precedent)
		{
			if (messages == null || messages.Count == 0)
			{
				return null;
			}
			List<string> list = messages.Where((string m) => m != precedent).ToList();
			if (list.Count == 0)
			{
				return messages[0];
			}
			return list[hasard.Next(list.Count)];
		}

		public static string Rendre(string modele, IDictionary<string, string> valeurs, bool feminin)
		{
			if (string.IsNullOrEmpty(modele))
			{
				return "";
			}
			return Regex.Replace(Regex.Replace(Jeton.Replace(modele, delegate(Match m)
			{
				string value = m.Groups[1].Value;
				int num = value.IndexOf('/');
				if (num >= 0)
				{
					if (!feminin)
					{
						return value.Substring(0, num);
					}
					return value.Substring(num + 1);
				}
				if (value == "e")
				{
					if (!feminin)
					{
						return "";
					}
					return "e";
				}
				if (valeurs != null)
				{
					foreach (KeyValuePair<string, string> valeur in valeurs)
					{
						if (string.Equals(valeur.Key, value, StringComparison.OrdinalIgnoreCase))
						{
							return valeur.Value ?? "";
						}
					}
				}
				return m.Value;
			}), "\\s{2,}", " "), "\\s+([.,])", "$1").Trim();
		}

		public static int LireCompteur(string valeur)
		{
			if (!int.TryParse(valeur, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0)
			{
				return 0;
			}
			return result;
		}

		public static string Resume(string nom, int morts, IDictionary<string, int> mortsParCause, int boss, int etoiles1, int etoiles2, bool francais = true)
		{
			string text = string.Join(", ", from p in (from p in mortsParCause
					where p.Value > 0
					select new
					{
						Texte = Libelle(p.Key, francais),
						Nombre = p.Value
					} into p
					orderby p.Nombre descending
					select p).ThenBy(p => p.Texte, StringComparer.Ordinal).Take(3)
				select p.Texte + " x" + p.Nombre);
			int num = etoiles1 + etoiles2;
			if (!francais)
			{
				return "Saga of " + nom + ": " + Compter(morts, "no deaths", "death", "deaths") + ((text.Length > 0) ? (" (" + text + ")") : "") + " - " + Compter(boss, "no bosses defeated", "boss defeated", "bosses defeated") + " - " + Compter(num, "no starred monsters killed", "starred monster killed", "starred monsters killed") + ((etoiles2 <= 0) ? "" : ((etoiles2 < num) ? (" (" + etoiles2 + " of them with two stars)") : ((num == 1) ? " (with two stars)" : " (all with two stars)")));
			}
			return "Saga " + De(nom) + " : " + Compter(morts, "aucune mort", "mort", "morts") + ((text.Length > 0) ? (" (" + text + ")") : "") + " - " + Compter(boss, "aucun boss vaincu", "boss vaincu", "boss vaincus") + " - " + Compter(num, "aucun monstre étoilé tué", "monstre étoilé tué", "monstres étoilés tués") + ((etoiles2 > 0) ? (" (dont " + etoiles2 + " à deux étoiles)") : "");
		}

		private static string De(string nom)
		{
			if (string.IsNullOrEmpty(nom))
			{
				return "de " + nom;
			}
			char c = char.ToLowerInvariant(nom[0]);
			return (("aeiouàáâäåæéèêëíîïóôöøœúùûüý".IndexOf(c) >= 0 || (c == 'y' && nom.Length > 1 && "aeiouàáâäåæéèêëíîïóôöøœúùûüý".IndexOf(char.ToLowerInvariant(nom[1])) < 0)) ? "d'" : "de ") + nom;
		}

		private static string Compter(int n, string aucun, string un, string plusieurs)
		{
			if (n <= 0)
			{
				return aucun;
			}
			return n + " " + ((n == 1) ? un : plusieurs);
		}
	}
}