Files
AdamuSw/src/Persistence/Initialization/GameConfigurationInitializerBase.cs
2026-07-14 19:00:35 +03:00

287 lines
14 KiB
C#

// <copyright file="GameConfigurationInitializerBase.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
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;
/// <summary>
/// Common base for an initialization of a <see cref="GameConfiguration"/>.
/// </summary>
public abstract class GameConfigurationInitializerBase : InitializerBase
{
/// <summary>
/// Initializes a new instance of the <see cref="GameConfigurationInitializerBase"/> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
protected GameConfigurationInitializerBase(IContext context, GameConfiguration gameConfiguration)
: base(context, gameConfiguration)
{
}
/// <summary>
/// Gets the option types which are available.
/// </summary>
protected abstract IEnumerable<ItemOptionType> OptionTypes { get; }
/// <inheritdoc />
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));
}
/// <summary>
/// Calculates the necessary experience for the specified character level.
/// </summary>
/// <param name="level">The character level.</param>
/// <returns>The calculated necessary experience.</returns>
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));
}
/// <summary>
/// Creates an item option definition for the specified attribute definition.
/// </summary>
/// <param name="attributeDefinition">The attribute definition.</param>
/// <param name="number">The amount of the item option definition.</param>
/// <param name="baseValue">The base value for the item option.</param>
/// <returns>The created item option definition.</returns>
protected ItemOptionDefinition CreateOptionDefinition(AttributeDefinition attributeDefinition, short number, byte baseValue = 4)
{
var definition = this.Context.CreateNew<ItemOptionDefinition>();
definition.SetGuid(number);
definition.Name = attributeDefinition.Designation + " Option";
definition.AddChance = 0.25f;
definition.AddsRandomly = true;
definition.MaximumOptionsPerItem = 1;
var itemOption = this.Context.CreateNew<IncreasableItemOption>();
itemOption.SetGuid(number);
itemOption.OptionType =
this.GameConfiguration.ItemOptionTypes.FirstOrDefault(o => o == ItemOptionTypes.Option);
itemOption.PowerUpDefinition = this.Context.CreateNew<PowerUpDefinition>();
itemOption.PowerUpDefinition.TargetAttribute =
this.GameConfiguration.Attributes.First(a => a == attributeDefinition);
itemOption.PowerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
itemOption.PowerUpDefinition.Boost.ConstantValue!.Value = baseValue;
for (short level = 2; level <= this.MaximumOptionLevel; level++)
{
var levelDependentOption = this.Context.CreateNew<ItemOptionOfLevel>();
levelDependentOption.Level = level;
var powerUpDefinition = this.Context.CreateNew<PowerUpDefinition>();
powerUpDefinition.TargetAttribute = itemOption.PowerUpDefinition.TargetAttribute;
powerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
powerUpDefinition.Boost.ConstantValue!.Value = level * baseValue;
levelDependentOption.PowerUpDefinition = powerUpDefinition;
itemOption.LevelDependentOptions.Add(levelDependentOption);
}
definition.PossibleOptions.Add(itemOption);
return definition;
}
/// <summary>
/// Assigns the character class home maps.
/// Needs to be called after the character classes and maps have been initialized.
/// </summary>
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<DropItemGroup>();
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<DropItemGroup>();
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<DropItemGroup>();
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<DropItemGroup>();
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<ItemOptionDefinition>();
definition.SetGuid(ItemOptionDefinitionNumbers.Luck);
definition.Name = "Luck";
definition.AddChance = 0.25f;
definition.AddsRandomly = true;
definition.MaximumOptionsPerItem = 1;
var itemOption = this.Context.CreateNew<IncreasableItemOption>();
itemOption.SetGuid(ItemOptionDefinitionNumbers.Luck);
itemOption.OptionType =
this.GameConfiguration.ItemOptionTypes.FirstOrDefault(o => o == ItemOptionTypes.Luck);
itemOption.PowerUpDefinition = this.Context.CreateNew<PowerUpDefinition>();
itemOption.PowerUpDefinition.TargetAttribute =
this.GameConfiguration.Attributes.FirstOrDefault(a => a == Stats.CriticalDamageChance);
itemOption.PowerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
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<ItemOptionType>();
persistentOptionType.Description = optionType.Description;
persistentOptionType.Id = optionType.Id;
persistentOptionType.Name = optionType.Name;
persistentOptionType.IsVisible = optionType.IsVisible;
this.GameConfiguration.ItemOptionTypes.Add(persistentOptionType);
}
}
/// <summary>
/// Creates the stat attributes.
/// </summary>
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<AttributeDefinition>()
.ToList();
foreach (var attribute in attributes)
{
var persistentAttribute = this.Context.CreateNew<AttributeDefinition>(attribute.Id, attribute.Designation, attribute.Description);
persistentAttribute.MaximumValue = attribute.MaximumValue;
this.GameConfiguration.Attributes.Add(persistentAttribute);
}
}
/// <summary>
/// Adds the global base attribute values which apply to all characters.
/// </summary>
private void AddGlobalBaseAttributeValues()
{
var moneyAmountRate = this.Context.CreateNew<ConstValueAttribute>(1f, Stats.MoneyAmountRate.GetPersistent(this.GameConfiguration));
this.GameConfiguration.GlobalBaseAttributeValues.Add(moneyAmountRate);
var randomExperienceMinMultiplier = this.Context.CreateNew<ConstValueAttribute>(0.8f, Stats.RandomExperienceMinMultiplier.GetPersistent(this.GameConfiguration));
this.GameConfiguration.GlobalBaseAttributeValues.Add(randomExperienceMinMultiplier);
var randomExperienceMaxMultiplier = this.Context.CreateNew<ConstValueAttribute>(1.2f, Stats.RandomExperienceMaxMultiplier.GetPersistent(this.GameConfiguration));
this.GameConfiguration.GlobalBaseAttributeValues.Add(randomExperienceMaxMultiplier);
this.GameConfiguration.GlobalBaseAttributeValues.Add(this.Context.CreateNew<ConstValueAttribute>(1f, Stats.MovementSpeedFactor.GetPersistent(this.GameConfiguration)));
}
}