baseline: OpenMU upstream b5a0961 (fresh source)

This commit is contained in:
Acentech Dev
2026-07-14 19:00:35 +03:00
parent 450402e47d
commit 36fc125d5c
11968 changed files with 748705 additions and 0 deletions

View File

@@ -0,0 +1,327 @@
// <copyright file="BaseMapInitializer.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.Diagnostics;
using System.IO;
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;
using MUnique.OpenMU.GameLogic.Attributes;
using MUnique.OpenMU.Network;
using MUnique.OpenMU.Persistence.Initialization.Updates;
/// <summary>
/// Base class for a map initializer which provides some common basic functionality.
/// </summary>
internal abstract class BaseMapInitializer : IMapInitializer
{
private static readonly IList<DropItemGroup> DefaultDropItemGroups = new List<DropItemGroup>();
private GameMapDefinition? _mapDefinition;
private Dictionary<short, MonsterDefinition>? _npcDictionary;
/// <summary>
/// Initializes a new instance of the <see cref="BaseMapInitializer"/> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
protected BaseMapInitializer(IContext context, GameConfiguration gameConfiguration)
{
this.Context = context;
this.GameConfiguration = gameConfiguration;
this._mapDefinition = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == this.MapNumber && map.Discriminator == this.Discriminator);
}
/// <summary>
/// Gets the context.
/// </summary>
/// <value>
/// The context.
/// </value>
protected IContext Context { get; }
/// <summary>
/// Gets the game configuration.
/// </summary>
/// <value>
/// The game configuration.
/// </value>
protected GameConfiguration GameConfiguration { get; }
/// <summary>
/// Gets the map definition.
/// </summary>
protected GameMapDefinition? MapDefinition => this._mapDefinition;
/// <summary>
/// Gets the NPC dictionary.
/// </summary>
protected Dictionary<short, MonsterDefinition> NpcDictionary => this._npcDictionary ??= this.GameConfiguration.Monsters.ToDictionary(npc => npc.Number, npc => npc);
/// <summary>
/// Gets the map number which will be set as <see cref="GameMapDefinition.Number"/>.
/// </summary>
protected abstract byte MapNumber { get; }
/// <summary>
/// Gets the name of the map which will be set as <see cref="GameMapDefinition.Name"/>.
/// </summary>
protected abstract string MapName { get; }
/// <summary>
/// Gets the version prefix for Terrain resources.
/// </summary>
protected virtual string TerrainVersionPrefix => string.Empty;
/// <summary>
/// Gets the discriminator of the map definition.
/// </summary>
protected virtual byte Discriminator { get; }
/// <summary>
/// Gets the map number of the safezone map where a player respawns after death.
/// </summary>
protected virtual byte SafezoneMapNumber => (byte)(this._mapDefinition?.ExitGates.Any(g => g.IsSpawnGate) ?? false ? this._mapDefinition.Number : Version075.Maps.Lorencia.Number);
private short MapId
{
get
{
if (this.MapDefinition!.Discriminator is 0)
{
return this.MapDefinition.Number;
}
return (short)NumberConversionExtensions.MakeWord(
(byte)this._mapDefinition!.Number,
(byte)this._mapDefinition.Discriminator);
}
}
/// <inheritdoc />
public void Initialize()
{
this.CreateMonsters();
this._mapDefinition = this.Context.CreateNew<GameMapDefinition>();
this._mapDefinition.SetGuid(this.MapNumber, this.Discriminator);
this._mapDefinition.Number = this.MapNumber;
this._mapDefinition.Name = this.MapName;
this._mapDefinition.Discriminator = this.Discriminator;
this._mapDefinition.UpdateTerrainFromResources(this.TerrainVersionPrefix);
this._mapDefinition.ExpMultiplier = 1;
foreach (var spawn in this.CreateNpcSpawns().Concat(this.CreateMonsterSpawns()))
{
this._mapDefinition.MonsterSpawns.Add(spawn);
}
this.CreateMapAttributeRequirements();
this.InitializeDropItemGroups();
this.AdditionalInitialization(this._mapDefinition);
this.GameConfiguration.Maps.Add(this._mapDefinition);
}
/// <inheritdoc/>
public void SetSafezoneMap()
{
if (this._mapDefinition is null)
{
throw new InvalidOperationException("Map is not initialized yet.");
}
var safezoneMap = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == this.SafezoneMapNumber);
this._mapDefinition.SafezoneMap = safezoneMap;
}
/// <summary>
/// Registers the default drop item group.
/// </summary>
/// <param name="dropItemGroup">The drop item group.</param>
internal static void RegisterDefaultDropItemGroup(DropItemGroup dropItemGroup)
{
DefaultDropItemGroups.Add(dropItemGroup);
}
/// <summary>
/// Clears the default drop item groups.
/// </summary>
internal static void ClearDefaultDropItemGroups() => DefaultDropItemGroups.Clear();
/// <summary>
/// Does additional initialization.
/// </summary>
/// <param name="mapDefinition">The map definition.</param>
protected virtual void AdditionalInitialization(GameMapDefinition mapDefinition)
{
// can be overwritten
}
/// <summary>
/// Initializes the drop item groups for this map.
/// By default, we add money and random items. On event or special maps, this can be overwritten.
/// </summary>
protected virtual void InitializeDropItemGroups()
{
if (this._mapDefinition is null)
{
throw new InvalidOperationException("MapDefiniton not set yet.");
}
DefaultDropItemGroups.ForEach(this._mapDefinition.DropItemGroups.Add);
}
/// <summary>
/// Creates all monster spawn areas.
/// </summary>
/// <returns>
/// The spawn areas of the game map.
/// </returns>
/// <remarks>
/// Can be extracted from MonsterSetBase.txt by Regex:
/// Search (single): (?m)^(\d+)[ \t]+(\d+)[ \t]+(\d+)[ \t]+(\d+)[ \t]+(\d+)[ \t]+((-|)\d+).*?$
/// Replace by (single): <![CDATA[yield return this.CreateMonsterSpawn(NpcDictionary[$1], $4, $5, (Direction)$6, SpawnTrigger.Automatic);]]>
/// Search (multiple): (?m)^(\d+)\t*?(\d+)\t+?(\d+)\t+?(\d+)\t+?(\d+)\t+?(\d+)\t+?(\d+)\t+?(-*\d+)\t+?(\d+).*?$
/// Replace by (multiple): <![CDATA[yield return this.CreateMonsterSpawn(NpcDictionary[$1], $4, $6, $5, $7, $9, Direction.Undefined, SpawnTrigger.Automatic);]]>.
/// </remarks>
protected virtual IEnumerable<MonsterSpawnArea> CreateMonsterSpawns()
{
// can be overwritten to add Monsters
yield break;
}
/// <summary>
/// Creates all NPC spawns.
/// </summary>
/// <returns>
/// The spawn areas of the game map.
/// </returns>
protected virtual IEnumerable<MonsterSpawnArea> CreateNpcSpawns()
{
// can be overwritten to add NPCs
yield break;
}
/// <summary>
/// Creates all map specific <see cref="MonsterDefinition"/>s and adds them to the gameConfiguration.
/// </summary>
/// <remarks>
/// Can be extracted from Monsters.txt by Regex: (?m)^(\d+)\t1\t"(.*?)"\t*?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+)\t?(\d+).*?$.
/// <![CDATA[Replace by: {\r\n var monster = this.Context.CreateNew<MonsterDefinition>();\r\n this.GameConfiguration.Monsters.Add(monster);\r\n monster.Number = $1;\r\n monster.Designation = "$2";\r\n monster.MoveRange = $12;\r\n monster.AttackRange = $14;\r\n monster.ViewRange = $15;\r\n monster.MoveDelay = new TimeSpan\($16 * TimeSpan.TicksPerMillisecond\);\r\n monster.AttackDelay = new TimeSpan\($17 * TimeSpan.TicksPerMillisecond\);\r\n monster.RespawnDelay = new TimeSpan\($18 * TimeSpan.TicksPerSecond\);\r\n monster.Attribute = $19;\r\n monster.NumberOfMaximumItemDrops = 1;\r\n var attributes = new Dictionary<AttributeDefinition, float>\r\n {\r\n { Stats.Level, $3 },\r\n { Stats.MaximumHealth, $4 },\r\n { Stats.MinimumPhysBaseDmg, $6 },\r\n { Stats.MaximumPhysBaseDmg, $7 },\r\n { Stats.DefenseBase, $8 },\r\n { Stats.AttackRatePvm, $10 },\r\n { Stats.DefenseRatePvm, $11 },\r\n { Stats.WindResistance, $23f / 255 },\r\n { Stats.PoisonResistance, $24f / 255 },\r\n { Stats.IceResistance, $25f / 255 },\r\n { Stats.WaterResistance, $26f / 255 },\r\n { Stats.FireResistance, $27f / 255 },\r\n };\r\n monster.AddAttributes(attributes, this.Context, this.GameConfiguration);\r\n }\r\n]]>
/// </remarks>
protected virtual void CreateMonsters()
{
// can be overwritten to create new monster definitions
}
/// <summary>
/// Creates a new <see cref="MonsterSpawnArea" /> with the specified data.
/// </summary>
/// <param name="number">The number of the spawn for later reference.</param>
/// <param name="monsterDefinition">The monster definition.</param>
/// <param name="x1">The x1 coordinate.</param>
/// <param name="x2">The x2 coordinate.</param>
/// <param name="y1">The y1 coordinate.</param>
/// <param name="y2">The y2 coordinate.</param>
/// <param name="quantity">The quantity.</param>
/// <param name="direction">The direction.</param>
/// <param name="spawnTrigger">The spawn trigger.</param>
/// <param name="waveNumber">The wave number.</param>
/// <returns>
/// The created monster spawn area.
/// </returns>
protected MonsterSpawnArea CreateMonsterSpawn(short number, MonsterDefinition monsterDefinition, byte x1, byte x2, byte y1, byte y2, short quantity = 1, Direction direction = Direction.Undefined, SpawnTrigger spawnTrigger = SpawnTrigger.Automatic, byte waveNumber = 0)
{
var area = this.Context.CreateNew<MonsterSpawnArea>();
area.SetGuid(this.MapId, number);
area.GameMap = this._mapDefinition;
area.MonsterDefinition = monsterDefinition;
area.Quantity = quantity;
area.Direction = direction;
area.SpawnTrigger = spawnTrigger;
area.X1 = x1;
area.X2 = x2;
area.Y1 = y1;
area.Y2 = y2;
area.WaveNumber = waveNumber;
return area;
}
/// <summary>
/// Creates a new <see cref="MonsterSpawnArea" /> with the specified data.
/// </summary>
/// <param name="number">The number.</param>
/// <param name="monsterDefinition">The monster definition.</param>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
/// <param name="direction">The direction.</param>
/// <param name="spawnTrigger">The spawn trigger.</param>
/// <returns>
/// The created monster spawn area.
/// </returns>
protected MonsterSpawnArea CreateMonsterSpawn(short number, MonsterDefinition monsterDefinition, byte x, byte y, Direction direction = Direction.Undefined, SpawnTrigger spawnTrigger = SpawnTrigger.Automatic)
=> this.CreateMonsterSpawn(number, monsterDefinition, x, x, y, y, 1, direction, spawnTrigger);
/// <summary>
/// Can be used to add additional map requirements.
/// </summary>
protected virtual void CreateMapAttributeRequirements()
{
// needs to be overwritten if a requirement needs to be added.
}
/// <summary>
/// Creates an attribute requirement with the specified minimum value.
/// </summary>
/// <param name="attribute">The attribute.</param>
/// <param name="minimumValue">The minimum value.</param>
protected void CreateRequirement(AttributeDefinition attribute, int minimumValue)
{
if (this._mapDefinition is null)
{
throw new InvalidOperationException("MapDefinition not set yet.");
}
var requirement = this.Context.CreateNew<AttributeRequirement>();
requirement.SetGuid(this.MapId, attribute.Id.ExtractFirstTwoBytes());
requirement.Attribute = attribute.GetPersistent(this.GameConfiguration);
requirement.MinimumValue = minimumValue;
this._mapDefinition.MapRequirements.Add(requirement);
}
/// <summary>
/// Adds a character power up to the current map.
/// </summary>
/// <param name="attribute">The target attribute.</param>
/// <param name="value">The power up value.</param>
/// <param name="aggregateType">The aggregate type.</param>
protected void AddCharacterPowerUp(AttributeDefinition attribute, float value, AggregateType aggregateType = AggregateType.AddRaw)
{
if (this._mapDefinition is null)
{
throw new InvalidOperationException("MapDefinition not set yet.");
}
var powerUp = this.Context.CreateNew<PowerUpDefinition>();
powerUp.TargetAttribute = attribute.GetPersistent(this.GameConfiguration);
powerUp.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
powerUp.Boost.ConstantValue.Value = value;
powerUp.Boost.ConstantValue.AggregateType = aggregateType;
this._mapDefinition.CharacterPowerUpDefinitions.Add(powerUp);
}
/// <summary>
/// Marks the current map as underwater for character movement.
/// </summary>
protected void AddUnderwaterMovementPowerUp()
{
this.AddCharacterPowerUp(Stats.IsUnderwater, 1);
}
}

View File

@@ -0,0 +1,208 @@
// <copyright file="CharacterClassHelper.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Helper class related to <see cref="CharacterClass"/>.
/// </summary>
public static class CharacterClassHelper
{
private static readonly (CharacterClasses Classes, CharacterClassNumber Number)[] NumberMapping =
{
(CharacterClasses.GrandMaster, CharacterClassNumber.GrandMaster),
(CharacterClasses.SoulMaster, CharacterClassNumber.SoulMaster),
(CharacterClasses.DarkWizard, CharacterClassNumber.DarkWizard),
(CharacterClasses.BladeMaster, CharacterClassNumber.BladeMaster),
(CharacterClasses.BladeKnight, CharacterClassNumber.BladeKnight),
(CharacterClasses.DarkKnight, CharacterClassNumber.DarkKnight),
(CharacterClasses.HighElf, CharacterClassNumber.HighElf),
(CharacterClasses.MuseElf, CharacterClassNumber.MuseElf),
(CharacterClasses.FairyElf, CharacterClassNumber.FairyElf),
(CharacterClasses.DuelMaster, CharacterClassNumber.DuelMaster),
(CharacterClasses.MagicGladiator, CharacterClassNumber.MagicGladiator),
(CharacterClasses.LordEmperor, CharacterClassNumber.LordEmperor),
(CharacterClasses.DarkLord, CharacterClassNumber.DarkLord),
(CharacterClasses.DimensionMaster, CharacterClassNumber.DimensionMaster),
(CharacterClasses.BloodySummoner, CharacterClassNumber.BloodySummoner),
(CharacterClasses.Summoner, CharacterClassNumber.Summoner),
(CharacterClasses.FistMaster, CharacterClassNumber.FistMaster),
(CharacterClasses.RageFighter, CharacterClassNumber.RageFighter),
};
/// <summary>
/// Determines the <see cref="CharacterClass"/>es, depending on the given class ranks which are provided by original configuration files.
/// </summary>
/// <param name="gameConfiguration">The game configuration which contains the character classes.</param>
/// <param name="wizardClass">Class rank of the wizard class.</param>
/// <param name="knightClass">Class rank of the knight class.</param>
/// <param name="elfClass">Class rank of the elf class.</param>
/// <param name="magicGladiatorClass">Class rank of the magic gladiator class.</param>
/// <param name="darkLordClass">Class rank of the dark lord class.</param>
/// <param name="summonerClass">Class rank of the summoner class.</param>
/// <param name="ragefighterClass">Class rank of the ragefighter class.</param>
/// <returns>The corresponding <see cref="CharacterClass"/>es of the provided class ranks.</returns>
[Obsolete("Use the overload with the CharacterClasses enum instead.")]
public static IEnumerable<CharacterClass> DetermineCharacterClasses(this GameConfiguration gameConfiguration, int wizardClass, int knightClass, int elfClass, int magicGladiatorClass, int darkLordClass, int summonerClass, int ragefighterClass)
{
var characterClasses = gameConfiguration.CharacterClasses;
IEnumerable<CharacterClass> DetermineClass(int classValue, CharacterClassNumber masterClass, CharacterClassNumber? secondClass, CharacterClassNumber firstClass)
{
if (classValue > 0)
{
if (characterClasses.FirstOrDefault(c => c.Number == (int)masterClass) is { } master)
{
yield return master;
}
if (classValue < 3)
{
if (secondClass is { } && characterClasses.FirstOrDefault(c => c.Number == (int)secondClass) is { } second)
{
yield return second;
}
if (characterClasses.FirstOrDefault(c => c.Number == (int)firstClass) is { } first)
{
yield return first;
}
}
}
}
var result = new List<CharacterClass>();
result.AddRange(DetermineClass(wizardClass, CharacterClassNumber.GrandMaster, CharacterClassNumber.SoulMaster, CharacterClassNumber.DarkWizard));
result.AddRange(DetermineClass(knightClass, CharacterClassNumber.BladeMaster, CharacterClassNumber.BladeKnight, CharacterClassNumber.DarkKnight));
result.AddRange(DetermineClass(elfClass, CharacterClassNumber.HighElf, CharacterClassNumber.MuseElf, CharacterClassNumber.FairyElf));
result.AddRange(DetermineClass(magicGladiatorClass, CharacterClassNumber.DuelMaster, null, CharacterClassNumber.MagicGladiator));
result.AddRange(DetermineClass(darkLordClass, CharacterClassNumber.LordEmperor, null, CharacterClassNumber.DarkLord));
result.AddRange(DetermineClass(summonerClass, CharacterClassNumber.DimensionMaster, CharacterClassNumber.BloodySummoner, CharacterClassNumber.Summoner));
result.AddRange(DetermineClass(ragefighterClass, CharacterClassNumber.FistMaster, null, CharacterClassNumber.RageFighter));
return result;
}
/// <summary>
/// Determines the <see cref="CharacterClass" />es, depending on the given class ranks which are provided by original configuration files.
/// </summary>
/// <param name="gameConfiguration">The game configuration which contains the character classes.</param>
/// <param name="classes">The flags enum of character classes.</param>
/// <param name="ignoreMissing">If set to <c>true</c>, missing classes are ignored and throw no exception.</param>
/// <returns>
/// The corresponding <see cref="CharacterClass" />es of the provided class ranks.
/// </returns>
public static IEnumerable<CharacterClass> DetermineCharacterClasses(this GameConfiguration gameConfiguration, CharacterClasses classes, bool ignoreMissing = false)
{
var characterClasses = gameConfiguration.CharacterClasses;
foreach (var characterClassNumber in NumberMapping.Where(c => classes.HasFlag(c.Classes)).Select(c => c.Number))
{
yield return characterClasses.First(c => c.Number == (int)characterClassNumber);
}
}
/// <summary>
/// Determines the <see cref="CharacterClass"/>es, depending on the given classes which are provided by original configuration files.
/// </summary>
/// <param name="gameConfiguration">The game configuration which contains the character classes.</param>
/// <param name="wizardClass">Class of the wizard class.</param>
/// <param name="knightClass">Class of the knight class.</param>
/// <param name="elfClass">Class of the elf class.</param>
/// <returns>The corresponding <see cref="CharacterClass"/>es of the provided class ranks.</returns>
public static IEnumerable<CharacterClass> DetermineCharacterClasses(this GameConfiguration gameConfiguration, bool wizardClass, bool knightClass, bool elfClass)
{
var characterClasses = gameConfiguration.CharacterClasses;
if (wizardClass)
{
yield return characterClasses.First(c => c.Number == (int)CharacterClassNumber.DarkWizard);
}
if (knightClass)
{
yield return characterClasses.First(c => c.Number == (int)CharacterClassNumber.DarkKnight);
}
if (elfClass)
{
yield return characterClasses.First(c => c.Number == (int)CharacterClassNumber.FairyElf);
}
}
/// <summary>
/// Creates the attribute relationship.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
/// <param name="targetAttribute">The target attribute.</param>
/// <param name="multiplier">The multiplier.</param>
/// <param name="sourceAttribute">The source attribute.</param>
/// <param name="inputOperator">The input operator.</param>
/// <param name="aggregateType">The aggregate type with which the relationship will effect the <paramref name="targetAttribute"/>.</param>
/// <returns>The attribute relationship.</returns>
public static AttributeRelationship CreateAttributeRelationship(IContext context, GameConfiguration gameConfiguration, AttributeDefinition targetAttribute, float multiplier, AttributeDefinition sourceAttribute, InputOperator inputOperator = InputOperator.Multiply, AggregateType aggregateType = AggregateType.AddRaw)
{
return context.CreateNew<AttributeRelationship>(
targetAttribute.GetPersistent(gameConfiguration) ?? targetAttribute,
multiplier,
sourceAttribute.GetPersistent(gameConfiguration) ?? sourceAttribute,
inputOperator,
default(AttributeDefinition?),
aggregateType);
}
/// <summary>
/// Creates the attribute relationship.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
/// <param name="targetAttribute">The target attribute.</param>
/// <param name="multiplierAttribute">The multiplier attribute.</param>
/// <param name="sourceAttribute">The source attribute.</param>
/// <param name="inputOperator">The input operator.</param>
/// <param name="aggregateType">The aggregate type with which the relationship will effect the <paramref name="targetAttribute"/>.</param>
/// <returns>The attribute relationship.</returns>
public static AttributeRelationship CreateAttributeRelationship(IContext context, GameConfiguration gameConfiguration, AttributeDefinition targetAttribute, AttributeDefinition multiplierAttribute, AttributeDefinition sourceAttribute, InputOperator inputOperator = InputOperator.Multiply, AggregateType aggregateType = AggregateType.AddRaw)
{
return context.CreateNew<AttributeRelationship>(
targetAttribute.GetPersistent(gameConfiguration) ?? targetAttribute,
0f,
sourceAttribute.GetPersistent(gameConfiguration) ?? sourceAttribute,
inputOperator,
multiplierAttribute.GetPersistent(gameConfiguration) ?? multiplierAttribute,
aggregateType);
}
/// <summary>
/// Creates the conditional relationship between attributes.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
/// <param name="targetAttribute">The target attribute.</param>
/// <param name="conditionalAttribute">The conditional attribute.</param>
/// <param name="sourceAttribute">The source attribute.</param>
/// <param name="aggregateType">The aggregate type with which the relationship will effect the <paramref name="targetAttribute"/>.</param>
/// <returns>The attribute relationship.</returns>
public static AttributeRelationship CreateConditionalRelationship(IContext context, GameConfiguration gameConfiguration, AttributeDefinition targetAttribute, AttributeDefinition conditionalAttribute, AttributeDefinition sourceAttribute, AggregateType aggregateType = AggregateType.AddRaw)
{
return context.CreateNew<AttributeRelationship>(
targetAttribute.GetPersistent(gameConfiguration) ?? targetAttribute,
conditionalAttribute.GetPersistent(gameConfiguration) ?? conditionalAttribute,
sourceAttribute.GetPersistent(gameConfiguration) ?? sourceAttribute,
aggregateType);
}
/// <summary>
/// Creates the constant value attribute.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
/// <param name="value">The value.</param>
/// <param name="attribute">The attribute.</param>
/// <returns>The constant value attribute.</returns>
public static ConstValueAttribute CreateConstValueAttribute(IContext context, GameConfiguration gameConfiguration, float value, AttributeDefinition attribute)
{
return context.CreateNew<ConstValueAttribute>(value, attribute.GetPersistent(gameConfiguration));
}
}

View File

