/// <summary> /// Randomizes the values on the given weapon /// </summary> /// <param name="weapon">The weapon to randomize</param> /// <param name="nameRandomizer">The name randomizer</param> private static void RandomizeWeapon(WeaponItem weapon, WeaponAndArmorNameRandomizer nameRandomizer) { if (weapon.Type == WeaponType.Slingshot) { //TODO: assign the name here after we deal with the slingshot name hardcoding issue // Doing this to advance the RNG - don't actually assign the name nameRandomizer.GenerateRandomWeaponName(weapon.Type, (WeaponIndexes)weapon.Id); return; } RandomizeWeaponType(weapon); RandomizeWeaponDamage(weapon); RandomizeWeaponCrits(weapon); RandomizeWeaponKnockback(weapon); RandomizeWeaponSpeed(weapon); RandomizeWeaponAOE(weapon); RandomizeWeaponPrecision(weapon); RandomizeWeaponDefense(weapon); RandomizeWeaponDropInfo(weapon); SetWeaponDescription(weapon); string weaponName = nameRandomizer.GenerateRandomWeaponName(weapon.Type); if ((Globals.Config.Weapons.Randomize && Globals.Config.Weapons.RandomizeGalaxySwordName) || weapon.Name != "Galaxy Sword") { weapon.OverrideName = weaponName; } }
/// <summary> /// Randomizes weapon damage based on the original max damage /// - if the max damage is under 10 - has a 50% variance /// - if the max damage is under 50 - has a 30% variance /// - if the max damage is over 50,- has a 20% variance /// </summary> /// <param name="weapon">The weapon to randomize</param> private static void RandomizeWeaponDamage(WeaponItem weapon) { const int percentageUnder10 = 50; const int percentageUnder50 = 30; const int percentageOver50 = 20; int minDamage = weapon.Damage.MinValue; int maxDamage = weapon.Damage.MaxValue; int percentage = 0; if (maxDamage < 10) { percentage = percentageUnder10; } else if (maxDamage < 50) { percentage = percentageUnder50; } else { percentage = percentageOver50; } int minValueToUse = Globals.RNGGetIntWithinPercentage(minDamage, percentage); int maxValueToUse = Globals.RNGGetIntWithinPercentage(maxDamage, percentage); weapon.Damage = new Range(minValueToUse, maxValueToUse); }
/// <summary> /// Gets the weapon type from the given position in the image /// </summary> /// <param name="position">The position</param> /// <returns /> private WeaponType GetWeaponTypeFromPosition(Point position) { int weaponId = WeaponPositionToIDMap[position]; WeaponItem weapon = WeaponRandomizer.Weapons[weaponId]; return(weapon.Type); }
/// <summary> /// Randomizes the weapon type /// - 1/4 chance of each type that isn't a slingshot /// </summary> /// <param name="weapon">The weapon to randomize</param> private static void RandomizeWeaponType(WeaponItem weapon) { weapon.Type = (WeaponType)Range.GetRandomValue(0, 3); if (weapon.Type == WeaponType.StabbingSword) { weapon.Type = WeaponType.SlashingSword; } }
/// <summary> /// Assigns a random defense value to the weapon /// - 95% chance of 0 /// - else 1-5 /// </summary> /// <param name="weapon">The weapon to add the defense value</param> private static void RandomizeWeaponDefense(WeaponItem weapon) { if (Globals.RNGGetNextBoolean(95)) { weapon.AddedDefense = 0; } else { weapon.AddedDefense = Range.GetRandomValue(1, 5); } }
/// <summary> /// Assigns a random precision value to the weapon /// - 80% chance of 0 /// - else 1 - 10 /// </summary> /// <param name="weapon">The weapon to assign the precision value</param> private static void RandomizeWeaponPrecision(WeaponItem weapon) { if (Globals.RNGGetNextBoolean(80)) { weapon.AddedPrecision = 0; } else { weapon.AddedPrecision = Range.GetRandomValue(1, 10); } }
/// <summary> /// Assigns a random AOE value to the weapon /// - 80% chance of 0 /// - Else, value from 1 - 4 /// </summary> /// <param name="weapon">The weapon to assign the AOE to</param> private static void RandomizeWeaponAOE(WeaponItem weapon) { if (Globals.RNGGetNextBoolean(80)) { weapon.AddedAOE = 0; } else { weapon.AddedAOE = Range.GetRandomValue(1, 4); } }
/// <summary> /// Assigns a random weapon knockback /// - 5% chance of 1.6 - 2.0 /// - else 0.5 - 1.6 /// </summary> /// <param name="weapon">The weapon to set the knockback for</param> private static void RandomizeWeaponKnockback(WeaponItem weapon) { if (Globals.RNGGetNextBoolean(5)) { weapon.Knockback = Range.GetRandomValue(16, 20) / 10d; } else { weapon.Knockback = Range.GetRandomValue(5, 16) / 10d; } }
/// <summary> /// The weapon items in dictionary form - data taken from weapons.xnb /// </summary> /// <returns /> public static Dictionary <int, WeaponItem> Items() { Dictionary <int, WeaponItem> weaponItemMap = new Dictionary <int, WeaponItem>(); foreach (int id in DefaultStringData.Keys) { WeaponItem weapon = new WeaponItem(id); FillDefaultWeaponInfo(weapon); weaponItemMap.Add(id, weapon); } return(weaponItemMap); }
/// <summary> /// Randomize the weapon crit stats /// - 1% chance of a 0.1% crit with a multiplier of 100 /// - 4% chance of 8-12% crit with a multiplier of 3 - 3.1x /// - Else, 2-3% crit with a multiplier of 3 - 4x /// </summary> /// <param name="weapon">The weapon to randomize</param> private static void RandomizeWeaponCrits(WeaponItem weapon) { if (Globals.RNGGetNextBoolean(1)) { weapon.CritChance = 0.001; weapon.CritMultiplier = 100; } else if (Globals.RNGGetNextBoolean(4)) { weapon.CritChance = Range.GetRandomValue(8, 12) / 100d; weapon.CritMultiplier = Range.GetRandomValue(30, 31) / 10d; } else { weapon.CritChance = Range.GetRandomValue(20, 30) / 1000d; weapon.CritMultiplier = Range.GetRandomValue(30, 40) / 10d; } }
/// <summary> /// Assigns the weapon's speed /// - 5% chance of max speed /// - 10% chance of slow speed (-16 to -1) /// - 50% chance of 0 /// - Else, value from -8 to 8 /// </summary> /// <param name="weapon">The weapon to set the speed for</param> private static void RandomizeWeaponSpeed(WeaponItem weapon) { if (Globals.RNGGetNextBoolean(5)) { weapon.Speed = 308; } else if (Globals.RNGGetNextBoolean(10)) { weapon.Speed = Range.GetRandomValue(-16, -1); } else if (Globals.RNGGetNextBoolean()) { weapon.Speed = 0; } else { weapon.Speed = Range.GetRandomValue(-8, 8); } }
/// <summary> /// Randomizes the weapon drop info (where you receive weapons in mine containers). /// This does not affect galaxy items. /// - If you currently can't receive the weapon in the mines, set its base floor based on its max damage /// - less than 10: floor between 1 and 20 /// - less than 30: floor between 21 and 60 /// - less than 50: floor between 61 and 100 /// - else: floor between 110 and 110 /// - else, set the base floor to be + or - 10 floors of the original value /// /// In either case, set the min floor to be between 10 and 30 floors lower than the base /// </summary> /// <param name="weapon">The weapon to set drop info for</param> private static void RandomizeWeaponDropInfo(WeaponItem weapon) { if (!weapon.ShouldRandomizeDropInfo()) { return; } int baseMineLevel = weapon.BaseMineLevelDrop; if (baseMineLevel == -1) { int maxDamage = weapon.Damage.MaxValue; if (maxDamage < 10) { baseMineLevel = Range.GetRandomValue(1, 20); } else if (maxDamage < 30) { baseMineLevel = Range.GetRandomValue(21, 60); } else if (maxDamage < 50) { baseMineLevel = Range.GetRandomValue(61, 100); } else { baseMineLevel = Range.GetRandomValue(101, 110); } } else { baseMineLevel = FixMineLevelValue(baseMineLevel + Range.GetRandomValue(-10, 10)); } weapon.BaseMineLevelDrop = baseMineLevel; weapon.MinMineLevelDrop = FixMineLevelValue(baseMineLevel - Range.GetRandomValue(10, 30), true); }
/// <summary> /// Writes the changed weapon info to the spoiler log /// </summary> /// <param name="modifiedWeaponDictionary">The dictionary with changed info</param> private static void WriteToSpoilerLog(Dictionary <int, WeaponItem> modifiedWeaponDictionary) { if (!Globals.Config.Weapons.Randomize) { return; } Globals.SpoilerWrite("==== WEAPONS ===="); foreach (int id in modifiedWeaponDictionary.Keys) { WeaponItem weapon = modifiedWeaponDictionary[id]; Globals.SpoilerWrite($"{id}: {weapon.OverrideName}"); Globals.SpoilerWrite($"Type: {Enum.GetName(typeof(WeaponType), weapon.Type)}"); Globals.SpoilerWrite($"Damage: {weapon.Damage.MinValue} - {weapon.Damage.MaxValue}"); Globals.SpoilerWrite($"Crit Chance / Multiplier: {weapon.CritChance} / {weapon.CritMultiplier}"); Globals.SpoilerWrite($"Knockback / Speed / AOE: {weapon.Knockback} / {weapon.Speed} / {weapon.AddedAOE}"); Globals.SpoilerWrite($"Added Precision / Defense: {weapon.AddedPrecision} / {weapon.AddedDefense}"); Globals.SpoilerWrite($"Base / Min Mine Level Drop: {weapon.BaseMineLevelDrop} / {weapon.MinMineLevelDrop}"); Globals.SpoilerWrite("---"); } Globals.SpoilerWrite(""); }
/// <summary> /// Populates the given weapon with its default info /// </summary> /// <param name="weapon">The weapon</param> private static void FillDefaultWeaponInfo(WeaponItem weapon) { string input = DefaultStringData[weapon.Id]; string[] fields = input.Split('/'); if (fields.Length != 14) { Globals.ConsoleError($"Incorrect number of fields when parsing weapons with input: {input}"); return; } // Name/Description weapon.OverrideName = fields[(int)WeaponFields.Name]; weapon.Description = fields[(int)WeaponFields.Description]; // Damage if (!int.TryParse(fields[(int)WeaponFields.MinDamage], out int minDamage)) { Globals.ConsoleError($"Could not parse the min damage when parsing weapon with input: {input}"); return; } if (!int.TryParse(fields[(int)WeaponFields.MaxDamage], out int maxDamage)) { Globals.ConsoleError($"Could not parse the max damage when parsing weapon with input: {input}"); return; } weapon.Damage = new Range(minDamage, maxDamage); // Knockback if (!double.TryParse(fields[(int)WeaponFields.Knockback], out double knockback)) { Globals.ConsoleError($"Could not parse knockback when parsing weapon with input: {input}"); return; } weapon.Knockback = knockback; // Speed if (!int.TryParse(fields[(int)WeaponFields.Speed], out int speed)) { Globals.ConsoleError($"Could not parse speed when parsing weapon with input: {input}"); return; } weapon.Speed = speed; // Added Precision if (!int.TryParse(fields[(int)WeaponFields.AddedPrecision], out int addedPrecision)) { Globals.ConsoleError($"Could not parse the added precision when parsing weapon with input: {input}"); return; } weapon.AddedPrecision = addedPrecision; // Added Defense if (!int.TryParse(fields[(int)WeaponFields.AddedDefense], out int addedDefense)) { Globals.ConsoleError($"Could not parse the added defense when parsing weapon with input: {input}"); return; } weapon.AddedPrecision = addedPrecision; // Type if (!int.TryParse(fields[(int)WeaponFields.Type], out int type)) { Globals.ConsoleError($"Could not parse the weapon type when parsing weapon with input: {input}"); return; } weapon.Type = (WeaponType)type; // Base Mine Level Drop if (!int.TryParse(fields[(int)WeaponFields.BaseMineLevelDrop], out int baseMineLevelDrop)) { Globals.ConsoleError($"Could not parse the base mine level drop when parsing weapon with input: {input}"); return; } weapon.BaseMineLevelDrop = baseMineLevelDrop; // Minimum Mine Level Drop if (!int.TryParse(fields[(int)WeaponFields.MinMineLevelDrop], out int minMineLevelDrop)) { Globals.ConsoleError($"Could not parse the minimum mine level drop when parsing weapon with input: {input}"); return; } weapon.MinMineLevelDrop = minMineLevelDrop; // Added AOE if (!int.TryParse(fields[(int)WeaponFields.AddedAOE], out int addedAOE)) { Globals.ConsoleError($"Could not parse the added AOE value when parsing weapon with input: {input}"); return; } weapon.AddedAOE = addedAOE; // Crit Chance if (!double.TryParse(fields[(int)WeaponFields.CritChance], out double critChance)) { Globals.ConsoleError($"Could not parse crit chance when parsing weapon with input: {input}"); return; } weapon.CritChance = critChance; // Crit Multiplier if (!double.TryParse(fields[(int)WeaponFields.CritMultiplier], out double critMultiplier)) { Globals.ConsoleError($"Could not parse the crit multiplier when parsing weapon with input: {input}"); return; } weapon.CritMultiplier = critMultiplier; }
/// <summary> /// Sets a weapon's description based on its attributes /// </summary> /// <param name="weapon">The weapon to set the description for</param> private static void SetWeaponDescription(WeaponItem weapon) { string description = ""; switch (weapon.Type) { case WeaponType.Dagger: case WeaponType.StabbingSword: description = Globals.GetTranslation("weapon-description-stabbing"); break; case WeaponType.SlashingSword: description = Globals.GetTranslation("weapon-description-slashing"); break; case WeaponType.ClubOrHammer: description = Globals.GetTranslation("weapon-description-crushing"); break; default: Globals.ConsoleError($"Assigning description to an invalid weapon type: {weapon.ToString()}"); break; } if (weapon.CritMultiplier == 100) { description += $" {Globals.GetTranslation("weapon-description-crit-high-damage")}"; } else if (weapon.CritChance >= 8) { description += $" {Globals.GetTranslation("weapon-description-crit-high-frequency")}"; } if (weapon.Knockback >= 15) { description += $" {Globals.GetTranslation("weapon-description-high-knockback")}"; } if (weapon.Speed > 100) { description += $" {Globals.GetTranslation("weapon-description-fast")}"; } if (weapon.AddedAOE > 0) { description += $" {Globals.GetTranslation("weapon-description-aoe")}"; } if (weapon.AddedPrecision > 4) { description += $" {Globals.GetTranslation("weapon-description-accurate")}"; } if (weapon.AddedDefense > 0) { description += $" {Globals.GetTranslation("weapon-description-defense")}"; } if (weapon.Id == (int)WeaponIndexes.DarkSword) { description += $" {Globals.GetTranslation("weapon-description-heals")}"; } weapon.Description = description; }