// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.Persistence.Initialization; using System.Reflection; using MUnique.OpenMU.AttributeSystem; using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.DataModel.Configuration.Items; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.Persistence.Initialization.CharacterClasses; using MUnique.OpenMU.Persistence.Initialization.Version075.Items; using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps; /// /// Common base for an initialization of a . /// public abstract class GameConfigurationInitializerBase : InitializerBase { /// /// Initializes a new instance of the class. /// /// The context. /// The game configuration. protected GameConfigurationInitializerBase(IContext context, GameConfiguration gameConfiguration) : base(context, gameConfiguration) { } /// /// Gets the option types which are available. /// protected abstract IEnumerable OptionTypes { get; } /// public override void Initialize() { this.GameConfiguration.ExperienceRate = 1.0f; this.GameConfiguration.MinimumMonsterLevelForMasterExperience = 95; this.GameConfiguration.MaximumLevel = 400; this.GameConfiguration.MaximumMasterLevel = 200; this.GameConfiguration.InfoRange = 12; this.GameConfiguration.AreaSkillHitsPlayer = false; this.GameConfiguration.MaximumInventoryMoney = int.MaxValue; this.GameConfiguration.MaximumVaultMoney = int.MaxValue; this.GameConfiguration.PreventExperienceOverflow = false; this.GameConfiguration.ClampMoneyOnPickup = false; this.GameConfiguration.ExcellentItemDropLevelDelta = 25; this.GameConfiguration.RecoveryInterval = 3000; this.GameConfiguration.MaximumLetters = 50; this.GameConfiguration.LetterSendPrice = 1000; this.GameConfiguration.MaximumCharactersPerAccount = 5; this.GameConfiguration.CharacterNameRegex = "^[a-zA-Z0-9]{3,10}$"; this.GameConfiguration.MaximumPasswordLength = 20; this.GameConfiguration.MaximumPartySize = 5; this.GameConfiguration.ShouldDropMoney = true; this.GameConfiguration.ItemDropDuration = TimeSpan.FromSeconds(60); this.GameConfiguration.MaximumItemOptionLevelDrop = 3; this.GameConfiguration.DamagePerOneItemDurability = 2000; this.GameConfiguration.DamagePerOnePetDurability = 100000; this.GameConfiguration.HitsPerOneItemDurability = 10000; this.GameConfiguration.ExperienceFormula = "if(level == 0, 0, if(level < 256, 10 * (level + 8) * (level - 1) * (level - 1), (10 * (level + 8) * (level - 1) * (level - 1)) + (1000 * (level - 247) * (level - 256) * (level - 256))))"; this.GameConfiguration.MasterExperienceFormula = "(505 * level * level * level) + (35278500 * level) + (228045 * level * level)"; this.AddItemDropGroups(); this.CreateStatAttributes(); this.AddGlobalBaseAttributeValues(); new SlotTypesInitializer(this.Context, this.GameConfiguration).Initialize(); this.CreateItemOptionTypes(); this.GameConfiguration.ItemOptions.Add(this.CreateLuckOptionDefinition()); this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.DefenseBase, ItemOptionDefinitionNumbers.DefenseOption)); this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.PhysicalBaseDmg, ItemOptionDefinitionNumbers.PhysicalAttack)); this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.WizardryBaseDmg, ItemOptionDefinitionNumbers.WizardryAttack)); this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.DefenseRatePvm, ItemOptionDefinitionNumbers.DefenseRateOption, 5)); } /// /// Calculates the necessary experience for the specified character level. /// /// The character level. /// The calculated necessary experience. internal static long CalculateNeededExperience(long level) { if (level == 0) { return 0; } if (level < 256) { return 10 * (level + 8) * (level - 1) * (level - 1); } return (10 * (level + 8) * (level - 1) * (level - 1)) + (1000 * (level - 247) * (level - 256) * (level - 256)); } /// /// Creates an item option definition for the specified attribute definition. /// /// The attribute definition. /// The amount of the item option definition. /// The base value for the item option. /// The created item option definition. protected ItemOptionDefinition CreateOptionDefinition(AttributeDefinition attributeDefinition, short number, byte baseValue = 4) { var definition = this.Context.CreateNew(); definition.SetGuid(number); definition.Name = attributeDefinition.Designation + " Option"; definition.AddChance = 0.25f; definition.AddsRandomly = true; definition.MaximumOptionsPerItem = 1; var itemOption = this.Context.CreateNew(); itemOption.SetGuid(number); itemOption.OptionType = this.GameConfiguration.ItemOptionTypes.FirstOrDefault(o => o == ItemOptionTypes.Option); itemOption.PowerUpDefinition = this.Context.CreateNew(); itemOption.PowerUpDefinition.TargetAttribute = this.GameConfiguration.Attributes.First(a => a == attributeDefinition); itemOption.PowerUpDefinition.Boost = this.Context.CreateNew(); itemOption.PowerUpDefinition.Boost.ConstantValue!.Value = baseValue; for (short level = 2; level <= this.MaximumOptionLevel; level++) { var levelDependentOption = this.Context.CreateNew(); levelDependentOption.Level = level; var powerUpDefinition = this.Context.CreateNew(); powerUpDefinition.TargetAttribute = itemOption.PowerUpDefinition.TargetAttribute; powerUpDefinition.Boost = this.Context.CreateNew(); powerUpDefinition.Boost.ConstantValue!.Value = level * baseValue; levelDependentOption.PowerUpDefinition = powerUpDefinition; itemOption.LevelDependentOptions.Add(levelDependentOption); } definition.PossibleOptions.Add(itemOption); return definition; } /// /// Assigns the character class home maps. /// Needs to be called after the character classes and maps have been initialized. /// protected void AssignCharacterClassHomeMaps() { foreach (var characterClass in this.GameConfiguration.CharacterClasses) { byte mapNumber; switch ((CharacterClassNumber)characterClass.Number) { case CharacterClassNumber.FairyElf: case CharacterClassNumber.HighElf: case CharacterClassNumber.MuseElf: mapNumber = Noria.Number; break; case CharacterClassNumber.BloodySummoner: case CharacterClassNumber.Summoner: mapNumber = Elvenland.Number; break; default: mapNumber = Lorencia.Number; break; } characterClass.HomeMap = this.GameConfiguration.Maps.First(map => map.Number == mapNumber); } } private void AddItemDropGroups() { var moneyDropItemGroup = this.Context.CreateNew(); moneyDropItemGroup.SetGuid(1); moneyDropItemGroup.Chance = 0.5; moneyDropItemGroup.ItemType = SpecialItemType.Money; moneyDropItemGroup.Description = "The common money drop item group (50 % drop chance)"; this.GameConfiguration.DropItemGroups.Add(moneyDropItemGroup); BaseMapInitializer.RegisterDefaultDropItemGroup(moneyDropItemGroup); var randomItemDropItemGroup = this.Context.CreateNew(); randomItemDropItemGroup.SetGuid(2); randomItemDropItemGroup.Chance = 0.3; randomItemDropItemGroup.ItemType = SpecialItemType.RandomItem; randomItemDropItemGroup.Description = "The common drop item group for random items (30 % drop chance)"; this.GameConfiguration.DropItemGroups.Add(randomItemDropItemGroup); BaseMapInitializer.RegisterDefaultDropItemGroup(randomItemDropItemGroup); if (this.OptionTypes.Contains(ItemOptionTypes.Excellent)) { var excellentItemDropItemGroup = this.Context.CreateNew(); excellentItemDropItemGroup.SetGuid(3); excellentItemDropItemGroup.Chance = 0.0001; excellentItemDropItemGroup.ItemType = SpecialItemType.Excellent; excellentItemDropItemGroup.ItemLevel = 0; excellentItemDropItemGroup.Description = "The common drop item group for random excellent items (0.01 % drop chance)"; this.GameConfiguration.DropItemGroups.Add(excellentItemDropItemGroup); BaseMapInitializer.RegisterDefaultDropItemGroup(excellentItemDropItemGroup); } var jewelsDropItemGroup = this.Context.CreateNew(); jewelsDropItemGroup.SetGuid(4); jewelsDropItemGroup.Chance = 0.001; jewelsDropItemGroup.ItemType = SpecialItemType.Jewel; jewelsDropItemGroup.Description = "The jewels drop item group (0.1 % drop chance)"; this.GameConfiguration.DropItemGroups.Add(jewelsDropItemGroup); BaseMapInitializer.RegisterDefaultDropItemGroup(jewelsDropItemGroup); } private ItemOptionDefinition CreateLuckOptionDefinition() { var definition = this.Context.CreateNew(); definition.SetGuid(ItemOptionDefinitionNumbers.Luck); definition.Name = "Luck"; definition.AddChance = 0.25f; definition.AddsRandomly = true; definition.MaximumOptionsPerItem = 1; var itemOption = this.Context.CreateNew(); itemOption.SetGuid(ItemOptionDefinitionNumbers.Luck); itemOption.OptionType = this.GameConfiguration.ItemOptionTypes.FirstOrDefault(o => o == ItemOptionTypes.Luck); itemOption.PowerUpDefinition = this.Context.CreateNew(); itemOption.PowerUpDefinition.TargetAttribute = this.GameConfiguration.Attributes.FirstOrDefault(a => a == Stats.CriticalDamageChance); itemOption.PowerUpDefinition.Boost = this.Context.CreateNew(); itemOption.PowerUpDefinition.Boost.ConstantValue!.Value = 0.05f; definition.PossibleOptions.Add(itemOption); return definition; } private void CreateItemOptionTypes() { foreach (var optionType in this.OptionTypes) { var persistentOptionType = this.Context.CreateNew(); persistentOptionType.Description = optionType.Description; persistentOptionType.Id = optionType.Id; persistentOptionType.Name = optionType.Name; persistentOptionType.IsVisible = optionType.IsVisible; this.GameConfiguration.ItemOptionTypes.Add(persistentOptionType); } } /// /// Creates the stat attributes. /// private void CreateStatAttributes() { var attributes = typeof(Stats) .GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) .Where(p => p.PropertyType == typeof(AttributeDefinition)) .Select(p => p.GetValue(typeof(Stats))) .OfType() .ToList(); foreach (var attribute in attributes) { var persistentAttribute = this.Context.CreateNew(attribute.Id, attribute.Designation, attribute.Description); persistentAttribute.MaximumValue = attribute.MaximumValue; this.GameConfiguration.Attributes.Add(persistentAttribute); } } /// /// Adds the global base attribute values which apply to all characters. /// private void AddGlobalBaseAttributeValues() { var moneyAmountRate = this.Context.CreateNew(1f, Stats.MoneyAmountRate.GetPersistent(this.GameConfiguration)); this.GameConfiguration.GlobalBaseAttributeValues.Add(moneyAmountRate); var randomExperienceMinMultiplier = this.Context.CreateNew(0.8f, Stats.RandomExperienceMinMultiplier.GetPersistent(this.GameConfiguration)); this.GameConfiguration.GlobalBaseAttributeValues.Add(randomExperienceMinMultiplier); var randomExperienceMaxMultiplier = this.Context.CreateNew(1.2f, Stats.RandomExperienceMaxMultiplier.GetPersistent(this.GameConfiguration)); this.GameConfiguration.GlobalBaseAttributeValues.Add(randomExperienceMaxMultiplier); this.GameConfiguration.GlobalBaseAttributeValues.Add(this.Context.CreateNew(1f, Stats.MovementSpeedFactor.GetPersistent(this.GameConfiguration))); } }