@@ -0,0 +1,207 @@
// <copyright file="CharacterClassInitialization.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Initialization of character classes data.
/// </summary>
internal partial class CharacterClassInitialization : InitializerBase
{
private const int LorenciaMapId = 0;
private const int NoriaMapId = 3;
private const int ElvenlandMapId = 51;
/// <summary>
/// Initializes a new instance of the <see cref="CharacterClassInitialization" /> class.
/// </summary>
/// <param name="context">The persistence context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
public CharacterClassInitialization(IContext context, GameConfiguration gameConfiguration)
: base(context, gameConfiguration)
{
}
/// <summary>
/// Gets a value indicating whether to use classic PVP, which uses no shield stats and the same attack/defense rate as PvM.
/// </summary>
protected virtual bool UseClassicPvp => false;
/// <summary>
/// Creates the character classes.
/// </summary>
public override void Initialize()
{
var bladeMaster = this.CreateBladeMaster();
var bladeKnight = this.CreateBladeKnight(bladeMaster);
this.CreateDarkKnight(CharacterClassNumber.DarkKnight, "Dark Knight", false, bladeKnight, true);
var grandMaster = this.CreateGrandMaster();
var soulMaster = this.CreateSoulMaster(grandMaster);
this.CreateDarkWizard(CharacterClassNumber.DarkWizard, "Dark Wizard", false, soulMaster, true);
var highElf = this.CreateHighElf();
var museElf = this.CreateMuseElf(highElf);
this.CreateFairyElf(CharacterClassNumber.FairyElf, "Fairy Elf", false, museElf, true);
var dimensionMaster = this.CreateDimensionMaster();
var bloodySummoner = this.CreateBloodySummoner(dimensionMaster);
this.CreateSummoner(CharacterClassNumber.Summoner, "Summoner", false, bloodySummoner, true);
var duelMaster = this.CreateDuelMaster();
this.CreateMagicGladiator(CharacterClassNumber.MagicGladiator, "Magic Gladiator", false, duelMaster, true);
var lordEmperor = this.CreateLordEmperor();
this.CreateDarkLord(CharacterClassNumber.DarkLord, "Dark Lord", false, lordEmperor, true);
var fistMaster = this.CreateFistMaster();
this.CreateRageFighter(CharacterClassNumber.RageFighter, "Rage Fighter", false, fistMaster, true);
}
private StatAttributeDefinition CreateStatAttributeDefinition(AttributeDefinition attribute, int value, bool increasableByPlayer)
{
var definition = this.Context.CreateNew<StatAttributeDefinition>(attribute.GetPersistent(this.GameConfiguration), value, increasableByPlayer);
return definition;
}
private AttributeRelationship CreateAttributeRelationship(AttributeDefinition targetAttribute, float multiplier, AttributeDefinition sourceAttribute, InputOperator inputOperator = InputOperator.Multiply, AggregateType aggregateType = AggregateType.AddRaw)
{
return CharacterClassHelper.CreateAttributeRelationship(this.Context, this.GameConfiguration, targetAttribute, multiplier, sourceAttribute, inputOperator, aggregateType);
}
private AttributeRelationship CreateAttributeRelationship(AttributeDefinition targetAttribute, AttributeDefinition multiplierAttribute, AttributeDefinition sourceAttribute, InputOperator inputOperator = InputOperator.Multiply, AggregateType aggregateType = AggregateType.AddRaw)
{
return CharacterClassHelper.CreateAttributeRelationship(this.Context, this.GameConfiguration, targetAttribute, multiplierAttribute, sourceAttribute, inputOperator, aggregateType);
}
private AttributeRelationship CreateConditionalRelationship(AttributeDefinition targetAttribute, AttributeDefinition conditionalAttribute, AttributeDefinition sourceAttribute, AggregateType aggregateType = AggregateType.AddRaw)
{
return CharacterClassHelper.CreateConditionalRelationship(this.Context, this.GameConfiguration, targetAttribute, conditionalAttribute, sourceAttribute, aggregateType);
}
private ConstValueAttribute CreateConstValueAttribute(float value, AttributeDefinition attribute)
{
return CharacterClassHelper.CreateConstValueAttribute(this.Context, this.GameConfiguration, value, attribute);
}
private void AddCommonAttributeRelationships(ICollection<AttributeRelationship> attributeRelationships)
{
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.TotalLevel, 1, Stats.Level));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.TotalLevel, 1, Stats.MasterLevel));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.TotalStrength, 1, Stats.BaseStrength));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.TotalAgility, 1, Stats.BaseAgility));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.TotalVitality, 1, Stats.BaseVitality));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.TotalEnergy, 1, Stats.BaseEnergy));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1, Stats.DefenseShield));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseFinal, 0.5f, Stats.DefenseBase));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefensePvm, 1, Stats.DefenseFinal));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefensePvp, 1, Stats.DefenseFinal));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackSpeedAny, 1, Stats.AttackSpeedByWeapon));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1, Stats.AttackSpeedAny));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MagicSpeed, 1, Stats.AttackSpeedAny));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.MinimumPhysBaseDmgByWeapon));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.MaximumPhysBaseDmgByWeapon));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.BaseMinDamageBonus));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.BaseMaxDamageBonus));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.PhysicalBaseDmg, 1, Stats.BaseDamageBonus, aggregateType: AggregateType.AddFinal));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.PhysicalBaseDmg));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.PhysicalBaseDmg));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.PhysicalBaseDmgIncrease, aggregateType: AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.PhysicalBaseDmgIncrease, aggregateType: AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1, Stats.SwellLifeHealthIncrease, aggregateType: AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1, Stats.SwellLifeManaIncrease, aggregateType: AggregateType.Multiplicate));
// If two weapons are equipped (DK, MG, Sum, RF) we subtract the half of the sum of the speeds again from the attack speed
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AreTwoWeaponsEquipped, 1, Stats.EquippedWeaponCount));
var tempSpeed = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Temp Half weapon attack speed", string.Empty);
this.GameConfiguration.Attributes.Add(tempSpeed);
attributeRelationships.Add(this.CreateAttributeRelationship(tempSpeed, -0.5f, Stats.AttackSpeedByWeapon));
attributeRelationships.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.AreTwoWeaponsEquipped, tempSpeed));
var tempDefense = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Temp Defense Bonus multiplier with Shield", string.Empty);
this.GameConfiguration.Attributes.Add(tempDefense);
attributeRelationships.Add(this.CreateConditionalRelationship(tempDefense, Stats.IsShieldEquipped, Stats.DefenseIncreaseWithEquippedShield));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseFinal, 1, tempDefense, InputOperator.Add, AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateConditionalRelationship(Stats.DefenseFinal, Stats.DefenseShield, Stats.ShieldItemDefenseIncrease));
attributeRelationships.Add(this.CreateConditionalRelationship(Stats.DefenseFinal, Stats.IsShieldEquipped, Stats.BonusDefenseWithShield, AggregateType.AddFinal));
attributeRelationships.Add(this.CreateConditionalRelationship(Stats.DefenseRatePvm, Stats.IsShieldEquipped, Stats.BonusDefenseRateWithShield, AggregateType.AddFinal));
var tempInnovDefDec = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Temp Innovation defense decrement", string.Empty);
this.GameConfiguration.Attributes.Add(tempInnovDefDec);
attributeRelationships.Add(this.CreateAttributeRelationship(tempInnovDefDec, -1, Stats.InnovationDefDecrement));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseDecrement, 1, tempInnovDefDec, InputOperator.Add, AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.01f, Stats.IsInSafezone));
if (this.UseClassicPvp)
{
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 1, Stats.DefenseRatePvm));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 1, Stats.AttackRatePvm));
}
else
{
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 0.01f, Stats.IsInSafezone));
}
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumGuildSize, 0.1f, Stats.Level));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.CanFly, 1, Stats.IsDinorantEquipped));
}
private void AddCommonBaseAttributeValues(ICollection<ConstValueAttribute> baseAttributeValues, bool isMaster)
{
baseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 27.5f, Stats.ManaRecoveryMultiplier));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.DamageReceiveDecrement));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.AttackDamageIncrease));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.ExperienceRate));
baseAttributeValues.Add(this.CreateConstValueAttribute(0.03f, Stats.PoisonDamageMultiplier));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.ItemDurationIncrease));
baseAttributeValues.Add(this.CreateConstValueAttribute(2, Stats.AbilityRecoveryAbsolute));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.PhysicalBaseDmgIncrease));
baseAttributeValues.Add(this.CreateConstValueAttribute(-1, Stats.AreTwoWeaponsEquipped));
baseAttributeValues.Add(this.CreateConstValueAttribute(-1, Stats.HasDoubleWield));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.DefenseDecrement));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SwellLifeHealthIncrease));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SwellLifeManaIncrease));
baseAttributeValues.Add(this.CreateConstValueAttribute(0.1f, Stats.DurabilityReductionFactor));
if (isMaster)
{
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.MasterPointsPerLevelUp));
baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.MasterExperienceRate));
}
if (!this.UseClassicPvp)
{
baseAttributeValues.Add(this.CreateConstValueAttribute(0.01f, Stats.ShieldRecoveryMultiplier));
}
}
/// <summary>
/// Adds double wield attribute relationships applicable to characters that can double wield (DK, MG, and RF).
/// A double wield grants 110% physical attack damage (55% base damage, later doubled on damage calculations).
/// </summary>
private void AddDoubleWieldAttributeRelationships(ICollection<AttributeRelationship> attributeRelationships)
{
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HasDoubleWield, 1, Stats.DoubleWieldWeaponCount, InputOperator.Maximum));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.PhysicalBaseDmgIncrease, 0.55f, Stats.HasDoubleWield, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateConditionalRelationship(Stats.MinimumPhysBaseDmgByWeapon, Stats.HasDoubleWield, Stats.MinPhysBaseDmgByRightWeapon));
attributeRelationships.Add(this.CreateConditionalRelationship(Stats.MaximumPhysBaseDmgByWeapon, Stats.HasDoubleWield, Stats.MaxPhysBaseDmgByRightWeapon));
// We need to average the base damage of the two weapons and their item option and excellent options.
// For PhysicalBaseDmgIncrease, to avoid using extra attributes, we use ad-hoc AggregateTypes (see ItemPowerUpFactory.GetPowerUpsOfItemOptions())
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmgByWeapon, 0.5f, Stats.HasDoubleWield, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmgByWeapon, 0.5f, Stats.HasDoubleWield, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.PhysicalBaseDmg, 0.5f, Stats.HasDoubleWield, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.PhysicalBaseDmgIncrease, 0.5f, Stats.HasDoubleWield, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.PhysicalBaseDmgIncrease, 1, Stats.HasDoubleWield));
}
}

View File

@@ -0,0 +1,101 @@
// <copyright file="CharacterClassNumber.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.CharacterClasses;
/// <summary>
/// The default character class numbers.
/// </summary>
public enum CharacterClassNumber : byte
{
/// <summary>
/// The dark wizard.
/// </summary>
DarkWizard = 0,
/// <summary>
/// The soul master.
/// </summary>
SoulMaster = 2,
/// <summary>
/// The grand master.
/// </summary>
GrandMaster = 3,
/// <summary>
/// The dark knight.
/// </summary>
DarkKnight = 4,
/// <summary>
/// The blade knight.
/// </summary>
BladeKnight = 6,
/// <summary>
/// The blade master.
/// </summary>
BladeMaster = 7,
/// <summary>
/// The fairy elf.
/// </summary>
FairyElf = 8,
/// <summary>
/// The muse elf.
/// </summary>
MuseElf = 10,
/// <summary>
/// The high elf.
/// </summary>
HighElf = 11,
/// <summary>
/// The magic gladiator.
/// </summary>
MagicGladiator = 12,
/// <summary>
/// The duel master.
/// </summary>
DuelMaster = 13,
/// <summary>
/// The dark lord.
/// </summary>
DarkLord = 16,
/// <summary>
/// The lord emperor.
/// </summary>
LordEmperor = 17,
/// <summary>
/// The summoner.
/// </summary>
Summoner = 20,
/// <summary>
/// The bloody summoner.
/// </summary>
BloodySummoner = 22,
/// <summary>
/// The dimension master.
/// </summary>
DimensionMaster = 23,
/// <summary>
/// The rage fighter.
/// </summary>
RageFighter = 24,
/// <summary>
/// The fist master.
/// </summary>
FistMaster = 25,
}

View File

@@ -0,0 +1,197 @@
// <copyright file="CharacterClasses.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.CharacterClasses;
/// <summary>
/// Defines the character classes as flags enum.
/// </summary>
[Flags]
public enum CharacterClasses : long
{
/// <summary>
/// None of the classes.
/// </summary>
None = 0,
/// <summary>
/// All of the classes.
/// </summary>
All = 0xFFFFFFFF,
/// <summary>
/// The dark wizard.
/// </summary>
DarkWizard = 0b0000_0001,
/// <summary>
/// The soul master.
/// </summary>
SoulMaster = 0b0000_0010,
/// <summary>
/// The grand master.
/// </summary>
GrandMaster = 0b0000_0100,
/// <summary>
/// The dark knight.
/// </summary>
DarkKnight = 0b0001_0000,
/// <summary>
/// The blade knight.
/// </summary>
BladeKnight = 0b0010_0000,
/// <summary>
/// The blade master.
/// </summary>
BladeMaster = 0b0100_0000,
/// <summary>
/// The fairy elf.
/// </summary>
FairyElf = 0b0001_000_0000,
/// <summary>
/// The muse elf.
/// </summary>
MuseElf = 0b0010_0000_0000,
/// <summary>
/// The high elf.
/// </summary>
HighElf = 0b0100_0000_0000,
/// <summary>
/// The magic gladiator.
/// </summary>
MagicGladiator = 0b0001_0000_0000_0000,
/// <summary>
/// The duel master.
/// </summary>
DuelMaster = 0b0010_0000_0000_0000,
/// <summary>
/// The dark lord.
/// </summary>
DarkLord = 0b0001_0000_0000_0000_0000,
/// <summary>
/// The lord emperor.
/// </summary>
LordEmperor = 0b0010_0000_0000_0000_0000,
/// <summary>
/// The summoner.
/// </summary>
Summoner = 0b0001_0000_0000_0000_0000_0000,
/// <summary>
/// The bloody summoner.
/// </summary>
BloodySummoner = 0b0010_0000_0000_0000_0000_0000,
/// <summary>
/// The dimension master.
/// </summary>
DimensionMaster = 0b0100_0000_0000_0000_0000_0000,
/// <summary>
/// The rage fighter.
/// </summary>
RageFighter = 0b0001_0000_0000_0000_0000_0000_0000,
/// <summary>
/// The fist master.
/// </summary>
FistMaster = 0b0010_0000_0000_0000_0000_0000_0000,
/// <summary>
/// All wizards.
/// </summary>
AllWizards = DarkWizard | SoulMaster | GrandMaster,
/// <summary>
/// All knights.
/// </summary>
AllKnights = DarkKnight | BladeKnight | BladeMaster,
/// <summary>
/// All elfs.
/// </summary>
AllElfs = FairyElf | MuseElf | HighElf,
/// <summary>
/// All summoners.
/// </summary>
AllSummoners = Summoner | BloodySummoner | DimensionMaster,
/// <summary>
/// All magic gladiators.
/// </summary>
AllMGs = MagicGladiator | DuelMaster,
/// <summary>
/// All lords.
/// </summary>
AllLords = DarkLord | LordEmperor,
/// <summary>
/// All fighters.
/// </summary>
AllFighters = RageFighter | FistMaster,
/// <summary>
/// All magicians (wizards and magic gladiators).
/// </summary>
AllMagicians = AllWizards | AllMGs,
/// <summary>
/// All knights, lords and magic gladiators.
/// </summary>
AllKnightsLordsAndMGs = AllKnights | AllLords | AllMGs,
/// <summary>
/// The soul master and grand master.
/// </summary>
SoulMasterAndGrandMaster = SoulMaster | GrandMaster,
/// <summary>
/// The blade knight and blade master.
/// </summary>
BladeKnightAndBladeMaster = BladeKnight | BladeMaster,
/// <summary>
/// The muse elf and high elf.
/// </summary>
MuseElfAndHighElf = MuseElf | HighElf,
/// <summary>
/// The bloody summoner and dimension master.
/// </summary>
BloodySummonerAndDimensionMaster = BloodySummoner | DimensionMaster,
/// <summary>
/// All masters.
/// </summary>
AllMasters = GrandMaster | BladeMaster | HighElf | DimensionMaster | DuelMaster | LordEmperor | FistMaster,
/// <summary>
/// All masters except fist master.
/// </summary>
AllMastersExceptFistMaster = GrandMaster | BladeMaster | HighElf | DimensionMaster | DuelMaster | LordEmperor,
/// <summary>
/// All second classes.
/// </summary>
AllSecondClass = SoulMaster | BladeKnight | MuseElf | BloodySummoner | MagicGladiator | DarkLord | RageFighter,
/// <summary>
/// All masters and second classes.
/// </summary>
AllMastersAndSecondClass = AllMasters | AllSecondClass,
}

View File

@@ -0,0 +1,127 @@
// <copyright file="ClassDarkKnight.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Initialization of character classes data.
/// </summary>
internal partial class CharacterClassInitialization
{
/// <summary>
/// Creates the dark knight.
/// </summary>
/// <param name="number">The number.</param>
/// <param name="name">The name.</param>
/// <param name="isMaster">if set to <c>true</c>, the class is a master class which has access to the master skill tree.</param>
/// <param name="nextGenerationClass">The next generation class.</param>
/// <param name="canGetCreated">If set to <c>true</c>, it can get created by the player.</param>
/// <returns>The created character class.</returns>
protected CharacterClass CreateDarkKnight(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated)
{
var result = this.Context.CreateNew<CharacterClass>();
result.SetGuid((byte)number);
this.GameConfiguration.CharacterClasses.Add(result);
result.CanGetCreated = canGetCreated;
result.HomeMap = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == LorenciaMapId);
result.Number = (byte)number;
result.Name = name;
result.IsMasterClass = isMaster;
result.NextGenerationClass = nextGenerationClass;
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Level, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.PointsPerLevelUp, 5, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseStrength, 28, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseAgility, 20, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseVitality, 25, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseEnergy, 10, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 110, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 20, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false));
this.AddCommonAttributeRelationships(result.AttributeCombinations);
this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations);
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1.0f / 3, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvm, 1.0f / 3, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 5, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 0.25f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1.0f / 15, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MagicSpeed, 1.0f / 20, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 1, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.15f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 0.5f, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 2, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 3, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 6, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 4, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.SkillMultiplier, 0.001f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ComboBonus, 0.5f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ComboBonus, 0.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ComboBonus, 0.5f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.IsOneHandedSwordEquipped, Stats.WeaponMasteryAttackSpeed));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.MaceMasteryStunChance, Stats.IsMaceEquipped, Stats.MasteryStunChance));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.DoubleDamageChance, Stats.IsSpearEquipped, Stats.SpearMasteryDoubleDamageChance));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.TotalEnergy));
if (!this.UseClassicPvp)
{
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 2, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 4.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3, Stats.TotalLevel));
}
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(10, Stats.MaximumMana));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(35, Stats.MaximumHealth));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(2, Stats.SkillMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.05f, Stats.AbilityRecoveryMultiplier));
this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster);
return result;
}
private CharacterClass CreateBladeMaster()
{
var result = this.CreateDarkKnight(CharacterClassNumber.BladeMaster, "Blade Master", true, null, false);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false));
return result;
}
private CharacterClass CreateBladeKnight(CharacterClass bladeMaster)
{
return this.CreateDarkKnight(CharacterClassNumber.BladeKnight, "Blade Knight", false, bladeMaster, false);
}
}

View File

@@ -0,0 +1,144 @@
// <copyright file="ClassDarkLord.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Initialization of character classes data.
/// </summary>
internal partial class CharacterClassInitialization
{
private CharacterClass CreateLordEmperor()
{
var result = this.CreateDarkLord(CharacterClassNumber.LordEmperor, "Lord Emperor", true, null, false);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false));
return result;
}
private CharacterClass CreateDarkLord(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated)
{
var energyMinus15 = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "TotalEnergy minus 15", "TotalEnergy minus 15");
this.GameConfiguration.Attributes.Add(energyMinus15);
var result = this.Context.CreateNew<CharacterClass>();
result.SetGuid((byte)number);
this.GameConfiguration.CharacterClasses.Add(result);
result.CanGetCreated = canGetCreated;
result.CreationAllowedFlag = 2;
result.HomeMap = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == LorenciaMapId);
result.Number = (byte)number;
result.Name = name;
result.LevelRequirementByCreation = 250;
result.IsMasterClass = isMaster;
result.NextGenerationClass = nextGenerationClass;
result.LevelWarpRequirementReductionPercent = (int)Math.Ceiling(100.0 / 3);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Level, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.PointsPerLevelUp, 7, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseStrength, 26, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseAgility, 20, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseVitality, 20, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseEnergy, 15, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseLeadership, 25, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 90, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 40, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false));
this.AddCommonAttributeRelationships(result.AttributeCombinations);
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.TotalLeadership, 1, Stats.BaseLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1.0f / 7, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvm, 1.0f / 7, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 5, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 2.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.0f / 6, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.0f / 10, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1.0f / 10, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MagicSpeed, 1.0f / 10, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.15f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.1f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(energyMinus15, -15, Stats.TotalEnergy, InputOperator.Add));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1.5f, energyMinus15));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1.5f, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 2, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 7, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 5, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 14, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 10, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.SkillMultiplier, 0.0005f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.RavenAttackDamageIncrease, 1.0f / 100, Stats.ScepterRise));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumGuildSize, 0.1f, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DamageReceiveDecrement, 1, Stats.DamageReceiveHorseDecrement, InputOperator.Add, AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.RavenMinimumDamage, 1.0f / 8, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.RavenMinimumDamage, 15, Stats.RavenLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.RavenMaximumDamage, 1.0f / 4, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.RavenMaximumDamage, 15, Stats.RavenLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.RavenAttackSpeed, 1.0f / 50, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.RavenAttackSpeed, 4.0f / 5, Stats.RavenLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.RavenAttackRate, 16f / 15, Stats.RavenLevel));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.RavenBonusDamage, Stats.IsScepterEquipped, Stats.ScepterPetBonusDamage));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.BonusDamageWithScepter, Stats.BonusDamageWithScepterCmdDiv, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.MasterSkillPhysBonusDmg, Stats.IsScepterEquipped, Stats.BonusDamageWithScepter));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.DefenseFinal, Stats.IsHorseEquipped, Stats.BonusDefenseWithHorse, aggregateType: AggregateType.AddFinal));
if (!this.UseClassicPvp)
{
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 2, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 4.0f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3, Stats.TotalLevel));
}
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(38, Stats.MaximumMana));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(48.5f, Stats.MaximumHealth));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(2, Stats.SkillMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.PetDurationIncrease));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(180, Stats.RavenMinimumDamage));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(200, Stats.RavenMaximumDamage));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(20, Stats.RavenAttackSpeed));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1000, Stats.RavenAttackRate));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.3f, Stats.RavenCriticalDamageChance));
this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster);
return result;
}
}

View File

@@ -0,0 +1,134 @@
// <copyright file="ClassDarkWizard.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Initialization of character classes data.
/// </summary>
internal partial class CharacterClassInitialization
{
/// <summary>
/// Creates the dark wizard.
/// </summary>
/// <param name="number">The number.</param>
/// <param name="name">The name.</param>
/// <param name="isMaster">if set to <c>true</c>, the class is a master class which has access to the master skill tree.</param>
/// <param name="nextGenerationClass">The next generation class.</param>
/// <param name="canGetCreated">If set to <c>true</c>, it can get created by the player.</param>
/// <returns>The created character class.</returns>
protected CharacterClass CreateDarkWizard(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated)
{
var result = this.Context.CreateNew<CharacterClass>();
result.SetGuid((byte)number);
this.GameConfiguration.CharacterClasses.Add(result);
result.CanGetCreated = canGetCreated;
result.HomeMap = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == LorenciaMapId);
result.Number = (byte)number;
result.Name = name;
result.IsMasterClass = isMaster;
result.NextGenerationClass = nextGenerationClass;
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Level, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.PointsPerLevelUp, 5, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseStrength, 18, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseAgility, 18, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseVitality, 15, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseEnergy, 30, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 60, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 60, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false));
this.AddCommonAttributeRelationships(result.AttributeCombinations);
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 0.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvm, 1.0f / 3, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 5, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 0.25f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1.0f / 20, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MagicSpeed, 1.0f / 10, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.4f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 2, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 2, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 2, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 8, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 4, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1.0f / 9, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1.0f / 4, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.BaseMinDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.BaseMaxDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.WizardryBaseDmg, 1, Stats.BaseDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.WizardryBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.WizardryBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.WizardryBaseDmgIncrease, aggregateType: AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.WizardryBaseDmgIncrease, aggregateType: AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.WizardryAttackDamageIncrease, 1.0f / 100, Stats.StaffRise));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.WizardryBaseDmg, Stats.IsOneHandedStaffEquipped, Stats.OneHandedStaffBonusBaseDamage));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.IsOneHandedStaffEquipped, Stats.WeaponMasteryAttackSpeed));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.WizardryBaseDmg, Stats.IsTwoHandedStaffEquipped, Stats.TwoHandedStaffBonusBaseDamage));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalEnergy));
if (!this.UseClassicPvp)
{
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 2, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 4, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3, Stats.TotalLevel));
}
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(30, Stats.MaximumHealth));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SkillMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f, Stats.WizardryAttackDamageIncrease));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.WizardryBaseDmgIncrease));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.NovaStageDamage)); // placeholder value
this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster);
return result;
}
private CharacterClass CreateSoulMaster(CharacterClass grandMaster)
{
return this.CreateDarkWizard(CharacterClassNumber.SoulMaster, "Soul Master", false, grandMaster, false);
}
private CharacterClass CreateGrandMaster()
{
var result = this.CreateDarkWizard(CharacterClassNumber.GrandMaster, "Grand Master", true, null, false);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false));
return result;
}
}

