diff --git a/TeammateRevive/Configuration/PluginConfig.cs b/TeammateRevive/Configuration/PluginConfig.cs index ea271f9..33cecbe 100644 --- a/TeammateRevive/Configuration/PluginConfig.cs +++ b/TeammateRevive/Configuration/PluginConfig.cs @@ -220,10 +220,16 @@ static BindCollection BindRuleValues(ConfigFile configFile, ReviveRuleValues val defaultValue: values.CutReviveeHp) .Bind( key: "Death Curse chance", - description: "[Only with Death Curse enabled] Chance to receive Death Curse on revival (Range: 0-100%)", + description: "[Only with Death Curse enabled] Chance to receive Death Curse on revival for revivee (Range: 0-100%)", set: v => values.DeathCurseChance = v, defaultValue: values.DeathCurseChance, metadata: new FloatMetadata(0, 100, 1)) + .Bind( + key: "Reviver Death Curse chance", + description: "[Only with Death Curse enabled] Chance to receive Death Curse on revival for reviver (Range: 0-100%)", + set: v => values.ReviverDeathCurseChance = v, + defaultValue: values.ReviverDeathCurseChance, + metadata: new FloatMetadata(0, 100, 1)) .Bind( key: "Post revive regeneration time", description: "After reviving, 40% of revivee and linked revivers HP is restored. This value specify how long regeneration buff is active in seconds. If set to 0 - revive regen is disabled.", diff --git a/TeammateRevive/Content/Artifact/ArtifactBase.cs b/TeammateRevive/Content/Artifact/ArtifactBase.cs index a28cf6f..cba98d1 100644 --- a/TeammateRevive/Content/Artifact/ArtifactBase.cs +++ b/TeammateRevive/Content/Artifact/ArtifactBase.cs @@ -6,18 +6,11 @@ namespace TeammateRevive.Artifact { public abstract class ArtifactBase { - public abstract string ArtifactName { get; } public abstract string ArtifactLangTokenName { get; } - public abstract string ArtifactDescription { get; } public abstract Sprite ArtifactEnabledIcon { get; } public abstract Sprite ArtifactDisabledIcon { get; } public ArtifactDef ArtifactDef; public bool ArtifactEnabled => RunArtifactManager.instance?.IsArtifactEnabled(ArtifactDef) ?? false; - protected void CreateLang() - { - LanguageAPI.Add("ARTIFACT_" + ArtifactLangTokenName + "_NAME", ArtifactName); - LanguageAPI.Add("ARTIFACT_" + ArtifactLangTokenName + "_DESCRIPTION", ArtifactDescription); - } public abstract void Init(); diff --git a/TeammateRevive/Content/Artifact/DeathCurseArtifact.cs b/TeammateRevive/Content/Artifact/DeathCurseArtifact.cs index 6dbc5b5..adfd592 100644 --- a/TeammateRevive/Content/Artifact/DeathCurseArtifact.cs +++ b/TeammateRevive/Content/Artifact/DeathCurseArtifact.cs @@ -1,5 +1,6 @@ using RoR2; using TeammateRevive.Common; +using TeammateRevive.Localization; using TeammateRevive.Logging; using TeammateRevive.Resources; using TeammateRevive.Revive.Rules; @@ -8,19 +9,14 @@ namespace TeammateRevive.Artifact { public class DeathCurseArtifact : ArtifactBase - { - public override string ArtifactName => "Artifact of Death Curse"; + { public override string ArtifactLangTokenName => "DEATH_CURSE"; - public override string ArtifactDescription => - "Adds Death Curse on revive, but also adds Charon's Obol item that will make revive easier."; - public override Sprite ArtifactEnabledIcon => CustomResources.DeathCurseArtifactEnabledIcon; public override Sprite ArtifactDisabledIcon => CustomResources.DeathCurseArtifactDisabledIcon; public override void Init() { - CreateLang(); CreateArtifact(); } @@ -34,7 +30,7 @@ public void EnsureEnabled(ReviveRules rules) RunArtifactManager.instance.SetArtifactEnabledServer(ArtifactDef, false); Chat.SendBroadcastChat(new Chat.SimpleChatMessage { - baseToken = TextFormatter.Yellow("Artifact of Death Curse is disabled because run started in single player.") + baseToken = TextFormatter.Yellow(Language.GetString(LanguageConsts.ARTIFACT_DEATH_CURSE_DISABLED)) }); return; } @@ -46,7 +42,7 @@ public void EnsureEnabled(ReviveRules rules) && !ArtifactEnabled && NetworkHelper.IsServer ) { - var message = "Artifact of Death Curse is enforced by server."; + var message = Language.GetString(LanguageConsts.ARTIFACT_DEATH_CURSE_ENFORCED_BY_SERVER); RunArtifactManager.instance.SetArtifactEnabledServer(ArtifactDef, true); Log.Info(message); Chat.SendBroadcastChat(new Chat.SimpleChatMessage diff --git a/TeammateRevive/Content/CharonsObol.cs b/TeammateRevive/Content/CharonsObol.cs index 471e839..15e7bff 100644 --- a/TeammateRevive/Content/CharonsObol.cs +++ b/TeammateRevive/Content/CharonsObol.cs @@ -1,25 +1,19 @@ using R2API; using RoR2; +using TeammateRevive.Localization; using TeammateRevive.Resources; using UnityEngine; -using static TeammateRevive.Common.TextFormatter; namespace TeammateRevive.Content { public class CharonsObol : ContentBase { public static string Name = "ITEM_CharonsObol"; - public static string NameToken = "Charon's Obol"; + public static string NameToken = LanguageConsts.ITEM_CHARONS_OBOL_NAME; public static ItemIndex Index; public override void Init() { - var full = - $"- Reduces {Yellow("revival time")}. " + - $"\n- Can be consumed for {Yellow("instant")} revival without Death Curse. " + - $"\n- {Yellow("Removes")} additional {Red("Death Curse")} on stage change." + - $"\n- Increase {Yellow("range")} and decrease {Yellow("damage")} for your revival."; - var generalScale = new Vector3(0.05f, 0.05f, 0.05f); var rules = new ItemDisplayRuleDict(new ItemDisplayRule[] { @@ -34,10 +28,11 @@ public override void Init() } }); - ItemAPI.Add(DeprecatedCustomItem.Create(Name, NameToken, - full, - full, - "Makes revival easier. Can be consumed for instant revival.", + ItemAPI.Add(DeprecatedCustomItem.Create(Name, + NameToken, + LanguageConsts.ITEM_CHARONS_OBOL_DESCRIPTION, + LanguageConsts.ITEM_CHARONS_OBOL_DESCRIPTION, + LanguageConsts.ITEM_CHARONS_OBOL_PICKUP, CustomResources.CharonsObolItemIcon, CustomResources.CharonsObolItemPrefab, ItemTier.Tier2, new[] { diff --git a/TeammateRevive/Content/DeadMansHandItem.cs b/TeammateRevive/Content/DeadMansHandItem.cs index e243671..520c3d4 100644 --- a/TeammateRevive/Content/DeadMansHandItem.cs +++ b/TeammateRevive/Content/DeadMansHandItem.cs @@ -1,26 +1,23 @@ using R2API; using RoR2; +using TeammateRevive.Localization; using TeammateRevive.Resources; -using static TeammateRevive.Common.TextFormatter; namespace TeammateRevive.Content { public class DeadMansHandItem : ContentBase { public static string Name = "ITEM_ReviveEverywhere"; - public static string NameToken = "Dead Man's Hand"; + public static string NameToken = LanguageConsts.ITEM_REVIVE_EVERYWHERE_NAME; public static ItemIndex Index; public override void Init() { - var full = - $"- {Yellow("Revive")} dead teammates {Yellow("everywhere")} on map." + - $"\n- First item {Red("increases")} {Yellow("revival time")} by x2." + - $"\n- Every subsequent item decreases {Yellow("revival time")} when you are reviving"; - - ItemAPI.Add(DeprecatedCustomItem.Create(Name, NameToken, - full, "Revive death teammates everywhere on map.", - "Revive death teammates everywhere on map", + ItemAPI.Add(DeprecatedCustomItem.Create(Name, + NameToken, + LanguageConsts.ITEM_REVIVE_EVERYWHERE_DESCRIPTION, + LanguageConsts.ITEM_REVIVE_EVERYWHERE_LORE, + LanguageConsts.ITEM_REVIVE_EVERYWHERE_PICKUP, CustomResources.LunarHandIcon, CustomResources.HandItemPrefab, ItemTier.Lunar, new[] { diff --git a/TeammateRevive/Content/DeathCurse.cs b/TeammateRevive/Content/DeathCurse.cs index 155e373..1cd079f 100644 --- a/TeammateRevive/Content/DeathCurse.cs +++ b/TeammateRevive/Content/DeathCurse.cs @@ -1,6 +1,7 @@ using R2API; using RoR2; using TeammateRevive.Common; +using TeammateRevive.Localization; using TeammateRevive.Resources; using TeammateRevive.Revive.Rules; using UnityEngine; @@ -16,7 +17,6 @@ public class DeathCurse : ContentBase public static string ItemName = "ITEM_DeathCurse"; public static string BuffName = "BUFF_DeathCurse"; - public static string NameToken = "Death curse"; public static ItemIndex ItemIndex; public static BuffIndex BuffIndex; @@ -48,9 +48,11 @@ public override void Init() static void CreateDeathCurseHiddenItem() { - ItemAPI.Add(DeprecatedCustomItem.Create(ItemName, NameToken, - "Reduces your max HP/Shield. Removed on next stage.", "Reduces your max HP/Shield. Removed on next stage.", - "ITEM_REDUCEHP_PICK", + ItemAPI.Add(DeprecatedCustomItem.Create(ItemName, + LanguageConsts.ITEM_DEATH_CURSE_NAME, + LanguageConsts.ITEM_DEATH_CURSE_DESCRIPTION, + LanguageConsts.ITEM_DEATH_CURSE_LORE, + LanguageConsts.ITEM_DEATH_CURSE_PICKUP, CustomResources.DeathCurseBuffIcon, CustomResources.CharonsObolItemPrefab, ItemTier.NoTier, new[] { diff --git a/TeammateRevive/Content/RevivalToken.cs b/TeammateRevive/Content/RevivalToken.cs index 62e2c3f..0b686fe 100644 --- a/TeammateRevive/Content/RevivalToken.cs +++ b/TeammateRevive/Content/RevivalToken.cs @@ -1,5 +1,6 @@ using R2API; using RoR2; +using TeammateRevive.Localization; using TeammateRevive.Resources; using UnityEngine; using CharacterBody = On.RoR2.CharacterBody; @@ -13,9 +14,11 @@ public class RevivalToken : ContentBase public override void Init() { - var description = "Reduces chance to receive curse"; - ItemAPI.Add(DeprecatedCustomItem.Create(Name, "Revival Token", - description, description, description, + ItemAPI.Add(DeprecatedCustomItem.Create(Name, + LanguageConsts.ITEM_REVIVAL_TOKEN_NAME, + LanguageConsts.ITEM_REVIVAL_TOKEN_DESCRIPTION, + LanguageConsts.ITEM_REVIVAL_TOKEN_DESCRIPTION, + LanguageConsts.ITEM_REVIVAL_TOKEN_DESCRIPTION, CustomResources.ReviveLinkBuffIcon, CustomResources.CharonsObolItemPrefab, ItemTier.NoTier, new[] { diff --git a/TeammateRevive/ContentManager.cs b/TeammateRevive/ContentManager.cs index 183c049..45c460f 100644 --- a/TeammateRevive/ContentManager.cs +++ b/TeammateRevive/ContentManager.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; -using On.RoR2; +using RoR2; using TeammateRevive.Artifact; using TeammateRevive.Content; +using TeammateRevive.Localization; using TeammateRevive.Logging; -using TeammateRevive.Resources; using TeammateRevive.Revive.Rules; +using BuffCatalog = On.RoR2.BuffCatalog; +using ItemCatalog = On.RoR2.ItemCatalog; using RoR2Content = RoR2.RoR2Content; namespace TeammateRevive @@ -24,16 +26,16 @@ public ContentManager(ReviveRules rules, RunTracker run, DeathCurseArtifact deat this.rules = rules; this.run = run; this.deathCurseArtifact = deathCurseArtifact; - Language.SetStringByToken += LanguageOnSetStringByToken; + On.RoR2.Language.SetStringByToken += LanguageOnSetStringByToken; On.RoR2.ItemCatalog.Init += ItemsOnInit; On.RoR2.BuffCatalog.Init += BuffsOnInit; } - private void LanguageOnSetStringByToken(Language.orig_SetStringByToken orig, RoR2.Language self, string token, string localizedstring) + private void LanguageOnSetStringByToken(On.RoR2.Language.orig_SetStringByToken orig, RoR2.Language self, string token, string localizedstring) { if (RoR2Content.Items.ShieldOnly && token == RoR2Content.Items.ShieldOnly.descriptionToken) { - localizedstring += " Teammate reviving will damage shield instead of health."; + localizedstring += Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_SHIELD_ONLY_POSTFIX); } orig(self, token, localizedstring); if (RoR2Content.Items.ShieldOnly && token == RoR2Content.Items.ShieldOnly.descriptionToken) diff --git a/TeammateRevive/Death Totem/DeathTotemTracker.cs b/TeammateRevive/Death Totem/DeathTotemTracker.cs index 7055d10..6749245 100644 --- a/TeammateRevive/Death Totem/DeathTotemTracker.cs +++ b/TeammateRevive/Death Totem/DeathTotemTracker.cs @@ -129,8 +129,6 @@ void CreateInteraction(GameObject gameObject) meshGo.AddIfMissing().entity = gameObject; - // game object need's collider in order to be interactible - // gameObject.AddIfMissing().sharedMesh = AddedAssets.CubeMesh; Log.DebugMethod("done"); } diff --git a/TeammateRevive/Debugging/ConsoleCommands.cs b/TeammateRevive/Debugging/ConsoleCommands.cs index cf964da..4de01b2 100644 --- a/TeammateRevive/Debugging/ConsoleCommands.cs +++ b/TeammateRevive/Debugging/ConsoleCommands.cs @@ -102,25 +102,29 @@ private void OnChatChanged() } DebugHelper.DamageTargetIndex = idx; AddLog($"Target set to {PlayersTracker.instance.All[DebugHelper.DamageTargetIndex].networkUser.userName}"); - } + }, + helpText = "Damage player with entered index" } }, { "trv_tglbuff", new RoR2.Console.ConCommand() { - action = args => DebugHelper.ToggleRegenBuff() + action = args => DebugHelper.ToggleRegenBuff(), + helpText = "Toggle post-revive regen buff" } }, { "trv_give_item", new RoR2.Console.ConCommand() { - action = args => PlayersTracker.instance.All.ForEach(p => p.GiveItem(ItemCatalog.FindItemIndex(args.GetArgString(0)))) + action = args => PlayersTracker.instance.All.ForEach(p => p.GiveItem(ItemCatalog.FindItemIndex(args.GetArgString(0)))), + helpText = "Give all players item by name" } }, { "trv_remove_item", new RoR2.Console.ConCommand() { - action = args => PlayersTracker.instance.All.ForEach(p => p.master.master.inventory.RemoveItem(ItemCatalog.FindItemIndex(args.GetArgString(0)))) + action = args => PlayersTracker.instance.All.ForEach(p => p.master.master.inventory.RemoveItem(ItemCatalog.FindItemIndex(args.GetArgString(0)))), + helpText = "Remove item by name from all players" } } }; diff --git a/TeammateRevive/Integrations/BetterUiModIntegration.cs b/TeammateRevive/Integrations/BetterUiModIntegration.cs index 5cc4df9..1a2c558 100644 --- a/TeammateRevive/Integrations/BetterUiModIntegration.cs +++ b/TeammateRevive/Integrations/BetterUiModIntegration.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using RoR2; using TeammateRevive.Content; +using TeammateRevive.Localization; using TeammateRevive.Logging; namespace TeammateRevive.Integrations @@ -27,9 +28,9 @@ void RegisterBuffs() { try { - BetterUI.Buffs.RegisterBuffInfo(BuffCatalog.GetBuffDef(DeathCurse.BuffIndex), "Death Curse", "Reduces your max HP/Shield."); - BetterUI.Buffs.RegisterBuffInfo(BuffCatalog.GetBuffDef(ReviveLink.Index), "Revive Link", "Marks that character will receive Death curse when dead character is revived. Removed after some time."); - BetterUI.Buffs.RegisterBuffInfo(BuffCatalog.GetBuffDef(ReviveRegen.Index), "Revive Regeneration", "Rapidly regenerates 40% of your HP after revival."); + BetterUI.Buffs.RegisterBuffInfo(BuffCatalog.GetBuffDef(DeathCurse.BuffIndex), LanguageConsts.ITEM_DEATH_CURSE_NAME, LanguageConsts.BUFF_DEATH_CURSE_DESCRIPTION); + BetterUI.Buffs.RegisterBuffInfo(BuffCatalog.GetBuffDef(ReviveLink.Index), LanguageConsts.BUFF_REVIVE_LINK_NAME, LanguageConsts.BUFF_REVIVE_LINK_DESCRIPTION); + BetterUI.Buffs.RegisterBuffInfo(BuffCatalog.GetBuffDef(ReviveRegen.Index), LanguageConsts.BUFF_REVIVE_REGEN_NAME, LanguageConsts.BUFF_REVIVE_REGEN_DESCRIPTION); Log.Info($"Better UI integration: OK!"); } catch (Exception e) diff --git a/TeammateRevive/Integrations/ItemsStatsModIntegration.cs b/TeammateRevive/Integrations/ItemsStatsModIntegration.cs index f4129e9..a5f3ef4 100644 --- a/TeammateRevive/Integrations/ItemsStatsModIntegration.cs +++ b/TeammateRevive/Integrations/ItemsStatsModIntegration.cs @@ -7,6 +7,7 @@ using TeammateRevive.Content; using TeammateRevive.Logging; using TeammateRevive.Revive.Rules; +using static TeammateRevive.Localization.LanguageConsts; namespace TeammateRevive.Integrations { @@ -28,7 +29,9 @@ public ItemsStatsModIntegration(ReviveRules rules) } }; } - + + string Format(string key, string value) => string.Format(Language.GetString(key), value); + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] void AddToItemStats() { @@ -44,19 +47,23 @@ void AddToItemStats() { new( (itemCount, ctx) => rules.GetReviveIncrease((int)itemCount), - (value, ctx) => $"Revive speed increased by {value.FormatPercentage(signed: true, decimalPlaces: 1)}" + (value, ctx) => + Format(ITEM_STAT_CHARON_OBOL_REVIVE_SPEED_INCREASE, value.FormatPercentage(signed: true, decimalPlaces: 1)) ), new( (itemCount, ctx) => rules.GetReviveTime((int)itemCount, ctx.CountItems(DeadMansHandItem.Index)), - (value, ctx) => $"Time to revive alone: {value.FormatInt(postfix: "s", decimals: 1)}" + (value, ctx) => + Format(ITEM_STAT_CHARON_OBOL_REVIVE_TIME_ALONE, value.FormatInt(postfix: "s", decimals: 1)) ), new( (itemCount, ctx) => rules.CalculateDeathTotemRadius((int)itemCount, 1), - (value, ctx) => $"Revive circle range: {value.FormatInt(postfix: "m", decimals: 1)}" + (value, ctx) => + Format(ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_RANGE, value.FormatInt(postfix: "m", decimals: 1)) ), new( (itemCount, ctx) => rules.GetReviveReduceDamageFactor((int)itemCount, 0) - 1, - (value, ctx) => $"Damage from your circle: {value.FormatPercentage(decimalPlaces: 1, signed: true)}" + (value, ctx) => + Format(ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_DAMAGE, value.FormatPercentage(decimalPlaces: 1, signed: true)) ) } }); @@ -67,7 +74,8 @@ void AddToItemStats() { new( (itemCount, ctx) => rules.GetReviveTimeIncrease(ctx.CountItems(CharonsObol.Index), (int)itemCount), - (value, ctx) => $"Revive time increase: {value.FormatPercentage(decimalPlaces: 1, signed: true)}" + (value, ctx) => + Format(ITEM_STAT_REVIVE_EVERYWHERE_REVIVE_CIRCLE_DAMAGE,value.FormatPercentage(decimalPlaces: 1, signed: true)) ) } }); diff --git a/TeammateRevive/Localization/LanguageConsts.cs b/TeammateRevive/Localization/LanguageConsts.cs new file mode 100644 index 0000000..7642e74 --- /dev/null +++ b/TeammateRevive/Localization/LanguageConsts.cs @@ -0,0 +1,183 @@ +// This file is auto-generated from GenerateLanguageConsts.ps1 script, do not edit by hand. + +// ReSharper disable InconsistentNaming +namespace TeammateRevive.Localization; + +public class LanguageConsts +{ + /// + /// [EN] "Artifact of Death Curse" + /// + public static string ARTIFACT_DEATH_CURSE_NAME = nameof(ARTIFACT_DEATH_CURSE_NAME); + + /// + /// [EN] "Adds Death Curse on revive, but also adds Charon's Obol item that will make revive easier." + /// + public static string ARTIFACT_DEATH_CURSE_DESCRIPTION = nameof(ARTIFACT_DEATH_CURSE_DESCRIPTION); + + /// + /// [EN] "Artifact of Death Curse is disabled because run started in single player." + /// + public static string ARTIFACT_DEATH_CURSE_DISABLED = nameof(ARTIFACT_DEATH_CURSE_DISABLED); + + /// + /// [EN] "Artifact of Death Curse is enforced by server." + /// + public static string ARTIFACT_DEATH_CURSE_ENFORCED_BY_SERVER = nameof(ARTIFACT_DEATH_CURSE_ENFORCED_BY_SERVER); + + /// + /// [EN] "Charon's Obol" + /// + public static string ITEM_CHARONS_OBOL_NAME = nameof(ITEM_CHARONS_OBOL_NAME); + + /// + /// [EN] "- Reduces <color="yellow">revival time</color>. + /// - Can be consumed for <color="yellow">instant</color> revival without Death Curse. + /// - <color="yellow">Removes</color> additional <color="red">Death Curse</color> on stage change. + /// - Increase <color="yellow">range</color> and decrease <color="yellow">damage</color> for your revival." + /// + public static string ITEM_CHARONS_OBOL_DESCRIPTION = nameof(ITEM_CHARONS_OBOL_DESCRIPTION); + + /// + /// [EN] "Makes revival easier. Can be consumed for instant revival." + /// + public static string ITEM_CHARONS_OBOL_PICKUP = nameof(ITEM_CHARONS_OBOL_PICKUP); + + /// + /// [EN] "Dead Man's Hand" + /// + public static string ITEM_REVIVE_EVERYWHERE_NAME = nameof(ITEM_REVIVE_EVERYWHERE_NAME); + + /// + /// [EN] "- <color="yellow">Revive</color> dead teammates <color="yellow">everywhere</color> on map. + /// - First item <color="red">increases</color> <color="yellow">revival time</color> by x2. + /// - Every subsequent item decreases <color="yellow">revival time</color> when you are reviving" + /// + public static string ITEM_REVIVE_EVERYWHERE_DESCRIPTION = nameof(ITEM_REVIVE_EVERYWHERE_DESCRIPTION); + + /// + /// [EN] "Revive death teammates everywhere on map" + /// + public static string ITEM_REVIVE_EVERYWHERE_LORE = nameof(ITEM_REVIVE_EVERYWHERE_LORE); + + /// + /// [EN] "Revive death teammates everywhere on map" + /// + public static string ITEM_REVIVE_EVERYWHERE_PICKUP = nameof(ITEM_REVIVE_EVERYWHERE_PICKUP); + + /// + /// [EN] "Death curse" + /// + public static string ITEM_DEATH_CURSE_NAME = nameof(ITEM_DEATH_CURSE_NAME); + + /// + /// [EN] "Reduces your max HP/Shield. Removed on next stage." + /// + public static string ITEM_DEATH_CURSE_DESCRIPTION = nameof(ITEM_DEATH_CURSE_DESCRIPTION); + + /// + /// [EN] "Reduces your max HP/Shield. Removed on next stage." + /// + public static string ITEM_DEATH_CURSE_LORE = nameof(ITEM_DEATH_CURSE_LORE); + + /// + /// [EN] "Reduces your max HP/Shield. Removed on next stage." + /// + public static string ITEM_DEATH_CURSE_PICKUP = nameof(ITEM_DEATH_CURSE_PICKUP); + + /// + /// [EN] "Revival Token" + /// + public static string ITEM_REVIVAL_TOKEN_NAME = nameof(ITEM_REVIVAL_TOKEN_NAME); + + /// + /// [EN] "Reduces chance to receive curse" + /// + public static string ITEM_REVIVAL_TOKEN_DESCRIPTION = nameof(ITEM_REVIVAL_TOKEN_DESCRIPTION); + + /// + /// [EN] "Reduces your max HP/Shield." + /// + public static string BUFF_DEATH_CURSE_DESCRIPTION = nameof(BUFF_DEATH_CURSE_DESCRIPTION); + + /// + /// [EN] "Revive Link" + /// + public static string BUFF_REVIVE_LINK_NAME = nameof(BUFF_REVIVE_LINK_NAME); + + /// + /// [EN] "Marks that character will receive Death curse when dead character is revived. Removed after some time." + /// + public static string BUFF_REVIVE_LINK_DESCRIPTION = nameof(BUFF_REVIVE_LINK_DESCRIPTION); + + /// + /// [EN] "Revive Regeneration" + /// + public static string BUFF_REVIVE_REGEN_NAME = nameof(BUFF_REVIVE_REGEN_NAME); + + /// + /// [EN] "Rapidly regenerates 40% of your HP after revival." + /// + public static string BUFF_REVIVE_REGEN_DESCRIPTION = nameof(BUFF_REVIVE_REGEN_DESCRIPTION); + + /// + /// [EN] "Player" + /// + public static string TEAMMATE_REVIVAL_UI_PLAYER = nameof(TEAMMATE_REVIVAL_UI_PLAYER); + + /// + /// [EN] "Use <color="green">Charon's Obol</color> to revive (<color="red">Reduces Max Hp/Shield</color>)" + /// + public static string TEAMMATE_REVIVAL_UI_USE_OBOL = nameof(TEAMMATE_REVIVAL_UI_USE_OBOL); + + /// + /// [EN] "Cannot instantly resurrect without Charon's Obol!" + /// + public static string TEAMMATE_REVIVAL_UI_NO_OBOL = nameof(TEAMMATE_REVIVAL_UI_NO_OBOL); + + /// + /// [EN] " Teammate reviving will damage shield instead of health." + /// + public static string TEAMMATE_REVIVAL_SHIELD_ONLY_POSTFIX = nameof(TEAMMATE_REVIVAL_SHIELD_ONLY_POSTFIX); + + /// + /// [EN] "{0} avoided curse! (Luck value {1})" + /// + public static string TEAMMATE_REVIVAL_UI_AVOIDED_CURSE = nameof(TEAMMATE_REVIVAL_UI_AVOIDED_CURSE); + + /// + /// [EN] "<color="red">{0} was cursed! (Luck value: {1})</color>" + /// + public static string TEAMMATE_REVIVAL_UI_CURSED = nameof(TEAMMATE_REVIVAL_UI_CURSED); + + /// + /// [EN] "Reviving " + /// + public static string TEAMMATE_REVIVAL_UI_PROGRESS_BAR_REVIVING = nameof(TEAMMATE_REVIVAL_UI_PROGRESS_BAR_REVIVING); + + /// + /// [EN] "Revive speed increased by {0}" + /// + public static string ITEM_STAT_CHARON_OBOL_REVIVE_SPEED_INCREASE = nameof(ITEM_STAT_CHARON_OBOL_REVIVE_SPEED_INCREASE); + + /// + /// [EN] "Time to revive alone: {0}" + /// + public static string ITEM_STAT_CHARON_OBOL_REVIVE_TIME_ALONE = nameof(ITEM_STAT_CHARON_OBOL_REVIVE_TIME_ALONE); + + /// + /// [EN] "Revive circle range: {0}" + /// + public static string ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_RANGE = nameof(ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_RANGE); + + /// + /// [EN] "Damage from your circle: {0}" + /// + public static string ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_DAMAGE = nameof(ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_DAMAGE); + + /// + /// [EN] "Revive time increase: {0}" + /// + public static string ITEM_STAT_REVIVE_EVERYWHERE_REVIVE_CIRCLE_DAMAGE = nameof(ITEM_STAT_REVIVE_EVERYWHERE_REVIVE_CIRCLE_DAMAGE); + +} diff --git a/TeammateRevive/Localization/LanguageManager.cs b/TeammateRevive/Localization/LanguageManager.cs new file mode 100644 index 0000000..dfb6e2d --- /dev/null +++ b/TeammateRevive/Localization/LanguageManager.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using RoR2; + +namespace TeammateRevive.Localization; + +public class LanguageManager +{ + public static void RegisterLanguages() + { + Language.collectLanguageRootFolders += LanguageOnCollectLanguageRootFolders; + } + + private static void LanguageOnCollectLanguageRootFolders(List folders) + { + folders.Add(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(MainTeammateRevival.instance.Info.Location)!, "Localization", "Languages")); + } +} \ No newline at end of file diff --git a/TeammateRevive/Localization/Languages/en/tokens.json b/TeammateRevive/Localization/Languages/en/tokens.json new file mode 100644 index 0000000..5fe7ce7 --- /dev/null +++ b/TeammateRevive/Localization/Languages/en/tokens.json @@ -0,0 +1,46 @@ +{ + "strings": { + "ARTIFACT_DEATH_CURSE_NAME": "Artifact of Death Curse", + "ARTIFACT_DEATH_CURSE_DESCRIPTION": "Adds Death Curse on revive, but also adds Charon's Obol item that will make revive easier.", + "ARTIFACT_DEATH_CURSE_DISABLED": "Artifact of Death Curse is disabled because run started in single player.", + "ARTIFACT_DEATH_CURSE_ENFORCED_BY_SERVER": "Artifact of Death Curse is enforced by server.", + + "ITEM_CHARONS_OBOL_NAME": "Charon's Obol", + "ITEM_CHARONS_OBOL_DESCRIPTION": "- Reduces revival time. \n- Can be consumed for instant revival without Death Curse. \n- Removes additional Death Curse on stage change.\n- Increase range and decrease damage for your revival.", + "ITEM_CHARONS_OBOL_PICKUP": "Makes revival easier. Can be consumed for instant revival.", + + "ITEM_REVIVE_EVERYWHERE_NAME": "Dead Man's Hand", + "ITEM_REVIVE_EVERYWHERE_DESCRIPTION": "- Revive dead teammates everywhere on map.\n- First item increases revival time by x2.\n- Every subsequent item decreases revival time when you are reviving", + "ITEM_REVIVE_EVERYWHERE_LORE": "Revive death teammates everywhere on map", + "ITEM_REVIVE_EVERYWHERE_PICKUP": "Revive death teammates everywhere on map", + + "ITEM_DEATH_CURSE_NAME": "Death curse", + "ITEM_DEATH_CURSE_DESCRIPTION": "Reduces your max HP/Shield. Removed on next stage.", + "ITEM_DEATH_CURSE_LORE": "Reduces your max HP/Shield. Removed on next stage.", + "ITEM_DEATH_CURSE_PICKUP": "Reduces your max HP/Shield. Removed on next stage.", + + "ITEM_REVIVAL_TOKEN_NAME": "Revival Token", + "ITEM_REVIVAL_TOKEN_DESCRIPTION": "Reduces chance to receive curse", + + "BUFF_DEATH_CURSE_DESCRIPTION": "Reduces your max HP/Shield.", + "BUFF_REVIVE_LINK_NAME": "Revive Link", + "BUFF_REVIVE_LINK_DESCRIPTION": "Marks that character will receive Death curse when dead character is revived. Removed after some time.", + "BUFF_REVIVE_REGEN_NAME": "Revive Regeneration", + "BUFF_REVIVE_REGEN_DESCRIPTION": "Rapidly regenerates 40% of your HP after revival.", + + "TEAMMATE_REVIVAL_UI_PLAYER": "Player", + "TEAMMATE_REVIVAL_UI_USE_OBOL": "Use Charon's Obol to revive (Reduces Max Hp/Shield)", + "TEAMMATE_REVIVAL_UI_NO_OBOL": "Cannot instantly resurrect without Charon's Obol!", + + "TEAMMATE_REVIVAL_SHIELD_ONLY_POSTFIX": " Teammate reviving will damage shield instead of health.", + "TEAMMATE_REVIVAL_UI_AVOIDED_CURSE": "{0} avoided curse! (Luck value {1})", + "TEAMMATE_REVIVAL_UI_CURSED": "{0} was cursed! (Luck value: {1})", + "TEAMMATE_REVIVAL_UI_PROGRESS_BAR_REVIVING": "Reviving ", + + "ITEM_STAT_CHARON_OBOL_REVIVE_SPEED_INCREASE": "Revive speed increased by {0}", + "ITEM_STAT_CHARON_OBOL_REVIVE_TIME_ALONE": "Time to revive alone: {0}", + "ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_RANGE": "Revive circle range: {0}", + "ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_DAMAGE": "Damage from your circle: {0}", + "ITEM_STAT_REVIVE_EVERYWHERE_REVIVE_CIRCLE_DAMAGE": "Revive time increase: {0}" + } +} \ No newline at end of file diff --git a/TeammateRevive/MainTeammateRevival.cs b/TeammateRevive/MainTeammateRevival.cs index d1eac93..7758d08 100644 --- a/TeammateRevive/MainTeammateRevival.cs +++ b/TeammateRevive/MainTeammateRevival.cs @@ -17,6 +17,7 @@ using TeammateRevive.Revive; using TeammateRevive.Revive.Rules; using TeammateRevive.DeathTotem; +using TeammateRevive.Localization; using UnityEngine; using UnityEngine.Networking; @@ -71,6 +72,7 @@ public void Awake() { instance = this; pluginConfig = PluginConfig.Load(Config); + LanguageManager.RegisterLanguages(); NetworkingAPI.RegisterMessageType(); NetworkingAPI.RegisterMessageType(); diff --git a/TeammateRevive/ProgressBar/ProgressBarController.cs b/TeammateRevive/ProgressBar/ProgressBarController.cs index 672f8db..306e532 100644 --- a/TeammateRevive/ProgressBar/ProgressBarController.cs +++ b/TeammateRevive/ProgressBar/ProgressBarController.cs @@ -1,8 +1,10 @@ using R2API; +using RoR2; using TMPro; using UnityEngine; using UnityEngine.UI; using TeammateRevive.Common; +using TeammateRevive.Localization; using TeammateRevive.Logging; using TeammateRevive.Resources; @@ -13,15 +15,14 @@ namespace TeammateRevive.ProgressBar /// public class ProgressBarController { - private const string DefaultName = "Player"; - public static float WidthModifier = 1; + private static string DefaultName => Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_PLAYER); + private static string Reviving => Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_PROGRESS_BAR_REVIVING); public static ProgressBarController Instance; private TextMeshProUGUI textComponent; private string currentName = DefaultName; private readonly CharArrayBuilder charArrayBuilder; public bool showing = false; - public bool inited = false; Slider progressBar; public float progress; RoR2.UI.HUD hudRef; @@ -143,6 +144,7 @@ public void Show() UpdatePositionAndSize(); showing = true; progressBar.GetComponent().alpha = 1; + charArrayBuilder.UpdatePart(0, Reviving); } public void AttachProgressBar(RoR2.UI.HUD hud) diff --git a/TeammateRevive/Revive/RevivalTracker.cs b/TeammateRevive/Revive/RevivalTracker.cs index cb30440..1495dba 100644 --- a/TeammateRevive/Revive/RevivalTracker.cs +++ b/TeammateRevive/Revive/RevivalTracker.cs @@ -8,6 +8,7 @@ using TeammateRevive.ProgressBar; using TeammateRevive.Revive.Rules; using TeammateRevive.DeathTotem; +using TeammateRevive.Localization; using UnityEngine; namespace TeammateRevive.Revive @@ -204,42 +205,47 @@ void Revive(Player dead) private void ApplyDeathCurses(Player dead, Player[] linkedPlayers) { // invert - from "chance to get curse" to "chance to avoid curse" - var percentChance = Mathf.Clamp(100 - rules.Values.DeathCurseChance, 0, 100); + var reviveeChance = Mathf.Clamp(100 - rules.Values.DeathCurseChance, 0, 100); + var reviverChance = Mathf.Clamp(100 - rules.Values.ReviverDeathCurseChance, 0, 100); - bool RollCurse(Player player, float extraLuck) + bool RollCurse(Player player, float chance, float extraLuck) { // negative luck - take largest roll value // if value > chance - roll is failed // therefore positive luck - outcome is more likely // using inverted roll, so clover sound effect will be triggered if clover is present var luckTotal = extraLuck + player.master.master.luck; - var curseAvoided = Util.CheckRoll(percentChance, luckTotal, player.master.master.luck > 0 ? player.master.master : null); + var curseAvoided = Util.CheckRoll(chance, luckTotal, player.master.master.luck > 0 ? player.master.master : null); + + Log.Info($"Rolled curse for {player.networkUser.userName}: {chance:F1}% (luck: {player.master.master.luck:F0}+{extraLuck:F0}). Success: {curseAvoided}"); if (!curseAvoided) { player.GiveItem(DeathCurse.ItemIndex); Chat.SendBroadcastChat(new Chat.SimpleChatMessage { - baseToken = $"{player.networkUser.userName} was cursed! (Luck value: {luckTotal})" + baseToken = LanguageConsts.TEAMMATE_REVIVAL_UI_CURSED, + paramTokens = new[] { player.networkUser.userName, $"{luckTotal:F0}" } }); return true; } - - Log.Info($"Rolled curse for {player.networkUser.userName}: {percentChance:F1}% (luck: {player.master.master.luck:F0}+{extraLuck:F0}). Success: {curseAvoided}"); + Chat.SendBroadcastChat(new Chat.SimpleChatMessage { - baseToken = $"{player.networkUser.userName} avoided curse! (Luck value: {luckTotal})" + baseToken = LanguageConsts.TEAMMATE_REVIVAL_UI_AVOIDED_CURSE, + paramTokens = new[] { player.networkUser.userName, $"{luckTotal:F0}" } }); return false; } - RollCurse(dead, dead.ItemCount(RevivalToken.Index)); + // roll for revivee + RollCurse(dead, reviveeChance, dead.ItemCount(RevivalToken.Index)); // the more time spent reviving this player - the greater chance to receive a curse var array = SortByLinkDuration(linkedPlayers, dead).ToArray(); for (var index = 0; index < array.Length; index++) { var player = array[index]; - if (RollCurse(player, index)) + if (RollCurse(player, reviverChance, index)) { // when at least one player received curse, stop rolling for other members break; diff --git a/TeammateRevive/Revive/ReviveInteraction.cs b/TeammateRevive/Revive/ReviveInteraction.cs index eb08544..45de3c0 100644 --- a/TeammateRevive/Revive/ReviveInteraction.cs +++ b/TeammateRevive/Revive/ReviveInteraction.cs @@ -5,18 +5,15 @@ using TeammateRevive.Logging; using TeammateRevive.Players; using TeammateRevive.DeathTotem; +using TeammateRevive.Localization; using UnityEngine; using UnityEngine.Networking; -using static TeammateRevive.Common.TextFormatter; namespace TeammateRevive.Revive { public class ReviveInteraction : MonoBehaviour, IInteractable, IDisplayNameProvider { - private static readonly string DisplayString = - $"Use {Green("Charon's Obol")} to revive ({Red("Reduces Max Hp/Shield")})"; - - public string GetContextString(Interactor activator) => DisplayString; + public string GetContextString(Interactor activator) => Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_USE_OBOL); public Interactability GetInteractability(Interactor activator) { @@ -58,7 +55,7 @@ public static void HandleInteraction(NetworkInstanceId playerNetId, NetworkInsta if (!playerHasRespawnItem) { - ChatMessage.SendColored("Cannot instantly resurrect without Charon's Obol!", Color.red); + ChatMessage.SendColored(Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_NO_OBOL), Color.red); return; } @@ -76,8 +73,7 @@ public bool ShouldShowOnScanner() return false; } - public string GetDisplayName() => DisplayString; - + public string GetDisplayName() => Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_USE_OBOL); public void OnEnable() => InstanceTracker.Add(this); diff --git a/TeammateRevive/Revive/Rules/ReviveRuleValues.cs b/TeammateRevive/Revive/Rules/ReviveRuleValues.cs index 3c5b19a..14b6927 100644 --- a/TeammateRevive/Revive/Rules/ReviveRuleValues.cs +++ b/TeammateRevive/Revive/Rules/ReviveRuleValues.cs @@ -33,6 +33,8 @@ public class ReviveRuleValues public float DeathCurseChance { get; set; } = 66f; + public float ReviverDeathCurseChance { get; set; } = 66f; + public ReviveRuleValues Clone() { return (ReviveRuleValues)MemberwiseClone(); diff --git a/TeammateRevive/TeammateRevive.csproj b/TeammateRevive/TeammateRevive.csproj index fa5bc93..ee7d405 100644 --- a/TeammateRevive/TeammateRevive.csproj +++ b/TeammateRevive/TeammateRevive.csproj @@ -4,7 +4,7 @@ netstandard2.0 preview true - + C:\Users\amada\AppData\Roaming\Thunderstore Mod Manager\DataFolder\RiskOfRain2\profiles\Default\BepInEx\plugins\KosmosisDire-TeammateRevival @@ -54,13 +54,23 @@ false + + + + PreserveNewest + + - <_FilesToCopy Include="$(OutputPath)*.*" /> + <_FilesToCopy Include="$(OutputPath)**/*.*" /> - + + + + + diff --git a/TeammateRevive/Tools/GenerateLanguageConsts.ps1 b/TeammateRevive/Tools/GenerateLanguageConsts.ps1 new file mode 100644 index 0000000..3e8f16e --- /dev/null +++ b/TeammateRevive/Tools/GenerateLanguageConsts.ps1 @@ -0,0 +1,23 @@ +$LangPath = [System.IO.Path]::Combine($PSScriptRoot, '..\Localization\Languages\en\tokens.json') +$ResultPath = [System.IO.Path]::Combine($PSScriptRoot, '..\Localization\LanguageConsts.cs') + +$dict = (Get-Content $LangPath | ConvertFrom-Json).strings + +function GenerateSummary($value) { + # escaping opening tag + $value = $value -replace '<','<' + $result = " /// `n /// [EN] `"$(($value -replace '(?\n)','${NL} /// '))`"`n /// " + return $result +} + +$fileContent = "// This file is auto-generated from GenerateLanguageConsts.ps1 script, do not edit by hand. + +// ReSharper disable InconsistentNaming +namespace TeammateRevive.Localization; + +public class LanguageConsts +{$($dict.PSObject.Properties | % { "`n$(GenerateSummary $_.Value)`n public static string $($_.Name) = nameof($($_.Name));`n" }) +} +" +Write-Host $fileContent +[System.IO.File]::WriteAllText($ResultPath, $fileContent)