View File

@@ -0,0 +1,139 @@
// <copyright file="ClassFairyElf.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Initialization of character classes data.
/// </summary>
internal partial class CharacterClassInitialization
{
/// <summary>
/// Creates the fairy elf.
/// </summary>
/// <param name="number">The number.</param>
/// <param name="name">The name.</param>
/// <param name="isMaster">if set to <c>true</c>, the class is a master class which has access to the master skill tree.</param>
/// <param name="nextGenerationClass">The next generation class.</param>
/// <param name="canGetCreated">If set to <c>true</c>, it can get created by the player.</param>
/// <returns>The created character class.</returns>
protected CharacterClass CreateFairyElf(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated)
{
var ammunitionDmgIncrease = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Ammunition damage increase", string.Empty);
this.GameConfiguration.Attributes.Add(ammunitionDmgIncrease);
var result = this.Context.CreateNew<CharacterClass>();
result.SetGuid((byte)number);
this.GameConfiguration.CharacterClasses.Add(result);
result.CanGetCreated = canGetCreated;
result.HomeMap = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == NoriaMapId);
result.Number = (byte)number;
result.Name = name;
result.IsMasterClass = isMaster;
result.NextGenerationClass = nextGenerationClass;
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Level, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.PointsPerLevelUp, 5, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseStrength, 22, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseAgility, 25, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseVitality, 20, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseEnergy, 15, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 80, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 30, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.AmmunitionAmount, 0, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false));
this.AddCommonAttributeRelationships(result.AttributeCombinations);
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.TotalStrengthAndAgility, Stats.TotalAgility, Stats.TotalStrength, InputOperator.Add));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1.0f / 10, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvm, 0.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 5, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 0.25f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1.0f / 50, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MagicSpeed, 1.0f / 50, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1.5f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1.5f, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 2, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ArcheryMinDmg, 1.0f / 7, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ArcheryMaxDmg, 1.0f / 4, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ArcheryMinDmg, 1.0f / 14, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ArcheryMaxDmg, 1.0f / 8, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MeleeMinDmg, 1.0f / 7, Stats.TotalStrengthAndAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MeleeMaxDmg, 1.0f / 4, Stats.TotalStrengthAndAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.PhysicalBaseDmgIncrease, 1, ammunitionDmgIncrease, InputOperator.Add, AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ArcheryAttackMode, 1, Stats.IsBowEquipped));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.ArcheryAttackMode, 1, Stats.IsCrossBowEquipped));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MeleeAttackMode, 0, Stats.ArcheryAttackMode, InputOperator.ExponentiateByAttribute));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.MinimumPhysBaseDmg, Stats.ArcheryAttackMode, Stats.ArcheryMinDmg));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.MaximumPhysBaseDmg, Stats.ArcheryAttackMode, Stats.ArcheryMaxDmg));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(ammunitionDmgIncrease, Stats.ArcheryAttackMode, Stats.AmmunitionDamageBonus));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.MinimumPhysBaseDmg, Stats.MeleeAttackMode, Stats.MeleeMinDmg));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.MaximumPhysBaseDmg, Stats.MeleeAttackMode, Stats.MeleeMaxDmg));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.IsBowEquipped, Stats.WeaponMasteryAttackSpeed));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalEnergy));
if (!this.UseClassicPvp)
{
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.1f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 2, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 0.6f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3, Stats.TotalLevel));
}
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(39, Stats.MaximumHealth));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(6, Stats.MaximumMana));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SkillMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier));
this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster);
return result;
}
private CharacterClass CreateMuseElf(CharacterClass highElf)
{
return this.CreateFairyElf(CharacterClassNumber.MuseElf, "Muse Elf", false, highElf, false);
}
private CharacterClass CreateHighElf()
{
var result = this.CreateFairyElf(CharacterClassNumber.HighElf, "High Elf", true, null, false);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false));
return result;
}
}

View File

@@ -0,0 +1,140 @@
// <copyright file="ClassMagicGladiator.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Initialization of character classes data.
/// </summary>
internal partial class CharacterClassInitialization
{
/// <summary>
/// Creates the magic gladiator.
/// </summary>
/// <param name="number">The number.</param>
/// <param name="name">The name.</param>
/// <param name="isMaster">if set to <c>true</c>, the class is a master class which has access to the master skill tree.</param>
/// <param name="nextGenerationClass">The next generation class.</param>
/// <param name="canGetCreated">If set to <c>true</c>, it can get created by the player.</param>
/// <returns>The created character class.</returns>
protected CharacterClass CreateMagicGladiator(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated)
{
var result = this.Context.CreateNew<CharacterClass>();
result.SetGuid((byte)number);
this.GameConfiguration.CharacterClasses.Add(result);
result.CanGetCreated = canGetCreated;
result.HomeMap = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == LorenciaMapId);
result.Number = (byte)number;
result.Name = name;
result.CreationAllowedFlag = 4;
result.IsMasterClass = isMaster;
result.LevelRequirementByCreation = 220;
result.NextGenerationClass = nextGenerationClass;
result.LevelWarpRequirementReductionPercent = (int)Math.Ceiling(100.0 / 3);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Level, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.PointsPerLevelUp, 7, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseStrength, 26, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseAgility, 26, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseVitality, 26, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseEnergy, 26, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 110, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 60, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false));
this.AddCommonAttributeRelationships(result.AttributeCombinations);
this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations);
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1.0f / 5, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvm, 1.0f / 3, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 5, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 0.25f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1.0f / 15, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MagicSpeed, 1.0f / 20, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.15f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 2, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 2, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 6, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 4, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 12, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 8, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1.0f / 9, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1.0f / 4, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.BaseMinDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.BaseMaxDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.WizardryBaseDmg, 1, Stats.BaseDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.WizardryBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.WizardryBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.WizardryBaseDmgIncrease, aggregateType: AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.WizardryBaseDmgIncrease, aggregateType: AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.WizardryAttackDamageIncrease, 1.0f / 100, Stats.StaffRise));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.WizardryBaseDmg, Stats.IsOneHandedStaffEquipped, Stats.OneHandedStaffBonusBaseDamage));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.WizardryBaseDmg, Stats.IsTwoHandedStaffEquipped, Stats.TwoHandedStaffBonusBaseDamage));
// If a one-handed staff is equipped, we assume it's an energy MG and "switch off" the one-handed sword attribute,
// otherwise the mastery attack speed bonus would double in case of a staff-sword wield
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.IsOneHandedSwordEquipped, 0, Stats.IsOneHandedStaffEquipped, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.IsOneHandedStaffEquipped, Stats.WeaponMasteryAttackSpeed));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.IsOneHandedSwordEquipped, Stats.WeaponMasteryAttackSpeed));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.TotalEnergy));
if (!this.UseClassicPvp)
{
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 2, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3, Stats.TotalLevel));
}
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(57, Stats.MaximumHealth));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(7, Stats.MaximumMana));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(2, Stats.SkillMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f, Stats.WizardryAttackDamageIncrease));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.WizardryBaseDmgIncrease));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.IsOneHandedSwordEquipped));
this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster);
return result;
}
private CharacterClass CreateDuelMaster()
{
var result = this.CreateMagicGladiator(CharacterClassNumber.DuelMaster, "Duel Master", true, null, false);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false));
return result;
}
}

View File

@@ -0,0 +1,108 @@
// <copyright file="ClassRageFighter.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Initialization of character classes data.
/// </summary>
internal partial class CharacterClassInitialization
{
private CharacterClass CreateFistMaster()
{
var result = this.CreateRageFighter(CharacterClassNumber.FistMaster, "Fist Master", true, null, false);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false));
return result;
}
private CharacterClass CreateRageFighter(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated)
{
var result = this.Context.CreateNew<CharacterClass>();
result.SetGuid((byte)number);
this.GameConfiguration.CharacterClasses.Add(result);
result.CanGetCreated = canGetCreated;
result.HomeMap = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == LorenciaMapId);
result.Number = (byte)number;
result.Name = name;
result.CreationAllowedFlag = 8;
result.IsMasterClass = isMaster;
result.LevelRequirementByCreation = 150;
result.NextGenerationClass = nextGenerationClass;
result.LevelWarpRequirementReductionPercent = (int)Math.Ceiling(100.0 / 3);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Level, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.PointsPerLevelUp, 7, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseStrength, 32, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseAgility, 27, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseVitality, 25, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseEnergy, 20, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 100, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 40, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false));
this.AddCommonAttributeRelationships(result.AttributeCombinations);
this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations);
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1.0f / 8, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvm, 1.0f / 10, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 1.5f, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 3, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.0f / 6, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3.6f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 2.6f, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1.0f / 9, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MagicSpeed, 1.0f / 9, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 1.0f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.15f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1.3f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1.3f, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 2, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 7, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 5, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 15, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 12, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.SkillMultiplier, 0.001f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.VitalitySkillMultiplier, 0.001f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.TotalEnergy));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(57, Stats.MaximumHealth));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(7, Stats.MaximumMana));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.5f, Stats.SkillMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.5f, Stats.VitalitySkillMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier));
this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster);
return result;
}
}

View File

@@ -0,0 +1,164 @@
// <copyright file="ClassSummoner.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.CharacterClasses;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Initialization of character classes data.
/// </summary>
internal partial class CharacterClassInitialization
{
private CharacterClass CreateBloodySummoner(CharacterClass dimensionMaster)
{
return this.CreateSummoner(CharacterClassNumber.BloodySummoner, "Bloody Summoner", false, dimensionMaster, false);
}
private CharacterClass CreateDimensionMaster()
{
var result = this.CreateSummoner(CharacterClassNumber.DimensionMaster, "Dimension Master", true, null, false);
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false));
return result;
}
private CharacterClass CreateSummoner(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated)
{
var statsDefense = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Stats defense", string.Empty);
this.GameConfiguration.Attributes.Add(statsDefense);
var statsMinWizAndCurseBaseDmg = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Stats min wiz and curse base dmg", string.Empty);
this.GameConfiguration.Attributes.Add(statsMinWizAndCurseBaseDmg);
var statsMaxWizAndCurseBaseDmg = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Stats max wiz and curse base dmg", string.Empty);
this.GameConfiguration.Attributes.Add(statsMaxWizAndCurseBaseDmg);
var minBerserkerHealthDecrement = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Min Berserker health decrement", string.Empty);
this.GameConfiguration.Attributes.Add(minBerserkerHealthDecrement);
var finalBerserkerHealthDecrement = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Final Berserker health decrement", string.Empty);
this.GameConfiguration.Attributes.Add(finalBerserkerHealthDecrement);
var result = this.Context.CreateNew<CharacterClass>();
result.SetGuid((byte)number);
this.GameConfiguration.CharacterClasses.Add(result);
result.CanGetCreated = canGetCreated;
result.HomeMap = this.GameConfiguration.Maps.FirstOrDefault(map => map.Number == ElvenlandMapId);
result.Number = (byte)number;
result.Name = name;
result.CreationAllowedFlag = 1;
result.IsMasterClass = isMaster;
result.NextGenerationClass = nextGenerationClass;
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Level, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.PointsPerLevelUp, 5, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseStrength, 21, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseAgility, 21, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseVitality, 18, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseEnergy, 23, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 70, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 40, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false));
this.AddCommonAttributeRelationships(result.AttributeCombinations);
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.TotalStrengthAndAgility, Stats.TotalAgility, Stats.TotalStrength, InputOperator.Add));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(statsDefense, 1.0f / 3, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1, statsDefense));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvm, 0.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 2, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 5, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 0.25f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1.0f / 20, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MagicSpeed, 1.0f / 20, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.15f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.3f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumAbility, 0.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1.7f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1.5f, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 2, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 7, Stats.TotalStrengthAndAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 4, Stats.TotalStrengthAndAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(statsMinWizAndCurseBaseDmg, 1.0f / 9, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(statsMaxWizAndCurseBaseDmg, 1.0f / 4, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, statsMinWizAndCurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, statsMaxWizAndCurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.BaseMinDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.BaseMaxDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.WizardryBaseDmg, 1, Stats.BaseDamageBonus));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.WizardryBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.WizardryBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumWizBaseDmg, 1, Stats.WizardryBaseDmgIncrease, aggregateType: AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1, Stats.WizardryBaseDmgIncrease, aggregateType: AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.WizardryAttackDamageIncrease, 1.0f / 100, Stats.StaffRise));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumCurseBaseDmg, 1, statsMinWizAndCurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumCurseBaseDmg, 1, statsMaxWizAndCurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumCurseBaseDmg, 1, Stats.CurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumCurseBaseDmg, 1, Stats.CurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.CurseAttackDamageIncrease, 1.0f / 100, Stats.BookRise));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.WizardryBaseDmg, Stats.IsStickEquipped, Stats.StickBonusBaseDamage));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.CurseBaseDmg, Stats.IsBookEquipped, Stats.BookBonusBaseDamage));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.IsBookEquipped, Stats.WeaponMasteryAttackSpeed));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1, Stats.BerserkerManaMultiplier, InputOperator.Add, AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(finalBerserkerHealthDecrement, -0.1f, Stats.BerserkerHealthDecrement, InputOperator.Minimum));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(finalBerserkerHealthDecrement, 1, Stats.BerserkerMinPhysDmgBonus, InputOperator.Minimum, AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1, finalBerserkerHealthDecrement, InputOperator.Add, AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.DefenseFinal, statsDefense, finalBerserkerHealthDecrement, AggregateType.AddFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerMinPhysDmgBonus, 1, Stats.BerserkerManaMultiplier, aggregateType: AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerMaxPhysDmgBonus, 1, Stats.BerserkerManaMultiplier, aggregateType: AggregateType.Multiplicate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerWizardryMultiplier, 1, Stats.BerserkerManaMultiplier));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerCurseMultiplier, 1, Stats.BerserkerManaMultiplier));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerWizardryMultiplier, 1, Stats.BerserkerProficiencyMultiplier));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerMinWizDmgBonus, Stats.BerserkerWizardryMultiplier, statsMinWizAndCurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerMaxWizDmgBonus, Stats.BerserkerWizardryMultiplier, statsMaxWizAndCurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerMinCurseDmgBonus, Stats.BerserkerCurseMultiplier, statsMinWizAndCurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerMaxCurseDmgBonus, Stats.BerserkerCurseMultiplier, statsMaxWizAndCurseBaseDmg));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalEnergy));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(39, Stats.MaximumHealth));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(6, Stats.MaximumMana));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SkillMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f, Stats.WizardryAttackDamageIncrease));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f, Stats.CurseAttackDamageIncrease));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.WizardryBaseDmgIncrease));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.BerserkerManaMultiplier)); // placeholder value
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.BerserkerHealthDecrement)); // placeholder value
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.BerserkerProficiencyMultiplier)); // placeholder value
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.6f, Stats.BleedingDamageMultiplier));
this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster);
return result;
}
}

View File

@@ -0,0 +1,81 @@
// <copyright file="CharacterTransformationSkin.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;
/// <summary>
/// The skin numbers of the character transformation rings.
/// </summary>
public enum CharacterTransformationSkin
{
/// <summary>
/// No skin.
/// </summary>
None,
/// <summary>
/// The budge dragon skin.
/// </summary>
BudgeDragon = 2,
/// <summary>
/// The giant skin.
/// </summary>
Giant = 7,
/// <summary>
/// The poison bull fighter skin.
/// </summary>
PoisonBullFighter = 8,
/// <summary>
/// The thunder lich skin.
/// </summary>
ThunderLich = 9,
/// <summary>
/// The skeleton warrior skin.
/// </summary>
SkeletonWarrior = 14,
/// <summary>
/// The death cow skin.
/// </summary>
DeathCow = 41,
/// <summary>
/// The elite skeleton skin.
/// </summary>
EliteSkeleton = 372,
/// <summary>
/// The jack olantern skin.
/// </summary>
JackOlantern = 373,
/// <summary>
/// The christmas skin.
/// </summary>
Christmas = 374,
/// <summary>
/// The game master skin.
/// </summary>
GameMaster = 378,
/// <summary>
/// The snowman skin.
/// </summary>
Snowman = 477,
/// <summary>
/// The panda skin.
/// </summary>
Panda = 503,
/// <summary>
/// The skeleton skin.
/// </summary>
Skeleton = 548,
}

View File

@@ -0,0 +1,311 @@
// <copyright file="DataInitializationBase.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.ComponentModel.Design;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.GameLogic.PlayerActions.ItemConsumeActions;
using MUnique.OpenMU.GameLogic.Resets;
using MUnique.OpenMU.GameServer.MessageHandler;
using MUnique.OpenMU.Network;
using MUnique.OpenMU.Network.PlugIns;
using MUnique.OpenMU.Persistence.Initialization.Updates;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Class to manage data initialization.
/// </summary>
public abstract class DataInitializationBase : IDataInitializationPlugIn
{
private readonly IPersistenceContextProvider _persistenceContextProvider;
private readonly ILoggerFactory _loggerFactory;
private GameConfiguration? _gameConfiguration;
private IContext? _context;
/// <summary>
/// Initializes a new instance of the <see cref="DataInitializationBase" /> class.
/// </summary>
/// <param name="persistenceContextProvider">The persistence context provider.</param>
/// <param name="loggerFactory">The logger factory.</param>
protected DataInitializationBase(IPersistenceContextProvider persistenceContextProvider, ILoggerFactory loggerFactory)
{
this._persistenceContextProvider = persistenceContextProvider;
this._loggerFactory = loggerFactory;
}
/// <inheritdoc />
public abstract string Key { get; }
/// <inheritdoc />
public abstract string Caption { get; }
/// <summary>
/// Gets or sets the game configuration.
/// </summary>
/// <exception cref="InvalidOperationException">not initialized yet.</exception>
protected GameConfiguration GameConfiguration
{
get => this._gameConfiguration ?? throw new InvalidOperationException("not initialized yet.");
set => this._gameConfiguration = value;
}
/// <summary>
/// Gets or sets the context.
/// </summary>
/// <exception cref="InvalidOperationException">not initialized yet.</exception>
protected IContext Context
{
get => this._context ?? throw new InvalidOperationException("not initialized yet.");
set => this._context = value;
}
/// <summary>
/// Gets the game configuration initializer.
/// </summary>
protected abstract IInitializer GameConfigurationInitializer { get; }
/// <summary>
/// Gets the maps initializer.
/// </summary>
protected abstract IGameMapsInitializer GameMapsInitializer { get; }
/// <summary>
/// Gets the test accounts initializer.
/// </summary>
protected abstract IInitializer? TestAccountsInitializer { get; }
/// <summary>
/// Creates the initial data for a server.
/// </summary>
/// <param name="numberOfGameServers">The number of game servers.</param>
/// <param name="createTestAccounts">If set to <c>true</c>, test accounts should be created.</param>
public async Task CreateInitialDataAsync(byte numberOfGameServers, bool createTestAccounts)
{
BaseMapInitializer.ClearDefaultDropItemGroups();
using (var temporaryContext = this._persistenceContextProvider.CreateNewContext())
{
this.GameConfiguration = temporaryContext.CreateNew<GameConfiguration>();
this.GameConfiguration.SetGuid(1);
this.CreateSystemConfiguration(temporaryContext);
using var tempSuspension = temporaryContext.SuspendChangeNotifications();
await temporaryContext.SaveChangesAsync().ConfigureAwait(false);
}
using var contextWithConfiguration = this._persistenceContextProvider.CreateNewContext(this.GameConfiguration);
using var notificationSuspension = contextWithConfiguration.SuspendChangeNotifications();
this.Context = contextWithConfiguration;
this.CreateGameClientDefinition();
await this.CreateChatServerDefinitionAsync().ConfigureAwait(false);
this.GameConfigurationInitializer.Initialize();
var gameServerConfiguration = this.CreateGameServerConfiguration(this.GameConfiguration.Maps);
await this.CreateGameServerDefinitionsAsync(gameServerConfiguration, numberOfGameServers).ConfigureAwait(false);
await this.CreateConnectServerDefinitionAsync().ConfigureAwait(false);
await this.Context.SaveChangesAsync().ConfigureAwait(false);
this.GameMapsInitializer.SetSafezoneMaps();
if (createTestAccounts)
{
this.TestAccountsInitializer?.Initialize();
}
if (!AppDomain.CurrentDomain.GetAssemblies().Contains(typeof(GameServer.GameServer).Assembly))
{
// should never happen, but the access to the GameServer type is a trick to load the assembly into the current domain.
}
var dataSource = new GameConfigurationDataSource(this._loggerFactory.CreateLogger<GameConfigurationDataSource>(), this._persistenceContextProvider);
await dataSource.GetOwnerAsync(this.GameConfiguration.GetId());
var referenceHandler = new ByDataSourceReferenceHandler(dataSource);
var serviceContainer = new ServiceContainer();
serviceContainer.AddService(typeof(IPersistenceContextProvider), this._persistenceContextProvider);
var plugInManager = new PlugInManager(null, this._loggerFactory, serviceContainer, referenceHandler);
plugInManager.DiscoverAndRegisterPlugIns();
plugInManager.KnownPlugInTypes.ForEach(plugInType =>
{
var plugInConfiguration = this.Context.CreateNew<PlugInConfiguration>();
plugInConfiguration.SetGuid(plugInType.GUID);
plugInConfiguration.TypeId = plugInType.GUID;
plugInConfiguration.IsActive = !plugInType.IsAssignableTo(typeof(IDisabledByDefault));
this.GameConfiguration.PlugInConfigurations.Add(plugInConfiguration);
if (plugInType.GetInterfaces().Contains(typeof(ISupportDefaultCustomConfiguration)))
{
this.CreateDefaultPlugInConfiguration(plugInType, plugInConfiguration, referenceHandler);
}
if (plugInType == typeof(BlessJewelConsumeHandlerPlugIn))
{
var config = new BlessJewelConsumeHandlerPlugInConfiguration
{
MaximumLevel = 5,
MinimumLevel = 0,
SuccessRatePercentage = 100,
SuccessRateBonusWithLuckPercentage = 0,
ResetToLevel0WhenFailMinLevel = 0,
};
if (this.GameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 37 }) is { } fenrir)
{
config.RepairTargetItems.Add(fenrir);
}
plugInConfiguration.SetConfiguration(config, referenceHandler);
}
// We don't move the player anymore by his request. This was usually requested after a player performed a skill.
// However, it adds way for cheaters to move through the map.
// The plugin is therefore deactivated by default.
if (plugInType.IsAssignableTo(typeof(CharacterMoveBaseHandlerPlugIn)))
{
plugInConfiguration.IsActive = false;
}
// Disable plugins marked as disabled by default.
if (plugInType.IsAssignableTo(typeof(IDisabledByDefault)))
{
plugInConfiguration.IsActive = false;
}
});
this.AddAllUpdateEntries(plugInManager);
await this.Context.SaveChangesAsync().ConfigureAwait(false);
}
/// <summary>
/// Creates the game client definition.
/// </summary>
protected abstract void CreateGameClientDefinition();
private void CreateDefaultPlugInConfiguration(Type plugInType, PlugInConfiguration plugInConfiguration, ReferenceHandler referenceHandler)
{
try
{
var plugin = (ISupportDefaultCustomConfiguration)Activator.CreateInstance(plugInType)!;
var defaultConfig = plugin.CreateDefaultConfig();
plugInConfiguration.SetConfiguration(defaultConfig, referenceHandler);
}
catch (Exception ex)
{
this._loggerFactory.CreateLogger(this.GetType()).LogWarning(ex, "Could not create custom default configuration for plugin type {plugInType}", plugInType);
}
}
private void AddAllUpdateEntries(PlugInManager plugInManager)
{
var updates = plugInManager.GetStrategyProvider<int, IConfigurationUpdatePlugIn>()
?.AvailableStrategies.Where(up => up.DataInitializationKey == this.Key)
.OrderBy(up => up.Version)
.ToList();
if (updates is not { Count: > 0 })
{
return;
}
foreach (var update in updates)
{
var entry = this.Context.CreateNew<ConfigurationUpdate>();
entry.Version = (int)update.Version;
entry.Name = update.Name;
entry.Description = update.Description;
entry.CreatedAt = update.CreatedAt;
entry.InstalledAt = DateTime.UtcNow;
}
var updateState = this.Context.CreateNew<ConfigurationUpdateState>();
updateState.InitializationKey = this.Key;
updateState.CurrentInstalledVersion = updates.Max(u => (int)u.Version);
}
private async ValueTask CreateConnectServerDefinitionAsync()
{
var port = 44405;
var clients = await this.Context.GetAsync<GameClientDefinition>().ConfigureAwait(false);
foreach (var client in clients.OrderBy(c => c.Season))
{
var connectServer = this.Context.CreateNew<ConnectServerDefinition>();
connectServer.InitializeDefaults();
connectServer.SetGuid(client.Season, client.Episode);
connectServer.Client = client;
connectServer.ClientListenerPort = port;
connectServer.Description = $"Connect Server ({client.Description})";
port++;
}
}
private async ValueTask CreateGameServerDefinitionsAsync(GameServerConfiguration gameServerConfiguration, int numberOfServers)
{
var port = 55901;
for (int i = 0; i < numberOfServers; i++)
{
var server = this.Context!.CreateNew<GameServerDefinition>();
server.SetGuid((short)i);
server.ServerID = (byte)i;
server.Description = $"Server {i}";
server.ExperienceRate = 1.0f;
server.GameConfiguration = this.GameConfiguration;
server.ServerConfiguration = gameServerConfiguration;
foreach (var client in await this.Context.GetAsync<GameClientDefinition>().ConfigureAwait(false))
{
var endPoint = this.Context.CreateNew<GameServerEndpoint>();
endPoint.SetGuid((short)i, (short)server.Endpoints.Count);
endPoint.Client = client;
endPoint.NetworkPort = port;
server.Endpoints.Add(endPoint);
port++;
}
}
}
private async ValueTask CreateChatServerDefinitionAsync()
{
var server = this.Context!.CreateNew<ChatServerDefinition>();
server.SetGuid(0);
server.ServerId = 0;
server.Description = "Chat Server";
var client = (await this.Context!.GetAsync<GameClientDefinition>().ConfigureAwait(false)).First();
var endPoint = this.Context.CreateNew<ChatServerEndpoint>();
server.SetGuid(0);
endPoint.Client = client;
endPoint.NetworkPort = 55980;
server.Endpoints.Add(endPoint);
}
private GameServerConfiguration CreateGameServerConfiguration(ICollection<GameMapDefinition> maps)
{
var gameServerConfiguration = this.Context.CreateNew<GameServerConfiguration>();
gameServerConfiguration.SetGuid(0);
gameServerConfiguration.MaximumPlayers = 1000;
// by default we add every map to a server configuration
foreach (var map in maps)
{
gameServerConfiguration.Maps.Add(map);
}
return gameServerConfiguration;
}
private void CreateSystemConfiguration(IContext context)
{
var systemConfiguration = context.CreateNew<SystemConfiguration>();
systemConfiguration.SetGuid(0);
systemConfiguration.AutoStart = true;
systemConfiguration.AutoUpdateSchema = true;
systemConfiguration.ReadConsoleInput = false;
var (type, param) = IpAddressResolverFactory.DetermineBestFittingResolver(Environment.GetCommandLineArgs());
systemConfiguration.IpResolver = type;
systemConfiguration.IpResolverParameter = param;
}
}

View File

@@ -0,0 +1,51 @@
// <copyright file="ElementalType.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;
/// <summary>
/// The elemental type for skills, etc..
/// </summary>
public enum ElementalType
{
/// <summary>
/// The undefined type.
/// </summary>
Undefined,
/// <summary>
/// The ice elemental type.
/// </summary>
Ice,
/// <summary>
/// The poison elemental type.
/// </summary>
Poison,
/// <summary>
/// The lightning elemental type.
/// </summary>
Lightning,
/// <summary>
/// The fire elemental type.
/// </summary>
Fire,
/// <summary>
/// The earth elemental type.
/// </summary>
Earth,
/// <summary>
/// The wind elemental type.
/// </summary>
Wind,
/// <summary>
/// The water elemental type.
/// </summary>
Water,
}

View File

@@ -0,0 +1,286 @@
// <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)));
}
}

View File

@@ -0,0 +1,65 @@
// <copyright file="GameMapsInitializerBase.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 MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Base class which initializes game maps.
/// </summary>
public abstract class GameMapsInitializerBase : InitializerBase, IGameMapsInitializer
{
private IList<IMapInitializer>? _mapInitializers;
/// <summary>
/// Initializes a new instance of the <see cref="GameMapsInitializerBase"/> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
protected GameMapsInitializerBase(IContext context, GameConfiguration gameConfiguration)
: base(context, gameConfiguration)
{
}
/// <summary>
/// Gets the map initializer types.
/// </summary>
protected abstract IEnumerable<Type> MapInitializerTypes { get; }
/// <summary>
/// Gets the map initializers.
/// </summary>
private IList<IMapInitializer> MapInitializers => this._mapInitializers ??= this.CreateInitializers();
/// <inheritdoc />
public override void Initialize()
{
foreach (var mapInitializer in this.MapInitializers)
{
mapInitializer.Initialize();
}
}
/// <summary>
/// Sets the safezone maps.
/// Needs to be called after the context has been saved, otherwise referencing between the maps will fail.
/// </summary>
public void SetSafezoneMaps()
{
foreach (var mapInitializer in this.MapInitializers)
{
mapInitializer.SetSafezoneMap();
}
}
private IList<IMapInitializer> CreateInitializers()
{
var parameters = new object[] { this.Context, this.GameConfiguration };
return this.MapInitializerTypes
.Select(type => type.GetConstructors().First().Invoke(parameters))
.OfType<IMapInitializer>()
.ToList();
}
}

View File

@@ -0,0 +1,16 @@
// <copyright file="GlobalUsings.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
#pragma warning disable SA1200 // Using directives should be placed correctly
global using System.ComponentModel.DataAnnotations;
global using Microsoft.Extensions.Logging;
global using MUnique.OpenMU.DataModel;
global using MUnique.OpenMU.DataModel.Configuration;
global using MUnique.OpenMU.DataModel.Entities;
global using MUnique.OpenMU.GameLogic.Properties;
global using MUnique.OpenMU.Persistence.Initialization.Properties;
global using PlugInResources = MUnique.OpenMU.Persistence.Initialization.Properties.PlugInResources;

View File

@@ -0,0 +1,241 @@
// <copyright file="GuidHelper.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.Diagnostics;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Attributes;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Network;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// This class helps to create the same GUIDs for the same initial configuration data.
/// </summary>
internal static class GuidHelper
{
private static readonly Dictionary<Type, short> TypeIds = new();
/// <summary>
/// Initializes static members of the <see cref="GuidHelper"/> class.
/// </summary>
static GuidHelper()
{
TypeIds.Add(typeof(GameConfiguration), 1);
TypeIds.Add(typeof(GameClientDefinition), 2);
TypeIds.Add(typeof(GameServerDefinition), 3);
TypeIds.Add(typeof(GameServerConfiguration), 4);
TypeIds.Add(typeof(GameServerEndpoint), 5);
TypeIds.Add(typeof(ConnectServerDefinition), 6);
TypeIds.Add(typeof(ChatServerDefinition), 8);
TypeIds.Add(typeof(ChatServerEndpoint), 9);
TypeIds.Add(typeof(SystemConfiguration), 0x10);
TypeIds.Add(typeof(AttributeDefinition), 0x20);
TypeIds.Add(typeof(AttributeRelationship), 0x21);
TypeIds.Add(typeof(AttributeRequirement), 0x22);
TypeIds.Add(typeof(ConstValueAttribute), 0x23);
TypeIds.Add(typeof(PowerUpDefinition), 0x24);
TypeIds.Add(typeof(PowerUpDefinitionValue), 0x25);
TypeIds.Add(typeof(CharacterClass), 0x40);
TypeIds.Add(typeof(StatAttributeDefinition), 0x41);
TypeIds.Add(typeof(ItemDefinition), 0x80);
TypeIds.Add(typeof(ItemOption), 0x81);
TypeIds.Add(typeof(ItemOptionCombinationBonus), 0x82);
TypeIds.Add(typeof(ItemOptionDefinition), 0x83);
TypeIds.Add(typeof(ItemOptionOfLevel), 0x84);
TypeIds.Add(typeof(ItemOptionType), 0x85);
TypeIds.Add(typeof(ItemSlotType), 0x86);
TypeIds.Add(typeof(CombinationBonusRequirement), 0x87);
TypeIds.Add(typeof(IncreasableItemOption), 0x88);
TypeIds.Add(typeof(ItemBasePowerUpDefinition), 0x89);
TypeIds.Add(typeof(ItemLevelBonusTable), 0x90);
TypeIds.Add(typeof(LevelBonus), 0x91);
TypeIds.Add(typeof(ItemSetGroup), 0x92);
TypeIds.Add(typeof(ItemOfItemSet), 0x93);
TypeIds.Add(typeof(ItemCrafting), 0x100);
TypeIds.Add(typeof(ItemCraftingRequiredItem), 0x101);
TypeIds.Add(typeof(ItemCraftingResultItem), 0x102);
TypeIds.Add(typeof(SimpleCraftingSettings), 0x103);
TypeIds.Add(typeof(JewelMix), 0x104);
TypeIds.Add(typeof(DropItemGroup), 0x200);
TypeIds.Add(typeof(ItemDropItemGroup), 0x201);
TypeIds.Add(typeof(GameMapDefinition), 0x300);
TypeIds.Add(typeof(EnterGate), 0x301);
TypeIds.Add(typeof(ExitGate), 0x302);
TypeIds.Add(typeof(BattleZoneDefinition), 0x310);
TypeIds.Add(typeof(WarpInfo), 0x320);
TypeIds.Add(typeof(Skill), 0x400);
TypeIds.Add(typeof(SkillComboDefinition), 0x401);
TypeIds.Add(typeof(SkillComboStep), 0x402);
TypeIds.Add(typeof(MagicEffectDefinition), 0x410);
TypeIds.Add(typeof(MasterSkillDefinition), 0x420);
TypeIds.Add(typeof(MasterSkillRoot), 0x421);
TypeIds.Add(typeof(MiniGameDefinition), 0x500);
TypeIds.Add(typeof(MiniGameChangeEvent), 0x501);
TypeIds.Add(typeof(MiniGameReward), 0x502);
TypeIds.Add(typeof(MiniGameSpawnWave), 0x503);
TypeIds.Add(typeof(MiniGameTerrainChange), 0x504);
TypeIds.Add(typeof(Rectangle), 0x505);
TypeIds.Add(typeof(MonsterDefinition), 0x600);
TypeIds.Add(typeof(MonsterAttribute), 0x601);
TypeIds.Add(typeof(MonsterSpawnArea), 0x602);
TypeIds.Add(typeof(QuestDefinition), 0x700);
TypeIds.Add(typeof(QuestItemRequirement), 0x701);
TypeIds.Add(typeof(QuestMonsterKillRequirement), 0x702);
TypeIds.Add(typeof(QuestReward), 0x703);
TypeIds.Add(typeof(PlugInConfiguration), 0x800);
TypeIds.Add(typeof(ItemStorage), 0x1000);
TypeIds.Add(typeof(Item), 0x1001);
}
/// <summary>
/// Sets the unique identifier for a <see cref="IIdentifiable"/> object.
/// </summary>
/// <typeparam name="T">The type of object which should get the id.</typeparam>
/// <param name="obj">The object.</param>
/// <param name="guid">The unique identifier.</param>
/// <exception cref="System.InvalidOperationException">Object is not identifiable.</exception>
public static void SetGuid<T>(this T obj, Guid guid)
{
if (obj is IIdentifiable identifiable)
{
identifiable.Id = guid;
return;
}
throw new InvalidOperationException("Object is not identifiable");
}
/// <summary>
/// Gets an item identifier.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The item identifier.</returns>
public static short GetItemId(this ItemDefinition item)
{
return ((ushort)((item.Group << 12) | (ushort)item.Number)).ToSigned();
}
/// <summary>
/// Creates and sets the unique identifier based on a single number.
/// </summary>
/// <typeparam name="T">The type of object which should get the id.</typeparam>
/// <param name="obj">The object.</param>
/// <param name="number">The number.</param>
public static void SetGuid<T>(this T obj, short number)
{
obj.SetGuid(CreateGuid<T>(number));
}
/// <summary>
/// Creates and sets the unique identifier based on a parent and child number.
/// </summary>
/// <typeparam name="T">The type of object which should get the id.</typeparam>
/// <param name="obj">The object.</param>
/// <param name="parentNumber">The parent number.</param>
/// <param name="number">The child number.</param>
public static void SetGuid<T>(this T obj, short parentNumber, short number)
{
obj.SetGuid(CreateGuid<T>(parentNumber, number));
}
/// <summary>
/// Creates and sets the unique identifier based on three numbers.
/// </summary>
/// <typeparam name="T">The type of object which should get the id.</typeparam>
/// <param name="obj">The object.</param>
/// <param name="parentNumber">The parent number.</param>
/// <param name="number">The child number.</param>
/// <param name="subNumber">The sub number.</param>
public static void SetGuid<T>(this T obj, short parentNumber, short number, byte subNumber)
{
obj.SetGuid(CreateGuid<T>(parentNumber, number, subNumber));
}
/// <summary>
/// Extracts the first two bytes of a guid.
/// </summary>
/// <param name="guid">The unique identifier.</param>
/// <returns>The first two bytes of a guid.</returns>
public static short ExtractFirstTwoBytes(this Guid guid)
{
var bytes = guid.ToByteArray();
return NumberConversionExtensions.MakeWord(bytes[0], bytes[1]).ToSigned();
}
/// <summary>
/// Creates the unique identifier based on a single number.
/// </summary>
/// <typeparam name="T">The type of object which should get the id.</typeparam>
/// <param name="number">The number.</param>
/// <returns>The created unique identifier.</returns>
public static Guid CreateGuid<T>(short number)
{
if (!TypeIds.TryGetValue(typeof(T), out var typeId))
{
Debug.Fail($"unregistered type {typeof(T)}");
return Guid.NewGuid();
}
return new Guid(typeId, number, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
/// <summary>
/// Creates the unique identifier based on a parent and child number.
/// </summary>
/// <typeparam name="T">The type of object which should get the id.</typeparam>
/// <param name="parentNumber">The parent number.</param>
/// <param name="number">The child number.</param>
/// <returns>The created unique identifier.</returns>
public static Guid CreateGuid<T>(short parentNumber, short number)
{
if (!TypeIds.TryGetValue(typeof(T), out var typeId))
{
Debug.Fail($"unregistered type {typeof(T)}");
return Guid.NewGuid();
}
return new Guid(typeId, parentNumber, number, 0, 0, 0, 0, 0, 0, 0, 0);
}
/// <summary>
/// Creates the unique identifier based on three numbers.
/// </summary>
/// <typeparam name="T">The type of object which should get the id.</typeparam>
/// <param name="parentNumber">The parent number.</param>
/// <param name="number">The child number.</param>
/// <param name="subNumber">The sub number.</param>
/// <returns>
/// The created unique identifier.
/// </returns>
public static Guid CreateGuid<T>(short parentNumber, short number, byte subNumber)
{
if (!TypeIds.TryGetValue(typeof(T), out var typeId))
{
Debug.Fail($"unregistered type {typeof(T)}");
return Guid.NewGuid();
}
return new Guid(typeId, parentNumber, number, subNumber, 0, 0, 0, 0, 0, 0, 0);
}
}

View File

@@ -0,0 +1,28 @@
// <copyright file="IDataInitializationPlugIn.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.Runtime.InteropServices;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// An interface for a plug in which provides initial data.
/// </summary>
[Guid("A8124680-8562-456C-9952-567E5A40F6D0")]
[PlugInPoint("Data initialization", "Provides an initialization for initial data.")]
public interface IDataInitializationPlugIn : IStrategyPlugIn<string>
{
/// <summary>
/// Gets the caption for this initialization plugin which will be visible on the setup page.
/// </summary>
string Caption { get; }
/// <summary>
/// Creates the initial data for a server.
/// </summary>
/// <param name="numberOfGameServers">The number of game servers.</param>
/// <param name="createTestAccounts">If set to <c>true</c>, test accounts should be created.</param>
Task CreateInitialDataAsync(byte numberOfGameServers, bool createTestAccounts);
}

View File

@@ -0,0 +1,16 @@
// <copyright file="IGameMapsInitializer.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;
/// <summary>
/// Initializer for game map data.
/// </summary>
public interface IGameMapsInitializer : IInitializer
{
/// <summary>
/// Sets the safezone maps.
/// </summary>
void SetSafezoneMaps();
}

View File

@@ -0,0 +1,16 @@
// <copyright file="IInitializer.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;
/// <summary>
/// Interface for an initializer.
/// </summary>
public interface IInitializer
{
/// <summary>
/// Runs the initializer which creates the initial data.
/// </summary>
void Initialize();
}

View File

@@ -0,0 +1,19 @@
// <copyright file="IMapInitializer.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 MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Initializer for a <see cref="GameMapDefinition"/>.
/// </summary>
internal interface IMapInitializer : IInitializer
{
/// <summary>
/// Sets the <see cref="GameMapDefinition.SafezoneMap"/>.
/// This needs to be done after the first commit, because of possible circular references.
/// </summary>
void SetSafezoneMap();
}

View File

@@ -0,0 +1,209 @@
// <copyright file="InitializerBase.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 MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Attributes;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
/// <summary>
/// Base class for an <see cref="IInitializer"/>.
/// </summary>
/// <seealso cref="MUnique.OpenMU.Persistence.Initialization.IInitializer" />
public abstract class InitializerBase : IInitializer
{
/// <summary>
/// Initializes a new instance of the <see cref="InitializerBase"/> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
protected InitializerBase(IContext context, GameConfiguration gameConfiguration)
{
this.Context = context;
this.GameConfiguration = gameConfiguration;
}
/// <summary>
/// Gets the persistence context.
/// </summary>
/// <value>
/// The persistence context.
/// </value>
protected IContext Context { get; }
/// <summary>
/// Gets the game configuration.
/// </summary>
/// <value>
/// The game configuration.
/// </value>
protected GameConfiguration GameConfiguration { get; }
/// <summary>
/// Gets the maximum option level.
/// </summary>
protected virtual int MaximumOptionLevel => 4;
/// <inheritdoc />
public abstract void Initialize();
/// <summary>
/// Creates an attribute requirement with the specified minimum value.
/// </summary>
/// <param name="attribute">The attribute.</param>
/// <param name="minimumValue">The minimum value.</param>
/// <returns>The created requirement.</returns>
protected AttributeRequirement CreateRequirement(AttributeDefinition attribute, int minimumValue)
{
var requirement = this.Context.CreateNew<AttributeRequirement>();
requirement.Attribute = attribute.GetPersistent(this.GameConfiguration);
requirement.MinimumValue = minimumValue;
return requirement;
}
/// <summary>
/// Creates an item requirement, if the required value is greater than 0.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="attribute">The attribute.</param>
/// <param name="requiredValue">The required value.</param>
protected void CreateItemRequirementIfNeeded(ItemDefinition item, AttributeDefinition attribute, int requiredValue)
{
if (requiredValue == 0)
{
return;
}
var requirement = this.CreateRequirement(attribute, requiredValue);
item.Requirements.Add(requirement);
}
/// <summary>
/// Gets the character class of the specified number.
/// </summary>
/// <param name="classNumber">The class number.</param>
/// <returns>The character class of the specified number.</returns>
protected CharacterClass? GetCharacterClass(CharacterClassNumber classNumber)
{
return this.GameConfiguration.CharacterClasses.FirstOrDefault(c => c.Number == (byte)classNumber);
}
/// <summary>
/// Creates the power up definition with the given values.
/// </summary>
/// <param name="attributeDefinition">The attribute definition.</param>
/// <param name="value">The value.</param>
/// <param name="aggregateType">Type of the aggregate.</param>
/// <returns>The created power up definition.</returns>
protected PowerUpDefinition CreatePowerUpDefinition(AttributeDefinition attributeDefinition, float value, AggregateType aggregateType)
{
var powerUpDefinition = this.Context.CreateNew<PowerUpDefinition>();
powerUpDefinition.TargetAttribute = attributeDefinition.GetPersistent(this.GameConfiguration);
if (value != 0)
{
powerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
powerUpDefinition.Boost.ConstantValue.Value = value;
powerUpDefinition.Boost.ConstantValue.AggregateType = aggregateType;
}
return powerUpDefinition;
}
/// <summary>
/// Creates the <see cref="ItemBasePowerUpDefinition" />.
/// </summary>
/// <param name="attributeDefinition">The attribute definition.</param>
/// <param name="value">The value.</param>
/// <param name="aggregateType">Type of the aggregate.</param>
/// <returns>
/// The created <see cref="ItemBasePowerUpDefinition" />.
/// </returns>
protected ItemBasePowerUpDefinition CreateItemBasePowerUpDefinition(AttributeDefinition attributeDefinition, float value, AggregateType aggregateType)
{
var powerUpDefinition = this.Context.CreateNew<ItemBasePowerUpDefinition>();
powerUpDefinition.TargetAttribute = attributeDefinition.GetPersistent(this.GameConfiguration);
powerUpDefinition.BaseValue = value;
powerUpDefinition.AggregateType = aggregateType;
return powerUpDefinition;
}
/// <summary>
/// Creates a new item bonus table and adds it to the <see cref="GameConfiguration"/>.
/// </summary>
/// <param name="values">The values, index is the item level.</param>
/// <param name="name">The name.</param>
/// <param name="description">The description.</param>
/// <returns>The created table.</returns>
protected ItemLevelBonusTable CreateItemBonusTable(float[] values, string name, string description)
{
var table = this.Context.CreateNew<ItemLevelBonusTable>();
this.GameConfiguration.ItemLevelBonusTables.Add(table);
table.Name = name;
table.Description = description;
for (int level = 0; level < values.Length; level++)
{
var value = values[level];
if (value != 0)
{
var levelBonus = this.Context.CreateNew<LevelBonus>();
levelBonus.Level = level;
levelBonus.AdditionalValue = values[level];
table.BonusPerLevel.Add(levelBonus);
}
}
return table;
}
/// <summary>
/// Creates a new <see cref="IncreasableItemOption" /> with the specified parameters
/// with options for multiple option levels until <see cref="MaximumOptionLevel" />.
/// </summary>
/// <param name="number">The number.</param>
/// <param name="attributeDefinition">The attribute definition.</param>
/// <param name="value">The value.</param>
/// <param name="aggregateType">Type of the aggregate.</param>
/// <param name="valueIncrementPerLevel">The value increment per level.</param>
/// <param name="optionNumber">The option number.</param>
/// <returns>
/// The created <see cref="IncreasableItemOption" />.
/// </returns>
protected IncreasableItemOption CreateItemOption(int number, AttributeDefinition attributeDefinition, float value, AggregateType aggregateType, float valueIncrementPerLevel, short optionNumber)
{
var itemOption = this.Context.CreateNew<IncreasableItemOption>();
itemOption.SetGuid(optionNumber, attributeDefinition.Id.ExtractFirstTwoBytes());
itemOption.OptionType = this.GameConfiguration.ItemOptionTypes.First(t => t == ItemOptionTypes.Option);
itemOption.Number = number;
itemOption.PowerUpDefinition = this.CreatePowerUpDefinition(attributeDefinition, value, aggregateType);
float baseValue = aggregateType == AggregateType.Multiplicate ? 1.0f : 0.0f;
for (int level = 1; level <= this.MaximumOptionLevel; level++)
{
var optionOfLevel = this.Context.CreateNew<ItemOptionOfLevel>();
optionOfLevel.Level = level;
optionOfLevel.PowerUpDefinition = this.CreatePowerUpDefinition(
itemOption.PowerUpDefinition.TargetAttribute!,
baseValue + (level * valueIncrementPerLevel),
aggregateType);
itemOption.LevelDependentOptions.Add(optionOfLevel);
}
return itemOption;
}
/// <summary>
/// Register the jewel (or jewel-like item) in the drop item group for jewels.
/// </summary>
/// <param name="item">The jewel you want to register.</param>
protected void AddItemToJewelItemDrop(ItemDefinition item)
{
var id = GuidHelper.CreateGuid<DropItemGroup>(4);
var jewelsItemDrop = this.GameConfiguration.DropItemGroups.First(x => x.GetId() == id);
jewelsItemDrop.PossibleItems.Add(item);
}
}

View File

@@ -0,0 +1,227 @@
// <copyright file="ItemOptionDefinitionNumbers.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;
/// <summary>
/// Internal numbers for item option definitions which are helpful when creating their identifiers.
/// </summary>
internal static class ItemOptionDefinitionNumbers
{
/// <summary>
/// Gets the luck option number.
/// </summary>
public static short Luck => 0x01;
/// <summary>
/// Gets the defense option option number.
/// </summary>
public static short DefenseOption => 0x02;
/// <summary>
/// Gets the physical attack option number.
/// </summary>
public static short PhysicalAttack => 0x03;
/// <summary>
/// Gets the wizardry attack option number.
/// </summary>
public static short WizardryAttack => 0x04;
/// <summary>
/// Gets the curse attack option number.
/// </summary>
public static short CurseAttack => 0x05;
/// <summary>
/// Gets the defense option option number.
/// </summary>
public static short DefenseRateOption => 0x06;
/// <summary>
/// Gets the physical and wizardry attack option number.
/// </summary>
/// <remarks>Used for MG "magic swords", which always increase both damage types simultaneously.</remarks>
public static short PhysicalAndWizardryAttack => 0x07;
/// <summary>
/// Gets the jewelery health option number.
/// </summary>
public static short JeweleryHealth => 0x10;
/// <summary>
/// Gets the excellent defense option number.
/// </summary>
public static short ExcellentDefense => 0x12;
/// <summary>
/// Gets the excellent physical option number.
/// </summary>
public static short ExcellentPhysical => 0x13;
/// <summary>
/// Gets the excellent wizardry option number.
/// </summary>
public static short ExcellentWizardry => 0x14;
/// <summary>
/// Gets the excellent curse option number.
/// </summary>
public static short ExcellentCurse => 0x15;
/// <summary>
/// Gets the defense set bonus option number.
/// </summary>
public static short DefenseSetBonusOption => 0x20;
/// <summary>
/// Gets the defense rate set bonus option number.
/// </summary>
public static short DefenseRateSetBonusOption => 0x21;
/// <summary>
/// Gets the ancient option option number.
/// </summary>
public static short AncientOption => 0x29;
/// <summary>
/// Gets the ancient bonus option number.
/// </summary>
public static short AncientBonus => 0x30;
/// <summary>
/// Gets the socket bonus option number.
/// </summary>
public static short SocketBonus => 0x31;
/// <summary>
/// Gets the socket fire option number.
/// </summary>
public static short SocketFire => 0x32;
/// <summary>
/// Gets the socket water option number.
/// </summary>
public static short SocketWater => 0x33;
/// <summary>
/// Gets the socket ice option number.
/// </summary>
public static short SocketIce => 0x34;
/// <summary>
/// Gets the socket wind option number.
/// </summary>
public static short SocketWind => 0x35;
/// <summary>
/// Gets the socket lightning option number.
/// </summary>
public static short SocketLightning => 0x36;
/// <summary>
/// Gets the socket earth option number.
/// </summary>
public static short SocketEarth => 0x37;
/// <summary>
/// Gets the guardian option1 option number.
/// </summary>
public static short GuardianOption1 => 0x41;
/// <summary>
/// Gets the guardian option2 option number.
/// </summary>
public static short GuardianOption2 => 0x42;
/// <summary>
/// Gets the harmony defense option number.
/// </summary>
public static short HarmonyDefense => 0x50;
/// <summary>
/// Gets the harmony physical option number.
/// </summary>
public static short HarmonyPhysical => 0x51;
/// <summary>
/// Gets the harmony wizardry option number.
/// </summary>
public static short HarmonyWizardry => 0x52;
/// <summary>
/// Gets the harmony curse option number.
/// </summary>
public static short HarmonyCurse => 0x53;
/// <summary>
/// Gets the wing defense option number.
/// </summary>
public static short WingDefense => 0x60;
/// <summary>
/// Gets the wing physical option number.
/// </summary>
public static short WingPhysical => 0x61;
/// <summary>
/// Gets the wing wizardry option number.
/// </summary>
public static short WingWizardry => 0x62;
/// <summary>
/// Gets the wing curse option number.
/// </summary>
public static short WingCurse => 0x63;
/// <summary>
/// Gets the wing health recover option number.
/// </summary>
public static short WingHealthRecover => 0x64;
/// <summary>
/// Gets the wing2nd option number.
/// </summary>
public static short Wing2nd => 0x65;
/// <summary>
/// Gets the cape option number.
/// </summary>
public static short Cape => 0x66;
/// <summary>
/// Gets the wing3rd option number.
/// </summary>
public static short Wing3rd => 0x67;
/// <summary>
/// Gets the elite elite skeleton transformation ring option number.
/// </summary>
public static short EliteSkeletonTransformationRing => 0x70;
/// <summary>
/// Gets the skeleton transformation ring option number.
/// </summary>
public static short SkeletonTransformationRing => 0x71;
/// <summary>
/// Gets the wizard ring option number.
/// </summary>
public static short WizardRing => 0x73;
/// <summary>
/// Gets the dino option number.
/// </summary>
public static short Dino => 0x80;
/// <summary>
/// Gets the fenrir option number.
/// </summary>
public static short Fenrir => 0x81;
/// <summary>
/// Gets the dark horse option number.
/// </summary>
public static short Horse => 0x82;
}

View File

@@ -0,0 +1,478 @@
// <copyright file="ArmorInitializerBase.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.Items;
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.VersionSeasonSix.Items;
/// <summary>
/// Base class for an initializer for armor items.
/// </summary>
public abstract class ArmorInitializerBase : InitializerBase
{
private static readonly float[] DefenseIncreaseByLevel = { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 31, 36, 42, 49, 57, 66 };
private static readonly float[] ShieldDefenseIncreaseByLevel = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
private ItemLevelBonusTable? _defenseIncreaseTable;
private ItemLevelBonusTable? _shieldDefenseIncreaseTable;
private ItemLevelBonusTable? _shieldDefenseRateIncreaseTable;
private ItemLevelBonusTable? _runningMovementSpeedTable;
/// <summary>
/// Initializes a new instance of the <see cref="ArmorInitializerBase"/> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
protected ArmorInitializerBase(IContext context, GameConfiguration gameConfiguration)
: base(context, gameConfiguration)
{
}
/// <summary>
/// Gets the maximum armor level.
/// </summary>
protected abstract byte MaximumArmorLevel { get; }
/// <inheritdoc />
public override void Initialize()
{
this._defenseIncreaseTable = this.CreateItemBonusTable(DefenseIncreaseByLevel, "Defense Increase (Armors)", "Defines the defense increase per item level for armors. It's 3 per item level until level 9, then it's always 1 more for each level.");
this._shieldDefenseIncreaseTable = this.CreateItemBonusTable(ShieldDefenseIncreaseByLevel, "Defense Increase (Shields)", "Defines the defense increase per item level for shields. It's always 1 per item level.");
this._shieldDefenseRateIncreaseTable = this.CreateItemBonusTable(DefenseIncreaseByLevel, "Defense Rate Increase (Shields)", "Defines the defense rate increase per item level for shields. It's 3 per item level until level 9, then it's always 1 more for each level.");
this._runningMovementSpeedTable = this.CreateRunningMovementSpeedTable();
}
/// <summary>
/// Builds the <see cref="ItemSetGroup"/> for whole sets.
/// </summary>
protected void BuildSets()
{
var sets = this.GameConfiguration.Items.Where(item => item.Group is >= 7 and <= 11).GroupBy(item => item.Number);
var defenseRateBonusDef = this.Context.CreateNew<ItemOptionDefinition>();
defenseRateBonusDef.SetGuid(ItemOptionDefinitionNumbers.DefenseRateSetBonusOption);
defenseRateBonusDef.Name = "Complete Set Bonus (any level)";
defenseRateBonusDef.AddChance = 0;
defenseRateBonusDef.AddsRandomly = false;
this.GameConfiguration.ItemOptions.Add(defenseRateBonusDef);
var defenseRateBonus = this.Context.CreateNew<IncreasableItemOption>();
defenseRateBonus.SetGuid(ItemOptionDefinitionNumbers.DefenseRateSetBonusOption);
defenseRateBonus.PowerUpDefinition = this.Context.CreateNew<PowerUpDefinition>();
defenseRateBonus.PowerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
defenseRateBonus.PowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
defenseRateBonus.PowerUpDefinition.Boost.ConstantValue.Value = 1.1f;
defenseRateBonus.PowerUpDefinition.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(this.GameConfiguration);
defenseRateBonusDef.PossibleOptions.Add(defenseRateBonus);
var defenseBonus = new Dictionary<int, ItemOptionDefinition>();
for (byte setLevel = 10; setLevel <= this.MaximumArmorLevel; setLevel++)
{
var def = this.Context.CreateNew<ItemOptionDefinition>();
def.SetGuid(setLevel);
def.Name = $"Complete Set Bonus (Level {setLevel})";
def.PossibleOptions.Add(this.BuildDefenseBonusOption(1 + ((setLevel - 9) * 0.05f), setLevel));
defenseBonus.Add(setLevel, def);
this.GameConfiguration.ItemOptions.Add(def);
}
foreach (var group in sets)
{
var setForDefenseRate = this.Context.CreateNew<ItemSetGroup>();
this.GameConfiguration.ItemSetGroups.Add(setForDefenseRate);
setForDefenseRate.Name = group.First().Name.Value!.Split(' ')[0] + " Defense Rate Bonus";
setForDefenseRate.MinimumItemCount = group.Count();
setForDefenseRate.Options = defenseRateBonusDef;
setForDefenseRate.AlwaysApplies = true;
foreach (var item in group)
{
item.PossibleItemSetGroups.Add(setForDefenseRate);
var itemOfSet = this.Context.CreateNew<ItemOfItemSet>();
itemOfSet.SetGuid(item.Group, item.Number, 0xFF);
itemOfSet.ItemDefinition = item;
setForDefenseRate.Items.Add(itemOfSet);
}
for (byte setLevel = 10; setLevel <= this.MaximumArmorLevel; setLevel++)
{
this.CreateSetGroup(setLevel, defenseBonus[setLevel], group.ToList());
}
}
}
/// <summary>
/// Creates a shield.
/// </summary>
/// <param name="number">The item number.</param>
/// <param name="slot">The equipment slot.</param>
/// <param name="skill">The skill number.</param>
/// <param name="width">The item width.</param>
/// <param name="height">The item height.</param>
/// <param name="name">The item name.</param>
/// <param name="dropLevel">The item drop level.</param>
/// <param name="defense">The defense value.</param>
/// <param name="defenseRate">The defense rate value.</param>
/// <param name="durability">The item durability.</param>
/// <param name="strengthRequirement">The required strength attribute.</param>
/// <param name="agilityRequirement">The required agility attribute.</param>
/// <param name="darkWizardClassLevel">The dark wizard class level.</param>
/// <param name="darkKnightClassLevel">The dark knight class level.</param>
/// <param name="elfClassLevel">The elf class level.</param>
protected void CreateShield(byte number, byte slot, byte skill, byte width, byte height, string name, byte dropLevel, int defense, int defenseRate, byte durability, int strengthRequirement, int agilityRequirement, int darkWizardClassLevel, int darkKnightClassLevel, int elfClassLevel)
{
this.CreateShield(number, slot, skill, width, height, name, dropLevel, defense, defenseRate, durability, 0, strengthRequirement, agilityRequirement, 0, 0, 0, darkWizardClassLevel, darkKnightClassLevel, elfClassLevel, 0, 0, 0, 0);
}
/// <summary>
/// Creates a shield.
/// </summary>
/// <param name="number">The item number.</param>
/// <param name="slot">The equipment slot.</param>
/// <param name="skill">The skill number.</param>
/// <param name="width">The item width.</param>
/// <param name="height">The item height.</param>
/// <param name="name">The item name.</param>
/// <param name="dropLevel">The item drop level.</param>
/// <param name="defense">The defense value.</param>
/// <param name="defenseRate">The defense rate value.</param>
/// <param name="durability">The item durability.</param>
/// <param name="levelRequirement">The required character level.</param>
/// <param name="strengthRequirement">The required strength attribute.</param>
/// <param name="agilityRequirement">The required agility attribute.</param>
/// <param name="energyRequirement">The required energy attribute.</param>
/// <param name="vitalityRequirement">The required vitality attribute.</param>
/// <param name="leadershipRequirement">The required leadership attribute.</param>
/// <param name="darkWizardClassLevel">The dark wizard class level.</param>
/// <param name="darkKnightClassLevel">The dark knight class level.</param>
/// <param name="elfClassLevel">The elf class level.</param>
/// <param name="magicGladiatorClassLevel">The magic gladiator class level.</param>
/// <param name="darkLordClassLevel">The dark lord class level.</param>
/// <param name="summonerClassLevel">The summoner class level.</param>
/// <param name="ragefighterClassLevel">The rage fighter class level.</param>
protected void CreateShield(byte number, byte slot, byte skill, byte width, byte height, string name, byte dropLevel, int defense, int defenseRate, byte durability, int levelRequirement, int strengthRequirement, int agilityRequirement, int energyRequirement, int vitalityRequirement, int leadershipRequirement, int darkWizardClassLevel, int darkKnightClassLevel, int elfClassLevel, int magicGladiatorClassLevel, int darkLordClassLevel, int summonerClassLevel, int ragefighterClassLevel)
{
var shield = this.CreateArmor(number, slot, width, height, name, dropLevel, 0, durability, levelRequirement, strengthRequirement, agilityRequirement, energyRequirement, vitalityRequirement, leadershipRequirement, darkWizardClassLevel, darkKnightClassLevel, elfClassLevel, magicGladiatorClassLevel, darkLordClassLevel, summonerClassLevel, ragefighterClassLevel, true);
if (skill != 0)
{
shield.Skill = this.GameConfiguration.Skills.First(s => s.Number == skill);
}
if (defense > 0)
{
var powerUp = this.CreateItemBasePowerUpDefinition(Stats.DefenseShield, defense, AggregateType.AddRaw);
powerUp.BonusPerLevelTable = this._shieldDefenseIncreaseTable;
shield.BasePowerUpAttributes.Add(powerUp);
}
if (defenseRate > 0)
{
var powerUp = this.CreateItemBasePowerUpDefinition(Stats.DefenseRatePvm, defenseRate, AggregateType.AddRaw);
powerUp.BonusPerLevelTable = this._shieldDefenseRateIncreaseTable;
shield.BasePowerUpAttributes.Add(powerUp);
}
var isShieldEquipped = this.Context.CreateNew<ItemBasePowerUpDefinition>();
isShieldEquipped.TargetAttribute = Stats.IsShieldEquipped.GetPersistent(this.GameConfiguration);
isShieldEquipped.BaseValue = 1;
shield.BasePowerUpAttributes.Add(isShieldEquipped);
shield.PossibleItemOptions.Add(this.GameConfiguration.GetDefenseRateOption());
}
/// <summary>
/// Creates gloves.
/// </summary>
/// <param name="number">The item number.</param>
/// <param name="name">The item name.</param>
/// <param name="dropLevel">The item drop level.</param>
/// <param name="defense">The defense value.</param>
/// <param name="attackSpeed">The attack speed bonus.</param>
/// <param name="durability">The item durability.</param>
/// <param name="strengthRequirement">The required strength attribute.</param>
/// <param name="agilityRequirement">The required agility attribute.</param>
/// <param name="darkWizardClassLevel">The dark wizard class level.</param>
/// <param name="darkKnightClassLevel">The dark knight class level.</param>
/// <param name="elfClassLevel">The elf class level.</param>
/// <returns>The created item definition.</returns>
protected ItemDefinition CreateGloves(byte number, string name, byte dropLevel, int defense, int attackSpeed, byte durability, int strengthRequirement, int agilityRequirement, int darkWizardClassLevel, int darkKnightClassLevel, int elfClassLevel)
{
var gloves = this.CreateArmor(number, 5, 2, 2, name, dropLevel, defense, durability, strengthRequirement, agilityRequirement, darkWizardClassLevel, darkKnightClassLevel, elfClassLevel);
if (attackSpeed > 0)
{
gloves.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(Stats.AttackSpeedAny, attackSpeed, AggregateType.AddRaw));
}
this.AddRunningMovementSpeed(gloves, Stats.MovementSpeedUnderwater);
return gloves;
}
/// <summary>
/// Creates gloves.
/// </summary>
/// <param name="number">The item number.</param>
/// <param name="name">The item name.</param>
/// <param name="dropLevel">The item drop level.</param>
/// <param name="defense">The defense value.</param>
/// <param name="attackSpeed">The attack speed bonus.</param>
/// <param name="durability">The item durability.</param>
/// <param name="levelRequirement">The required character level.</param>
/// <param name="strengthRequirement">The required strength attribute.</param>
/// <param name="agilityRequirement">The required agility attribute.</param>
/// <param name="darkWizardClassLevel">The dark wizard class level.</param>
/// <param name="darkKnightClassLevel">The dark knight class level.</param>
/// <param name="elfClassLevel">The elf class level.</param>
/// <param name="magicGladiatorClassLevel">The magic gladiator class level.</param>
/// <param name="darkLordClassLevel">The dark lord class level.</param>
/// <param name="summonerClassLevel">The summoner class level.</param>
/// <returns>The created item definition.</returns>
protected ItemDefinition CreateGloves(byte number, string name, byte dropLevel, int defense, int attackSpeed, byte durability, int levelRequirement, int strengthRequirement, int agilityRequirement, int darkWizardClassLevel, int darkKnightClassLevel, int elfClassLevel, int magicGladiatorClassLevel, int darkLordClassLevel, int summonerClassLevel)
{
var gloves = this.CreateArmor(number, 5, 2, 2, name, dropLevel, defense, durability, levelRequirement, strengthRequirement, agilityRequirement, 0, 0, 0, darkWizardClassLevel, darkKnightClassLevel, elfClassLevel, magicGladiatorClassLevel, darkLordClassLevel, summonerClassLevel, 0);
if (attackSpeed > 0)
{
gloves.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(Stats.AttackSpeedAny, attackSpeed, AggregateType.AddRaw));
}
this.AddRunningMovementSpeed(gloves, Stats.MovementSpeedUnderwater);
return gloves;
}
/// <summary>
/// Creates boots.
/// </summary>
/// <param name="number">The item number.</param>
/// <param name="slot">The equipment slot.</param>
/// <param name="width">The item width.</param>
/// <param name="height">The item height.</param>
/// <param name="name">The item name.</param>
/// <param name="dropLevel">The item drop level.</param>
/// <param name="defense">The defense value.</param>
/// <param name="walkSpeed">The walk speed bonus.</param>
/// <param name="durability">The item durability.</param>
/// <param name="strengthRequirement">The required strength attribute.</param>
/// <param name="agilityRequirement">The required agility attribute.</param>
/// <param name="darkWizardClassLevel">The dark wizard class level.</param>
/// <param name="darkKnightClassLevel">The dark knight class level.</param>
/// <param name="elfClassLevel">The elf class level.</param>
/// <returns>The created item definition.</returns>
protected ItemDefinition CreateBoots(byte number, byte slot, byte width, byte height, string name, byte dropLevel, int defense, int walkSpeed, byte durability, int strengthRequirement, int agilityRequirement, int darkWizardClassLevel, int darkKnightClassLevel, int elfClassLevel)
{
var boots = this.CreateArmor(number, 6, 2, 2, name, dropLevel, defense, durability, strengthRequirement, agilityRequirement, darkWizardClassLevel, darkKnightClassLevel, elfClassLevel);
if (walkSpeed > 0)
{
boots.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(Stats.WalkSpeed, walkSpeed, AggregateType.AddRaw));
}
this.AddRunningMovementSpeed(boots, Stats.MovementSpeed);
return boots;
}
/// <summary>
/// Creates boots.
/// </summary>
/// <param name="number">The item number.</param>
/// <param name="name">The item name.</param>
/// <param name="dropLevel">The item drop level.</param>
/// <param name="defense">The defense value.</param>
/// <param name="walkSpeed">The walk speed bonus.</param>
/// <param name="durability">The item durability.</param>
/// <param name="levelRequirement">The required character level.</param>
/// <param name="strengthRequirement">The required strength attribute.</param>
/// <param name="agilityRequirement">The required agility attribute.</param>
/// <param name="energyRequirement">The required energy attribute.</param>
/// <param name="vitalityRequirement">The required vitality attribute.</param>
/// <param name="leadershipRequirement">The required leadership attribute.</param>
/// <param name="darkWizardClassLevel">The dark wizard class level.</param>
/// <param name="darkKnightClassLevel">The dark knight class level.</param>
/// <param name="elfClassLevel">The elf class level.</param>
/// <param name="magicGladiatorClassLevel">The magic gladiator class level.</param>
/// <param name="darkLordClassLevel">The dark lord class level.</param>
/// <param name="summonerClassLevel">The summoner class level.</param>
/// <param name="ragefighterClassLevel">The rage fighter class level.</param>
/// <returns>The created item definition.</returns>
protected ItemDefinition CreateBoots(byte number, string name, byte dropLevel, int defense, int walkSpeed, byte durability, int levelRequirement, int strengthRequirement, int agilityRequirement, int energyRequirement, int vitalityRequirement, int leadershipRequirement, int darkWizardClassLevel, int darkKnightClassLevel, int elfClassLevel, int magicGladiatorClassLevel, int darkLordClassLevel, int summonerClassLevel, int ragefighterClassLevel)
{
var boots = this.CreateArmor(number, 6, 2, 2, name, dropLevel, defense, durability, levelRequirement, strengthRequirement, agilityRequirement, energyRequirement, vitalityRequirement, leadershipRequirement, darkWizardClassLevel, darkKnightClassLevel, elfClassLevel, magicGladiatorClassLevel, darkLordClassLevel, summonerClassLevel, ragefighterClassLevel);
if (walkSpeed > 0)
{
boots.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(Stats.WalkSpeed, walkSpeed, AggregateType.AddRaw));
}
this.AddRunningMovementSpeed(boots, Stats.MovementSpeed);
return boots;
}
/// <summary>
/// Creates armor.
/// </summary>
/// <param name="number">The item number.</param>
/// <param name="slot">The equipment slot.</param>
/// <param name="width">The item width.</param>
/// <param name="height">The item height.</param>
/// <param name="name">The item name.</param>
/// <param name="dropLevel">The item drop level.</param>
/// <param name="defense">The defense value.</param>
/// <param name="durability">The item durability.</param>
/// <param name="strengthRequirement">The required strength attribute.</param>
/// <param name="agilityRequirement">The required agility attribute.</param>
/// <param name="darkWizardClassLevel">The dark wizard class level.</param>
/// <param name="darkKnightClassLevel">The dark knight class level.</param>
/// <param name="elfClassLevel">The elf class level.</param>
/// <returns>The created item definition.</returns>
protected ItemDefinition CreateArmor(byte number, byte slot, byte width, byte height, string name, byte dropLevel, int defense, byte durability, int strengthRequirement, int agilityRequirement, int darkWizardClassLevel, int darkKnightClassLevel, int elfClassLevel)
{
var magicGladiatorClassLevel = 0;
if ((slot + 5) != (int)ItemGroups.Helm
&& (darkKnightClassLevel == 1 || darkWizardClassLevel == 1)
&& this.GameConfiguration.CharacterClasses.Any(c => c.Number == (byte)CharacterClassNumber.MagicGladiator))
{
magicGladiatorClassLevel = 1;
}
return this.CreateArmor(number, slot, width, height, name, dropLevel, defense, durability, 0, strengthRequirement, agilityRequirement, 0, 0, 0, darkWizardClassLevel, darkKnightClassLevel, elfClassLevel, magicGladiatorClassLevel, 0, 0, 0);
}
/// <summary>
/// Creates armor.
/// </summary>
/// <param name="number">The item number.</param>
/// <param name="slot">The equipment slot.</param>
/// <param name="width">The item width.</param>
/// <param name="height">The item height.</param>
/// <param name="name">The item name.</param>
/// <param name="dropLevel">The item drop level.</param>
/// <param name="defense">The defense value.</param>
/// <param name="durability">The item durability.</param>
/// <param name="levelRequirement">The required character level.</param>
/// <param name="strengthRequirement">The required strength attribute.</param>
/// <param name="agilityRequirement">The required agility attribute.</param>
/// <param name="energyRequirement">The required energy attribute.</param>
/// <param name="vitalityRequirement">The required vitality attribute.</param>
/// <param name="leadershipRequirement">The required leadership attribute.</param>
/// <param name="darkWizardClassLevel">The dark wizard class level.</param>
/// <param name="darkKnightClassLevel">The dark knight class level.</param>
/// <param name="elfClassLevel">The elf class level.</param>
/// <param name="magicGladiatorClassLevel">The magic gladiator class level.</param>
/// <param name="darkLordClassLevel">The dark lord class level.</param>
/// <param name="summonerClassLevel">The summoner class level.</param>
/// <param name="ragefighterClassLevel">The rage fighter class level.</param>
/// <param name="isShield">If set to <c>true</c> the armor is considered a shield.</param>
/// <returns>The created item definition.</returns>
protected ItemDefinition CreateArmor(byte number, byte slot, byte width, byte height, string name, byte dropLevel, int defense, byte durability, int levelRequirement, int strengthRequirement, int agilityRequirement, int energyRequirement, int vitalityRequirement, int leadershipRequirement, int darkWizardClassLevel, int darkKnightClassLevel, int elfClassLevel, int magicGladiatorClassLevel, int darkLordClassLevel, int summonerClassLevel, int ragefighterClassLevel, bool isShield = false)
{
var armor = this.Context.CreateNew<ItemDefinition>();
this.GameConfiguration.Items.Add(armor);
armor.Group = (byte)(slot + 5);
armor.Number = number;
armor.Width = width;
armor.Height = height;
armor.Name = name;
armor.DropLevel = dropLevel;
armor.MaximumItemLevel = this.MaximumArmorLevel;
armor.DropsFromMonsters = true;
armor.Durability = durability;
armor.ItemSlot = this.GameConfiguration.ItemSlotTypes.First(st => st.ItemSlots.Contains(slot));
this.CreateItemRequirementIfNeeded(armor, Stats.Level, levelRequirement);
this.CreateItemRequirementIfNeeded(armor, Stats.TotalStrengthRequirementValue, strengthRequirement);
this.CreateItemRequirementIfNeeded(armor, Stats.TotalAgilityRequirementValue, agilityRequirement);
this.CreateItemRequirementIfNeeded(armor, Stats.TotalEnergyRequirementValue, energyRequirement);
this.CreateItemRequirementIfNeeded(armor, Stats.TotalVitalityRequirementValue, vitalityRequirement);
this.CreateItemRequirementIfNeeded(armor, Stats.TotalLeadershipRequirementValue, leadershipRequirement);
if (defense > 0)
{
var powerUp = this.CreateItemBasePowerUpDefinition(Stats.DefenseBase, defense, AggregateType.AddRaw);
powerUp.BonusPerLevelTable = this._defenseIncreaseTable;
armor.BasePowerUpAttributes.Add(powerUp);
}
var classes = this.GameConfiguration.DetermineCharacterClasses(darkWizardClassLevel, darkKnightClassLevel, elfClassLevel, magicGladiatorClassLevel, darkLordClassLevel, summonerClassLevel, ragefighterClassLevel);
foreach (var characterClass in classes)
{
armor.QualifiedCharacters.Add(characterClass);
}
armor.PossibleItemOptions.Add(this.GameConfiguration.GetLuck());
if (!isShield)
{
armor.PossibleItemOptions.Add(this.GameConfiguration.GetDefenseOption());
}
if (this.GameConfiguration.ItemOptions.FirstOrDefault(o => o.Name == ExcellentOptions.DefenseOptionsName) is { } excellentOption)
{
armor.PossibleItemOptions.Add(excellentOption);
}
if (this.GameConfiguration.ItemOptions.FirstOrDefault(o => o.Name == HarmonyOptions.DefenseOptionsName) is { } harmonyOptionsName)
{
armor.PossibleItemOptions.Add(harmonyOptionsName);
}
armor.SetGuid(armor.Group, armor.Number);
return armor;
}
private void AddRunningMovementSpeed(ItemDefinition item, AttributeDefinition targetAttribute)
{
var powerUp = this.CreateItemBasePowerUpDefinition(targetAttribute, 0, AggregateType.Maximum);
powerUp.BonusPerLevelTable = this._runningMovementSpeedTable;
item.BasePowerUpAttributes.Add(powerUp);
}
private ItemLevelBonusTable CreateRunningMovementSpeedTable()
{
var table = this.Context.CreateNew<ItemLevelBonusTable>();
this.GameConfiguration.ItemLevelBonusTables.Add(table);
table.Name = "Running Movement Speed";
table.Description = "Defines the running movement speed for boots and underwater gloves from item level 5.";
for (int level = MovementSpeedConstants.RunningGearMinimumLevel; level <= this.MaximumArmorLevel; level++)
{
var levelBonus = this.Context.CreateNew<LevelBonus>();
levelBonus.Level = level;
levelBonus.AdditionalValue = MovementSpeedConstants.RunningGearMovementSpeed;
table.BonusPerLevel.Add(levelBonus);
}
return table;
}
private IncreasableItemOption BuildDefenseBonusOption(float bonus, short level)
{
var defenseBonus = this.Context.CreateNew<IncreasableItemOption>();
defenseBonus.SetGuid(ItemOptionDefinitionNumbers.DefenseSetBonusOption, level);
defenseBonus.PowerUpDefinition = this.Context.CreateNew<PowerUpDefinition>();
defenseBonus.PowerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
defenseBonus.PowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
defenseBonus.PowerUpDefinition.Boost.ConstantValue.Value = bonus;
defenseBonus.PowerUpDefinition.TargetAttribute = Stats.DefenseBase.GetPersistent(this.GameConfiguration);
return defenseBonus;
}
private void CreateSetGroup(byte setLevel, ItemOptionDefinition options, ICollection<ItemDefinition> group)
{
var setForDefense = this.Context.CreateNew<ItemSetGroup>();
this.GameConfiguration.ItemSetGroups.Add(setForDefense);
setForDefense.Name = $"{group.First().Name.Value!.Split(' ')[0]} Defense Bonus (Level {setLevel})";
setForDefense.MinimumItemCount = group.Count;
setForDefense.Options = options;
setForDefense.AlwaysApplies = true;
setForDefense.SetLevel = setLevel;
foreach (var item in group)
{
var itemOfSet = this.Context.CreateNew<ItemOfItemSet>();
itemOfSet.SetGuid(item.Group, item.Number, setLevel);
itemOfSet.ItemDefinition = item;
setForDefense.Items.Add(itemOfSet);
item.PossibleItemSetGroups.Add(setForDefense);
}
}
}

View File

@@ -0,0 +1,165 @@
// <copyright file="ExcellentOptions.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.Items;
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;
/// <summary>
/// Initializer for excellent options.
/// </summary>
public class ExcellentOptions : InitializerBase
{
/// <summary>
/// The name of the <see cref="ItemOptionDefinition"/> of excellent defense options.
/// </summary>
public static readonly string DefenseOptionsName = "Excellent Defense Options";
/// <summary>
/// The name of the <see cref="ItemOptionDefinition"/> of excellent physical attack options.
/// </summary>
public static readonly string PhysicalAttackOptionsName = "Excellent Physical Attack Options";
/// <summary>
/// The name of the <see cref="ItemOptionDefinition"/> of excellent wizardry attack options.
/// </summary>
public static readonly string WizardryAttackOptionsName = "Excellent Wizardry Attack Options";
/// <summary>
/// The name of the <see cref="ItemOptionDefinition"/> of excellent curse attack options.
/// </summary>
public static readonly string CurseAttackOptionsName = "Excellent Curse Attack Options";
/// <summary>
/// Initializes a new instance of the <see cref="ExcellentOptions"/> class.
/// </summary>
/// <param name="context">The persistence context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
public ExcellentOptions(IContext context, GameConfiguration gameConfiguration)
: base(context, gameConfiguration)
{
}
/// <inheritdoc/>
public override void Initialize()
{
this.CreateDefenseOptions();
this.CreatePhysicalAttackOptions();
this.CreateWizardryAttackOptions();
// this.CreateCurseAttackOptions();
}
[Obsolete(@"There are no curse attack excellent options until Season 14 (Summoner renewal). Until then, excellent curse spell books had excellent wizardry attack options.
Reference: https://muonline.webzen.com/pt/gameinfo/guide/detail/91")]
private void CreateCurseAttackOptions()
{
var definition = this.Context.CreateNew<ItemOptionDefinition>();
definition.SetGuid(ItemOptionDefinitionNumbers.ExcellentCurse);
this.GameConfiguration.ItemOptions.Add(definition);
definition.Name = CurseAttackOptionsName;
definition.AddChance = 0.001f;
definition.AddsRandomly = true;
definition.MaximumOptionsPerItem = 2;
definition.PossibleOptions.Add(this.CreateExcellentOption(1, Stats.ManaAfterMonsterKillMultiplier, 1f / 8f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentCurse));
definition.PossibleOptions.Add(this.CreateExcellentOption(2, Stats.HealthAfterMonsterKillMultiplier, 1f / 8f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentCurse));
definition.PossibleOptions.Add(this.CreateExcellentOption(3, Stats.AttackSpeedAny, 7, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentCurse));
definition.PossibleOptions.Add(this.CreateExcellentOption(4, Stats.WizardryBaseDmgIncrease, 1.02f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentCurse));
definition.PossibleOptions.Add(this.CreateRelatedExcellentOption(5, Stats.WizardryBaseDmg, Stats.TotalLevel, 1f / 20f, ItemOptionDefinitionNumbers.ExcellentCurse));
definition.PossibleOptions.Add(this.CreateExcellentOption(6, Stats.ExcellentDamageChance, 0.1f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentCurse));
}
private void CreateWizardryAttackOptions()
{
var definition = this.Context.CreateNew<ItemOptionDefinition>();
definition.SetGuid(ItemOptionDefinitionNumbers.ExcellentWizardry);
this.GameConfiguration.ItemOptions.Add(definition);
definition.Name = WizardryAttackOptionsName;
definition.AddChance = 0.001f;
definition.AddsRandomly = true;
definition.MaximumOptionsPerItem = 2;
definition.PossibleOptions.Add(this.CreateExcellentOption(1, Stats.ManaAfterMonsterKillMultiplier, 1f / 8f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentWizardry));
definition.PossibleOptions.Add(this.CreateExcellentOption(2, Stats.HealthAfterMonsterKillMultiplier, 1f / 8f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentWizardry));
definition.PossibleOptions.Add(this.CreateExcellentOption(3, Stats.AttackSpeedAny, 7, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentWizardry));
definition.PossibleOptions.Add(this.CreateExcellentOption(4, Stats.WizardryBaseDmgIncrease, 1.02f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentWizardry));
definition.PossibleOptions.Add(this.CreateRelatedExcellentOption(5, Stats.WizardryBaseDmg, Stats.TotalLevel, 1f / 20f, ItemOptionDefinitionNumbers.ExcellentWizardry));
definition.PossibleOptions.Add(this.CreateExcellentOption(6, Stats.ExcellentDamageChance, 0.1f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentWizardry));
}
private void CreatePhysicalAttackOptions()
{
var definition = this.Context.CreateNew<ItemOptionDefinition>();
definition.SetGuid(ItemOptionDefinitionNumbers.ExcellentPhysical);
this.GameConfiguration.ItemOptions.Add(definition);
definition.Name = PhysicalAttackOptionsName;
definition.AddChance = 0.001f;
definition.AddsRandomly = true;
definition.MaximumOptionsPerItem = 2;
definition.PossibleOptions.Add(this.CreateExcellentOption(1, Stats.ManaAfterMonsterKillMultiplier, 1f / 8f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentPhysical));
definition.PossibleOptions.Add(this.CreateExcellentOption(2, Stats.HealthAfterMonsterKillMultiplier, 1f / 8f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentPhysical));
definition.PossibleOptions.Add(this.CreateExcellentOption(3, Stats.AttackSpeedAny, 7, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentPhysical));
definition.PossibleOptions.Add(this.CreateExcellentOption(4, Stats.PhysicalBaseDmgIncrease, 1.02f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentPhysical));
definition.PossibleOptions.Add(this.CreateRelatedExcellentOption(5, Stats.PhysicalBaseDmg, Stats.TotalLevel, 1f / 20f, ItemOptionDefinitionNumbers.ExcellentPhysical));
definition.PossibleOptions.Add(this.CreateExcellentOption(6, Stats.ExcellentDamageChance, 0.1f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentPhysical));
}
private void CreateDefenseOptions()
{
var definition = this.Context.CreateNew<ItemOptionDefinition>();
definition.SetGuid(ItemOptionDefinitionNumbers.ExcellentDefense);
this.GameConfiguration.ItemOptions.Add(definition);
definition.Name = DefenseOptionsName;
definition.AddChance = 0.001f;
definition.AddsRandomly = true;
definition.MaximumOptionsPerItem = 2;
definition.PossibleOptions.Add(this.CreateExcellentOption(1, Stats.MoneyAmountRate, 1.4f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentDefense));
definition.PossibleOptions.Add(this.CreateExcellentOption(2, Stats.DefenseRatePvm, 1.1f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentDefense));
definition.PossibleOptions.Add(this.CreateExcellentOption(3, Stats.DamageReflection, 0.05f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentDefense));
definition.PossibleOptions.Add(this.CreateExcellentOption(4, Stats.ArmorDamageDecrease, 0.04f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentDefense));
definition.PossibleOptions.Add(this.CreateExcellentOption(5, Stats.MaximumMana, 1.04f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentDefense));
definition.PossibleOptions.Add(this.CreateExcellentOption(6, Stats.MaximumHealth, 1.04f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentDefense));
}
private IncreasableItemOption CreateExcellentOption(short number, AttributeDefinition attributeDefinition, float value, AggregateType aggregateType, short parentNumber)
{
var itemOption = this.Context.CreateNew<IncreasableItemOption>();
itemOption.SetGuid(parentNumber, number);
itemOption.OptionType = this.GameConfiguration.ItemOptionTypes.First(t => t == ItemOptionTypes.Excellent);
itemOption.Number = number;
itemOption.PowerUpDefinition = this.Context.CreateNew<PowerUpDefinition>();
itemOption.PowerUpDefinition.TargetAttribute = attributeDefinition.GetPersistent(this.GameConfiguration);
itemOption.PowerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
itemOption.PowerUpDefinition.Boost.ConstantValue.Value = value;
itemOption.PowerUpDefinition.Boost.ConstantValue.AggregateType = aggregateType;
return itemOption;
}
private IncreasableItemOption CreateRelatedExcellentOption(short number, AttributeDefinition targetAttribute, AttributeDefinition sourceAttribute, float multiplier, short parentNumber)
{
var itemOption = this.Context.CreateNew<IncreasableItemOption>();
itemOption.SetGuid(parentNumber, number);
itemOption.OptionType = this.GameConfiguration.ItemOptionTypes.First(t => t == ItemOptionTypes.Excellent);
itemOption.Number = number;
itemOption.PowerUpDefinition = this.Context.CreateNew<PowerUpDefinition>();
itemOption.PowerUpDefinition.TargetAttribute = targetAttribute.GetPersistent(this.GameConfiguration);
itemOption.PowerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
var attributeRelationship = this.Context.CreateNew<AttributeRelationship>();
attributeRelationship.SetGuid(parentNumber, number); // because of a different type, the id will be different from the item option above.
itemOption.PowerUpDefinition.Boost.RelatedValues.Add(attributeRelationship);
attributeRelationship.InputOperator = InputOperator.Multiply;
attributeRelationship.InputOperand = multiplier;
attributeRelationship.InputAttribute = sourceAttribute.GetPersistent(this.GameConfiguration);
attributeRelationship.TargetAttribute = targetAttribute.GetPersistent(this.GameConfiguration);
return itemOption;
}
}

View File

@@ -0,0 +1,98 @@
// <copyright file="ItemGroups.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.Items;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Enumeration of <see cref="ItemDefinition.Group"/> values.
/// </summary>
internal enum ItemGroups
{
/// <summary>
/// The swords group.
/// </summary>
Swords = 0,
/// <summary>
/// The axes group.
/// </summary>
Axes = 1,
/// <summary>
/// The scepters group.
/// </summary>
Scepters = 2,
/// <summary>
/// The spears group.
/// </summary>
Spears = 3,
/// <summary>
/// The bows group.
/// </summary>
Bows = 4,
/// <summary>
/// The staff group.
/// </summary>
Staff = 5,
/// <summary>
/// The shields group.
/// </summary>
Shields = 6,
/// <summary>
/// The helm group.
/// </summary>
Helm = 7,
/// <summary>
/// The armor group.
/// </summary>
Armor = 8,
/// <summary>
/// The pants group.
/// </summary>
Pants = 9,
/// <summary>
/// The gloves group.
/// </summary>
Gloves = 10,
/// <summary>
/// The boots group.
/// </summary>
Boots = 11,
/// <summary>
/// The orbs group.
/// </summary>
Orbs = 12,
/// <summary>
/// Pets, Rings, Pendants and Misc.
/// </summary>
Misc1 = 13,
/// <summary>
/// Pots and Misc.
/// </summary>
Misc2 = 14,
/// <summary>
/// The scrolls group.
/// </summary>
Scrolls = 15,
/// <summary>
/// The weapon group, which is used to identify weapon guardian options, but not a valid value at <see cref="ItemDefinition.Group"/>.
/// </summary>
Weapon = 0xF0,
}

View File

@@ -0,0 +1,233 @@
// <copyright file="ItemHelper.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.Items;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.DataModel.Entities;
/// <summary>
/// A helper class which contains some convenient methods to create items.
/// </summary>
internal class ItemHelper
{
private readonly IContext _context;
private readonly GameConfiguration _gameConfiguration;
/// <summary>
/// Initializes a new instance of the <see cref="ItemHelper"/> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
public ItemHelper(IContext context, GameConfiguration gameConfiguration)
{
this._context = context;
this._gameConfiguration = gameConfiguration;
}
/// <summary>
/// Creates an orb.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="learnableId">The learnable identifier.</param>
/// <returns>The orb.</returns>
public Item CreateOrb(byte itemSlot, byte learnableId)
{
return this.CreateLearnable(itemSlot, learnableId, ItemGroups.Orbs);
}
/// <summary>
/// Creates an summon orb.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="level">The level of the orb - indirectly defines the skill which will be learned.</param>
/// <returns>The orb.</returns>
public Item CreateSummonOrb(byte itemSlot, byte level)
{
var orb = this.CreateLearnable(itemSlot, 11, ItemGroups.Orbs);
orb.Level = level;
return orb;
}
/// <summary>
/// Creates a scroll.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="learnableId">The learnable identifier.</param>
/// <returns>The scroll.</returns>
public Item CreateScroll(byte itemSlot, byte learnableId)
{
return this.CreateLearnable(itemSlot, learnableId, ItemGroups.Scrolls);
}
/// <summary>
/// Creates the learnable.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="learnableId">The learnable identifier.</param>
/// <param name="group">The group.</param>
/// <returns>The learnable.</returns>
public Item CreateLearnable(byte itemSlot, byte learnableId, ItemGroups group)
{
var item = this._context.CreateNew<Item>();
item.Definition = this._gameConfiguration.Items.First(def => def.Group == (byte)group && def.Number == learnableId);
item.ItemSlot = itemSlot;
item.Durability = 1;
return item;
}
/// <summary>
/// Creates a potion.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="potionId">The potion identifier.</param>
/// <param name="stackSize">Size of the stack.</param>
/// <param name="level">The level.</param>
/// <returns>The potion.</returns>
public Item CreatePotion(byte itemSlot, byte potionId, byte stackSize, byte level)
{
var item = this._context.CreateNew<Item>();
item.Definition = this._gameConfiguration.Items.First(def => def.Group == 14 && def.Number == potionId);
item.ItemSlot = itemSlot;
item.Durability = stackSize;
item.Level = level;
return item;
}
/// <summary>
/// Creates an item.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="itemId">The item identifier.</param>
/// <param name="itemGroup">The item group.</param>
/// <param name="stackSize">Size of the stak.</param>
/// <param name="level">The level.</param>
/// <returns>The created item.</returns>
public Item CreateItem(byte itemSlot, byte itemId, byte itemGroup, byte stackSize, byte level)
{
var item = this._context.CreateNew<Item>();
item.Definition = this._gameConfiguration.Items.First(def => def.Group == itemGroup && def.Number == itemId);
item.ItemSlot = itemSlot;
item.Durability = stackSize;
item.Level = level;
return item;
}
/// <summary>
/// Creates a new item of an armor set.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="setNumber">The set number.</param>
/// <param name="group">The group.</param>
/// <param name="targetExcellentOption">The target excellent option.</param>
/// <param name="level">The level.</param>
/// <param name="optionLevel">The option level.</param>
/// <param name="luck">if set to <c>true</c>, the item has luck option.</param>
/// <returns>The created armor set item.</returns>
public Item CreateSetItem(byte itemSlot, byte setNumber, ItemGroups group, AttributeDefinition? targetExcellentOption = null, byte level = 0, byte optionLevel = 0, bool luck = false)
{
return this.CreateEquippableItem(itemSlot, group, setNumber, level, optionLevel, luck, targetExcellentOption);
}
/// <summary>
/// Creates a shield.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="setNumber">The set number.</param>
/// <param name="skill">if set to <c>true</c>, the item has skill.</param>
/// <param name="targetExcellentOption">The target excellent option.</param>
/// <param name="level">The level.</param>
/// <param name="optionLevel">The option level.</param>
/// <param name="luck">if set to <c>true</c>, the item has luck option.</param>
/// <returns>The created shield.</returns>
public Item CreateShield(byte itemSlot, byte setNumber, bool skill, AttributeDefinition? targetExcellentOption = null, byte level = 0, byte optionLevel = 0, bool luck = false)
{
var item = this.CreateEquippableItem(itemSlot, ItemGroups.Shields, setNumber, level, optionLevel, luck, targetExcellentOption);
item.HasSkill = skill;
return item;
}
/// <summary>
/// Creates an equippable item.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="group">The group.</param>
/// <param name="number">The number.</param>
/// <param name="level">The level.</param>
/// <param name="optionLevel">The option level.</param>
/// <param name="luck">if set to <c>true</c>, the item has luck option.</param>
/// <param name="targetExcellentOption">The target excellent option.</param>
/// <returns>The created item.</returns>
public Item CreateEquippableItem(byte itemSlot, ItemGroups group, byte number, byte level, byte optionLevel, bool luck, AttributeDefinition? targetExcellentOption)
{
var item = this._context.CreateNew<Item>();
item.Definition = this._gameConfiguration.Items.First(def => def.Group == (byte)group && def.Number == number);
item.Durability = item.Definition.Durability;
item.ItemSlot = itemSlot;
item.Level = level;
var excellentOption = this.GetExcellentOption(targetExcellentOption, item);
if (excellentOption != null)
{
var optionLink = this._context.CreateNew<ItemOptionLink>();
optionLink.ItemOption = excellentOption;
item.ItemOptions.Add(optionLink);
}
if (optionLevel > 0)
{
var optionLink = this._context.CreateNew<ItemOptionLink>();
optionLink.ItemOption = item.Definition.PossibleItemOptions.SelectMany(o => o.PossibleOptions)
.First(o => o.OptionType == ItemOptionTypes.Option);
optionLink.Level = optionLevel;
item.ItemOptions.Add(optionLink);
}
if (luck)
{
var optionLink = this._context.CreateNew<ItemOptionLink>();
optionLink.ItemOption = item.Definition.PossibleItemOptions.SelectMany(o => o.PossibleOptions)
.First(o => o.OptionType == ItemOptionTypes.Luck);
item.ItemOptions.Add(optionLink);
}
return item;
}
/// <summary>
/// Creates the weapon.
/// </summary>
/// <param name="itemSlot">The item slot.</param>
/// <param name="group">The group.</param>
/// <param name="number">The number.</param>
/// <param name="level">The level.</param>
/// <param name="optionLevel">The option level.</param>
/// <param name="luck">if set to <c>true</c>, the item has luck option.</param>
/// <param name="skill">if set to <c>true</c>, the item has skill.</param>
/// <param name="targetExcellentOption">The target excellent option.</param>
/// <returns>The created weapon.</returns>
public Item CreateWeapon(byte itemSlot, ItemGroups group, byte number, byte level, byte optionLevel, bool luck, bool skill, AttributeDefinition? targetExcellentOption)
{
var weapon = this.CreateEquippableItem(itemSlot, group, number, level, optionLevel, luck, targetExcellentOption);
weapon.HasSkill = skill;
return weapon;
}
private IncreasableItemOption? GetExcellentOption(AttributeDefinition? targetExcellentOption, Item item)
{
if (targetExcellentOption is null)
{
return null;
}
return item.Definition!.PossibleItemOptions.SelectMany(o => o.PossibleOptions)
.Where(o => o.OptionType == ItemOptionTypes.Excellent)
.First(o => o.PowerUpDefinition?.TargetAttribute == targetExcellentOption);
}
}

View File

@@ -0,0 +1,105 @@
// <copyright file="OptionExtensions.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.Items;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Extensions to get <see cref="ItemOptionDefinition"/>s of the <see cref="GameConfiguration"/>.
/// </summary>
public static class OptionExtensions
{
/// <summary>
/// Gets the luck item option definition.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The definition for the luck option.</returns>
public static ItemOptionDefinition GetLuck(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(iod => iod.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Luck));
}
/// <summary>
/// Gets the defense option definition.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The definition for the defense option.</returns>
public static ItemOptionDefinition GetDefenseOption(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(iod => iod.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == Stats.DefenseBase));
}
/// <summary>
/// Gets the physical damage option definition.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The definition for the physical damage option.</returns>
public static ItemOptionDefinition PhysicalDamageOption(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(iod => iod.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == Stats.PhysicalBaseDmg));
}
/// <summary>
/// Gets the wizardry damage option definition.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The definition for the wizardry damage option.</returns>
public static ItemOptionDefinition WizardryDamageOption(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(iod => iod.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == Stats.WizardryBaseDmg));
}
/// <summary>
/// Gets the defense rate option definition.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The definition for the defense rate option.</returns>
public static ItemOptionDefinition GetDefenseRateOption(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(iod => iod.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == Stats.DefenseRatePvm));
}
/// <summary>
/// Gets the physical and wizardry damage option definition.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The definition for the physical and wizardry damage option.</returns>
public static ItemOptionDefinition PhysicalAndWizardryDamageOption(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(iod => iod.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == Stats.BaseDamageBonus));
}
/// <summary>
/// Gets the excellent defense options.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The excellent defense options.</returns>
public static ItemOptionDefinition ExcellentDefenseOptions(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(o => o.Name == ExcellentOptions.DefenseOptionsName);
}
/// <summary>
/// Gets the excellent physical attack options.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The excellent physical attack options.</returns>
public static ItemOptionDefinition ExcellentPhysicalAttackOptions(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(o => o.Name == ExcellentOptions.PhysicalAttackOptionsName);
}
/// <summary>
/// Gets the excellent wizardry attack options.
/// </summary>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>The excellent wizardry attack options.</returns>
public static ItemOptionDefinition ExcellentWizardryAttackOptions(this GameConfiguration gameConfiguration)
{
return gameConfiguration.ItemOptions.First(o => o.Name == ExcellentOptions.WizardryAttackOptionsName);
}
}

View File

@@ -0,0 +1,204 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable;CS4014;VSTHRD110;VSTHRD100</WarningsAsErrors>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\..\..\bin\Debug\</OutputPath>
<DocumentationFile>..\..\..\bin\Debug\MUnique.OpenMU.Persistence.Initialization.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>..\..\bin\Release\</OutputPath>
<DocumentationFile>..\..\bin\Release\MUnique.OpenMU.Persistence.Initialization.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\075_Terrain1.att" />
<None Remove="Resources\075_Terrain2.att" />
<None Remove="Resources\075_Terrain3.att" />
<None Remove="Resources\075_Terrain4.att" />
<None Remove="Resources\075_Terrain5.att" />
<None Remove="Resources\075_Terrain8.att" />
<None Remove="Resources\Terrain1.att" />
<None Remove="Resources\Terrain10_1.att" />
<None Remove="Resources\Terrain10_2.att" />
<None Remove="Resources\Terrain10_3.att" />
<None Remove="Resources\Terrain10_4.att" />
<None Remove="Resources\Terrain11.att" />
<None Remove="Resources\Terrain12.att" />
<None Remove="Resources\Terrain19.att" />
<None Remove="Resources\Terrain2.att" />
<None Remove="Resources\Terrain25.att" />
<None Remove="Resources\Terrain3.att" />
<None Remove="Resources\Terrain31.att" />
<None Remove="Resources\Terrain32.att" />
<None Remove="Resources\Terrain33_5.att" />
<None Remove="Resources\Terrain33_6.att" />
<None Remove="Resources\Terrain33_7.att" />
<None Remove="Resources\Terrain34.att" />
<None Remove="Resources\Terrain34_Close.att" />
<None Remove="Resources\Terrain35.att" />
<None Remove="Resources\Terrain35_OCCUPIED.att" />
<None Remove="Resources\Terrain35_PEACE.att" />
<None Remove="Resources\Terrain35_WAR.att" />
<None Remove="Resources\Terrain36.att" />
<None Remove="Resources\Terrain37.att" />
<None Remove="Resources\Terrain38.att" />
<None Remove="Resources\Terrain39.att" />
<None Remove="Resources\Terrain4.att" />
<None Remove="Resources\Terrain40.att" />
<None Remove="Resources\Terrain40_CLOSE.att" />
<None Remove="Resources\Terrain40_OPEN.att" />
<None Remove="Resources\Terrain41.att" />
<None Remove="Resources\Terrain42.att" />
<None Remove="Resources\Terrain43.att" />
<None Remove="Resources\Terrain44.att" />
<None Remove="Resources\Terrain46.att" />
<None Remove="Resources\Terrain47.att" />
<None Remove="Resources\Terrain5.att" />
<None Remove="Resources\Terrain51.att" />
<None Remove="Resources\Terrain52.att" />
<None Remove="Resources\Terrain53.att" />
<None Remove="Resources\Terrain54.att" />
<None Remove="Resources\Terrain55.att" />
<None Remove="Resources\Terrain56.att" />
<None Remove="Resources\Terrain57.att" />
<None Remove="Resources\Terrain58.att" />
<None Remove="Resources\Terrain59.att" />
<None Remove="Resources\Terrain6.att" />
<None Remove="Resources\Terrain61.att" />
<None Remove="Resources\Terrain62.att" />
<None Remove="Resources\Terrain63.att" />
<None Remove="Resources\Terrain64.att" />
<None Remove="Resources\Terrain65.att" />
<None Remove="Resources\Terrain66.att" />
<None Remove="Resources\Terrain67.att" />
<None Remove="Resources\Terrain68.att" />
<None Remove="Resources\Terrain69.att" />
<None Remove="Resources\Terrain7.att" />
<None Remove="Resources\Terrain70.att" />
<None Remove="Resources\Terrain71.att" />
<None Remove="Resources\Terrain72.att" />
<None Remove="Resources\Terrain73.att" />
<None Remove="Resources\Terrain74.att" />
<None Remove="Resources\Terrain75.att" />
<None Remove="Resources\Terrain78.att" />
<None Remove="Resources\Terrain79.att" />
<None Remove="Resources\Terrain8.att" />
<None Remove="Resources\Terrain80.att" />
<None Remove="Resources\Terrain81.att" />
<None Remove="Resources\Terrain82.att" />
<None Remove="Resources\Terrain9.att" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\075_Terrain1.att" />
<EmbeddedResource Include="Resources\075_Terrain2.att" />
<EmbeddedResource Include="Resources\075_Terrain3.att" />
<EmbeddedResource Include="Resources\075_Terrain4.att" />
<EmbeddedResource Include="Resources\075_Terrain5.att" />
<EmbeddedResource Include="Resources\075_Terrain8.att" />
<EmbeddedResource Include="Resources\Terrain1.att" />
<EmbeddedResource Include="Resources\Terrain10_4.att" />
<EmbeddedResource Include="Resources\Terrain10_3.att" />
<EmbeddedResource Include="Resources\Terrain10_2.att" />
<EmbeddedResource Include="Resources\Terrain10_1.att" />
<EmbeddedResource Include="Resources\Terrain11.att" />
<EmbeddedResource Include="Resources\Terrain12.att" />
<EmbeddedResource Include="Resources\Terrain19.att" />
<EmbeddedResource Include="Resources\Terrain2.att" />
<EmbeddedResource Include="Resources\Terrain25.att" />
<EmbeddedResource Include="Resources\Terrain3.att" />
<EmbeddedResource Include="Resources\Terrain31.att" />
<EmbeddedResource Include="Resources\Terrain32.att" />
<EmbeddedResource Include="Resources\Terrain33_7.att" />
<EmbeddedResource Include="Resources\Terrain33_6.att" />
<EmbeddedResource Include="Resources\Terrain33_5.att" />
<EmbeddedResource Include="Resources\Terrain34.att" />
<EmbeddedResource Include="Resources\Terrain34_Close.att" />
<EmbeddedResource Include="Resources\Terrain35.att" />
<EmbeddedResource Include="Resources\Terrain35_OCCUPIED.att" />
<EmbeddedResource Include="Resources\Terrain35_PEACE.att" />
<EmbeddedResource Include="Resources\Terrain35_WAR.att" />
<EmbeddedResource Include="Resources\Terrain36.att" />
<EmbeddedResource Include="Resources\Terrain37.att" />
<EmbeddedResource Include="Resources\Terrain38.att" />
<EmbeddedResource Include="Resources\Terrain39.att" />
<EmbeddedResource Include="Resources\Terrain4.att" />
<EmbeddedResource Include="Resources\Terrain40.att" />
<EmbeddedResource Include="Resources\Terrain40_CLOSE.att" />
<EmbeddedResource Include="Resources\Terrain40_OPEN.att" />
<EmbeddedResource Include="Resources\Terrain41.att" />
<EmbeddedResource Include="Resources\Terrain42.att" />
<EmbeddedResource Include="Resources\Terrain43.att" />
<EmbeddedResource Include="Resources\Terrain44.att" />
<EmbeddedResource Include="Resources\Terrain46.att" />
<EmbeddedResource Include="Resources\Terrain47.att" />
<EmbeddedResource Include="Resources\Terrain5.att" />
<EmbeddedResource Include="Resources\Terrain51.att" />
<EmbeddedResource Include="Resources\Terrain52.att" />
<EmbeddedResource Include="Resources\Terrain53.att" />
<EmbeddedResource Include="Resources\Terrain54.att" />
<EmbeddedResource Include="Resources\Terrain55.att" />
<EmbeddedResource Include="Resources\Terrain56.att" />
<EmbeddedResource Include="Resources\Terrain57.att" />
<EmbeddedResource Include="Resources\Terrain58.att" />
<EmbeddedResource Include="Resources\Terrain59.att" />
<EmbeddedResource Include="Resources\Terrain6.att" />
<EmbeddedResource Include="Resources\Terrain61.att" />
<EmbeddedResource Include="Resources\Terrain62.att" />
<EmbeddedResource Include="Resources\Terrain63.att" />
<EmbeddedResource Include="Resources\Terrain64.att" />
<EmbeddedResource Include="Resources\Terrain65.att" />
<EmbeddedResource Include="Resources\Terrain66.att" />
<EmbeddedResource Include="Resources\Terrain67.att" />
<EmbeddedResource Include="Resources\Terrain68.att" />
<EmbeddedResource Include="Resources\Terrain69.att" />
<EmbeddedResource Include="Resources\Terrain7.att" />
<EmbeddedResource Include="Resources\Terrain70.att" />
<EmbeddedResource Include="Resources\Terrain71.att" />
<EmbeddedResource Include="Resources\Terrain72.att" />
<EmbeddedResource Include="Resources\Terrain73.att" />
<EmbeddedResource Include="Resources\Terrain74.att" />
<EmbeddedResource Include="Resources\Terrain75.att" />
<EmbeddedResource Include="Resources\Terrain78.att" />
<EmbeddedResource Include="Resources\Terrain79.att" />
<EmbeddedResource Include="Resources\Terrain8.att" />
<EmbeddedResource Include="Resources\Terrain80.att" />
<EmbeddedResource Include="Resources\Terrain81.att" />
<EmbeddedResource Include="Resources\Terrain82.att" />
<EmbeddedResource Include="Resources\Terrain9.att" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\AttributeSystem\MUnique.OpenMU.AttributeSystem.csproj" />
<ProjectReference Include="..\..\DataModel\MUnique.OpenMU.DataModel.csproj" />
<ProjectReference Include="..\..\GameLogic\MUnique.OpenMU.GameLogic.csproj" />
<ProjectReference Include="..\..\GameServer\MUnique.OpenMU.GameServer.csproj" />
<ProjectReference Include="..\MUnique.OpenMU.Persistence.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\PlugInResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>PlugInResources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\PlugInResources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>PlugInResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,17 @@
// <copyright file="MapIdentity.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 MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Record to identify a map, by considering the <see cref="GameMapDefinition.Discriminator"/>.
/// </summary>
/// <param name="MapNumber">The number of the map.</param>
/// <param name="Discriminator">The dicriminator of the map.</param>
internal record MapIdentity(short MapNumber, int Discriminator = 0)
{
public static implicit operator MapIdentity(short mapNumber) => new(mapNumber);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
// <copyright file="MonsterDefinitionExtensions.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 MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Extension methods for <see cref="MonsterDefinition"/>.
/// </summary>
internal static class MonsterDefinitionExtensions
{
/// <summary>
/// Adds the attributes with their values to the <see cref="MonsterDefinition"/>.
/// </summary>
/// <param name="monsterDefinition">The monster definition.</param>
/// <param name="attributesWithValues">The attributes with values.</param>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
public static void AddAttributes(this MonsterDefinition monsterDefinition, IDictionary<AttributeDefinition, float> attributesWithValues, IContext context, GameConfiguration gameConfiguration)
{
foreach (var attributeWithValue in attributesWithValues)
{
var attribute = context.CreateNew<MonsterAttribute>();
attribute.AttributeDefinition = attributeWithValue.Key.GetPersistent(gameConfiguration);
attribute.Value = attributeWithValue.Value;
monsterDefinition.Attributes.Add(attribute);
attribute.SetGuid(monsterDefinition.Number, attribute.AttributeDefinition.Id.ExtractFirstTwoBytes());
}
}
}

View File

@@ -0,0 +1,56 @@
// <copyright file="MovementSpeedConstants.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;
/// <summary>
/// Movement speed constants used by configuration initializers and update plugins.
/// </summary>
internal static class MovementSpeedConstants
{
/// <summary>
/// Gets the minimum level to use running gear.
/// </summary>
internal const int RunningGearMinimumLevel = 5;
/// <summary>
/// Gets the movement speed of running gear.
/// </summary>
internal const float RunningGearMovementSpeed = 15f;
/// <summary>
/// Gets the default movement speed of wings.
/// </summary>
internal const float DefaultWingMovementSpeed = 15f;
/// <summary>
/// Gets the movement speed of fast wings.
/// </summary>
internal const float FastWingMovementSpeed = 16f;
/// <summary>
/// Gets the basic movement speed of mounts.
/// </summary>
internal const float BasicMountMovementSpeed = 15f;
/// <summary>
/// Gets the movement speed of horse or fenrir.
/// </summary>
internal const float HorseOrFenrirMovementSpeed = 17f;
/// <summary>
/// Gets the movement speed of upgraded fenrir.
/// </summary>
internal const float UpgradedFenrirMovementSpeed = 19f;
/// <summary>
/// Gets the movement speed factor when iced.
/// </summary>
internal const float IcedMovementSpeedFactor = 0.5f;
/// <summary>
/// Gets the movement speed factor when slowed by cold.
/// </summary>
internal const float ColdMovementSpeedFactor = 0.33f;
}

View File

@@ -0,0 +1,41 @@
// <copyright file="AddArrowsForFairyElf.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Items;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds arrows to a created fairy elf character.
/// </summary>
[Guid("71B6EB8D-E676-4B22-9E7E-15C7C3969852")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddArrowsForFairyElf_Name), Description = nameof(PlugInResources.AddArrowsForFairyElf_Description), ResourceType = typeof(PlugInResources))]
public class AddArrowsForFairyElf : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddArrowsForFairyElf" /> class.
/// </summary>
public AddArrowsForFairyElf()
: base((byte)CharacterClassNumber.FairyElf, (byte)ItemGroups.Bows, 15, 0)
{
}
/// <inheritdoc />
protected override Item? CreateItem(Player player, Character createdCharacter)
{
if (base.CreateItem(player, createdCharacter) is { } item)
{
item.Durability = 255;
return item;
}
return null;
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddChargeForRageFighter.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the Charge skill to a created rage fighter character.
/// </summary>
[Guid("53F23DDD-3676-4D24-8DFF-2EF657255832")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddChargeForRageFighter_Name), Description = nameof(PlugInResources.AddChargeForRageFighter_Description), ResourceType = typeof(PlugInResources))]
public class AddChargeForRageFighter : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddChargeForRageFighter"/> class.
/// </summary>
public AddChargeForRageFighter()
: base((byte)CharacterClassNumber.RageFighter, (ushort)SkillNumber.Charge)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddCrescentMoonSlashForDarkKnight.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the crescent moon slash skill (active in castle siege) to a created dark knight character.
/// </summary>
[Guid("2BB94D35-0DEF-4458-84AC-ECAAE6E896BE")]
[PlugIn]
[Display(Name = nameof(AddCrescentMoonSlashForDarkKnight), Description = "Adds the crescent moon slash skill (active in castle siege) to a created dark knight character.")]
public class AddCrescentMoonSlashForDarkKnight : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddCrescentMoonSlashForDarkKnight"/> class.
/// </summary>
public AddCrescentMoonSlashForDarkKnight()
: base((byte)CharacterClassNumber.DarkKnight, (ushort)SkillNumber.CrescentMoonSlash)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddEnergyBallForDarkWizard.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the energy ball skill to a created dark wizard character.
/// </summary>
[Guid("6A721FED-51CD-41AD-BA7C-EC1642FFF00A")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddEnergyBallForDarkWizard_Name), Description = nameof(PlugInResources.AddEnergyBallForDarkWizard_Description), ResourceType = typeof(PlugInResources))]
public class AddEnergyBallForDarkWizard : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddEnergyBallForDarkWizard" /> class.
/// </summary>
public AddEnergyBallForDarkWizard()
: base((byte)CharacterClassNumber.DarkWizard, (ushort)SkillNumber.EnergyBall)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddFireBlastForDarkLord.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the Fire Blast skill (active in castle siege) to a created dark lord character.
/// </summary>
[Guid("49D2D6F2-4E70-4F4B-B7CE-0645EC2F094A")]
[PlugIn]
[Display(Name = nameof(AddFireBlastForDarkLord), Description = "Adds the Fire Blast skill (active in castle siege) to a created dark lord character.")]
public class AddFireBlastForDarkLord : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddFireBlastForDarkLord"/> class.
/// </summary>
public AddFireBlastForDarkLord()
: base((byte)CharacterClassNumber.DarkLord, (ushort)SkillNumber.FireBlast)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddForceForDarkLord.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the Force skill to a created dark lord character.
/// </summary>
[Guid("D197321F-1BAC-4A82-8548-13674AF6D82C")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddForceForDarkLord_Name), Description = nameof(PlugInResources.AddForceForDarkLord_Description), ResourceType = typeof(PlugInResources))]
public class AddForceForDarkLord : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddForceForDarkLord"/> class.
/// </summary>
public AddForceForDarkLord()
: base((byte)CharacterClassNumber.DarkLord, (ushort)SkillNumber.Force)
{
}
}

View File

@@ -0,0 +1,88 @@
// <copyright file="AddInitialItemPlugInBase.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.PlugIns.CharacterCreated;
using Microsoft.Extensions.Logging;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.GameLogic.PlugIns;
/// <summary>
/// Base class for a <see cref="ICharacterCreatedPlugIn"/> which adds an item to the inventory
/// of the created character.
/// </summary>
public class AddInitialItemPlugInBase : ICharacterCreatedPlugIn
{
private readonly byte? _characterClassNumber;
private readonly byte _itemGroup;
private readonly byte _itemNumber;
private readonly byte _itemSlot;
private readonly byte _itemLevel;
/// <summary>
/// Initializes a new instance of the <see cref="AddInitialItemPlugInBase" /> class.
/// </summary>
/// <param name="characterClassNumber">The character class number.</param>
/// <param name="itemGroup">The item group.</param>
/// <param name="itemNumber">The item number.</param>
/// <param name="itemSlot">The item slot.</param>
/// <param name="itemLevel">The item level.</param>
protected AddInitialItemPlugInBase(byte? characterClassNumber, byte itemGroup, byte itemNumber, byte itemSlot, byte itemLevel = 0)
{
this._characterClassNumber = characterClassNumber;
this._itemGroup = itemGroup;
this._itemNumber = itemNumber;
this._itemSlot = itemSlot;
this._itemLevel = itemLevel;
}
/// <inheritdoc/>
public void CharacterCreated(Player player, Character createdCharacter)
{
using var logScope = player.Logger.BeginScope(this.GetType());
if (this._characterClassNumber.HasValue && this._characterClassNumber != createdCharacter.CharacterClass?.Number)
{
player.Logger.LogDebug("Wrong character class {0}, expected {1}", createdCharacter.CharacterClass?.Number, this._characterClassNumber);
return;
}
if (createdCharacter.Inventory!.Items.FirstOrDefault(i => i.ItemSlot == this._itemSlot) is { } existingItem)
{
player.Logger.LogError("Item slot {0} already contains an item ({1}).", this._itemSlot, existingItem);
return;
}
if (this.CreateItem(player, createdCharacter) is { } item)
{
createdCharacter.Inventory!.Items.Add(item);
}
}
/// <summary>
/// Creates the item.
/// Can be overwritten to modify the default.
/// </summary>
/// <param name="player">The player.</param>
/// <param name="createdCharacter">The created character.</param>
/// <returns>The created item.</returns>
protected virtual Item? CreateItem(Player player, Character createdCharacter)
{
if (player.GameContext.Configuration.Items
.FirstOrDefault(def => def.Group == this._itemGroup && def.Number == this._itemNumber)
is { } itemDefinition)
{
var item = player.PersistenceContext.CreateNew<Item>();
item.Definition = itemDefinition;
item.Durability = item.Definition.Durability;
item.ItemSlot = this._itemSlot;
item.Level = this._itemLevel;
return item;
}
player.Logger.LogWarning($"Unknown item, group {this._itemGroup}, number {this._itemNumber}.");
return null;
}
}

View File

@@ -0,0 +1,59 @@
// <copyright file="AddInitialSkillPlugInBase.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.PlugIns.CharacterCreated;
using Microsoft.Extensions.Logging;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.GameLogic.PlugIns;
/// <summary>
/// Base class for a <see cref="ICharacterCreatedPlugIn"/> which adds an initial skill for a specific character class.
/// </summary>
public class AddInitialSkillPlugInBase : ICharacterCreatedPlugIn
{
private readonly byte _characterClassNumber;
private readonly ushort _skillNumber;
/// <summary>
/// Initializes a new instance of the <see cref="AddInitialSkillPlugInBase"/> class.
/// </summary>
/// <param name="characterClassNumber">The character class number.</param>
/// <param name="skillNumber">The skill number.</param>
protected AddInitialSkillPlugInBase(byte characterClassNumber, ushort skillNumber)
{
this._characterClassNumber = characterClassNumber;
this._skillNumber = skillNumber;
}
/// <inheritdoc />
public void CharacterCreated(Player player, Character createdCharacter)
{
using var logScope = player.Logger.BeginScope(this.GetType());
if (this._characterClassNumber != createdCharacter.CharacterClass?.Number)
{
player.Logger.LogDebug("Wrong character class {0}, expected {1}", createdCharacter.CharacterClass?.Number, this._characterClassNumber);
return;
}
var skillDefinition =
player.GameContext.Configuration.Skills.FirstOrDefault(s => s.Number == this._skillNumber);
if (skillDefinition is null)
{
player.Logger.LogError($"Skill not found: {this._skillNumber}");
return;
}
if (!skillDefinition.QualifiedCharacters.Contains(createdCharacter.CharacterClass))
{
player.Logger.LogError($"Skill {skillDefinition.Name} is not available for character class {createdCharacter.CharacterClass.Name}.");
return;
}
var skillEntry = player.PersistenceContext.CreateNew<SkillEntry>();
skillEntry.Skill = skillDefinition;
createdCharacter.LearnedSkills.Add(skillEntry);
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddLanceForSummoner.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the Lance skill to a created summoner character.
/// </summary>
[Guid("AC326833-D60E-4705-A7DF-740FA37ACBA8")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddLanceForSummoner_Name), Description = nameof(PlugInResources.AddLanceForSummoner_Description), ResourceType = typeof(PlugInResources))]
public class AddLanceForSummoner : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddLanceForSummoner"/> class.
/// </summary>
public AddLanceForSummoner()
: base((byte)CharacterClassNumber.Summoner, (ushort)SkillNumber.Lance)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddManaRaysForMagicGladiator.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the Mana Rays skill (active in castle siege) to a created magic gladiator character.
/// </summary>
[Guid("D83347DB-4D33-47E9-B898-5EDB2777B8A1")]
[PlugIn]
[Display(Name = nameof(AddManaRaysForMagicGladiator), Description = "Adds the Mana Rays skill (active in castle siege) to a created magic gladiator character.")]
public class AddManaRaysForMagicGladiator : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddManaRaysForMagicGladiator"/> class.
/// </summary>AddManaRaysForMagicGladiator.cs
public AddManaRaysForMagicGladiator()
: base((byte)CharacterClassNumber.MagicGladiator, (ushort)SkillNumber.ManaRays)
{
}
}

View File

@@ -0,0 +1,26 @@
// <copyright file="AddRingOfWarriorLevel40ForNewCharacters.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.DataModel;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the ring, which can be dropped at level 40.
/// </summary>
[Guid("19B0BB9D-32C9-4DBC-BD95-41801D078962")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddRingOfWarriorLevel40ForNewCharacters_Name), Description = nameof(PlugInResources.AddRingOfWarriorLevel40ForNewCharacters_Description), ResourceType = typeof(PlugInResources))]
public class AddRingOfWarriorLevel40ForNewCharacters : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddRingOfWarriorLevel40ForNewCharacters" /> class.
/// </summary>
public AddRingOfWarriorLevel40ForNewCharacters()
: base(null, 13, 20, (byte)(InventoryConstants.LastEquippableItemSlotIndex + 1), 1)
{
}
}

View File

@@ -0,0 +1,26 @@
// <copyright file="AddRingOfWarriorLevel80ForNewCharacters.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.DataModel;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the ring, which can be dropped at level 80.
/// </summary>
[Guid("BF9A7B67-9404-4E86-B1A4-77B96C19F55C")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddRingOfWarriorLevel80ForNewCharacters_Name), Description = nameof(PlugInResources.AddRingOfWarriorLevel80ForNewCharacters_Description), ResourceType = typeof(PlugInResources))]
public class AddRingOfWarriorLevel80ForNewCharacters : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddRingOfWarriorLevel80ForNewCharacters" /> class.
/// </summary>
public AddRingOfWarriorLevel80ForNewCharacters()
: base(null, 13, 20, (byte)(InventoryConstants.LastEquippableItemSlotIndex + 2), 2)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddShortBowForFairyElf.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Items;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds a short bow to a created fairy elf character.
/// </summary>
[Guid("9EF17296-0436-4059-BC4E-0A71967F36EC")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddShortBowForFairyElf_Name), Description = nameof(PlugInResources.AddShortBowForFairyElf_Description), ResourceType = typeof(PlugInResources))]
public class AddShortBowForFairyElf : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddShortBowForFairyElf" /> class.
/// </summary>
public AddShortBowForFairyElf()
: base((byte)CharacterClassNumber.FairyElf, (byte)ItemGroups.Bows, 0, 1)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddShortSwordForDarkLord.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Items;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds a short sword to a created dark lord character.
/// </summary>
[Guid("BAC120D0-D981-4EBB-8F5A-0EC19434AF16")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddShortSwordForDarkLord_Name), Description = nameof(PlugInResources.AddShortSwordForDarkLord_Description), ResourceType = typeof(PlugInResources))]
public class AddShortSwordForDarkLord : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddShortSwordForDarkLord" /> class.
/// </summary>
public AddShortSwordForDarkLord()
: base((byte)CharacterClassNumber.DarkLord, (byte)ItemGroups.Swords, 1, 0)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddShortSwordForMagicGladiator.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Items;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds a short sword to a created magic gladiator character.
/// </summary>
[Guid("3D2790E3-B757-46FD-8618-2441B7E9E2B3")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddShortSwordForMagicGladiator_Name), Description = nameof(PlugInResources.AddShortSwordForMagicGladiator_Description), ResourceType = typeof(PlugInResources))]
public class AddShortSwordForMagicGladiator : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddShortSwordForMagicGladiator" /> class.
/// </summary>
public AddShortSwordForMagicGladiator()
: base((byte)CharacterClassNumber.MagicGladiator, (byte)ItemGroups.Swords, 1, 0)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddSmallAxeForDarkKnight.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Items;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds a small axe to a created dark knight character.
/// </summary>
[Guid("2377C222-4418-4F17-8388-1F8825E6243C")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddSmallAxeForDarkKnight_Name), Description = nameof(PlugInResources.AddSmallAxeForDarkKnight_Description), ResourceType = typeof(PlugInResources))]
public class AddSmallAxeForDarkKnight : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddSmallAxeForDarkKnight" /> class.
/// </summary>
public AddSmallAxeForDarkKnight()
: base((byte)CharacterClassNumber.DarkKnight, (byte)ItemGroups.Axes, 0, 0)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddSmallShieldForDarkLord.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Items;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds a small shield to a created dark lord character.
/// </summary>
[Guid("CD60BD4A-2BD5-4E36-95D0-EDB6B94CDDD8")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddSmallShieldForDarkLord_Name), Description = nameof(PlugInResources.AddSmallShieldForDarkLord_Description), ResourceType = typeof(PlugInResources))]
public class AddSmallShieldForDarkLord : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddSmallShieldForDarkLord" /> class.
/// </summary>
public AddSmallShieldForDarkLord()
: base((byte)CharacterClassNumber.DarkLord, (byte)ItemGroups.Shields, 0, 1)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddSmallShieldForMagicGladiator.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Items;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds a small shield to a created magic gladiator character.
/// </summary>
[Guid("74FC7D85-7AA0-4437-88BA-CE008FD31745")]
[PlugIn]
[Display(Name = nameof(PlugInResources.AddSmallShieldForMagicGladiator_Name), Description = nameof(PlugInResources.AddSmallShieldForMagicGladiator_Description), ResourceType = typeof(PlugInResources))]
public class AddSmallShieldForMagicGladiator : AddInitialItemPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddSmallShieldForMagicGladiator" /> class.
/// </summary>
public AddSmallShieldForMagicGladiator()
: base((byte)CharacterClassNumber.MagicGladiator, (byte)ItemGroups.Shields, 0, 1)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddSpiralSlashForMagicGladiator.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the Spiral Slash skill (active in castle siege) to a created magic gladiator character.
/// </summary>
[Guid("83B0D163-7E97-40FC-851A-D5500B4BB33E")]
[PlugIn]
[Display(Name = nameof(AddSpiralSlashForMagicGladiator), Description = "Adds the Spiral Slash skill (active in castle siege) to a created magic gladiator character.")]
public class AddSpiralSlashForMagicGladiator : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddSpiralSlashForMagicGladiator"/> class.
/// </summary>
public AddSpiralSlashForMagicGladiator()
: base((byte)CharacterClassNumber.MagicGladiator, (ushort)SkillNumber.SpiralSlash)
{
}
}

View File

@@ -0,0 +1,27 @@
// <copyright file="AddStarfallForFairyElf.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.PlugIns.CharacterCreated;
using System.Runtime.InteropServices;
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Adds the Starfall skill (active in castle siege) to a created fairy elf character.
/// </summary>
[Guid("5AC4F8C8-8B4C-4DD6-B1CF-D4F6491DC17A")]
[PlugIn]
[Display(Name = nameof(AddStarfallForFairyElf), Description = "Adds the Starfall skill (active in castle siege) to a created fairy elf character.")]
public class AddStarfallForFairyElf : AddInitialSkillPlugInBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AddStarfallForFairyElf"/> class.
/// </summary>
public AddStarfallForFairyElf()
: base((byte)CharacterClassNumber.FairyElf, (ushort)SkillNumber.Starfall)
{
}
}

View File

@@ -0,0 +1,10 @@
// <copyright file="AssemblyInfo.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
using System.Reflection;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MUnique.OpenMU.Persistence.Initialization")]

View File

@@ -0,0 +1,351 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MUnique.OpenMU.Persistence.Initialization.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class PlugInResources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal PlugInResources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MUnique.OpenMU.Persistence.Initialization.Properties.PlugInResources", typeof(PlugInResources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Adds arrows to a created fairy elf character..
/// </summary>
public static string AddArrowsForFairyElf_Description {
get {
return ResourceManager.GetString("AddArrowsForFairyElf_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddArrowsForFairyElf.
/// </summary>
public static string AddArrowsForFairyElf_Name {
get {
return ResourceManager.GetString("AddArrowsForFairyElf_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds the Charge skill to a created rage fighter character..
/// </summary>
public static string AddChargeForRageFighter_Description {
get {
return ResourceManager.GetString("AddChargeForRageFighter_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddChargeForRageFighter.
/// </summary>
public static string AddChargeForRageFighter_Name {
get {
return ResourceManager.GetString("AddChargeForRageFighter_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds the energy ball skill to a created dark wizard character..
/// </summary>
public static string AddEnergyBallForDarkWizard_Description {
get {
return ResourceManager.GetString("AddEnergyBallForDarkWizard_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddEnergyBallForDarkWizard.
/// </summary>
public static string AddEnergyBallForDarkWizard_Name {
get {
return ResourceManager.GetString("AddEnergyBallForDarkWizard_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds the Force skill to a created dark lord character..
/// </summary>
public static string AddForceForDarkLord_Description {
get {
return ResourceManager.GetString("AddForceForDarkLord_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddForceForDarkLord.
/// </summary>
public static string AddForceForDarkLord_Name {
get {
return ResourceManager.GetString("AddForceForDarkLord_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds the Lance skill to a created summoner character..
/// </summary>
public static string AddLanceForSummoner_Description {
get {
return ResourceManager.GetString("AddLanceForSummoner_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddLanceForSummoner.
/// </summary>
public static string AddLanceForSummoner_Name {
get {
return ResourceManager.GetString("AddLanceForSummoner_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds the ring, which can be dropped at level 40..
/// </summary>
public static string AddRingOfWarriorLevel40ForNewCharacters_Description {
get {
return ResourceManager.GetString("AddRingOfWarriorLevel40ForNewCharacters_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddRingOfWarriorLevel40ForNewCharacters.
/// </summary>
public static string AddRingOfWarriorLevel40ForNewCharacters_Name {
get {
return ResourceManager.GetString("AddRingOfWarriorLevel40ForNewCharacters_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds the ring, which can be dropped at level 80..
/// </summary>
public static string AddRingOfWarriorLevel80ForNewCharacters_Description {
get {
return ResourceManager.GetString("AddRingOfWarriorLevel80ForNewCharacters_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddRingOfWarriorLevel80ForNewCharacters.
/// </summary>
public static string AddRingOfWarriorLevel80ForNewCharacters_Name {
get {
return ResourceManager.GetString("AddRingOfWarriorLevel80ForNewCharacters_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds a short bow to a created fairy elf character..
/// </summary>
public static string AddShortBowForFairyElf_Description {
get {
return ResourceManager.GetString("AddShortBowForFairyElf_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddShortBowForFairyElf.
/// </summary>
public static string AddShortBowForFairyElf_Name {
get {
return ResourceManager.GetString("AddShortBowForFairyElf_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds a short sword to a created dark lord character..
/// </summary>
public static string AddShortSwordForDarkLord_Description {
get {
return ResourceManager.GetString("AddShortSwordForDarkLord_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddShortSwordForDarkLord.
/// </summary>
public static string AddShortSwordForDarkLord_Name {
get {
return ResourceManager.GetString("AddShortSwordForDarkLord_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds a short sword to a created magic gladiator character..
/// </summary>
public static string AddShortSwordForMagicGladiator_Description {
get {
return ResourceManager.GetString("AddShortSwordForMagicGladiator_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddShortSwordForMagicGladiator.
/// </summary>
public static string AddShortSwordForMagicGladiator_Name {
get {
return ResourceManager.GetString("AddShortSwordForMagicGladiator_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds a small axe to a created dark knight character..
/// </summary>
public static string AddSmallAxeForDarkKnight_Description {
get {
return ResourceManager.GetString("AddSmallAxeForDarkKnight_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddSmallAxeForDarkKnight.
/// </summary>
public static string AddSmallAxeForDarkKnight_Name {
get {
return ResourceManager.GetString("AddSmallAxeForDarkKnight_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds a small shield to a created dark lord character..
/// </summary>
public static string AddSmallShieldForDarkLord_Description {
get {
return ResourceManager.GetString("AddSmallShieldForDarkLord_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddSmallShieldForDarkLord.
/// </summary>
public static string AddSmallShieldForDarkLord_Name {
get {
return ResourceManager.GetString("AddSmallShieldForDarkLord_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds a small shield to a created magic gladiator character..
/// </summary>
public static string AddSmallShieldForMagicGladiator_Description {
get {
return ResourceManager.GetString("AddSmallShieldForMagicGladiator_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to AddSmallShieldForMagicGladiator.
/// </summary>
public static string AddSmallShieldForMagicGladiator_Name {
get {
return ResourceManager.GetString("AddSmallShieldForMagicGladiator_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Provides initial data for Season 6 Episode 3..
/// </summary>
public static string DataInitialization_Description {
get {
return ResourceManager.GetString("DataInitialization_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Season 6 Episode 3 Initialization.
/// </summary>
public static string DataInitialization_Name {
get {
return ResourceManager.GetString("DataInitialization_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Provides initial data for Version 0.75.
/// </summary>
public static string DataInitialization075_Description {
get {
return ResourceManager.GetString("DataInitialization075_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Version 0.75 Initialization.
/// </summary>
public static string DataInitialization075_Name {
get {
return ResourceManager.GetString("DataInitialization075_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Provides initial data for Version 0.95d.
/// </summary>
public static string DataInitialization095d_Description {
get {
return ResourceManager.GetString("DataInitialization095d_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Version 0.95d Initialization.
/// </summary>
public static string DataInitialization095d_Name {
get {
return ResourceManager.GetString("DataInitialization095d_Name", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,197 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DataInitialization075_Name" xml:space="preserve">
<value>Version 0.75 Initialization</value>
</data>
<data name="DataInitialization075_Description" xml:space="preserve">
<value>Provides initial data for Version 0.75</value>
</data>
<data name="DataInitialization095d_Name" xml:space="preserve">
<value>Version 0.95d Initialization</value>
</data>
<data name="DataInitialization095d_Description" xml:space="preserve">
<value>Provides initial data for Version 0.95d</value>
</data>
<data name="DataInitialization_Name" xml:space="preserve">
<value>Season 6 Episode 3 Initialization</value>
</data>
<data name="DataInitialization_Description" xml:space="preserve">
<value>Provides initial data for Season 6 Episode 3.</value>
</data>
<data name="AddArrowsForFairyElf_Name" xml:space="preserve">
<value>AddArrowsForFairyElf</value>
</data>
<data name="AddArrowsForFairyElf_Description" xml:space="preserve">
<value>Adds arrows to a created fairy elf character.</value>
</data>
<data name="AddChargeForRageFighter_Name" xml:space="preserve">
<value>AddChargeForRageFighter</value>
</data>
<data name="AddChargeForRageFighter_Description" xml:space="preserve">
<value>Adds the Charge skill to a created rage fighter character.</value>
</data>
<data name="AddEnergyBallForDarkWizard_Name" xml:space="preserve">
<value>AddEnergyBallForDarkWizard</value>
</data>
<data name="AddEnergyBallForDarkWizard_Description" xml:space="preserve">
<value>Adds the energy ball skill to a created dark wizard character.</value>
</data>
<data name="AddForceForDarkLord_Name" xml:space="preserve">
<value>AddForceForDarkLord</value>
</data>
<data name="AddForceForDarkLord_Description" xml:space="preserve">
<value>Adds the Force skill to a created dark lord character.</value>
</data>
<data name="AddLanceForSummoner_Name" xml:space="preserve">
<value>AddLanceForSummoner</value>
</data>
<data name="AddLanceForSummoner_Description" xml:space="preserve">
<value>Adds the Lance skill to a created summoner character.</value>
</data>
<data name="AddRingOfWarriorLevel40ForNewCharacters_Name" xml:space="preserve">
<value>AddRingOfWarriorLevel40ForNewCharacters</value>
</data>
<data name="AddRingOfWarriorLevel40ForNewCharacters_Description" xml:space="preserve">
<value>Adds the ring, which can be dropped at level 40.</value>
</data>
<data name="AddRingOfWarriorLevel80ForNewCharacters_Name" xml:space="preserve">
<value>AddRingOfWarriorLevel80ForNewCharacters</value>
</data>
<data name="AddRingOfWarriorLevel80ForNewCharacters_Description" xml:space="preserve">
<value>Adds the ring, which can be dropped at level 80.</value>
</data>
<data name="AddShortBowForFairyElf_Name" xml:space="preserve">
<value>AddShortBowForFairyElf</value>
</data>
<data name="AddShortBowForFairyElf_Description" xml:space="preserve">
<value>Adds a short bow to a created fairy elf character.</value>
</data>
<data name="AddShortSwordForDarkLord_Name" xml:space="preserve">
<value>AddShortSwordForDarkLord</value>
</data>
<data name="AddShortSwordForDarkLord_Description" xml:space="preserve">
<value>Adds a short sword to a created dark lord character.</value>
</data>
<data name="AddShortSwordForMagicGladiator_Name" xml:space="preserve">
<value>AddShortSwordForMagicGladiator</value>
</data>
<data name="AddShortSwordForMagicGladiator_Description" xml:space="preserve">
<value>Adds a short sword to a created magic gladiator character.</value>
</data>
<data name="AddSmallAxeForDarkKnight_Name" xml:space="preserve">
<value>AddSmallAxeForDarkKnight</value>
</data>
<data name="AddSmallAxeForDarkKnight_Description" xml:space="preserve">
<value>Adds a small axe to a created dark knight character.</value>
</data>
<data name="AddSmallShieldForDarkLord_Name" xml:space="preserve">
<value>AddSmallShieldForDarkLord</value>
</data>
<data name="AddSmallShieldForDarkLord_Description" xml:space="preserve">
<value>Adds a small shield to a created dark lord character.</value>
</data>
<data name="AddSmallShieldForMagicGladiator_Name" xml:space="preserve">
<value>AddSmallShieldForMagicGladiator</value>
</data>
<data name="AddSmallShieldForMagicGladiator_Description" xml:space="preserve">
<value>Adds a small shield to a created magic gladiator character.</value>
</data>
</root>

View File

@@ -0,0 +1,185 @@
// <copyright file="QuestDefinitionExtensions.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 MUnique.OpenMU.DataModel;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.GameLogic;
/// <summary>
/// Extension methods to make quest creation easier.
/// </summary>
internal static class QuestDefinitionExtensions
{
/// <summary>
/// Adds a <see cref="QuestMonsterKillRequirement"/> to this quest definition.
/// </summary>
/// <param name="questDefinition">The quest definition.</param>
/// <param name="amount">The amount.</param>
/// <param name="monsterNumber">The monster number.</param>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
/// <returns>This quest definition.</returns>
public static QuestDefinition WithMonsterKillRequirement(this QuestDefinition questDefinition, int amount, short monsterNumber, IContext context, GameConfiguration gameConfiguration)
{
var killRequirement = context.CreateNew<QuestMonsterKillRequirement>();
var parentNumber = (ushort)((ushort)questDefinition.Number | (questDefinition.Group << 10));
killRequirement.SetGuid(parentNumber.ToSigned(), monsterNumber, questDefinition.QualifiedCharacter?.Number ?? 0);
killRequirement.MinimumNumber = amount;
killRequirement.Monster = gameConfiguration.Monsters.First(m => m.Number == monsterNumber);
questDefinition.RequiredMonsterKills.Add(killRequirement);
return questDefinition;
}
/// <summary>
/// Adds a <see cref="QuestReward"/> of <see cref="QuestRewardType.Money"/> to this quest definition.
/// </summary>
/// <param name="questDefinition">The quest definition.</param>
/// <param name="moneyAmount">The money amount.</param>
/// <param name="context">The context.</param>
/// <returns>This quest definition.</returns>
public static QuestDefinition WithMoneyReward(this QuestDefinition questDefinition, int moneyAmount, IContext context)
{
var reward = context.CreateNew<QuestReward>();
reward.Value = moneyAmount;
reward.RewardType = QuestRewardType.Money;
questDefinition.Rewards.Add(reward);
return questDefinition;
}
/// <summary>
/// Adds a <see cref="QuestReward"/> of <see cref="QuestRewardType.Experience"/> to this quest definition.
/// </summary>
/// <param name="questDefinition">The quest definition.</param>
/// <param name="experienceAmount">The experience amount.</param>
/// <param name="context">The context.</param>
/// <returns>This quest definition.</returns>
public static QuestDefinition WithExperienceReward(this QuestDefinition questDefinition, int experienceAmount, IContext context)
{
var reward = context.CreateNew<QuestReward>();
reward.Value = experienceAmount;
reward.RewardType = QuestRewardType.Experience;
questDefinition.Rewards.Add(reward);
return questDefinition;
}
/// <summary>
/// Adds a <see cref="QuestReward"/> of <see cref="QuestRewardType.Item"/> to this quest definition.
/// </summary>
/// <param name="questDefinition">The quest definition.</param>
/// <param name="itemGroup">The item group.</param>
/// <param name="itemNumber">The item number.</param>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
/// <param name="itemLevel">The item level.</param>
/// <param name="itemOption">The item option.</param>
/// <param name="hasLuck">if set to <c>true</c> [has luck].</param>
/// <param name="hasSkill">if set to <c>true</c> [has skill].</param>
/// <returns>This quest definition.</returns>
public static QuestDefinition WithItemReward(
this QuestDefinition questDefinition,
int itemGroup,
int itemNumber,
IContext context,
GameConfiguration gameConfiguration,
byte itemLevel = 0,
int itemOption = 0,
bool hasLuck = false,
bool hasSkill = false)
{
var reward = context.CreateNew<QuestReward>();
reward.RewardType = QuestRewardType.Item;
var item = context.CreateNew<Item>();
item.Definition = gameConfiguration.Items.First(def => def.Group == itemGroup && def.Number == itemNumber);
item.HasSkill = item.Definition.Skill != null && hasSkill;
item.Durability = item.IsWearable() ? item.GetMaximumDurabilityOfOnePiece() : item.Durability;
item.Level = itemLevel;
if (hasLuck
&& item.Definition.PossibleItemOptions.SelectMany(o => o.PossibleOptions)
.FirstOrDefault(o => o.OptionType == ItemOptionTypes.Luck) is { } luckOption)
{
var optionLink = context.CreateNew<ItemOptionLink>();
optionLink.ItemOption = luckOption;
item.ItemOptions.Add(optionLink);
}
if (itemOption > 0
&& item.Definition.PossibleItemOptions.SelectMany(o => o.PossibleOptions)
.FirstOrDefault(o => o.OptionType == ItemOptionTypes.Option) is { } optionOption)
{
var optionLink = context.CreateNew<ItemOptionLink>();
optionLink.ItemOption = optionOption;
optionLink.Level = itemOption / 4;
item.ItemOptions.Add(optionLink);
}
reward.ItemReward = item;
questDefinition.Rewards.Add(reward);
return questDefinition;
}
/// <summary>
/// Adds a <see cref="QuestItemRequirement"/> to this quest definition.
/// </summary>
/// <param name="questDefinition">The quest definition.</param>
/// <param name="itemGroup">The item group.</param>
/// <param name="itemNumber">The item number.</param>
/// <param name="context">The context.</param>
/// <param name="gameConfiguration">The game configuration.</param>
/// <param name="durability">The durability.</param>
/// <param name="itemLevel">The item level.</param>
/// <param name="itemOption">The item option (4 ~ 12).</param>
/// <param name="hasLuck">If set to <c>true</c>, the item must have luck.</param>
/// <param name="hasSkill">If set to <c>true</c>, the item must have skill.</param>
/// <param name="itemCount">The item count, default 1.</param>
/// <returns>This quest definition.</returns>
public static QuestDefinition WithItemRequirement(
this QuestDefinition questDefinition,
int itemGroup,
int itemNumber,
IContext context,
GameConfiguration gameConfiguration,
byte durability = 0,
byte itemLevel = 0,
int itemOption = 0,
bool hasLuck = false,
bool hasSkill = false,
byte itemCount = 1)
{
var requirement = context.CreateNew<QuestItemRequirement>();
requirement.MinimumNumber = itemCount;
var item = context.CreateNew<Item>();
item.Definition = gameConfiguration.Items.First(def => def.Group == itemGroup && def.Number == itemNumber);
item.HasSkill = item.Definition.Skill != null && hasSkill;
item.Durability = durability;
item.Level = itemLevel;
if (hasLuck
&& item.Definition.PossibleItemOptions.SelectMany(o => o.PossibleOptions)
.FirstOrDefault(o => o.OptionType == ItemOptionTypes.Luck) is { } luckOption)
{
var optionLink = context.CreateNew<ItemOptionLink>();
optionLink.ItemOption = luckOption;
item.ItemOptions.Add(optionLink);
}
if (itemOption > 0
&& item.Definition.PossibleItemOptions.SelectMany(o => o.PossibleOptions)
.FirstOrDefault(o => o.OptionType == ItemOptionTypes.Option) is { } optionOption)
{
var optionLink = context.CreateNew<ItemOptionLink>();
optionLink.ItemOption = optionOption;
optionLink.Level = itemOption / 4;
item.ItemOptions.Add(optionLink);
}
requirement.Item = item.Definition; // TODO?
questDefinition.RequiredItems.Add(requirement);
return questDefinition;
}
}

View File

@@ -0,0 +1,14 @@
# Intialization
This sub-project is about initializing the default data so the server is ready
to start. Currently, it also creates 10 accounts for testing.
The state of this sub-project is probably pretty messy and very incomplete, so
please don't wonder.
The initialization code is called by the a migration of entity framework core.
However, it's independent from it, so if we ever want to switch it's reuseable.
It's tested by an integration test in the corresponding Test-project. The test
runs the initialization on a real database instance and tries to load the game
configuration and one of the created accounts.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More