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,51 @@
// <copyright file="AssignableExtensions.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel;
using MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Extensions for <see cref="IAssignable{T}"/>.
/// </summary>
public static class AssignableExtensions
{
/// <summary>
/// Assigns a collection to another one, resolving the objects to the ones
/// which are contained in the given <see cref="GameConfiguration"/>.
/// </summary>
/// <typeparam name="T">The type of the collection elements.</typeparam>
/// <param name="collection">The target collection.</param>
/// <param name="other">The other collection.</param>
/// <param name="gameConfiguration">The game configuration.</param>
public static void AssignCollection<T>(this ICollection<T> collection, ICollection<T> other, GameConfiguration gameConfiguration)
where T : class
{
var itemsToRemove = collection.Except(other).ToList();
var itemsToAdd = other.Except(collection).ToList();
itemsToRemove.ForEach(i => collection.Remove(i));
itemsToAdd.ForEach(i => collection.Add(
gameConfiguration.GetObjectOfConfig(i)
?? (i as ICloneable<T>)?.Clone(gameConfiguration)
?? (i as ICloneable)?.Clone() as T
?? i));
}
/// <summary>
/// Assigns a collection to another one.
/// </summary>
/// <typeparam name="T">The type of the collection values.</typeparam>
/// <param name="collection">The target collection.</param>
/// <param name="other">The other collection.</param>
public static void AssignCollection<T>(this ICollection<T> collection, ICollection<T> other)
where T : struct
{
var itemsToRemove = collection.Except(other).ToList();
var itemsToAdd = other.Except(collection).ToList();
itemsToRemove.ForEach(i => collection.Remove(i));
itemsToAdd.ForEach(collection.Add);
}
}

View File

@@ -0,0 +1,56 @@
// <copyright file="PowerUpDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Attributes;
using System.Globalization;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// The power up definition which describes the boost of an target attribute.
/// </summary>
[Cloneable]
public partial class PowerUpDefinition
{
/// <summary>
/// Gets or sets the target attribute.
/// </summary>
public virtual AttributeDefinition? TargetAttribute { get; set; }
/// <summary>
/// Gets or sets the boost.
/// </summary>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? Boost { get; set; }
/// <inheritdoc/>
public override string ToString()
{
string value;
if (this.Boost?.ConstantValue?.Value > 0)
{
value = this.Boost.ConstantValue.Value.ToString(CultureInfo.InvariantCulture);
}
else if (this.Boost?.RelatedValues != null && this.Boost.RelatedValues.Any())
{
var relation = this.Boost.RelatedValues.First();
if (relation.InputOperator == InputOperator.ExponentiateByAttribute)
{
value = relation.InputOperand + relation.InputOperator.AsString() + new LocalizedString(relation.InputAttribute?.Designation).ToString();
}
else
{
value = new LocalizedString(relation.InputAttribute?.Designation).ToString() + relation.InputOperator.AsString() + relation.InputOperand;
}
}
else
{
// no value defined, so we assume "0"
value = "0";
}
return value + " " + new LocalizedString(this.TargetAttribute?.Designation).ToString();
}
}

View File

@@ -0,0 +1,40 @@
// <copyright file="PowerUpDefinitionValue.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Attributes;
using System.ComponentModel;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// The power up definition value which can consist of a constant value and several related values which are all added together to get the result.
/// </summary>
[Cloneable]
public partial class PowerUpDefinitionValue
{
/// <summary>
/// Gets or sets the constant value part of the value.
/// </summary>
[MemberOfAggregate]
[Browsable(false)]
public SimpleElement ConstantValue { get; protected set; } = null!;
/// <summary>
/// Gets or sets the related values.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<AttributeRelationship> RelatedValues { get; protected set; } = null!;
/// <summary>
/// Gets or sets the maximum allowable value.
/// </summary>
public float? MaximumValue { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.ConstantValue?.Value ?? 0} + {string.Join(" + ", this.RelatedValues.Select(v => $"({v})"))}";
}
}

View File

@@ -0,0 +1,17 @@
// <copyright file="AggregateRootAttribute.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Composition;
/// <summary>
/// Marks a class as an aggregate root.
/// An instance of an aggregate root is an object which can stand on its own
/// and consists of several other objects which are marked with the <see cref="MemberOfAggregateAttribute"/>.
/// Example: A car (aggregate root) which consists of several other parts, e.g. engine, wheels, doors etc.
/// Properties which are not part of the aggregate will not be marked with the <see cref="MemberOfAggregateAttribute"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class AggregateRootAttribute : Attribute
{
}

View File

@@ -0,0 +1,13 @@
// <copyright file="HiddenAtCreationAttribute.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Composition;
/// <summary>
/// Marks a property to be hidden on the UI when the object of the declaring type gets created.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class HiddenAtCreationAttribute : Attribute
{
}

View File

@@ -0,0 +1,14 @@
// <copyright file="MemberOfAggregateAttribute.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Composition;
/// <summary>
/// Marks a property as a member of an aggregate.
/// The declaring type owns the objects of the marked property.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class MemberOfAggregateAttribute : Attribute
{
}

View File

@@ -0,0 +1,14 @@
// <copyright file="TransientAttribute.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Composition;
/// <summary>
/// Marks a property as a transient property. That means, that it's not going to get persisted
/// and just holds some information at run-time.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class TransientAttribute : Attribute
{
}

View File

@@ -0,0 +1,106 @@
// <copyright file="AreaSkillSettings.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Settings for area skills.
/// </summary>
[Cloneable]
public partial class AreaSkillSettings
{
/// <summary>
/// Gets or sets a value indicating whether to use a frustum to filter potential targets.
/// </summary>
public bool UseFrustumFilter { get; set; }
/// <summary>
/// Gets or sets the width of the frustum at the start.
/// </summary>
public float FrustumStartWidth { get; set; }
/// <summary>
/// Gets or sets the width of the frustum at the end.
/// </summary>
public float FrustumEndWidth { get; set; }
/// <summary>
/// Gets or sets the distance.
/// </summary>
public float FrustumDistance { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to consider the target area coordinate to filter potential targets.
/// </summary>
public bool UseTargetAreaFilter { get; set; }
/// <summary>
/// Gets or sets the target area diameter.
/// </summary>
public float TargetAreaDiameter { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to use deferred hits,
/// for skills which take a while to visually arrive at the target.
/// </summary>
public bool UseDeferredHits { get; set; }
/// <summary>
/// Gets or sets the delay per one distance, when <see cref="UseDeferredHits"/> is active.
/// </summary>
public TimeSpan DelayPerOneDistance { get; set; }
/// <summary>
/// Gets or sets the delay between hits.
/// </summary>
public TimeSpan DelayBetweenHits { get; set; }
/// <summary>
/// Gets or sets the minimum number of hits per target.
/// </summary>
public int MinimumNumberOfHitsPerTarget { get; set; }
/// <summary>
/// Gets or sets the maximum number of hits per target.
/// </summary>
public int MaximumNumberOfHitsPerTarget { get; set; }
/// <summary>
/// Gets or sets the minimum number of hits per attack, after which subsequent hits have a reduced chance to hit.
/// </summary>
public int MinimumNumberOfHitsPerAttack { get; set; }
/// <summary>
/// Gets or sets the maximum number of hits per attack.
/// </summary>
public int MaximumNumberOfHitsPerAttack { get; set; }
/// <summary>
/// Gets or sets the hit chance per distance multiplier.
/// E.g. when set to 0.9 and the target is 5 steps away,
/// the chance to hit is 0.9^5 = 0.59.
/// </summary>
public float HitChancePerDistanceMultiplier { get; set; }
/// <summary>
/// Gets or sets the number of projectiles/arrows that are fired.
/// When greater than 1, the projectiles are evenly distributed within the frustum.
/// Each target can only be hit by projectiles whose paths cross the target's position.
/// Default is 1 (single projectile).
/// </summary>
public int ProjectileCount { get; set; }
/// <summary>
/// Gets or sets the effect range of the skill, which is the maximum distance from the target area center.
/// </summary>
public int EffectRange { get; set; }
/// <inheritdoc />
public override string ToString()
{
return "Area Skill Settings";
}
}

View File

@@ -0,0 +1,21 @@
// <copyright file="BattleType.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Defines the type of battle which can be done on a battle zone between two teams.
/// </summary>
public enum BattleType
{
/// <summary>
/// A normal pvp battle.
/// </summary>
Normal,
/// <summary>
/// A battle soccer match.
/// </summary>
Soccer,
}

View File

@@ -0,0 +1,63 @@
// <copyright file="BattleZoneDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a battle zone.
/// </summary>
[Cloneable]
public partial class BattleZoneDefinition
{
/// <summary>
/// Gets or sets the battle type.
/// </summary>
public BattleType Type { get; set; }
/// <summary>
/// Gets or sets the x-coordinate of the upper spawn point for the left team.
/// </summary>
public byte? LeftTeamSpawnPointX { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of the upper spawn point for the left team.
/// </summary>
public byte LeftTeamSpawnPointY { get; set; }
/// <summary>
/// Gets or sets the x-coordinate of the upper spawn point for the right team.
/// </summary>
public byte? RightTeamSpawnPointX { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of the upper spawn point for the right team.
/// </summary>
public byte RightTeamSpawnPointY { get; set; }
/// <summary>
/// Gets or sets the battle ground.
/// </summary>
[MemberOfAggregate]
public virtual Rectangle? Ground { get; set; }
/// <summary>
/// Gets or sets the first goal zone.
/// </summary>
[MemberOfAggregate]
public virtual Rectangle? LeftGoal { get; set; }
/// <summary>
/// Gets or sets the second goal zone.
/// </summary>
[MemberOfAggregate]
public virtual Rectangle? RightGoal { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"Battle Zone ({this.Type})";
}
}

View File

@@ -0,0 +1,120 @@
// <copyright file="CharacterClass.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Defines a character class.
/// </summary>
[Cloneable]
public partial class CharacterClass
{
/// <summary>
/// Gets or sets the id of a character class.
/// This will be used to identify the class when getting created,
/// and as identifier to be sent to client.
/// </summary>
public byte Number { get; set; }
/// <summary>
/// Gets or sets the name of the character class.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this character class can get created by the user.
/// </summary>
public bool CanGetCreated { get; set; }
/// <summary>
/// Gets or sets the level requirement when getting created.
/// The level requirement must be fulfilled by another character of the same account.
/// </summary>
public short LevelRequirementByCreation { get; set; }
/// <summary>
/// Gets or sets the creation allowed flag which is sent to the client in the character list message if this character class is in its <see cref="Account.UnlockedCharacterClasses"/>.
/// </summary>
/// <remarks>
/// Flag about which characters can be created with this account:
/// 1 = Summoner
/// 2 = Dark Lord
/// 4 = Magic Gladiator
/// 8 = Rage Fighter.
/// </remarks>
public byte CreationAllowedFlag { get; set; }
/// <summary>
/// Gets or sets the next generation class, to which a character can upgrade after
/// fulfilling certain requirements, like quests.
/// </summary>
public virtual CharacterClass? NextGenerationClass { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this class is a master class and therefore can receive master experience for the master tree.
/// </summary>
public bool IsMasterClass { get; set; }
/// <summary>
/// Gets or sets the percent by which the moving level requirement for warping to other maps is reduced.
/// </summary>
public int LevelWarpRequirementReductionPercent { get; set; }
/// <summary>
/// Gets or sets the fruit calculation strategy.
/// </summary>
public FruitCalculationStrategy FruitCalculation { get; set; }
/// <summary>
/// Gets or sets the stat attributes.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<StatAttributeDefinition> StatAttributes { get; protected set; } = null!;
/// <summary>
/// Gets or sets the attribute combinations.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<AttributeRelationship> AttributeCombinations { get; protected set; } = null!;
/// <summary>
/// Gets or sets the base attribute values.
/// For example the amount of health a character got without any added stat point.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ConstValueAttribute> BaseAttributeValues { get; protected set; } = null!;
/// <summary>
/// Gets or sets the home map.
/// </summary>
public virtual GameMapDefinition? HomeMap { get; set; }
/// <summary>
/// Gets or sets the combo definition for this class and all
/// following <see cref="NextGenerationClass"/>es without an explicit combo definition.
/// </summary>
[MemberOfAggregate]
public virtual SkillComboDefinition? ComboDefinition { get; set; }
/// <summary>
/// Gets StatAttributeDefinition corresponding to AttributeDefinition.
/// </summary>
/// <param name="attributeDefinition">The attribute.</param>
/// <returns>The corresponding StatAttributeDefinition.</returns>
public StatAttributeDefinition? GetStatAttribute(AttributeDefinition attributeDefinition)
{
return this.StatAttributes.FirstOrDefault(a => a.Attribute == attributeDefinition);
}
/// <inheritdoc />
public override string ToString()
{
return this.Name;
}
}

View File

@@ -0,0 +1,61 @@
// <copyright file="ChatServerDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Settings for the chat server.
/// </summary>
[AggregateRoot]
[Cloneable]
public partial class ChatServerDefinition
{
/// <summary>
/// Gets or sets the server identifier.
/// </summary>
public byte ServerId { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the maximum connections.
/// </summary>
public int MaximumConnections { get; set; } = int.MaxValue;
/// <summary>
/// Gets or sets the client timeout. When a client did not send any data in this timespan, it's automatically disconnected.
/// </summary>
/// <value>
/// The client timeout.
/// </value>
public TimeSpan ClientTimeout { get; set; } = TimeSpan.FromMinutes(1);
/// <summary>
/// Gets or sets the interval in which a client clean up takes place.
/// For all connected clients it's checked whether or not the <see cref="ClientTimeout"/> has been reached.
/// </summary>
public TimeSpan ClientCleanUpInterval { get; set; } = TimeSpan.FromMinutes(1);
/// <summary>
/// Gets or sets the interval in which empty chat rooms are cleaned up.
/// </summary>
public TimeSpan RoomCleanUpInterval { get; set; } = TimeSpan.FromSeconds(5);
/// <summary>
/// Gets or sets the endpoints of the game server.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ChatServerEndpoint> Endpoints { get; protected set; } = null!;
/// <inheritdoc/>
public override string ToString()
{
return $"[ChatServerDefinition ServerID={this.ServerId}, Description={this.Description}]";
}
}

View File

@@ -0,0 +1,12 @@
// <copyright file="ChatServerEndpoint.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Defines an endpoint of a chat server.
/// </summary>
public class ChatServerEndpoint : ServerEndpoint
{
}

View File

@@ -0,0 +1,48 @@
// <copyright file="ConfigurationUpdate.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Describes an applied configuration update.
/// Based on this information, the program can decide which updates are need to
/// be installed next.
/// After a fresh database initialization, an entry exists, so that the maximum
/// version can be determined in this case, too.
/// </summary>
public class ConfigurationUpdate
{
/// <summary>
/// Gets or sets the version of the update.
/// </summary>
public int Version { get; set; }
/// <summary>
/// Gets or sets the name of the update.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the description of the update with further information.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets the release date.
/// </summary>
public DateTime? CreatedAt { get; set; }
/// <summary>
/// Gets or sets the installation timestamp. If it's <c>null</c>, the update wasn't installed yet.
/// </summary>
public DateTime? InstalledAt { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"v{this.Version}: {this.Name}";
}
}

View File

@@ -0,0 +1,21 @@
// <copyright file="ConfigurationUpdateState.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Describes the current update state of the configuration.
/// </summary>
public class ConfigurationUpdateState
{
/// <summary>
/// Gets or sets the initialization key.
/// </summary>
public string? InitializationKey { get; set; }
/// <summary>
/// Gets or sets the highest <see cref="ConfigurationUpdate.Version"/> which is installed.
/// </summary>
public int CurrentInstalledVersion { get; set; }
}

View File

@@ -0,0 +1,140 @@
// <copyright file="ConnectServerDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// The definition of a connect server.
/// </summary>
[AggregateRoot]
[Cloneable]
public partial class ConnectServerDefinition : IConnectServerSettings
{
/// <summary>
/// Gets or sets the id of this definition.
/// </summary>
public Guid Id { get; set; }
/// <inheritdoc />
public Guid ConfigurationId => this.Id;
/// <summary>
/// Gets or sets the server identifier.
/// </summary>
public byte ServerId { get; set; }
/// <summary>
/// Gets or sets the description of the server.
/// </summary>
/// <remarks>
/// Will be displayed in the server list in the admin panel as <see cref="IManageableServer.Description"/>.
/// </remarks>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the client which is expected to connect.
/// </summary>
[Required]
public virtual GameClientDefinition? Client { get; set; }
/// <inheritdoc/>
IGameClientVersion IConnectServerSettings.Client => this.Client ?? throw new InvalidOperationException("ConnectServerDefinition.Client not initialized.");
/// <summary>
/// Gets or sets a value indicating whether the client should get disconnected when a unknown packet is getting received.
/// </summary>
public bool DisconnectOnUnknownPacket { get; set; }
/// <summary>
/// Gets or sets the maximum size of the packets which should be received from the client. If this size is exceeded, the client will be disconnected.
/// </summary>
/// <remarks>DOS protection.</remarks>
public byte MaximumReceiveSize { get; set; }
/// <summary>
/// Gets or sets the network port on which the server is listening.
/// </summary>
public int ClientListenerPort { get; set; }
/// <summary>
/// Gets or sets the timeout after which clients without activity get disconnected.
/// </summary>
public TimeSpan Timeout { get; set; }
/// <summary>
/// Gets or sets the current patch version.
/// </summary>
public byte[]? CurrentPatchVersion { get; set; }
/// <inheritdoc />
byte[] IConnectServerSettings.CurrentPatchVersion => this.CurrentPatchVersion ?? throw new InvalidOperationException("Patch Version is not initialized");
/// <summary>
/// Gets or sets the patch address.
/// </summary>
public string PatchAddress { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the maximum connections per ip.
/// </summary>
public int MaxConnectionsPerAddress { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the <see cref="MaxConnectionsPerAddress"/> should be checked.
/// </summary>
public bool CheckMaxConnectionsPerAddress { get; set; }
/// <summary>
/// Gets or sets the maximum connections the connect server should handle.
/// </summary>
public int MaxConnections { get; set; }
/// <summary>
/// Gets or sets the listener backlog for the client listener.
/// </summary>
public int ListenerBacklog { get; set; }
/// <summary>
/// Gets or sets the maximum FTP requests per connection.
/// </summary>
public int MaxFtpRequests { get; set; }
/// <summary>
/// Gets or sets the maximum ip requests per connection.
/// </summary>
public int MaxIpRequests { get; set; }
/// <summary>
/// Gets or sets the maximum server list requests per connection.
/// </summary>
public int MaxServerListRequests { get; set; }
/// <summary>
/// Initializes the defaults.
/// </summary>
public void InitializeDefaults()
{
this.DisconnectOnUnknownPacket = true;
this.MaximumReceiveSize = 6;
this.Timeout = new TimeSpan(0, 1, 0);
this.CurrentPatchVersion = new byte[] { 1, 3, 0x2B };
this.PatchAddress = "patch.muonline.webzen.com";
this.MaxConnectionsPerAddress = 30;
this.CheckMaxConnectionsPerAddress = true;
this.MaxConnections = 10000;
this.ListenerBacklog = 100;
this.MaxFtpRequests = 1;
this.MaxIpRequests = 5;
this.MaxServerListRequests = 20;
}
/// <inheritdoc />
public override string ToString()
{
return this.Description;
}
}

View File

@@ -0,0 +1,61 @@
// <copyright file="Direction.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Direction where an object is looking at.
/// </summary>
/// <remarks>
/// The directions are named/valued after how they look like due the game client.
/// That means, when a character looks to south, it looks straight downwards. Because the map is rotated on the game client, that's actually a corner.
/// Since we use the value 0 as 'Undefined' and the original game client uses value 0 as 'West', this has to be considered when communicating with it.
/// </remarks>
public enum Direction
{
/// <summary>
/// The undefined direction.
/// </summary>
Undefined = 0,
/// <summary>
/// The direction looking to the west.
/// </summary>
West = 1,
/// <summary>
/// The direction looking to the south east.
/// </summary>
SouthWest = 2,
/// <summary>
/// The direction looking to the south.
/// </summary>
South = 3,
/// <summary>
/// The direction looking to the south west.
/// </summary>
SouthEast = 4,
/// <summary>
/// The direction looking to the east.
/// </summary>
East = 5,
/// <summary>
/// The direction looking to the north east.
/// </summary>
NorthEast = 6,
/// <summary>
/// The direction looking to the north.
/// </summary>
North = 7,
/// <summary>
/// The direction looking to the north west.
/// </summary>
NorthWest = 8,
}

View File

@@ -0,0 +1,110 @@
// <copyright file="DropItemGroup.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Enumeration of special item types.
/// </summary>
public enum SpecialItemType
{
/// <summary>
/// No special item type.
/// </summary>
None,
/// <summary>
/// The ancient special item type.
/// </summary>
Ancient,
/// <summary>
/// The excellent special item type.
/// </summary>
Excellent,
/// <summary>
/// The random item special item type.
/// </summary>
RandomItem,
/// <summary>
/// The socket item special item type.
/// </summary>
SocketItem,
/// <summary>
/// The money special item type.
/// </summary>
Money,
/// <summary>
/// The jewel special item type.
/// </summary>
Jewel,
}
/// <summary>
/// Idea: append several "drop item groups" with its certain probability.
/// In the drop generator sort all DropItemGroups by its chance.
/// Classes which can have DropItemGroups: Maps, Monsters(for example the kundun drops), Players(for quest items).
/// </summary>
[Cloneable]
public partial class DropItemGroup
{
/// <summary>
/// Gets or sets the description.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets the chance of the item drop group to apply. From 0.0 to 1.0.
/// </summary>
public double Chance { get; set; }
/// <summary>
/// Gets or sets the minimum monster level. If <c>null</c>, then it doesn't apply.
/// </summary>
public byte? MinimumMonsterLevel { get; set; }
/// <summary>
/// Gets or sets the maximum monster level. If <c>null</c>, then it doesn't apply.
/// </summary>
public byte? MaximumMonsterLevel { get; set; }
/// <summary>
/// Gets or sets a specific monster for which this drop group is valid.
/// If <c>null</c>, then it doesn't apply and it's valid for all monsters.
/// </summary>
/// <remarks>This is required for some quest items which should drop only from specific monsters.</remarks>
public virtual MonsterDefinition? Monster { get; set; }
/// <summary>
/// Gets or sets the item level which will be assigned to the dropped instance of <see cref="PossibleItems"/>.
/// </summary>
/// <remarks>
/// Use cases: Quest items (e.g. Broken Sword+1 = Dark Stone), Event Ticket Items, Summoning Orbs, etc. where one item type is used
/// for multiple "visible" items.
/// </remarks>
public byte? ItemLevel { get; set; }
/// <summary>
/// Gets or sets the special type of the item.
/// </summary>
public SpecialItemType ItemType { get; set; }
/// <summary>
/// Gets or sets the possible items which can be dropped.
/// </summary>
public virtual ICollection<ItemDefinition> PossibleItems { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return this.Description;
}
}

View File

@@ -0,0 +1,40 @@
// <copyright file="DuelArea.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines an area where a duel can take place.
/// </summary>
[Cloneable]
public partial class DuelArea
{
/// <summary>
/// Gets or sets the index of the area.
/// </summary>
public short Index { get; set; }
/// <summary>
/// Gets or sets the first player gate to which the player is teleported when he enters the duel.
/// </summary>
public virtual ExitGate? FirstPlayerGate { get; set; }
/// <summary>
/// Gets or sets the second player gate to which the player is teleported when he enters the duel.
/// </summary>
public virtual ExitGate? SecondPlayerGate { get; set; }
/// <summary>
/// Gets or sets the gate to which a spectator is teleported when he enters the duel.
/// </summary>
public virtual ExitGate? SpectatorsGate { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.Index} - {this.FirstPlayerGate} - {this.SecondPlayerGate}";
}
}

View File

@@ -0,0 +1,52 @@
// <copyright file="DuelConfiguration.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Configuration for the duel feature.
/// </summary>
[Cloneable]
public partial class DuelConfiguration
{
/// <summary>
/// Gets or sets the maximum score at which the duel will end with a winner.
/// </summary>
public int MaximumScore { get; set; }
/// <summary>
/// Gets or sets the entrance fee for a duel.
/// </summary>
public int EntranceFee { get; set; }
/// <summary>
/// Gets or sets the minimum character level to start a duel.
/// </summary>
public int MinimumCharacterLevel { get; set; }
/// <summary>
/// Gets or sets the maximum spectators per duel room.
/// </summary>
public int MaximumSpectatorsPerDuelRoom { get; set; }
/// <summary>
/// Gets or sets the exit gate to which all players are ported after the duel has ended.
/// If not set, players will be ported to the safezone.
/// </summary>
public virtual ExitGate? Exit { get; set; }
/// <summary>
/// Gets or sets the available duel areas.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<DuelArea> DuelAreas { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return $"Duel Configuration {this.DuelAreas.Count} Arenas, Exit: {this.Exit}";
}
}

View File

@@ -0,0 +1,36 @@
// <copyright file="EnterGate.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a gate which a player can enter to move to another <see cref="ExitGate"/>.
/// </summary>
[Cloneable]
public partial class EnterGate : Gate
{
/// <summary>
/// Gets or sets the target gate.
/// </summary>
[Required]
public virtual ExitGate? TargetGate { get; set; }
/// <summary>
/// Gets or sets the level requirement which the player needs to move through the gate.
/// </summary>
public short LevelRequirement { get; set; }
/// <summary>
/// Gets or sets the number of the gate.
/// </summary>
public short Number { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()} ({this.Number}) (Level {this.LevelRequirement}) to {this.TargetGate}";
}
}

View File

@@ -0,0 +1,40 @@
// <copyright file="ExitGate.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a gate through which a player enters a map.
/// </summary>
[Cloneable]
public partial class ExitGate : Gate
{
/// <summary>
/// Gets or sets the direction to which the player looks when he enters the map.
/// </summary>
public Direction Direction { get; set; }
/// <summary>
/// Gets or sets the map which will be entered.
/// </summary>
[Required]
public virtual GameMapDefinition? Map { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is a spawn gate.
/// If it's not a spawn gate, it's a target of an <see cref="EnterGate"/>.
/// </summary>
/// <value>
/// <c>true</c> if this instance is spawn gate; otherwise, <c>false</c>.
/// </value>
public bool IsSpawnGate { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.Map?.Name} @ {base.ToString()}";
}
}

View File

@@ -0,0 +1,26 @@
// <copyright file="FruitCalculationStrategy.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// The calculation strategy for maximum fruit points.
/// </summary>
public enum FruitCalculationStrategy
{
/// <summary>
/// The default strategy (maximum 127).
/// </summary>
Default = 0,
/// <summary>
/// The strategy to calculate the fruits for magic gladiator classes (maximum 100).
/// </summary>
MagicGladiator = 1,
/// <summary>
/// The strategy to calculate the fruits for dark lord classes (maximum 115).
/// </summary>
DarkLord = 2,
}

View File

@@ -0,0 +1,59 @@
// <copyright file="GameClientDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
using MUnique.OpenMU.Network.PlugIns;
/// <summary>
/// Defines a game client.
/// </summary>
[AggregateRoot]
[Cloneable]
public partial class GameClientDefinition : IGameClientVersion
{
/// <summary>
/// Gets or sets the season.
/// </summary>
public byte Season { get; set; }
/// <summary>
/// Gets or sets the episode.
/// </summary>
public byte Episode { get; set; }
/// <summary>
/// Gets or sets the language.
/// </summary>
public ClientLanguage Language { get; set; }
/// <summary>
/// Gets or sets the version which is defined in the client binaries.
/// </summary>
public byte[]? Version { get; set; }
/// <inheritdoc />
byte[] IGameClientVersion.Version => this.Version ?? throw new InvalidOperationException("Version not initialized.");
/// <summary>
/// Gets or sets the serial which is defined in the client binaries.
/// </summary>
public byte[]? Serial { get; set; }
/// <inheritdoc />
byte[] IGameClientVersion.Serial => this.Serial ?? throw new InvalidOperationException("Serial not initialized.");
/// <summary>
/// Gets or sets the description.
/// </summary>
public string Description { get; set; } = string.Empty;
/// <inheritdoc />
public override string ToString()
{
return this.Description;
}
}

View File

@@ -0,0 +1,308 @@
// <copyright file="GameConfiguration.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Defines the game configuration.
/// A game configuration contains the whole configuration of a game, directly or indirectly.
/// </summary>
[AggregateRoot]
[Cloneable]
public partial class GameConfiguration
{
/// <summary>
/// Gets or sets the maximum reachable level.
/// </summary>
public short MaximumLevel { get; set; }
/// <summary>
/// Gets or sets the maximum reachable master level.
/// </summary>
public short MaximumMasterLevel { get; set; }
/// <summary>
/// Gets or sets the experience rate of the game.
/// </summary>
public float ExperienceRate { get; set; }
/// <summary>
/// Gets or sets the master experience rate of the game.
/// </summary>
public float MasterExperienceRate { get; set; } = 1.0f;
/// <summary>
/// Gets or sets a value indicating whether experience overflow should be prevented.
/// When <c>true</c>, if gaining experience would exceed the amount needed for the next level,
/// only the necessary experience for the next level is gained, and the overflow is discarded.
/// When <c>false</c>, excess experience is applied to subsequent levels (default behavior).
/// </summary>
public bool PreventExperienceOverflow { get; set; }
/// <summary>
/// Gets or sets the minimum monster level which are required to be killed
/// in order to gain master experience for master character classes.
/// </summary>
public byte MinimumMonsterLevelForMasterExperience { get; set; }
/// <summary>
/// Gets or sets the information range. This defines how far players can see other game objects.
/// </summary>
public byte InfoRange { get; set; }
/// <summary>
/// Gets or sets a value indicating whether area skills hit players.
/// </summary>
/// <remarks>
/// Usually false, during castle siege this might be true.
/// </remarks>
public bool AreaSkillHitsPlayer { get; set; }
/// <summary>
/// Gets or sets the maximum inventory money value.
/// </summary>
public int MaximumInventoryMoney { get; set; }
/// <summary>
/// Gets or sets the maximum vault money value.
/// </summary>
public int MaximumVaultMoney { get; set; }
/// <summary>
/// Gets or sets a value indicating whether money pickup should be clamped to the maximum inventory money limit instead of failing when the limit would be exceeded.
/// </summary>
/// <remarks>
/// When <c>true</c>, if picking up money would exceed the maximum inventory money, the player will receive as much as possible (up to the limit) instead of the pickup failing completely.
/// When <c>false</c>, the pickup will fail if it would exceed the maximum (default behavior).
/// </remarks>
public bool ClampMoneyOnPickup { get; set; }
/// <summary>
/// Gets or sets the level delta used to determine the pool of items eligible for excellent drops.
/// A monster must be at least this many levels above an item's DropLevel for the item to be eligible as excellent.
/// </summary>
public byte ExcellentItemDropLevelDelta { get; set; }
/// <summary>
/// Gets or sets the experience formula per level. The variable name for the level is "level".
/// </summary>
public string? ExperienceFormula { get; set; }
/// <summary>
/// Gets or sets the experience formula per master level. The variable name for the level is "level".
/// </summary>
public string? MasterExperienceFormula { get; set; }
/// <summary>
/// Gets or sets the interval for attribute recoveries. See also MUnique.OpenMU.GameLogic.Attributes.Stats.Regeneration.
/// </summary>
public int RecoveryInterval { get; set; }
/// <summary>
/// Gets or sets the maximum numbers of letters a player can have in his inbox.
/// </summary>
public int MaximumLetters { get; set; }
/// <summary>
/// Gets or sets the price of sending a letter.
/// </summary>
public int LetterSendPrice { get; set; }
/// <summary>
/// Gets or sets the maximum number of characters per account.
/// </summary>
public byte MaximumCharactersPerAccount { get; set; }
/// <summary>
/// Gets or sets the character name regex.
/// </summary>
/// <remarks>
/// "^[a-zA-Z0-9]{3,10}$";.
/// </remarks>
public string? CharacterNameRegex { get; set; }
/// <summary>
/// Gets or sets the maximum length of the password.
/// </summary>
public int MaximumPasswordLength { get; set; }
/// <summary>
/// Gets or sets the maximum size of parties.
/// </summary>
public byte MaximumPartySize { get; set; }
/// <summary>
/// Gets or sets a value indicating whether if a monster should drop or adds money to the character directly.
/// </summary>
public bool ShouldDropMoney { get; set; }
/// <summary>
/// Gets or sets the duration of item drops on the ground.
/// </summary>
public TimeSpan ItemDropDuration { get; set; }
/// <summary>
/// Gets or sets the maximum droppable item option level.
/// </summary>
public byte MaximumItemOptionLevelDrop { get; set; }
/// <summary>
/// Gets or sets the accumulated damage which needs to be done to decrease <see cref="Item.Durability"/> of a defending item by 1.
/// </summary>
public double DamagePerOneItemDurability { get; set; }
/// <summary>
/// Gets or sets the accumulated damage which needs to be done to decrease <see cref="Item.Durability"/> of a pet item by 1.
/// </summary>
public double DamagePerOnePetDurability { get; set; }
/// <summary>
/// Gets or sets the number of hits which needs to be done to decrease the <see cref="Item.Durability"/> of an offensive item by 1.
/// </summary>
public double HitsPerOneItemDurability { get; set; }
/// <summary>
/// Gets or sets the duel configuration.
/// </summary>
[MemberOfAggregate]
public virtual DuelConfiguration? DuelConfiguration { get; set; }
/// <summary>
/// Gets or sets the possible jewel mixes.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<JewelMix> JewelMixes { get; protected set; } = null!;
/// <summary>
/// Gets or sets the warp list.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<WarpInfo> WarpList { get; protected set; } = null!;
/// <summary>
/// Gets or sets the drop item groups which can be assigned to maps and characters.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<DropItemGroup> DropItemGroups { get; protected set; } = null!;
/// <summary>
/// Gets or sets the skills of this game configuration.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<Skill> Skills { get; protected set; } = null!;
/// <summary>
/// Gets or sets the character classes.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<CharacterClass> CharacterClasses { get; protected set; } = null!;
/// <summary>
/// Gets or sets the item definitions.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemDefinition> Items { get; protected set; } = null!;
/// <summary>
/// Gets or sets the item level bonus tables.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemLevelBonusTable> ItemLevelBonusTables { get; protected set; } = null!;
/// <summary>
/// Gets or sets the item slot types.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemSlotType> ItemSlotTypes { get; protected set; } = null!;
/// <summary>
/// Gets or sets the item option definitions.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemOptionDefinition> ItemOptions { get; protected set; } = null!;
/// <summary>
/// Gets or sets the item option types.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemOptionType> ItemOptionTypes { get; protected set; } = null!;
/// <summary>
/// Gets or sets the item set groups.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemSetGroup> ItemSetGroups { get; protected set; } = null!;
/// <summary>
/// Gets or sets the item option combination bonuses.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemOptionCombinationBonus> ItemOptionCombinationBonuses { get; protected set; } = null!;
/// <summary>
/// Gets or sets the map definitions.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<GameMapDefinition> Maps { get; protected set; } = null!;
/// <summary>
/// Gets or sets the monster definitions.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MonsterDefinition> Monsters { get; protected set; } = null!;
/// <summary>
/// Gets or sets the attributes.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<AttributeDefinition> Attributes { get; protected set; } = null!;
/// <summary>
/// Gets or sets the magic effects.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MagicEffectDefinition> MagicEffects { get; protected set; } = null!;
/// <summary>
/// Gets or sets the master skill roots.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MasterSkillRoot> MasterSkillRoots { get; protected set; } = null!;
/// <summary>
/// Gets or sets the attribute combinations.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<AttributeRelationship> GlobalAttributeCombinations { get; protected set; } = null!;
/// <summary>
/// Gets or sets the base attribute values.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ConstValueAttribute> GlobalBaseAttributeValues { get; protected set; } = null!;
/// <summary>
/// Gets or sets the plug in configurations.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<PlugInConfiguration> PlugInConfigurations { get; protected set; } = null!;
/// <summary>
/// Gets or sets the event definitions.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MiniGameDefinition> MiniGameDefinitions { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return "Default Game Configuration";
}
}

View File

@@ -0,0 +1,115 @@
// <copyright file="GameMapDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Attributes;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Configuration of a map. Contains all information to create an instance of a GameMap.
/// </summary>
/// <remarks>
/// Some maps have different possible status, for expample the crywolf map:
/// 1) Balgass is undeafeated - monsters are not dropping special items
/// 2) Balgass is defeated - monsters are dropping special items
/// 3) Crywolf event is ongoing - The normal monsters are not there(?), but event monsters.
/// For each of this status (<see cref="Discriminator"/>), there exist different terrain maps (safezones are different, etc.).
/// To reflect this requirement on this data model, for each status there must be one game map definition.
/// The switch between this status and its corresponding game map definitions should be done in game logic.
/// </remarks>
[Cloneable]
public partial class GameMapDefinition
{
/// <summary>
/// Gets or sets the number of the map.
/// </summary>
/// <remarks>
/// This number is identifying the map on the client.
/// </remarks>
public short Number { get; set; }
/// <summary>
/// Gets or sets the name of the map.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the terrain data.
/// </summary>
/// <remarks>
/// Content of the *.att file in the original server.
/// </remarks>
public byte[]? TerrainData { get; set; }
/// <summary>
/// Gets or sets the defined monster spawn areas.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MonsterSpawnArea> MonsterSpawns { get; protected set; } = null!;
/// <summary>
/// Gets or sets the enter gates, though which the player can move to other maps.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<EnterGate> EnterGates { get; protected set; } = null!;
/// <summary>
/// Gets or sets the exp multiplier for this map.
/// </summary>
/// <value>
/// The exp multiplier.
/// </value>
public double ExpMultiplier { get; set; }
/// <summary>
/// Gets or sets the discriminator which allows to identify different map definitions with the same <see cref="Number"/>.
/// </summary>
public int Discriminator { get; set; }
/// <summary>
/// Gets or sets the game map to which the player will be brought when it died.
/// One of the <see cref="ExitGates"/> where <see cref="ExitGate.IsSpawnGate"/> is selected.
/// </summary>
[Required]
public virtual GameMapDefinition? SafezoneMap { get; set; }
/// <summary>
/// Gets or sets the battle zone.
/// This is usually just defined by the Arena map.
/// </summary>
[MemberOfAggregate]
public virtual BattleZoneDefinition? BattleZone { get; set; }
/// <summary>
/// Gets or sets the spawn gates.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ExitGate> ExitGates { get; protected set; } = null!;
/// <summary>
/// Gets or sets the DropItemGroup of this map.
/// Some maps contain different drops. Examples: land of trials drops ancient items, kanturu drops gemstones.
/// </summary>
public virtual ICollection<DropItemGroup> DropItemGroups { get; protected set; } = null!;
/// <summary>
/// Gets or sets the map requirements for player to use this map.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<AttributeRequirement> MapRequirements { get; protected set; } = null!;
/// <summary>
/// Gets or sets the power ups which are applied to characters which are currently on this map.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<PowerUpDefinition> CharacterPowerUpDefinitions { get; protected set; } = null!;
/// <inheritdoc/>
public override string ToString()
{
return $"{this.Number} - {this.Name}";
}
}

View File

@@ -0,0 +1,31 @@
// <copyright file="GameServerConfiguration.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines the game server configuration.
/// </summary>
[AggregateRoot]
[Cloneable]
public partial class GameServerConfiguration
{
/// <summary>
/// Gets or sets the maximum number of players which can connect.
/// </summary>
public short MaximumPlayers { get; set; }
/// <summary>
/// Gets or sets the maps which should be hosted on the server.
/// </summary>
public virtual ICollection<GameMapDefinition> Maps { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return $"Default ({this.MaximumPlayers} players)"; // TODO Add Description field
}
}

View File

@@ -0,0 +1,61 @@
// <copyright file="GameServerDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using System.Globalization;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines the configuration of a game server.
/// </summary>
[AggregateRoot]
[Cloneable]
public partial class GameServerDefinition
{
/// <summary>
/// Gets or sets the server identifier.
/// </summary>
public byte ServerID { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the experience rate for the specific server.
/// Be aware that this multiplies with the <see cref="MUnique.OpenMU.DataModel.Configuration.GameConfiguration.ExperienceRate"/>.
/// </summary>
public float ExperienceRate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether PVP is enabled on this server.
/// </summary>
public bool PvpEnabled { get; set; } = true;
/// <summary>
/// Gets or sets the server configuration.
/// </summary>
[Required]
public virtual GameServerConfiguration? ServerConfiguration { get; set; }
/// <summary>
/// Gets or sets the game configuration.
/// </summary>
[Required]
public virtual GameConfiguration? GameConfiguration { get; set; }
/// <summary>
/// Gets or sets the endpoints of the game server.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<GameServerEndpoint> Endpoints { get; protected set; } = null!;
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "#{0} - {1}", this.ServerID, this.Description);
}
}

View File

@@ -0,0 +1,22 @@
// <copyright file="GameServerEndpoint.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines an endpoint of a game server.
/// </summary>
[Cloneable]
public partial class GameServerEndpoint : ServerEndpoint
{
/// <summary>
/// Gets or sets the alternative published network port. It's reported to the connect server instead of the <see cref="ServerEndpoint.NetworkPort"/>, if not <c>0</c>.
/// This allows to run a network analyzer program as a proxy, which listens to this <see cref="AlternativePublishedPort"/> and forwards to <see cref="ServerEndpoint.NetworkPort"/>
/// without changing the connection address of the game client.
/// This may also be useful if you use port forwarding over NAT.
/// </summary>
public int AlternativePublishedPort { get; set; }
}

View File

@@ -0,0 +1,46 @@
// <copyright file="Gate.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a gate through which a player can exit or enter to other maps.
/// </summary>
[Cloneable]
public partial class Gate : IMapArea
{
/// <summary>
/// Gets or sets the upper left corner, x-coordinate.
/// </summary>
public byte X1 { get; set; }
/// <summary>
/// Gets or sets the upper left corner, y-coordinate.
/// </summary>
public byte Y1 { get; set; }
/// <summary>
/// Gets or sets the bottom right corner, x-coordinate.
/// </summary>
public byte X2 { get; set; }
/// <summary>
/// Gets or sets the bottom right corner, y-coordinate.
/// </summary>
public byte Y2 { get; set; }
/// <inheritdoc />
public override string ToString()
{
var start = $"({this.X1}, {this.Y1})";
if (this.X1 == this.X2 && this.Y1 == this.Y2)
{
return start;
}
return $"{start} - ({this.X2}, {this.Y2})";
}
}

View File

@@ -0,0 +1,31 @@
// <copyright file="IMapArea.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Represents a rectangular area on a map, defined by two corner coordinates.
/// </summary>
public interface IMapArea
{
/// <summary>
/// Gets or sets the upper-left corner X coordinate.
/// </summary>
byte X1 { get; set; }
/// <summary>
/// Gets or sets the upper-left corner Y coordinate.
/// </summary>
byte Y1 { get; set; }
/// <summary>
/// Gets or sets the bottom-right corner X coordinate.
/// </summary>
byte X2 { get; set; }
/// <summary>
/// Gets or sets the bottom-right corner Y coordinate.
/// </summary>
byte Y2 { get; set; }
}

View File

@@ -0,0 +1,39 @@
// <copyright file="ItemCrafting.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Description of IItemCrafting.
/// </summary>
[Cloneable]
public partial class ItemCrafting
{
/// <summary>
/// Gets or sets the number.
/// </summary>
/// <remarks>
/// Referenced by the client with this number.
/// </remarks>
public byte Number { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the name of the item crafting handler class.
/// </summary>
public string ItemCraftingHandlerClassName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the simple crafting settings.
/// </summary>
[MemberOfAggregate]
public virtual SimpleCraftingSettings? SimpleCraftingSettings { get; set; }
}

View File

@@ -0,0 +1,116 @@
// <copyright file="ItemCraftingRequiredItem.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
using System.Globalization;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Describes an required item for a crafting.
/// </summary>
[Cloneable]
public partial class ItemCraftingRequiredItem
{
/// <summary>
/// Gets or sets the collection of possible items which are valid for this requirement.
/// </summary>
public virtual ICollection<ItemDefinition> PossibleItems { get; protected set; } = null!;
/// <summary>
/// Gets or sets the minimum item level.
/// </summary>
public byte MinimumItemLevel { get; set; }
/// <summary>
/// Gets or sets the maximum item level.
/// </summary>
public byte MaximumItemLevel { get; set; }
/// <summary>
/// Gets or sets the required item options.
/// </summary>
public virtual ICollection<ItemOptionType> RequiredItemOptions { get; protected set; } = null!;
/// <summary>
/// Gets or sets the minimum amount.
/// </summary>
public byte MinimumAmount { get; set; }
/// <summary>
/// Gets or sets the maximum amount.
/// </summary>
public byte MaximumAmount { get; set; }
/// <summary>
/// Gets or sets the success result.
/// </summary>
public MixResult SuccessResult { get; set; }
/// <summary>
/// Gets or sets the fail result.
/// </summary>
public MixResult FailResult { get; set; }
/// <summary>
/// Gets or sets the NPC price divisor. For each full division, the percentage gets increased by 1 percent, and the mix price rises.
/// </summary>
public int NpcPriceDivisor { get; set; }
/// <summary>
/// Gets or sets the add percentage per item.
/// </summary>
public byte AddPercentage { get; set; }
/// <summary>
/// Gets or sets the reference identifier to the corresponding <see cref="ItemCraftingResultItem.Reference"/>.
/// If <c>0</c>, no reference exists.
/// </summary>
public byte Reference { get; set; }
/// <inheritdoc />
public override string ToString()
{
string itemName;
if (!this.PossibleItems.Any())
{
itemName = "Random Item";
}
else
{
itemName = string.Join(", ", this.PossibleItems.Select(p => p.Name));
}
string amount = this.MinimumAmount == this.MaximumAmount
? this.MinimumAmount.ToString(CultureInfo.InvariantCulture)
: $"{this.MinimumAmount}~{this.MaximumAmount}";
string level;
if (this.MinimumItemLevel == this.MaximumItemLevel && this.MinimumItemLevel == 0)
{
level = string.Empty;
}
else if (this.MinimumItemLevel == this.MaximumItemLevel)
{
level = $"+{this.MinimumItemLevel}";
}
else
{
level = $"+{this.MinimumItemLevel}~{this.MaximumItemLevel}";
}
string options;
if (this.RequiredItemOptions.Any())
{
options = "+" + string.Join("+", this.RequiredItemOptions.Select(o => o.Name));
}
else
{
options = string.Empty;
}
return $"{amount} x {itemName}{level}{options}";
}
}

View File

@@ -0,0 +1,82 @@
// <copyright file="ItemCraftingResultItem.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Defines the resulting item of a crafting.
/// </summary>
[Cloneable]
public partial class ItemCraftingResultItem
{
/// <summary>
/// Gets or sets the item definition.
/// </summary>
public virtual ItemDefinition? ItemDefinition { get; set; }
/// <summary>
/// Gets or sets the random minimum level for a created item.
/// </summary>
public byte RandomMinimumLevel { get; set; }
/// <summary>
/// Gets or sets the random maximum level for a created item.
/// </summary>
public byte RandomMaximumLevel { get; set; }
/// <summary>
/// Gets or sets the durability for a created item explicitly.
/// </summary>
public byte? Durability { get; set; }
/// <summary>
/// Gets or sets the reference to the corresponding <see cref="ItemCraftingRequiredItem.Reference"/>.
/// If <c>0</c>, no reference exists.
/// </summary>
/// <remarks>
/// For Item Upping.
/// </remarks>
public byte Reference { get; set; }
/// <summary>
/// Gets or sets the add level.
/// </summary>
/// <remarks>
/// For Item Upping.
/// </remarks>
public byte AddLevel { get; set; }
/// <inheritdoc />
public override string ToString()
{
string itemName;
if (this.ItemDefinition is null)
{
itemName = $"Referenced item {this.Reference} will be modified.";
}
else
{
itemName = this.ItemDefinition.Name;
}
string level;
if (this.RandomMinimumLevel == this.RandomMaximumLevel && this.RandomMinimumLevel == 0)
{
level = string.Empty;
}
else if (this.RandomMinimumLevel == this.RandomMaximumLevel)
{
level = $"+{this.RandomMinimumLevel}";
}
else
{
level = $"+{this.RandomMinimumLevel}~{this.RandomMaximumLevel}";
}
return $"{itemName}{level}";
}
}

View File

@@ -0,0 +1,31 @@
// <copyright file="MixResult.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
/// <summary>
/// Defines what should happen with the required input items when the item crafting finished.
/// </summary>
public enum MixResult
{
/// <summary>
/// The item will disappear.
/// </summary>
Disappear = 0,
/// <summary>
/// The item will stay as is.
/// </summary>
StaysAsIs = 1,
/// <summary>
/// The item will be downgraded to a random level, may lose its skill, and its item option may be reduced by 1 level.
/// </summary>
ChaosWeaponAndFirstWingsDowngradedRandom = 2,
/// <summary>
/// The item will be downgraded 2 or 3 levels and its item option will be removed.
/// </summary>
ThirdWingsDowngradedRandom = 3,
}

View File

@@ -0,0 +1,21 @@
// <copyright file="ResultItemSelection.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
/// <summary>
/// Defines the result item selection.
/// </summary>
public enum ResultItemSelection
{
/// <summary>
/// One random item is selected as result from the result items.
/// </summary>
Any = 0,
/// <summary>
/// All items are selected as result from the result items.
/// </summary>
All = 1,
}

View File

@@ -0,0 +1,107 @@
// <copyright file="SimpleCraftingSettings.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Crafting settings for the simple item crafting handler.
/// </summary>
[Cloneable]
public partial class SimpleCraftingSettings
{
/// <summary>
/// Gets or sets the price to do the crafting.
/// </summary>
public int Money { get; set; }
/// <summary>
/// Gets or sets the price to do the crafting.
/// </summary>
public int MoneyPerFinalSuccessPercentage { get; set; }
/// <summary>
/// Gets or sets the NPC price divisor for the sum of crafting items' prices. For each full division, the percentage gets increased by 1 percent, and the mix price rises.
/// </summary>
/// <remarks>Used for Chaos Weapon and 1st Level Wing craftings.</remarks>
public int NpcPriceDivisor { get; set; }
/// <summary>
/// Gets or sets the success percent.
/// </summary>
public byte SuccessPercent { get; set; }
/// <summary>
/// Gets or sets the maximum success percent.
/// </summary>
public byte MaximumSuccessPercent { get; set; }
/// <summary>
/// Gets or sets a value indicating whether multiple crafting at the same time are allowed for this crafting.
/// </summary>
public bool MultipleAllowed { get; set; }
/// <summary>
/// Gets or sets the required items.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemCraftingRequiredItem> RequiredItems { get; protected set; } = null!;
/// <summary>
/// Gets or sets the result items, which are generated when the crafting succeeded.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemCraftingResultItem> ResultItems { get; protected set; } = null!;
/// <summary>
/// Gets or sets the result item selection.
/// </summary>
public ResultItemSelection ResultItemSelect { get; set; }
/// <summary>
/// Gets or sets the success percentage addition for a item with luck option which gets modified.
/// </summary>
public int SuccessPercentageAdditionForLuck { get; set; }
/// <summary>
/// Gets or sets the success percentage addition for an excellent item which gets modified.
/// </summary>
public int SuccessPercentageAdditionForExcellentItem { get; set; }
/// <summary>
/// Gets or sets the success percentage addition for an ancient item which gets modified.
/// </summary>
public int SuccessPercentageAdditionForAncientItem { get; set; }
/// <summary>
/// Gets or sets the success percentage addition for a "380 item" which gets modified.
/// </summary>
public int SuccessPercentageAdditionForGuardianItem { get; set; }
/// <summary>
/// Gets or sets the success percentage addition for a socket item which gets modified.
/// </summary>
public int SuccessPercentageAdditionForSocketItem { get; set; }
/// <summary>
/// Gets or sets the chance in percent of getting the luck option in the random result item.
/// </summary>
public byte ResultItemLuckOptionChance { get; set; }
/// <summary>
/// Gets or sets the chance in percent of getting the skill in the random result item.
/// </summary>
public byte ResultItemSkillChance { get; set; }
/// <summary>
/// Gets or sets the chance in percent of getting an excellent option in the random result item.
/// </summary>
public byte ResultItemExcellentOptionChance { get; set; }
/// <summary>
/// Gets or sets the maximum excellent options in the random result item.
/// </summary>
public byte ResultItemMaxExcOptionCount { get; set; }
}

View File

@@ -0,0 +1,76 @@
// <copyright file="ItemDropItemGroup.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Entities;
/// <summary>
/// Defines an effect which is shown in the game, when a <see cref="ItemDropItemGroup"/> is applied.
/// </summary>
public enum ItemDropEffect
{
/// <summary>
/// No effect.
/// </summary>
Undefined,
/// <summary>
/// A fireworks is shown at specific coordinates.
/// </summary>
Fireworks,
/// <summary>
/// A christmas fireworks is shown at specific coordinates.
/// </summary>
ChristmasFireworks,
/// <summary>
/// A fanfare sound is played.
/// </summary>
FanfareSound,
/// <summary>
/// A swirl is shown around an object, usually the player which caused the action.
/// </summary>
Swirl,
}
/// <summary>
/// A <see cref="DropItemGroup"/> which acts a definition of possible items when a special item (e.g. Box of Luck) is dropped by the player.
/// </summary>
[Cloneable]
public partial class ItemDropItemGroup : DropItemGroup
{
/// <summary>
/// Gets or sets the <see cref="Item.Level"/> of the source item which was dropped by the player.
/// </summary>
public byte SourceItemLevel { get; set; }
/// <summary>
/// Gets or sets the amount of money, in case <see cref="DropItemGroup.ItemType"/> is <see cref="SpecialItemType.Money"/>.
/// </summary>
public int MoneyAmount { get; set; }
/// <summary>
/// Gets or sets the minimum level of the <see cref="DropItemGroup.PossibleItems"/>.
/// </summary>
public byte MinimumLevel { get; set; }
/// <summary>
/// Gets or sets the maximum level of the <see cref="DropItemGroup.PossibleItems"/>.
/// </summary>
public byte MaximumLevel { get; set; }
/// <summary>
/// Gets or sets the character level which is required to drop this item.
/// </summary>
public short RequiredCharacterLevel { get; set; }
/// <summary>
/// Gets or sets the effect which should be shown in the game client when this drop group is applied.
/// </summary>
public ItemDropEffect DropEffect { get; set; }
}

View File

@@ -0,0 +1,32 @@
// <copyright file="AttributeRequirement.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// Defines a requirement of an attribute with the specified value.
/// </summary>
[Cloneable]
public partial class AttributeRequirement
{
/// <summary>
/// Gets or sets the attribute which is required.
/// </summary>
[Required]
public virtual AttributeDefinition? Attribute { get; set; }
/// <summary>
/// Gets or sets the minimum value the attribute needs to have.
/// </summary>
public int MinimumValue { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.Attribute}: {this.MinimumValue}";
}
}

View File

@@ -0,0 +1,35 @@
// <copyright file="CombinationBonusRequirement.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a requirement of existing item options on the equipped items of a character.
/// </summary>
[Cloneable]
public partial class CombinationBonusRequirement
{
/// <summary>
/// Gets or sets the required <see cref="ItemOption.OptionType"/>.
/// </summary>
public virtual ItemOptionType? OptionType { get; set; }
/// <summary>
/// Gets or sets the required <see cref="ItemOption.SubOptionType"/>.
/// </summary>
public int SubOptionType { get; set; }
/// <summary>
/// Gets or sets the minimum count of options in order to fulfill the requirement.
/// </summary>
public int MinimumCount { get; set; } = 1;
/// <inheritdoc />
public override string ToString()
{
return $"{this.OptionType}: SubOption Type {this.SubOptionType}, Min. Count {this.MinimumCount}";
}
}

View File

@@ -0,0 +1,75 @@
// <copyright file="IncreasableItemOption.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using System.Linq;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines by which "level" the option is increased with <see cref="IncreasableItemOption.LevelDependentOptions"/>.
/// </summary>
public enum LevelType
{
/// <summary>
/// It's increased by the option level.
/// </summary>
/// <remarks>
/// This one is used by item options which can be increased by separate jewels, e.g. Jewel of Life or Jewel of Harmony.
/// </remarks>
OptionLevel,
/// <summary>
/// It's increased by the level of the item which has the option.
/// </summary>
/// <remarks>
/// As far as I know, this is only required for wing options, e.g. 'Increase max HP +50~125'. That's why <see cref="OptionLevel"/> is the default, too.
/// </remarks>
ItemLevel,
}
/// <summary>
/// Defines an item option which can be increased.
/// </summary>
[Cloneable]
public partial class IncreasableItemOption : ItemOption
{
/// <summary>
/// Gets or sets a value which defines by which "level" the option is increased with <see cref="LevelDependentOptions"/>.
/// </summary>
/// <value>
/// The type of the level.
/// </value>
public LevelType LevelType { get; set; }
/// <summary>
/// Gets or sets a value which is considered when randomizing the option to an item.
/// </summary>
/// <remarks>This is used for Jewel of Harmony options rollout.</remarks>
/// <value>The statistical weight.</value>
public byte Weight { get; set; }
/// <summary>
/// Gets or sets the level dependent options for option levels over 1.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemOptionOfLevel> LevelDependentOptions { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
if (this.PowerUpDefinition != null)
{
return base.ToString();
}
var firstLevelOption = this.LevelDependentOptions?.OrderBy(l => l.Level).FirstOrDefault();
if (firstLevelOption?.PowerUpDefinition != null)
{
return $"{this.OptionType}: {firstLevelOption.PowerUpDefinition} ({this.Number})";
}
return base.ToString();
}
}

View File

@@ -0,0 +1,67 @@
// <copyright file="ItemBasePowerUpDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// Defines an item base power up definition.
/// </summary>
[Cloneable]
public partial class ItemBasePowerUpDefinition
{
private ConstantElement? _baseValueElement;
private float _baseValue;
private AggregateType _aggregateType;
/// <summary>
/// Gets or sets the target attribute.
/// </summary>
public virtual AttributeDefinition? TargetAttribute { get; set; }
/// <summary>
/// Gets the base value.
/// </summary>
[Transient]
public ConstantElement BaseValueElement => this._baseValueElement ??= new ConstantElement(this.BaseValue, this.AggregateType);
/// <summary>
/// Gets or sets the bonus per level.
/// </summary>
public virtual ItemLevelBonusTable? BonusPerLevelTable { get; set; }
/// <summary>
/// Gets or sets the additional value to the base value.
/// </summary>
public float BaseValue
{
get => this._baseValue;
set
{
this._baseValue = value;
this._baseValueElement = null;
}
}
/// <summary>
/// Gets or sets the type of the aggregate.
/// </summary>
public AggregateType AggregateType
{
get => this._aggregateType;
set
{
this._aggregateType = value;
this._baseValueElement = null;
}
}
/// <inheritdoc />
public override string ToString()
{
return $"{this.BaseValue} {this.TargetAttribute} {this.AggregateType}";
}
}

View File

@@ -0,0 +1,195 @@
// <copyright file="ItemDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Defines an item.
/// </summary>
[Cloneable]
public partial class ItemDefinition
{
/// <summary>
/// Gets or sets the (Sub-)Id of this item. Must be unique in an item group.
/// </summary>
public short Number { get; set; }
/// <summary>
/// Gets or sets the item slot where it can get equipped.
/// </summary>
public virtual ItemSlotType? ItemSlot { get; set; }
/// <summary>
/// Gets or sets the width of the Item.
/// </summary>
public byte Width { get; set; }
/// <summary>
/// Gets or sets the weight of the Item.
/// </summary>
public byte Height { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the item can be dropped by monsters.
/// </summary>
public bool DropsFromMonsters { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance acts as ammunition for another equipped weapon.
/// </summary>
public bool IsAmmunition { get; set; }
/// <summary>
/// Gets or sets a value indicating whether items of this kind are bound to character.
/// That means, it can't be traded, moved to the vault/personal shop or picked up from the ground by other players.
/// </summary>
public bool IsBoundToCharacter { get; set; }
/// <summary>
/// Gets or sets a value indicating whether items of this kind are quest items.
/// Quest items may additionally only be picked up from the ground by a character
/// which has an active quest that requires them, even if the character is a member
/// of the party which killed the monster that dropped it.
/// </summary>
public bool IsQuestItem { get; set; }
/// <summary>
/// Gets or sets the storage limit per character which is checked on pick-up.
/// A value of 0 means, that there is no limit.
/// A value 'n' above 0 means, that the inventory of the character can store
/// at most 'n' items of this kind.
/// </summary>
public int StorageLimitPerCharacter { get; set; }
/// <summary>
/// Gets or sets the name of the item.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the item drop level, which indicates the minimum monster lvl of which this item can be dropped.
/// </summary>
public byte DropLevel { get; set; }
/// <summary>
/// Gets or sets the maximum monster level at which this item can be dropped.
/// If no value is set, there is no upper limit.
/// </summary>
public byte? MaximumDropLevel { get; set; }
/// <summary>
/// Gets or sets the maximum item level.
/// </summary>
public byte MaximumItemLevel { get; set; }
/// <summary>
/// Gets or sets the maximum durability of this item at Level 0.
/// </summary>
public byte Durability { get; set; }
/// <summary>
/// Gets or sets the item Group (0-15). TODO: Might change item groups to classes, and replace this by it.
/// </summary>
public byte Group { get; set; }
/// <summary>
/// Gets or sets the value which defines the worth of an item in zen currency.
/// </summary>
public int Value { get; set; }
/// <summary>
/// Gets or sets the formula to calculate the required experience for a specific pet level.
/// Only applies, if this item is actually a trainable pet.
/// The variable for the pet level is "level".
/// </summary>
public string? PetExperienceFormula { get; set; }
/// <summary>
/// Gets or sets the effect which is applied when this item is consumed.
/// Creating a consume handler plugin is not required when this effect definition is set.
/// </summary>
public virtual MagicEffectDefinition? ConsumeEffect { get; set; }
/// <summary>
/// Gets or sets the maximum number of sockets an instance of this item can have.
/// </summary>
public int MaximumSockets { get; set; }
/// <summary>
/// Gets or sets the skill which this items adds to the skill list while wearing or which can be learned by consuming this item.
/// TODO: Split these two usages into different properties?.
/// </summary>
public virtual Skill? Skill { get; set; }
/// <summary>
/// Gets or sets the character classes which are qualified to wear this Item.
/// </summary>
public virtual ICollection<CharacterClass> QualifiedCharacters { get; protected set; } = null!;
/// <summary>
/// Gets or sets the possible item set groups.
/// </summary>
/// <remarks>
/// With this we can define a lot of things, for example:
/// - double wear bonus of single swords
/// - set bonus for defense rate
/// - set bonus for defense, if level is greater than 9
/// - ancient sets.
/// </remarks>
public virtual ICollection<ItemSetGroup> PossibleItemSetGroups { get; protected set; } = null!;
/// <summary>
/// Gets or sets the possible item options.
/// </summary>
public virtual ICollection<ItemOptionDefinition> PossibleItemOptions { get; protected set; } = null!;
/// <summary>
/// Gets or sets the requirements for wearing this item.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<AttributeRequirement> Requirements { get; protected set; } = null!;
/// <summary>
/// Gets or sets the base PowerUps of this item, for example min/max damage for weapons.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemBasePowerUpDefinition> BasePowerUpAttributes { get; protected set; } = null!;
/// <summary>
/// Gets or sets the drop item groups (one per possible item level), which are used to
/// generate a new item when this item gets dropped by a player.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemDropItemGroup> DropItems { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return $"{this.Name} ({this.Group}, {this.Number}) [{this.Width}x{this.Height}]";
}
/// <summary>
/// Gets the name for level.
/// </summary>
/// <param name="itemLevel">The item level.</param>
/// <returns>The name of the item of a certain level.</returns>
public string GetNameForLevel(byte itemLevel)
{
var itemName = this.Name.ToString();
if (itemName?.Contains(';') ?? false)
{
var tokens = itemName.Split(';');
if (tokens.Length > itemLevel)
{
itemName = tokens[itemLevel];
}
}
return itemName ?? string.Empty;
}
}

View File

@@ -0,0 +1,31 @@
// <copyright file="ItemLevelBonusTable.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Defines a table of item level related bonus values for <see cref="ItemBasePowerUpDefinition"/>s.
/// </summary>
[Cloneable]
public partial class ItemLevelBonusTable
{
/// <summary>
/// Gets or sets the name.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets the bonus per level.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<LevelBonus> BonusPerLevel { get; protected set; } = null!;
}

View File

@@ -0,0 +1,55 @@
// <copyright file="ItemOfItemSet.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using System.Text.Json.Serialization;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines additional bonus options for this item of a set.
/// </summary>
/// <remarks>
/// Here we can define additional bonus options, like the ancient options (e.g. +5 / +10 Str etc.).
/// </remarks>
[Cloneable]
public partial class ItemOfItemSet
{
/// <summary>
/// Gets or sets the ancient set discriminator.
/// </summary>
/// <remarks>
/// Only relevant to ancient sets. One item can only be in one ancient set with the same discriminator.
/// The original mu online protocol supports up to two different ancient sets per item - with discriminator values 1 and 2.
/// E.g. a 'Warrior Leather' set would have a discriminator value of 1, the 'Anonymous Leather' set would have 2.
/// </remarks>
public int AncientSetDiscriminator { get; set; }
/// <summary>
/// Gets or sets the item set group to which this instance belongs.
/// </summary>
public virtual ItemSetGroup? ItemSetGroup { get; set; }
/// <summary>
/// Gets or sets the item's definition for which the bonus should apply.
/// </summary>
public virtual ItemDefinition? ItemDefinition { get; set; }
/// <summary>
/// Gets or sets the bonus option.
/// </summary>
public virtual IncreasableItemOption? BonusOption { get; set; }
/// <summary>
/// Gets the name.
/// </summary>
[JsonIgnore]
public string Name => $"{this.ItemSetGroup?.Name} {this.ItemDefinition?.Name}";
/// <inheritdoc/>
public override string ToString()
{
return this.BonusOption?.PowerUpDefinition?.ToString() ?? string.Empty;
}
}

View File

@@ -0,0 +1,50 @@
// <copyright file="ItemOption.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Attributes;
/// <summary>
/// Defines the option of an item.
/// </summary>
[Cloneable]
public partial class ItemOption
{
/// <summary>
/// Gets or sets the number.
/// </summary>
/// <remarks>
/// This number in combination with the option type is a reference for the client.
/// </remarks>
public int Number { get; set; }
/// <summary>
/// Gets or sets a type of the sub option.
/// </summary>
/// <remarks>
/// This is required for socket options, for example.
/// There, it defines the element (fire, water, etc.) of the socket option.
/// </remarks>
public int SubOptionType { get; set; }
/// <summary>
/// Gets or sets the type of the option.
/// </summary>
public virtual ItemOptionType? OptionType { get; set; }
/// <summary>
/// Gets or sets the power up definition which should apply when this item is carried.
/// </summary>
[MemberOfAggregate]
[Required]
public virtual PowerUpDefinition? PowerUpDefinition { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.OptionType}: {this.PowerUpDefinition} ({this.Number})";
}
}

View File

@@ -0,0 +1,47 @@
// <copyright file="ItemOptionCombinationBonus.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Attributes;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Defines a bonus which gets granted when the equipped items
/// have at least the specified required item options in total.
/// </summary>
/// <remarks>
/// Usage example: The "Socket Package Options" when a character wears socket items with all kind of elemental options.
/// </remarks>
[Cloneable]
public partial class ItemOptionCombinationBonus
{
/// <summary>
/// Gets or sets the description.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets the number.
/// </summary>
public int Number { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this bonus applies multiple times, for each set of found options.
/// </summary>
public bool AppliesMultipleTimes { get; set; }
/// <summary>
/// Gets or sets the required item options which all have to be fulfilled in order to get the <see cref="Bonus"/>.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<CombinationBonusRequirement> Requirements { get; protected set; } = null!;
/// <summary>
/// Gets or sets the bonus power up.
/// </summary>
[MemberOfAggregate]
public virtual PowerUpDefinition? Bonus { get; set; }
}

View File

@@ -0,0 +1,50 @@
// <copyright file="ItemOptionDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// The definition of an item option.
/// </summary>
[Cloneable]
public partial class ItemOptionDefinition
{
/// <summary>
/// Gets or sets the name of the option, for example "Luck", "Skill", "Normal Option".
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this option adds randomly.
/// </summary>
public bool AddsRandomly { get; set; }
/// <summary>
/// Gets or sets the add chance if this option adds randomly.
/// </summary>
public float AddChance { get; set; }
/// <summary>
/// Gets or sets the maximum options per item when it adds randomly by drop.
/// </summary>
/// <remarks>
/// Usually this is 1. But for some options (e.g. excellent) this can be bigger than 1.
/// </remarks>
public int MaximumOptionsPerItem { get; set; }
/// <summary>
/// Gets or sets the possible options.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<IncreasableItemOption> PossibleOptions { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return this.Name;
}
}

View File

@@ -0,0 +1,37 @@
// <copyright file="ItemOptionOfLevel.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Attributes;
/// <summary>
/// The item option, depending on the specified item level.
/// </summary>
[Cloneable]
public partial class ItemOptionOfLevel
{
/// <summary>
/// Gets or sets the level.
/// </summary>
public int Level { get; set; }
/// <summary>
/// Gets or sets the required item level.
/// </summary>
public int RequiredItemLevel { get; set; }
/// <summary>
/// Gets or sets the power up definition.
/// </summary>
[MemberOfAggregate]
public virtual PowerUpDefinition? PowerUpDefinition { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"Level {this.Level}: {this.PowerUpDefinition}";
}
}

View File

@@ -0,0 +1,111 @@
// <copyright file="ItemOptionType.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Type of an item option.
/// </summary>
/// <remarks>For example excellent, option, socket, luck.</remarks>
[Cloneable]
public partial class ItemOptionType
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this item option is visible to other players.
/// </summary>
public bool IsVisible { get; set; }
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="lhs">The LHS.</param>
/// <param name="rhs">The RHS.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator ==(ItemOptionType? lhs, ItemOptionType? rhs)
{
if (ReferenceEquals(lhs, rhs))
{
return true;
}
if (lhs is null || rhs is null)
{
return false;
}
return lhs.Equals(rhs);
}
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="lhs">The LHS.</param>
/// <param name="rhs">The RHS.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator !=(ItemOptionType? lhs, ItemOptionType? rhs)
{
return !(lhs == rhs);
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// </returns>
public override string ToString()
{
return this.Name;
}
/// <summary>
/// Determines whether the specified <see cref="object" />, is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object? obj)
{
if (obj is not ItemOptionType other)
{
return false;
}
return this.Id == other.Id;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}

View File

@@ -0,0 +1,85 @@
// <copyright file="ItemOptionTypes.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Some standard option types.
/// </summary>
public static class ItemOptionTypes
{
/// <summary>
/// Gets the excellent item option type.
/// </summary>
public static ItemOptionType Excellent { get; } = new() { Name = "Excellent Option", Id = new Guid("{6487C498-58E0-48E5-B409-35D7598313FC}"), IsVisible = true };
/// <summary>
/// Gets the wing option type.
/// </summary>
public static ItemOptionType Wing { get; } = new() { Name = "Wing Option", Id = new Guid("{55CB57A7-4FC6-47BB-9FEE-84E6C4EBCE95}") };
/// <summary>
/// Gets the luck option type.
/// </summary>
public static ItemOptionType Luck { get; } = new() { Name = "Luck (Critical Damage Chance 5%)", Id = new Guid("{3E3E9BE8-4E16-4F27-A7CF-986D48454D76}") };
/// <summary>
/// Gets the standard option option type.
/// </summary>
public static ItemOptionType Option { get; } = new() { Name = "Option", Id = new Guid("{F193F91E-86D7-4456-ADD8-A3667E731303}") };
/// <summary>
/// Gets the harmony option type.
/// </summary>
public static ItemOptionType HarmonyOption { get; } = new() { Name = "Jewel of Harmony Option", Id = new Guid("{0CA234F0-4A0F-4FA1-8E07-CFB89C1EC94F}") };
/// <summary>
/// Gets the ancient option type.
/// </summary>
public static ItemOptionType AncientOption { get; } = new() { Name = "Ancient Option", Id = new Guid("{436D820F-6D50-429D-AF63-BB0F59567DD1}"), IsVisible = true };
/// <summary>
/// Gets the ancient bonus option type.
/// </summary>
public static ItemOptionType AncientBonus { get; } = new() { Name = "Ancient Bonus Option", Id = new Guid("{5E2C10EF-E580-48D5-A48B-0FFCD0678966}") };
/// <summary>
/// Gets the guardian option type.
/// </summary>
public static ItemOptionType GuardianOption { get; } = new() { Name = "Guardian Option", Id = new Guid("{4AA95715-1ED3-453D-8D1D-093B281416CA}"), Description = "This option is added by the chaos machine with a jewel of guardian on level 380 items." };
/// <summary>
/// Gets the socket option type.
/// </summary>
public static ItemOptionType SocketOption { get; } = new() { Name = "Socket Option", Id = new Guid("{AAB309D3-CD97-4F77-AE1B-E9F904102502}") };
/// <summary>
/// Gets the socket bonus option type.
/// </summary>
public static ItemOptionType SocketBonusOption { get; } = new() { Name = "Socket Bonus Option", Id = new Guid("{43DA2C68-D6E1-4B94-ADB1-8864D92F8FB9}") };
/// <summary>
/// Gets the blue fenrir option type.
/// </summary>
/// <remarks>Applies only to the fenrir pet.</remarks>
public static ItemOptionType BlueFenrir { get; } = new() { Name = "Blue Fenrir Option", Id = new Guid("{C3ED45BC-5713-494D-A8C8-DC4AFAE56223}"), IsVisible = true };
/// <summary>
/// Gets the black fenrir option type.
/// </summary>
/// <remarks>Applies only to the fenrir pet.</remarks>
public static ItemOptionType BlackFenrir { get; } = new() { Name = "Black Fenrir Option", Id = new Guid("{ED978695-BD3E-46EA-86D8-F8C30EA99B50}"), IsVisible = true };
/// <summary>
/// Gets the gold fenrir option type.
/// </summary>
/// <remarks>Applies only to the fenrir pet.</remarks>
public static ItemOptionType GoldFenrir { get; } = new() { Name = "Gold Fenrir Option", Id = new Guid("{78E6DB0B-AC53-454C-956F-CD2B5467856E}"), IsVisible = true };
/// <summary>
/// Gets the dark horse option type.
/// </summary>
/// <remarks>Applies only to the dark horse pet.</remarks>
public static ItemOptionType DarkHorse { get; } = new() { Name = "Dark Horse Option", Id = new Guid("{D2295C44-E458-40F8-8555-87CFD9626616}"), IsVisible = true };
}

View File

@@ -0,0 +1,73 @@
// <copyright file="ItemSetGroup.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Defines an item set group. With (partial) completion of the set, additional options are getting applied.
/// </summary>
/// <remarks>
/// With this we can define a lot of things, for example:
/// - double wear bonus of single swords
/// - set bonus for defense rate
/// - set bonus for defense, if level is greater than 9
/// - ancient sets.
/// </remarks>
[Cloneable]
public partial class ItemSetGroup
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the options of this item set always apply to an item,
/// even if the group wasn't explicitly added to the <see cref="Item.ItemSetGroups"/>.
/// The minimum item count and the minimum set levels are respected.
/// </summary>
public bool AlwaysApplies { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the items are counted distinctly.
/// </summary>
/// <remarks>
/// For example, for the double wear bonus this has to be non-distinct, else we wouldn't get the bonus for wearing two of the same kind of swords.
/// </remarks>
public bool CountDistinct { get; set; }
/// <summary>
/// Gets or sets the minimum item count which is needed to get the bonus.
/// </summary>
public int MinimumItemCount { get; set; }
/// <summary>
/// Gets or sets the minimum level which all of the items of the set need to have to get the bonus.
/// </summary>
public int SetLevel { get; set; }
/// <summary>
/// Gets or sets the options. If the options depend on the item count, this options need to be ordered correctly.
/// </summary>
/// <remarks>
/// The order is defined by <see cref="ItemOption.Number"/>.
/// </remarks>
public virtual ItemOptionDefinition? Options { get; set; } = null!;
/// <summary>
/// Gets or sets the items of this set.
/// </summary>
/// <remarks>
/// Here we can define additional bonus options, like the ancient options (e.g. +5 / +10 Str etc.).
/// </remarks>
[MemberOfAggregate]
public virtual ICollection<ItemOfItemSet> Items { get; protected set; } = null!;
}

View File

@@ -0,0 +1,25 @@
// <copyright file="ItemSlotType.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// The item slot type. Each of this may have one or more actual item slots.
/// </summary>
[Cloneable]
public partial class ItemSlotType
{
/// <summary>
/// Gets or sets the description.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets the item slots of this slot type.
/// </summary>
public virtual ICollection<int> ItemSlots { get; protected set; } = null!;
}

View File

@@ -0,0 +1,70 @@
// <copyright file="LevelBonus.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// Defines a constant bonus, depending on item level.
/// </summary>
[Cloneable]
public partial class LevelBonus
{
private float _additionalValue;
private ConstantElement? _additionalValueElement;
/// <summary>
/// Initializes a new instance of the <see cref="LevelBonus"/> class.
/// </summary>
public LevelBonus()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LevelBonus"/> class.
/// </summary>
/// <param name="level">The level.</param>
/// <param name="constantValue">The constant value.</param>
public LevelBonus(int level, float constantValue)
{
this.Level = level;
this.AdditionalValue = constantValue;
}
/// <summary>
/// Gets or sets the level of the item.
/// </summary>
public int Level { get; set; }
/// <summary>
/// Gets or sets the additional value to the base value.
/// </summary>
public float AdditionalValue
{
get => this._additionalValue;
set
{
this._additionalValue = value;
this._additionalValueElement = null;
}
}
/// <summary>
/// Gets the element which represents the <see cref="AdditionalValue"/>.
/// </summary>
/// <param name="aggregateType">Type of the aggregate.</param>
/// <returns>The element which represents the <see cref="AdditionalValue"/>.</returns>
public IElement GetAdditionalValueElement(AggregateType aggregateType)
{
return this._additionalValueElement ??= new ConstantElement(this.AdditionalValue, aggregateType);
}
/// <inheritdoc />
public override string ToString()
{
return $"Level: {this.Level}: {this.AdditionalValue}";
}
}

View File

@@ -0,0 +1,41 @@
// <copyright file="JewelMix.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Defines a jewel mix.
/// Some <see cref="SingleJewel"/> can be mixed together to a single <see cref="MixedJewel"/> to save storage place.
/// When single jewels are needed again, the client can unmix his <see cref="MixedJewel"/> back to several <see cref="SingleJewel"/>.
/// </summary>
[Cloneable]
public partial class JewelMix
{
/// <summary>
/// Gets or sets gets the number of the mix.
/// </summary>
/// <remarks>
/// This number is a reference for the client.
/// </remarks>
public byte Number { get; set; }
/// <summary>
/// Gets or sets gets the single jewel item definition.
/// </summary>
public virtual ItemDefinition? SingleJewel { get; set; }
/// <summary>
/// Gets or sets gets the mixed jewel item definition.
/// </summary>
public virtual ItemDefinition? MixedJewel { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.SingleJewel?.Name} <> {this.MixedJewel?.Name}";
}
}

View File

@@ -0,0 +1,30 @@
// <copyright file="LevelDependentDamage.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a level-dependent damage.
/// </summary>
[Cloneable]
public partial class LevelDependentDamage
{
/// <summary>
/// Gets or sets the level belonging to this damage value.
/// </summary>
public int Level { get; set; }
/// <summary>
/// Gets or sets the damage belonging to this level.
/// </summary>
public int Damage { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"Level {this.Level}: {this.Damage}";
}
}

View File

@@ -0,0 +1,123 @@
// <copyright file="MagicEffectDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Attributes;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Magic Effect Definition. It can be an effect from a consumed item, a buff, or the result of an attack skill.
/// </summary>
[Cloneable]
public partial class MagicEffectDefinition
{
/// <summary>
/// Gets or sets the number.
/// </summary>
/// <remarks>
/// This number is a reference for the game client.
/// Negative numbers are for internal usage and their effects are not meant to be exposed to the game client.
/// </remarks>
public short Number { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the sub type.
/// Same sub type = cant stack. Adding a magic effect with the same sub type will cause the existing magic effect to disappear.
/// </summary>
public byte SubType { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the effect change is sent to observers.
/// </summary>
/// <remarks>
/// Some effects are not externally visible, but only to the player himself.
/// </remarks>
public bool InformObservers { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the effect gets stopped by a death of the player.
/// </summary>
public bool StopByDeath { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the duration of the effect should be sent.
/// </summary>
public bool SendDuration { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the duration of the effect depends on the target's level.
/// </summary>
public bool DurationDependsOnTargetLevel { get; set; }
/// <summary>
/// Gets or sets a value by which the effect target's (monster) level should be divided in case <see cref="DurationDependsOnTargetLevel"/> is <c>true</c>.
/// </summary>
public float MonsterTargetLevelDivisor { get; set; } = 1f;
/// <summary>
/// Gets or sets a value by which the effect target's (player) level should be divided in case <see cref="DurationDependsOnTargetLevel"/> is <c>true</c>.
/// </summary>
public float PlayerTargetLevelDivisor { get; set; } = 1f;
/// <summary>
/// Gets or sets the chance of applying the effect, in decimals.
/// </summary>
/// <remarks>
/// Results in a value of 1.0 if not set.
/// </remarks>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? Chance { get; set; }
/// <summary>
/// Gets or sets the chance of applying the effect in PvP, in decimals.
/// </summary>
/// <remarks>
/// Results in the same value as <see cref="Chance"/> if not set.
/// </remarks>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? ChancePvp { get; set; }
/// <summary>
/// Gets or sets the duration which describes how long the <see cref="PowerUpDefinitions"/> apply, in seconds.
/// </summary>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? Duration { get; set; }
/// <summary>
/// Gets or sets the duration which describes how long the <see cref="PowerUpDefinitions"/> apply to PvP, in seconds.
/// </summary>
/// <remarks>
/// Results in the same value as <see cref="Duration"/> if not set.
/// </remarks>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? DurationPvp { get; set; }
/// <summary>
/// Gets or sets the power up definitions which are used to create the actual power up element.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<PowerUpDefinition> PowerUpDefinitions { get; protected set; } = null!;
/// <summary>
/// Gets or sets the power up definitions which are used to create the actual power up element for PvP.
/// </summary>
/// <remarks>
/// Results in the same collection as <see cref="PowerUpDefinitions"/> if not set.
/// </remarks>
[MemberOfAggregate]
public virtual ICollection<PowerUpDefinition> PowerUpDefinitionsPvp { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return $"{this.Name} ({this.Number})";
}
}

View File

@@ -0,0 +1,97 @@
// <copyright file="MasterSkillDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// The definition of a master skill. One skill can have 0-n master skill definitions,
/// for example one skill can be used for different character classes at different Rank-Levels at different Roots.
/// </summary>
[Cloneable]
public partial class MasterSkillDefinition
{
/// <summary>
/// Gets or sets the root.
/// </summary>
[Required]
public virtual MasterSkillRoot? Root { get; set; }
/// <summary>
/// Gets or sets a collection with the required skills.
/// Just one skill (of at least level 10) of this list is required
/// to meet the requirements when learning this skill.
/// </summary>
public virtual ICollection<Skill> RequiredMasterSkills { get; protected set; } = null!;
/// <summary>
/// Gets or sets the rank.
/// The rank determines on which level the skill is located.
/// A skill at a higher rank can be learned, if there is at least
/// one skill of the same tree root at the direct rank below,
/// with a level same or greater than 10.
/// </summary>
public byte Rank { get; set; }
/// <summary>
/// Gets or sets the maximum level.
/// </summary>
/// <remarks>
/// Usually it's 20, but for some skills it's 10.
/// </remarks>
public byte MaximumLevel { get; set; }
/// <summary>
/// Gets or sets the minimum level which is required until the skill gets active.
/// It's also the number of master points which are initially required to learn the skill.
/// </summary>
public byte MinimumLevel { get; set; }
/// <summary>
/// Gets or sets the formula to calculate the effective value, depending on the level of the master skill.
/// </summary>
/// <remarks>
/// We use the syntax of MathParser.org.
/// To use the level in the formula, use the argument "level".
/// </remarks>
public string ValueFormula { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the formula to calculate the visible value, depending on the level of the master skill.
/// </summary>
/// <remarks>
/// We use the syntax of MathParser.org.
/// To use the level in the formula, use the argument "level".
/// </remarks>
public string DisplayValueFormula { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the target attribute of a passive skill boost.
/// </summary>
public virtual AttributeDefinition? TargetAttribute { get; set; }
/// <summary>
/// Gets or sets the type of how the calculated value is aggregated to the <see cref="TargetAttribute"/>.
/// </summary>
public AggregateType Aggregation { get; set; }
/// <summary>
/// Gets or sets the replaced skill. If this skill is defined, this master skill replaces it in the skill list.
/// The attack damage is also inherited and increased by the damage AND value of the master skill.
/// </summary>
public virtual Skill? ReplacedSkill { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the <see cref="ValueFormula"/> applies to the duration of the <see cref="ReplacedSkill"/>.
/// </summary>
public bool ExtendsDuration { get; set; }
/// <inheritdoc />
public override string ToString()
{
return "Master Skill Definition";
}
}

View File

@@ -0,0 +1,28 @@
// <copyright file="MasterSkillRoot.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// The root of a master skill tree. One character can have more than one root.
/// </summary>
[Cloneable]
public partial class MasterSkillRoot
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public LocalizedString Name { get; set; }
/// <inheritdoc />
public override string? ToString()
{
return this.Name.ToString();
}
}

View File

@@ -0,0 +1,100 @@
// <copyright file="MiniGameChangeEvent.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Defines the kind of targets for the <see cref="MiniGameChangeEvent"/>.
/// </summary>
public enum KillTarget
{
/// <summary>
/// Any object counts as kill.
/// </summary>
AnyObject,
/// <summary>
/// Any monster counts as kill.
/// </summary>
AnyMonster,
/// <summary>
/// A specific monster or destructible, defined by <see cref="MiniGameChangeEvent.TargetDefinition"/> counts as kill.
/// </summary>
Specific,
}
/// <summary>
/// An event which changes the mini game to a next phase.
/// Defines what the player needs to do to reach it,
/// and what happens when the requirements are fulfilled.
/// </summary>
[Cloneable]
public partial class MiniGameChangeEvent
{
/// <summary>
/// Gets or sets the index to define the order of the event.
/// Usually, events are worked through in sequential order.
/// </summary>
public int Index { get; set; }
/// <summary>
/// Gets or sets the description about the event.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets the (golden) message which should be shown to the player.
/// One placeholder can be used to show the triggering player name.
/// </summary>
public LocalizedString Message { get; set; }
/// <summary>
/// Gets or sets the targets which need to be killed to reach the required <see cref="NumberOfKills"/>.
/// </summary>
public KillTarget Target { get; set; }
/// <summary>
/// Gets or sets the minimum level of the <see cref="Target"/>s,
/// if no specific <see cref="TargetDefinition"/> is supplied.
/// </summary>
public short? MinimumTargetLevel { get; set; }
/// <summary>
/// Gets or sets the number of kills which the player(s) have to reach.
/// </summary>
public short NumberOfKills { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the <see cref="NumberOfKills"/> are meant to be
/// a multiple of the players of the game, or not.
/// </summary>
public bool MultiplyKillsByPlayers { get; set; }
/// <summary>
/// Gets or sets the target definition.
/// </summary>
public virtual MonsterDefinition? TargetDefinition { get; set; }
/// <summary>
/// Gets or sets the optional spawns which will appear when the players reach the goal.
/// </summary>
[MemberOfAggregate]
public virtual MonsterSpawnArea? SpawnArea { get; set; }
/// <summary>
/// Gets or sets the changes which will be applied to the terrain when the player reach the goal.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MiniGameTerrainChange> TerrainChanges { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return $"{this.Index}: {this.Description}";
}
}

View File

@@ -0,0 +1,155 @@
// <copyright file="MiniGameDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Definition for a mini game.
/// </summary>
/// <remarks>
/// Each game level of a mini game has its own <see cref="MiniGameDefinition"/>.
/// </remarks>
[Cloneable]
public partial class MiniGameDefinition
{
/// <summary>
/// Gets or sets the type of the mini game.
/// </summary>
public MiniGameType Type { get; set; }
/// <summary>
/// Gets or sets the name of the mini game.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the description of the mini game.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets the level of the mini game.
/// </summary>
public byte GameLevel { get; set; }
/// <summary>
/// Gets or sets the creation policy of the mini game.
/// </summary>
public MiniGameMapCreationPolicy MapCreationPolicy { get; set; }
/// <summary>
/// Gets or sets the duration between opening the mini game map and actually starting the game.
/// </summary>
public TimeSpan EnterDuration { get; set; }
/// <summary>
/// Gets or sets the duration of the game.
/// </summary>
public TimeSpan GameDuration { get; set; }
/// <summary>
/// Gets or sets the duration after which the game map exists when the game finished.
/// </summary>
public TimeSpan ExitDuration { get; set; }
/// <summary>
/// Gets or sets the maximum player count which can enter the game.
/// </summary>
public int MaximumPlayerCount { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to save the score as <see cref="MUnique.OpenMU.DataModel.Statistics.MiniGameRankingEntry"/>.
/// </summary>
public bool SaveRankingStatistics { get; set; }
/// <summary>
/// Gets or sets a value indicating whether only characters with <see cref="CharacterClass.IsMasterClass"/> = <see langword="true"/> can enter.
/// </summary>
public bool RequiresMasterClass { get; set; }
/// <summary>
/// Gets or sets the minimum character level to which this mini game is available.
/// </summary>
public int MinimumCharacterLevel { get; set; }
/// <summary>
/// Gets or sets the maximum character level to which this mini game is available.
/// </summary>
public int MaximumCharacterLevel { get; set; }
/// <summary>
/// Gets or sets the minimum character level for special characters (MG, DL) to which this mini game is available.
/// </summary>
public int MinimumSpecialCharacterLevel { get; set; }
/// <summary>
/// Gets or sets the maximum character level for special characters (MG, DL) to which this mini game is available.
/// </summary>
public int MaximumSpecialCharacterLevel { get; set; }
/// <summary>
/// Gets or sets the ticket item level which is required to enter the event.
/// </summary>
public int TicketItemLevel { get; set; }
/// <summary>
/// Gets or sets the entrance fee which is deducted from the players inventory
/// when entering the mini game event.
/// </summary>
public int EntranceFee { get; set; }
/// <summary>
/// Gets or sets a value indicating whether player killers are allowed to
/// enter the mini game.
/// </summary>
/// <value>
/// <c>true</c> if player killers are allowed to enter; otherwise, <c>false</c>.
/// </value>
public bool ArePlayerKillersAllowedToEnter { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to allow being in a party during the event.
/// </summary>
public bool AllowParty { get; set; }
/// <summary>
/// Gets or sets the entrance gate to the mini game map.
/// </summary>
[Required]
public virtual ExitGate? Entrance { get; set; }
/// <summary>
/// Gets or sets the ticket item which is required to enter the mini game.
/// </summary>
public virtual ItemDefinition? TicketItem { get; set; }
/// <summary>
/// Gets or sets the rewards which are given to the player when the game has been finished successfully.
/// Multiple awards per players are possible.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MiniGameReward> Rewards { get; protected set; } = null!;
/// <summary>
/// Gets or sets the spawn waves of the mini game.
/// Overlapping waves are possible.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MiniGameSpawnWave> SpawnWaves { get; protected set; } = null!;
/// <summary>
/// Gets or sets the mini game change events.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MiniGameChangeEvent> ChangeEvents { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return this.Name;
}
}

View File

@@ -0,0 +1,26 @@
// <copyright file="MiniGameMapCreationPolicy.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Defines how a mini game map is created.
/// </summary>
public enum MiniGameMapCreationPolicy
{
/// <summary>
/// One map is created per party and game level.
/// </summary>
OnePerParty,
/// <summary>
/// One map is created for each player.
/// </summary>
OnePerPlayer,
/// <summary>
/// One map is created and shared for all players.
/// </summary>
Shared,
}

View File

@@ -0,0 +1,125 @@
// <copyright file="MiniGameReward.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// The success flags of a mini game player.
/// </summary>
[Flags]
public enum MiniGameSuccessFlags
{
/// <summary>
/// No defined flag.
/// </summary>
None = 0,
/// <summary>
/// The player submitted the required quest item to the NPC.
/// </summary>
Winner = 1,
/// <summary>
/// The player is part of the party of a player classified as <see cref="Winner"/>.
/// </summary>
WinningParty = 1 << 1,
/// <summary>
/// The player is the winner or in the party of a player classified as <see cref="Winner"/>.
/// </summary>
WinnerOrInWinningParty = 1 << 2,
/// <summary>
/// The player is not the winner and not in the winners party.
/// </summary>
Loser = 1 << 3,
/// <summary>
/// The player managed to stay alive until the end.
/// </summary>
Alive = 1 << 4,
/// <summary>
/// The player died during the game.
/// </summary>
Dead = 1 << 5,
/// <summary>
/// A winner exists in the game.
/// </summary>
WinnerExists = 1 << 6,
/// <summary>
/// A winner doesn't exist in the game.
/// </summary>
WinnerNotExists = 1 << 7,
}
/// <summary>
/// Defines a reward of a <see cref="MiniGameDefinition"/>.
/// </summary>
[Cloneable]
public partial class MiniGameReward
{
/// <summary>
/// Gets or sets the rank to which this reward is applicable.
/// It's applicable, when more than one player can complete the mini game and
/// you'd want to give different awards for a differently ranked players.
/// </summary>
public int? Rank { get; set; }
/// <summary>
/// Gets or sets the reward type.
/// </summary>
public MiniGameRewardType RewardType { get; set; }
/// <summary>
/// Gets or sets the amount of the rewards.
/// In case of <see cref="MiniGameRewardType.Money"/> it's the amount of money.
/// In case of <see cref="MiniGameRewardType.Experience"/> it's the amount of experience.
/// In case of <see cref="MiniGameRewardType.Item"/> it's the amount of <see cref="ItemReward"/>.
/// </summary>
public int RewardAmount { get; set; }
/// <summary>
/// Gets or sets the required success flags for the reward.
/// </summary>
public MiniGameSuccessFlags RequiredSuccess { get; set; }
/// <summary>
/// Gets or sets the <see cref="DropItemGroup"/> of this reward, if <see cref="RewardType"/> is <see cref="MiniGameRewardType.Item"/>.
/// </summary>
public virtual DropItemGroup? ItemReward { get; set; }
/// <summary>
/// Gets or sets the monster/gate/status which needs to be killed during the event, so that this reward applies.
/// </summary>
public virtual MonsterDefinition? RequiredKill { get; set; }
/// <inheritdoc />
public override string ToString()
{
var result = new StringBuilder();
if (this.Rank.HasValue)
{
result.Append("Rank ").Append(this.Rank.Value).Append(": ");
}
if (this.RequiredSuccess > 0)
{
result.Append(this.RequiredSuccess).Append(": ");
}
result.Append(this.RewardType.ToString()).Append(" ").Append(this.RewardAmount);
if (this.ItemReward != null)
{
result.Append(" ").Append(this.ItemReward.Description);
}
return result.ToString();
}
}

View File

@@ -0,0 +1,48 @@
// <copyright file="MiniGameRewardType.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Defines the type of the <see cref="MiniGameReward"/>.
/// </summary>
public enum MiniGameRewardType
{
/// <summary>
/// Undefined reward type.
/// </summary>
Undefined,
/// <summary>
/// The reward is money which is added to the character of the player.
/// </summary>
Money,
/// <summary>
/// The reward is experience which is added to the character of the player.
/// </summary>
Experience,
/// <summary>
/// The reward is experience which is added to the character of the player,
/// which is calculated based on the remaining seconds.
/// Reward Value = <see cref="MiniGameReward.RewardAmount"/> * Remaining Seconds.
/// </summary>
ExperiencePerRemainingSeconds,
/// <summary>
/// The reward is an item.
/// </summary>
Item,
/// <summary>
/// The reward is an item to be dropped.
/// </summary>
ItemDrop,
/// <summary>
/// The reward is a score for the mini game.
/// </summary>
Score,
}

View File

@@ -0,0 +1,46 @@
// <copyright file="MiniGameSpawnWave.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Defines a spawn wave of a <see cref="MiniGameDefinition"/>.
/// </summary>
[Cloneable]
public partial class MiniGameSpawnWave
{
/// <summary>
/// Gets or sets the number of the wave, <seealso cref="MonsterSpawnArea.WaveNumber"/>.
/// </summary>
public byte WaveNumber { get; set; }
/// <summary>
/// Gets or sets the description about this wave.
/// </summary>
public LocalizedString Description { get; set; }
/// <summary>
/// Gets or sets a message which is shown to the player when the wave starts.
/// </summary>
public LocalizedString Message { get; set; }
/// <summary>
/// Gets or sets the starting time of the wave.
/// </summary>
public TimeSpan StartTime { get; set; }
/// <summary>
/// Gets or sets the end time of the wave.
/// </summary>
public TimeSpan EndTime { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"Wave {this.WaveNumber}: {this.Description}";
}
}

View File

@@ -0,0 +1,55 @@
// <copyright file="MiniGameTerrainChange.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a terrain change of the mini game map.
/// </summary>
[Cloneable]
public partial class MiniGameTerrainChange
{
/// <summary>
/// Gets or sets the type of terrain attribute which should be added or removed to or from the terrain.
/// </summary>
public TerrainAttributeType TerrainAttribute { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to set or to remove the <see cref="TerrainAttribute"/> from the terrain.
/// </summary>
public bool SetTerrainAttribute { get; set; }
/// <summary>
/// Gets or sets a value indicating whether no client update is required.
/// </summary>
public bool IsClientUpdateRequired { get; set; }
/// <summary>
/// Gets or sets the start value of the X-coordinate of the terrain area.
/// </summary>
public byte StartX { get; set; }
/// <summary>
/// Gets or sets the start value of the Y-coordinate of the terrain area.
/// </summary>
public byte StartY { get; set; }
/// <summary>
/// Gets or sets the end value of the X-coordinate of the terrain area.
/// </summary>
public byte EndX { get; set; }
/// <summary>
/// Gets or sets the end value of the Y-coordinate of the terrain area.
/// </summary>
public byte EndY { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{(this.SetTerrainAttribute ? "Set" : "Remove")} Attribute '{this.TerrainAttribute}' from ({this.StartX}, {this.StartY}) to ({this.EndX}, {this.EndY}) {(this.IsClientUpdateRequired ? "with" : "without")} client update";
}
}

View File

@@ -0,0 +1,41 @@
// <copyright file="MiniGameType.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Defines the type of a mini game.
/// </summary>
public enum MiniGameType
{
/// <summary>
/// Undefined mini game type.
/// </summary>
Undefined,
/// <summary>
/// The devil square event.
/// </summary>
DevilSquare,
/// <summary>
/// The blood castle event.
/// </summary>
BloodCastle,
/// <summary>
/// The chaos castle event.
/// </summary>
ChaosCastle,
/// <summary>
/// The illusion temple event.
/// </summary>
IllusionTemple,
/// <summary>
/// The doppelganger event.
/// </summary>
Doppelganger,
}

View File

@@ -0,0 +1,34 @@
// <copyright file="MonsterAttribute.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// The attribute and value of a monster.
/// </summary>
/// <remarks>
/// Just needed for entity framework, because it does not support the mapping of dictionaries. May be removed in the future.
/// </remarks>
[Cloneable]
public partial class MonsterAttribute
{
/// <summary>
/// Gets or sets the attribute definition.
/// </summary>
public virtual AttributeDefinition? AttributeDefinition { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
public float Value { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.AttributeDefinition}: {this.Value}";
}
}

View File

@@ -0,0 +1,349 @@
// <copyright file="MonsterDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Type of the window which will be openend when talking to the npc.
/// TODO: Maybe change to a class and do it data-driven.
/// </summary>
public enum NpcWindow
{
/// <summary>
/// No window defined.
/// </summary>
Undefined,
/// <summary>
/// A merchant window.
/// </summary>
Merchant,
/// <summary>
/// Another merchant window.
/// </summary>
Merchant1,
/// <summary>
/// A storage window.
/// </summary>
Storage,
/// <summary>
/// A vault storage.
/// </summary>
VaultStorage,
/// <summary>
/// A chaos machine window.
/// </summary>
ChaosMachine,
/// <summary>
/// A devil square window.
/// </summary>
DevilSquare,
/// <summary>
/// A blood castle window.
/// </summary>
BloodCastle,
/// <summary>
/// The pet trainer window.
/// </summary>
PetTrainer,
/// <summary>
/// The lahap window.
/// </summary>
Lahap,
/// <summary>
/// The castle senior window.
/// </summary>
CastleSeniorNPC,
/// <summary>
/// The elphis refinery window.
/// </summary>
ElphisRefinery,
/// <summary>
/// The refine stone making window.
/// </summary>
RefineStoneMaking,
/// <summary>
/// The jewel of harmony option removal window.
/// </summary>
RemoveJohOption,
/// <summary>
/// The illusion temple window.
/// </summary>
IllusionTemple,
/// <summary>
/// The chaos card combination window.
/// </summary>
ChaosCardCombination,
/// <summary>
/// The cherry blossom branches assembly window.
/// </summary>
CherryBlossomBranchesAssembly,
/// <summary>
/// The seed master window.
/// </summary>
SeedMaster,
/// <summary>
/// The seed researcher window.
/// </summary>
SeedResearcher,
/// <summary>
/// The stat reinitializer window.
/// </summary>
StatReInitializer,
/// <summary>
/// The delgado lucky coin registration window.
/// </summary>
DelgadoLuckyCoinRegistration,
/// <summary>
/// The doorkeeper titus duel watch window.
/// </summary>
DoorkeeperTitusDuelWatch,
/// <summary>
/// The lugard doppelganger entry window.
/// </summary>
LugardDoppelgangerEntry,
/// <summary>
/// The jerint gaion event entry window.
/// </summary>
JerintGaionEvententry,
/// <summary>
/// The julia warp market server window.
/// </summary>
JuliaWarpMarketServer,
/// <summary>
/// The guild master window.
/// </summary>
GuildMaster,
/// <summary>
/// The dialog window which allows to exchange or refine Lucky Item.
/// Used by NPC "David".
/// </summary>
CombineLuckyItem,
/// <summary>
/// The specific npc dialog. The client knows which dialog should be shown.
/// </summary>
/// <remarks>
/// Npc Numbers: 257, 543, 544, 566, 567, 568, 581.
/// Warning: If the game client doesn't have a dialog for this npc, it will crash.
/// </remarks>
NpcDialog,
/// <summary>
/// The dialog for the legacy quest system.
/// </summary>
LegacyQuest,
}
/// <summary>
/// Type of a non-player-character object.
/// </summary>
public enum NpcObjectKind
{
/// <summary>
/// The npc is a monster.
/// </summary>
Monster,
/// <summary>
/// The npc is passive, e.g. a merchant.
/// </summary>
PassiveNpc,
/// <summary>
/// The npc is a guard.
/// </summary>
Guard,
/// <summary>
/// The npc is a trap.
/// </summary>
Trap,
/// <summary>
/// The npc is a gate.
/// </summary>
Gate,
/// <summary>
/// The npc is a statue.
/// </summary>
Statue,
/// <summary>
/// The npc is a soccer ball.
/// </summary>
SoccerBall,
/// <summary>
/// The npc is a destructible.
/// </summary>
Destructible,
}
/// <summary>
/// A definition for a monster (or NPC in general).
/// </summary>
[Cloneable]
public partial class MonsterDefinition
{
/// <summary>
/// Gets or sets the unique number of this monster.
/// </summary>
public short Number { get; set; }
/// <summary>
/// Gets or sets the designation of this monster.
/// Not relevant for the server, however helpful for debugging/logging.
/// </summary>
public LocalizedString Designation { get; set; }
/// <summary>
/// Gets or sets the range in which a monster will move randomly?
/// It is not used yet. TODO: Find out what it's really good for. Remove, if not needed.
/// </summary>
public byte MoveRange { get; set; }
/// <summary>
/// Gets or sets the attack range in which the monster can attack without moving closer to the target.
/// </summary>
/// <value>
/// The attack range.
/// </value>
public byte AttackRange { get; set; }
/// <summary>
/// Gets or sets the view range in which the monster can recognize its targets.
/// </summary>
public short ViewRange { get; set; }
/// <summary>
/// Gets or sets the move delay for each step.
/// </summary>
public TimeSpan MoveDelay { get; set; }
/// <summary>
/// Gets or sets the attack delay, which is the time between attacks.
/// </summary>
public TimeSpan AttackDelay { get; set; }
/// <summary>
/// Gets or sets the delay which is waited until a died instance respawns.
/// </summary>
public TimeSpan RespawnDelay { get; set; }
/// <summary>
/// Gets or sets the attribute.
/// Not sure what this is.
/// Maybe the maximum numbers of concurrent additional attributes / magic effects?
/// TODO.
/// </summary>
public byte Attribute { get; set; }
/// <summary>
/// Gets or sets the number of maximum item drops after an instance of this monster died.
/// </summary>
public int NumberOfMaximumItemDrops { get; set; }
/// <summary>
/// Gets or sets the id of the npc window.
/// </summary>
public NpcWindow NpcWindow { get; set; }
/// <summary>
/// Gets or sets the kind of the object.
/// </summary>
public NpcObjectKind ObjectKind { get; set; }
/// <summary>
/// Gets or sets the name of the intelligence type, if this npc/monster uses a specific implementation of an INpcIntelligence.
/// </summary>
public string? IntelligenceTypeName { get; set; }
/// <summary>
/// Gets or sets the skill with which this monster is attacking. Also known as "Attack type".
/// </summary>
/// <remarks>The additional damage of the skill is usually NOT applied; However, magic effects are.</remarks>
public virtual Skill? AttackSkill { get; set; }
/// <summary>
/// Gets or sets the items of the merchant store. Is only relevant for merchant NPCs.
/// </summary>
[MemberOfAggregate]
public virtual ItemStorage? MerchantStore { get; set; }
/// <summary>
/// Gets or sets the item craftings. Is only relevant for crafting NPCs (chaos goblin etc.).
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemCrafting.ItemCrafting> ItemCraftings { get; protected set; } = null!;
/// <summary>
/// Gets or sets the drop item groups.
/// Some monsters drop special items. Examples: Kundun has the chance to drop ancient items.
/// </summary>
public virtual ICollection<DropItemGroup> DropItemGroups { get; protected set; } = null!;
/// <summary>
/// Gets or sets the attributes of this monster.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<MonsterAttribute> Attributes { get; protected set; } = null!;
/// <summary>
/// Gets or sets the quests which can be started through this npc.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<QuestDefinition> Quests { get; protected set; } = null!;
/// <summary>
/// Attribute default accessor.
/// </summary>
/// <param name="key">The attribute.</param>
/// <returns>The value of the attribute.</returns>
public float this[AttributeDefinition key]
{
get
{
return this.Attributes.First(a => a.AttributeDefinition == key).Value;
}
}
/// <inheritdoc/>
public override string ToString()
{
return $"{this.Designation} ({this.Number})";
}
}

View File

@@ -0,0 +1,154 @@
// <copyright file="MonsterSpawnArea.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Validation;
/// <summary>
/// Defines the trigger when a monster spawns.
/// </summary>
public enum SpawnTrigger
{
/// <summary>
/// The monster spawns and respawns automatically.
/// </summary>
Automatic,
/// <summary>
/// The monster spawns automatically during an event.
/// </summary>
AutomaticDuringEvent,
/// <summary>
/// The monster spawns just once at the beginning of an event.
/// </summary>
/// <remarks>
/// For example blood castle gates, statues. Also golden monsters.
/// </remarks>
OnceAtEventStart,
/// <summary>
/// The monster spawns automatically during a wave of an event.
/// </summary>
/// <remarks>
/// For example, at devil square different monsters spawn in different waves.
/// </remarks>
AutomaticDuringWave,
/// <summary>
/// The monster spawns once at the start of a wave of an event.
/// </summary>
/// <remarks>
/// For example, at devil square there is a wave of bosses, which spawn only once.
/// </remarks>
OnceAtWaveStart,
/// <summary>
/// The monster spawns manually controlled by the code of an event.
/// </summary>
/// <remarks>
/// For example chaos castle enemies, because their number is not known beforehand.
/// </remarks>
ManuallyForEvent,
/// <summary>
/// The object is wandering between maps. It spawns just at one spawn area
/// at the same time.
/// </summary>
/// <remarks>
/// Used for wandering merchants.
/// </remarks>
Wandering,
}
/// <summary>
/// Defines a monster spawn area.
/// </summary>
[Cloneable]
public partial class MonsterSpawnArea : IMapArea
{
/// <summary>
/// Gets or sets the monster definition.
/// </summary>
public virtual MonsterDefinition? MonsterDefinition { get; set; }
/// <summary>
/// Gets or sets the game map.
/// </summary>
public virtual GameMapDefinition? GameMap { get; set; }
/// <summary>
/// Gets or sets the upper left corner x coordinate.
/// </summary>
[LessThanOrEqualTo(nameof(X2))]
public byte X1 { get; set; }
/// <summary>
/// Gets or sets the upper left corner y coordinate.
/// </summary>
[LessThanOrEqualTo(nameof(Y2))]
public byte Y1 { get; set; }
/// <summary>
/// Gets or sets the bottom right corner x coordinate.
/// </summary>
public byte X2 { get; set; }
/// <summary>
/// Gets or sets the bottom right corner y coordinate.
/// </summary>
public byte Y2 { get; set; }
/// <summary>
/// Gets or sets the looking direction when spawning.
/// </summary>
public Direction Direction { get; set; }
/// <summary>
/// Gets or sets the quantity of monsters which should spawn in the defined area.
/// </summary>
public short Quantity { get; set; }
/// <summary>
/// Gets or sets the spawn trigger.
/// </summary>
public SpawnTrigger SpawnTrigger { get; set; }
/// <summary>
/// Gets or sets the wave to which this spawn area belongs to.
/// </summary>
public byte WaveNumber { get; set; }
/// <summary>
/// Gets or sets the maximum health (override) just for this spawn area.
/// If <c>null</c>, the default health of the <see cref="MonsterDefinition"/> applies.
/// </summary>
public int? MaximumHealthOverride { get; set; }
/// <inheritdoc/>
public override string ToString()
{
var isPoint = this.IsPoint();
var result = isPoint
? $"{this.MonsterDefinition?.Designation} - Qty: {this.Quantity} @ {this.X1}/{this.Y1}"
: $"{this.MonsterDefinition?.Designation} - Qty: {this.Quantity} @ {this.X1}/{this.Y1} to {this.X2}/{this.Y2}";
if (this.SpawnTrigger == SpawnTrigger.AutomaticDuringWave || this.SpawnTrigger == SpawnTrigger.OnceAtWaveStart)
{
result += $" - Wave: {this.WaveNumber}";
}
return result;
}
/// <summary>
/// Determines whether this instance is a spawn point.
/// </summary>
/// <returns>
/// <c>true</c> if this instance is point; otherwise, <c>false</c>.
/// </returns>
public bool IsPoint() => this.X1 == this.X2 && this.Y1 == this.Y2;
}

View File

@@ -0,0 +1,107 @@
// <copyright file="QuestDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.Annotations;
/// <summary>
/// The definition of a quest.
/// </summary>
[Cloneable]
public partial class QuestDefinition
{
/// <summary>
/// Gets or sets the NPC which gives the quest.
/// </summary>
public virtual MonsterDefinition? QuestGiver { get; set; }
/// <summary>
/// Gets or sets the name of the quest.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the group identifier of the quest.
/// </summary>
public short Group { get; set; }
/// <summary>
/// Gets or sets the number of the quest which should be unique within the group.
/// </summary>
public short Number { get; set; }
/// <summary>
/// Gets or sets the starting number of the quest which should be unique within the group. It's used as an identifier before a quest is started.
/// It's an identifier which is required on the client side to show the correct starting text.
/// </summary>
public short StartingNumber { get; set; }
/// <summary>
/// Gets or sets the refuse number of the quest which should be unique within the group. It's used as an identifier after a quest has been refused by the player.
/// It's an identifier which is required on the client side to show the correct follow-up text.
/// </summary>
public short RefuseNumber { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="QuestDefinition"/> is repeatable
/// which means if the character can start this quest multiple times even after it already
/// completed it once.
/// </summary>
/// <value>
/// <c>true</c> if repeatable; otherwise, <c>false</c>.
/// </value>
public bool Repeatable { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this quest requires a client action.
/// </summary>
public bool RequiresClientAction { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this quest requires a certain amount of money in the characters
/// inventory before it can be started.
/// </summary>
public int RequiredStartMoney { get; set; }
/// <summary>
/// Gets or sets the minimum character level.
/// </summary>
public int MinimumCharacterLevel { get; set; }
/// <summary>
/// Gets or sets the maximum character level.
/// </summary>
public int MaximumCharacterLevel { get; set; }
/// <summary>
/// Gets or sets the qualified character class. If <c>null</c>, it's valid for all character classes.
/// </summary>
public virtual CharacterClass? QualifiedCharacter { get; set; }
/// <summary>
/// Gets or sets the required monster kills to be able to complete this quest.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<QuestMonsterKillRequirement> RequiredMonsterKills { get; protected set; } = null!;
/// <summary>
/// Gets or sets the required items which should be in the characters inventory when the
/// player requests to complete the quest.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<QuestItemRequirement> RequiredItems { get; protected set; } = null!;
/// <summary>
/// Gets or sets the rewards when completing the quest successfully.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<QuestReward> Rewards { get; protected set; } = null!;
/// <inheritdoc />
public override string? ToString()
{
return this.Name.ToString();
}
}

View File

@@ -0,0 +1,38 @@
// <copyright file="QuestItemRequirement.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Defines the required item(s) which should be in the inventory of the character
/// when the player requests to complete the quest.
/// </summary>
[Cloneable]
public partial class QuestItemRequirement
{
/// <summary>
/// Gets or sets the required item.
/// </summary>
[Required]
public virtual ItemDefinition? Item { get; set; }
/// <summary>
/// Gets or sets the drop item group which should be considered when this quest is active and this requirement applies.
/// </summary>
public virtual DropItemGroup? DropItemGroup { get; set; }
/// <summary>
/// Gets or sets the minimum number of <see cref="Item"/>s.
/// </summary>
public int MinimumNumber { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.MinimumNumber} x {this.Item?.Name}";
}
}

View File

@@ -0,0 +1,31 @@
// <copyright file="QuestMonsterKillRequirement.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.Annotations;
/// <summary>
/// The monster kill requirement of a <see cref="QuestDefinition"/>.
/// </summary>
[Cloneable]
public partial class QuestMonsterKillRequirement
{
/// <summary>
/// Gets or sets the monster which must be killed.
/// </summary>
[Required]
public virtual MonsterDefinition? Monster { get; set; }
/// <summary>
/// Gets or sets the minimum number of killed <see cref="Monster"/>s.
/// </summary>
public int MinimumNumber { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.MinimumNumber}x {this.Monster}";
}
}

View File

@@ -0,0 +1,72 @@
// <copyright file="QuestReward.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Entities;
/// <summary>
/// Defines the reward of a completed quest.
/// </summary>
[Cloneable]
public partial class QuestReward
{
/// <summary>
/// Gets or sets the type of the reward.
/// </summary>
/// <value>
/// The type of the reward.
/// </value>
public QuestRewardType RewardType { get; set; }
/// <summary>
/// Gets or sets the value of the reward. It may have a different meaning, depending on the <see cref="RewardType"/>.
/// </summary>
public int Value { get; set; }
/// <summary>
/// Gets or sets the item reward.
/// The <see cref="Value"/> contains how often the item is rewarded.
/// </summary>
[MemberOfAggregate]
public virtual Item? ItemReward { get; set; }
/// <summary>
/// Gets or sets the attribute reward. It's set when <see cref="RewardType"/> is <see cref="QuestRewardType.Attribute"/>.
/// </summary>
public virtual AttributeDefinition? AttributeReward { get; set; }
/// <summary>
/// Gets or sets the attribute reward. It's set when <see cref="RewardType"/> is <see cref="QuestRewardType.Skill"/>.
/// </summary>
public virtual Skill? SkillReward { get; set; }
/// <inheritdoc />
public override string ToString()
{
if (this.RewardType == QuestRewardType.Item)
{
return $"{this.Value} x {this.ItemReward}";
}
if (this.RewardType == QuestRewardType.Skill)
{
return $"Skill: {this.SkillReward?.Name}";
}
if (this.RewardType == QuestRewardType.Attribute)
{
return $"Attribute: {this.Value} x {this.AttributeReward}";
}
if (this.RewardType is QuestRewardType.Experience or QuestRewardType.Money or QuestRewardType.LevelUpPoints or QuestRewardType.GensAttribution)
{
return $"{this.Value} x {this.RewardType}";
}
return $"{this.RewardType}";
}
}

View File

@@ -0,0 +1,67 @@
// <copyright file="QuestRewardType.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.DataModel.Entities;
/// <summary>
/// Defines the type of the reward of a quest.
/// </summary>
public enum QuestRewardType
{
/// <summary>
/// Undefined quest reward. Rewards nothing.
/// </summary>
Undefined,
/// <summary>
/// The completed quest rewards additional <see cref="Character.Experience"/>.
/// </summary>
Experience,
/// <summary>
/// The completed quest rewards additional money to the characters inventory.
/// </summary>
Money,
/// <summary>
/// The completed quest rewards an item one or more times.
/// </summary>
Item,
/// <summary>
/// The completed quest rewards gens attribution points.
/// </summary>
GensAttribution,
/// <summary>
/// The completed quest rewards additional <see cref="Character.LevelUpPoints"/>.
/// </summary>
LevelUpPoints,
/// <summary>
/// When completing the quest, the <see cref="Character.CharacterClass"/> evolves from the first to the second generation.
/// </summary>
CharacterEvolutionFirstToSecond,
/// <summary>
/// When completing the quest, the <see cref="Character.CharacterClass"/> evolves from the second to the third generation (master classes).
/// </summary>
CharacterEvolutionSecondToThird,
/// <summary>
/// The completed quest rewards an additional attribute with the specified value.
/// </summary>
/// <remarks>
/// For example, it could mean to add an attribute "Completed Marlon Quest" or "Combo Skill Available" which could
/// further be a requirement for skills etc. With the the definition of an attribute, we have maximum flexibility.
/// </remarks>
Attribute,
/// <summary>
/// The completed quest rewards an additional skill, if not yet learned.
/// </summary>
Skill,
}

View File

@@ -0,0 +1,45 @@
// <copyright file="Rectangle.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a rectangle.
/// </summary>
[Cloneable]
public partial class Rectangle
{
/// <summary>
/// Gets or sets the upper left corner, x-coordinate.
/// </summary>
public byte X1 { get; set; }
/// <summary>
/// Gets or sets the upper left corner, y-coordinate.
/// </summary>
public byte Y1 { get; set; }
/// <summary>
/// Gets or sets the bottom right corner, x-coordinate.
/// </summary>
public byte X2 { get; set; }
/// <summary>
/// Gets or sets the bottom right corner, y-coordinate.
/// </summary>
public byte Y2 { get; set; }
/// <inheritdoc />
public override string ToString()
{
if (this.X1 == this.X2 && this.Y1 == this.Y2)
{
return $"{this.X1} / {this.Y1}";
}
return $"{this.X1} / {this.Y1} to {this.X2} / {this.Y2}";
}
}

View File

@@ -0,0 +1,43 @@
// <copyright file="ServerEndpoint.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines an endpoint of a server.
/// </summary>
[Cloneable]
public partial class ServerEndpoint
{
/// <summary>
/// Initializes a new instance of the <see cref="ServerEndpoint"/> class.
/// </summary>
protected ServerEndpoint()
{
}
/// <summary>
/// Gets or sets the network port on which the server is listening.
/// </summary>
public int NetworkPort { get; set; }
/// <summary>
/// Gets or sets the client which is expected to connect.
/// </summary>
[Required]
public virtual GameClientDefinition? Client { get; set; }
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// </returns>
public override string ToString()
{
return $"Client: {this.Client?.Description}; Port: {this.NetworkPort}";
}
}

View File

@@ -0,0 +1,332 @@
// <copyright file="Skill.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Defines the damage type of a skill.
/// </summary>
public enum DamageType
{
/// <summary>
/// No damage type.
/// </summary>
None = -1,
/// <summary>
/// The physical damage type.
/// </summary>
Physical = 0,
/// <summary>
/// The wizardry damage type.
/// </summary>
Wizardry = 1,
/// <summary>
/// The curse damage type.
/// </summary>
Curse = 2,
/// <summary>
/// The summoned monster damage type.
/// </summary>
SummonedMonster = 3,
/// <summary>
/// The damage of the fenrir pet.
/// </summary>
Fenrir = 4,
/// <summary>
/// All damage types.
/// </summary>
All = 5,
}
/// <summary>
/// The skill types.
/// </summary>
public enum SkillType
{
/// <summary>
/// The skill hits its target directly.
/// </summary>
DirectHit = 0,
/// <summary>
/// The castle siege special skill.
/// </summary>
CastleSiegeSpecial = 1,
/// <summary>
/// Same as <see cref="DirectHit"/> but only appliable during castle siege event.
/// </summary>
CastleSiegeSkill = 2,
/// <summary>
/// Area skill damage, which automatically hit all targets in its target area. No declaration of hits by the client.
/// </summary>
AreaSkillAutomaticHits = 3,
/// <summary>
/// Area skill damage, but the hits have to be declared by the client.
/// </summary>
AreaSkillExplicitHits = 4,
/// <summary>
/// Area skill, which only hits the explicit target. No declaration of hits by the client.
/// </summary>
AreaSkillExplicitTarget = 5,
/// <summary>
/// The buff skill type. Applies magic effects on players.
/// </summary>
Buff = 10,
/// <summary>
/// The regeneration skill type. Regenerates the target attribute of the defined effect.
/// </summary>
Regeneration = 11,
/// <summary>
/// The passive boost skill type. Applies boosts to the player who has learned this skill, without the need to be casted.
/// </summary>
PassiveBoost = 20,
/// <summary>
/// The skill type for monster summoning.
/// </summary>
SummonMonster = 30,
/// <summary>
/// Other skill type.
/// </summary>
Other = 40,
}
/// <summary>
/// Defines how the target(s) of a skill are determined.
/// </summary>
public enum SkillTarget
{
/// <summary>
/// The target selection is undefined.
/// </summary>
Undefined = 0,
/// <summary>
/// The skill target is stated explicitly.
/// </summary>
Explicit = 1,
/// <summary>
/// The targets are implicitly all party member which are in view range of the attacker.
/// </summary>
ImplicitParty = 2,
/// <summary>
/// The targets are implicitly all players which are in <see cref="Skill.ImplicitTargetRange"/> of the attacker.
/// </summary>
ImplicitPlayersInRange = 3,
/// <summary>
/// The targets are implicitly all non-player-characters in <see cref="Skill.ImplicitTargetRange"/> of the attacker.
/// </summary>
ImplicitNpcsInRange = 4,
/// <summary>
/// The targets are implicitly all objects in <see cref="Skill.ImplicitTargetRange"/> of the attacker.
/// </summary>
ImplicitAllInRange = 5,
/// <summary>
/// The primary target is stated explicitly, additional targets are all objects in the <see cref="Skill.ImplicitTargetRange"/> of the primary target.
/// </summary>
ExplicitWithImplicitInRange = 6,
/// <summary>
/// The skill target is only the own player implicitly.
/// </summary>
ImplicitPlayer = 7,
}
/// <summary>
/// Defines how a skill is restricted to specific targets.
/// </summary>
public enum SkillTargetRestriction
{
/// <summary>
/// Undefined restriction. Skill can be applied to all possible entities (players, NPCs, etc.).
/// </summary>
Undefined = 0,
/// <summary>
/// The skill can only be applied to the executor.
/// </summary>
Self = 1,
/// <summary>
/// The skill can only be applied to the executor or its party members.
/// </summary>
Party = 2,
/// <summary>
/// The skill can only be applied to players (and summoned monsters of a player).
/// </summary>
Player = 3,
}
/// <summary>
/// Defines a skill.
/// </summary>
[Cloneable]
public partial class Skill
{
/// <summary>
/// Gets or sets the skill number.
/// </summary>
/// <remarks>
/// The client is referencing skills by this number.
/// </remarks>
public short Number { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the attack damage. Only relevant for attack skills.
/// </summary>
public int AttackDamage { get; set; }
/// <summary>
/// Gets or sets the requirements to execute the skill.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<AttributeRequirement> Requirements { get; protected set; } = null!;
/// <summary>
/// Gets or sets the attributes which values will be consumed by executing this skill.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<AttributeRequirement> ConsumeRequirements { get; protected set; } = null!;
/// <summary>
/// Gets or sets the attribute relationships which are applied during damage calculations.
/// </summary>
/// <remarks>
/// For example, horse skill:
/// * new AttributeRelationship(Stats.SkillBaseDamageBonus, 1.0f / 10, Stats.TotalStrength)
/// * new AttributeRelationship(Stats.SkillBaseDamageBonus, 1.0f / 5, Stats.TotalLeadership)
/// * new AttributeRelationship(Stats.SkillBaseDamageBonus, 10, Stats.HorseLevel).
/// </remarks>
[MemberOfAggregate]
public virtual ICollection<AttributeRelationship> AttributeRelationships { get; protected set; } = null!;
/// <summary>
/// Gets or sets the maximum range between executor of the skill and the target object.
/// </summary>
public short Range { get; set; }
/// <summary>
/// Gets or sets the type of the damage.
/// </summary>
public DamageType DamageType { get; set; }
/// <summary>
/// Gets or sets the type of the skill.
/// </summary>
public SkillType SkillType { get; set; }
/// <summary>
/// Gets or sets the <see cref="SkillTarget"/> which defines how the target(s) of a skill are determined.
/// </summary>
public SkillTarget Target { get; set; }
/// <summary>
/// Gets or sets the range for automatic targeting of additional target.
/// Has only effect if greater than <c>0</c>.
/// </summary>
/// <remarks>
/// Possible use cases: Additional hits for the "Fireburst" or "Deathstab" skills. They use direct targeting, but also hit nearby enemies.
/// </remarks>
public short ImplicitTargetRange { get; set; }
/// <summary>
/// Gets or sets the target restriction.
/// </summary>
public SkillTargetRestriction TargetRestriction { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the skill moves the attacker to the target.
/// </summary>
/// <remarks>Used by dark knight weapon skills, e.g. Slash.</remarks>
public bool MovesToTarget { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the skill moves the target.
/// </summary>
/// <remarks>
/// Used by dark knight weapon physical attack skills, e.g. Slash. The target gets pushed around randomly.
/// This is not a use case for the lightning skill, since resistances play a role there.
/// </remarks>
public bool MovesTarget { get; set; }
/// <summary>
/// Gets or sets the elemental modifier target attribute.
/// If this is set, hitting the target (successfully or not) may apply additional effects.
/// A value of <c>255</c> means, the target is immune to effects of this element.
/// </summary>
public virtual AttributeDefinition? ElementalModifierTarget { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the elemental modifier resistance should be ignored, which means the skill uses a specific logic.
/// </summary>
/// <remarks>
/// Not all skills have magic effects corresponding to their element,
/// e.g. Pollution (book of lagle, lightning) has a 100% chance iceing effect.
/// Other skills have magic effects which may or may not be related to their element, but which apply regardless of resistance,
/// e.g. Explosion (book of samut, fire) and Requiem (book of neil, wind) have 100% bleeding effects.
/// </remarks>
public bool SkipElementalModifier { get; set; }
/// <summary>
/// Gets or sets the magic effect definition. It will be applied for buff skills.
/// </summary>
public virtual MagicEffectDefinition? MagicEffectDef { get; set; }
/// <summary>
/// Gets or sets the character classes which are qualified to learn and use this skill.
/// </summary>
public virtual ICollection<CharacterClass> QualifiedCharacters { get; protected set; } = null!;
/// <summary>
/// Gets or sets the master skill definition. Only relevant for master skills.
/// </summary>
[MemberOfAggregate]
public virtual MasterSkillDefinition? MasterDefinition { get; set; }
/// <summary>
/// Gets or sets the area skill settings.
/// </summary>
[MemberOfAggregate]
public virtual AreaSkillSettings? AreaSkillSettings { get; set; }
/// <summary>
/// Gets or sets the number of hits per attack.
/// </summary>
public short NumberOfHitsPerAttack { get; set; }
/// <inheritdoc />
public override string? ToString()
{
return this.Name.ToString();
}
}

View File

@@ -0,0 +1,36 @@
// <copyright file="SkillComboDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Definition for a skill combo sequence.
/// </summary>
[Cloneable]
public partial class SkillComboDefinition
{
/// <summary>
/// Gets or sets the name of the combo sequence.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the maximum time until the final step has to be done.
/// </summary>
public TimeSpan MaximumCompletionTime { get; set; }
/// <summary>
/// Gets or sets the steps of the combo sequence.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<SkillComboStep> Steps { get; protected set; } = null!;
/// <inheritdoc />
public override string? ToString()
{
return this.Name.ToString();
}
}

View File

@@ -0,0 +1,36 @@
// <copyright file="SkillComboStep.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Definition for one step for one skill of a combo sequence.
/// There can be multiple steps with the same <see cref="Order"/> but different skills.
/// </summary>
[Cloneable]
public partial class SkillComboStep
{
/// <summary>
/// Gets or sets the order for the step in the sequence.
/// </summary>
public int Order { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this step is a final step which ends the combo.
/// </summary>
public bool IsFinalStep { get; set; }
/// <summary>
/// Gets or sets the skill of this step.
/// </summary>
public virtual Skill? Skill { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.Order} - {this.Skill}";
}
}

View File

@@ -0,0 +1,64 @@
// <copyright file="StatAttributeDefinition.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// Defines a stat attribute, which may be increasable by the player.
/// </summary>
[Cloneable]
public partial class StatAttributeDefinition
{
private AttributeDefinition? attribute = null!;
/// <summary>
/// Initializes a new instance of the <see cref="StatAttributeDefinition"/> class.
/// </summary>
public StatAttributeDefinition()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="StatAttributeDefinition" /> class.
/// </summary>
/// <param name="attribute">The attribute.</param>
/// <param name="baseValue">The base value.</param>
/// <param name="increasableByPlayer">if set to <c>true</c> it is increasable by the player.</param>
public StatAttributeDefinition(AttributeDefinition attribute, float baseValue, bool increasableByPlayer)
{
this.attribute = attribute;
this.BaseValue = baseValue;
this.IncreasableByPlayer = increasableByPlayer;
}
/// <summary>
/// Gets or sets the attribute definition of this stat attribute.
/// </summary>
#pragma warning disable S2292 // When this would be an auto property, it would lead to a virtual member call in the constructor.
public virtual AttributeDefinition? Attribute
#pragma warning restore S2292
{
get { return this.attribute; }
set { this.attribute = value; }
}
/// <summary>
/// Gets or sets the base value, which is the initial value without an increase of the player.
/// </summary>
public float BaseValue { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this stat is increasable by the player in any way.
/// </summary>
public bool IncreasableByPlayer { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.Attribute}: {this.BaseValue}";
}
}

View File

@@ -0,0 +1,84 @@
// <copyright file="SystemConfiguration.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Properties;
using MUnique.OpenMU.Network;
/// <summary>
/// System-wide configuration values.
/// </summary>
[AggregateRoot]
[Cloneable]
[Display(ResourceType = typeof(Resources), Name = nameof(Resources.SystemConfiguration_Name), Description = nameof(Resources.SystemConfiguration_Description))]
public partial class SystemConfiguration
{
/// <summary>
/// Gets or sets the type of the ip resolver.
/// </summary>
[Display(
Order = 1,
Name = nameof(Resources.SystemConfiguration_IpResolver_Name),
Description = nameof(Resources.SystemConfiguration_IpResolver_Description),
GroupName = nameof(Resources.SystemConfiguration_IpResolver_Name),
ResourceType = typeof(Resources))]
public IpResolverType IpResolver { get; set; }
/// <summary>
/// Gets or sets the ip resolver parameter, when <see cref="IpResolverType.Custom"/>
/// is used.
/// </summary>
[Display(
Order = 2,
Name = nameof(Resources.SystemConfiguration_IpResolverParameter_Name),
Description = nameof(Resources.SystemConfiguration_IpResolverParameter_Description),
GroupName = nameof(Resources.SystemConfiguration_IpResolver_Name),
ResourceType = typeof(Resources))]
public string? IpResolverParameter { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to automatically start
/// the listeners of the servers when the server process starts.
/// Only applicable to the All-In-One Startup. The distributed processes
/// always start their listeners automatically.
/// </summary>
[Display(
Order = 3,
Name = nameof(Resources.SystemConfiguration_AutoStart_Name),
Description = nameof(Resources.SystemConfiguration_AutoStart_Description),
ResourceType = typeof(Resources))]
public bool AutoStart { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to automatically update the
/// database schema when the server process starts.
/// Only applicable to the All-In-One Startup. In a distributed deployment,
/// the user must start the update manually over the admin panel.
/// </summary>
[Display(
Order = 4,
Name = nameof(Resources.SystemConfiguration_AutoUpdateSchema_Name),
Description = nameof(Resources.SystemConfiguration_AutoUpdateSchema_Description),
ResourceType = typeof(Resources))]
public bool AutoUpdateSchema { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the user can enter commands in
/// the console of the All-In-One Startup process.
/// </summary>
[Display(
Order = 5,
Name = nameof(Resources.SystemConfiguration_ReadConsoleInput_Name),
Description = nameof(Resources.SystemConfiguration_ReadConsoleInput_Description),
ResourceType = typeof(Resources))]
public bool ReadConsoleInput { get; set; }
/// <inheritdoc />
public override string ToString()
{
return "System Configuration";
}
}

View File

@@ -0,0 +1,36 @@
// <copyright file="TerrainAttributeType.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// Defines the attribute which should be set/unset.
/// </summary>
public enum TerrainAttributeType
{
/// <summary>
/// The coordinate is a safezone.
/// </summary>
Safezone = 1,
/// <summary>
/// The coordinate is occupied by a character.
/// </summary>
Character = 2,
/// <summary>
/// The coordinate is blocked and cant be passed by a character.
/// </summary>
Blocked = 4,
/// <summary>
/// The coordinate is blocked, because there is no ground and cant be passed by a character.
/// </summary>
NoGround = 8,
/// <summary>
/// The coordinate is blocked by water and cant be passed by a character.
/// </summary>
Water = 16,
}

View File

@@ -0,0 +1,46 @@
// <copyright file="WarpInfo.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Annotations;
/// <summary>
/// Defines a warp list entry.
/// </summary>
[Cloneable]
public partial class WarpInfo
{
/// <summary>
/// Gets or sets the index.
/// </summary>
public int Index { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public LocalizedString Name { get; set; }
/// <summary>
/// Gets or sets the warp costs.
/// </summary>
public int Costs { get; set; }
/// <summary>
/// Gets or sets the level requirement which a character needs to fulfill so that it can warp to the <see cref="Gate"/>.
/// </summary>
public int LevelRequirement { get; set; }
/// <summary>
/// Gets or sets the gate.
/// </summary>
[Required]
public virtual ExitGate? Gate { get; set; }
/// <inheritdoc />
public override string? ToString()
{
return this.Name.ToString();
}
}

View File

@@ -0,0 +1,57 @@
// <copyright file="CultureHelper.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel;
using System.Globalization;
using System.Resources;
using Nito.Disposables;
using Nito.Disposables.Internals;
/// <summary>
/// Helper class for culture related operations.
/// </summary>
public static class CultureHelper
{
/// <summary>
/// Sets the temporary culture for the current thread.
/// Should be disposed after usage to revert to the previous culture.
/// Should not be used between async calls.
/// </summary>
/// <param name="cultureInfo">The culture information.</param>
/// <returns>A disposable to revert to the previous culture.</returns>
public static IDisposable SetTemporaryCulture(CultureInfo cultureInfo)
{
var oldUiCulture = CultureInfo.CurrentUICulture;
var oldCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentUICulture = cultureInfo;
CultureInfo.CurrentCulture = cultureInfo;
return Disposable.Create(() =>
{
CultureInfo.CurrentUICulture = oldUiCulture;
CultureInfo.CurrentCulture = oldCulture;
});
}
/// <summary>
/// Gets the available cultures for a specific resource.
/// </summary>
/// <typeparam name="TResources">The type of the resources.</typeparam>
/// <returns>An enumeration of <see cref="CultureInfo.TwoLetterISOLanguageName"/> of available cultures of the given resource type.</returns>
public static IEnumerable<CultureInfo> GetAvailableCultures<TResources>()
{
var resourceManager = new ResourceManager(typeof(TResources));
var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
var result = cultures
.Except([CultureInfo.InvariantCulture])
.Where(culture => culture is { IsNeutralCulture: true, TwoLetterISOLanguageName: "en" }
|| !object.Equals(resourceManager.GetResourceSet(culture, true, false), null))
.WhereNotNull()
.ToList();
return result;
}
}

View File

@@ -0,0 +1,155 @@
// <copyright file="Account.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using System.ComponentModel;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// The state of an account.
/// </summary>
public enum AccountState
{
/// <summary>
/// Normal player account.
/// </summary>
Normal,
/// <summary>
/// Spectator account, invisible to players and monsters.
/// </summary>
Spectator,
/// <summary>
/// Game Master account.
/// </summary>
GameMaster,
/// <summary>
/// Game Master account, invisible to players and monsters.
/// </summary>
GameMasterInvisible,
/// <summary>
/// Banned account.
/// </summary>
Banned,
/// <summary>
/// Temporarily banned account.
/// </summary>
TemporarilyBanned,
}
/// <summary>
/// The account of a player.
/// </summary>
[AggregateRoot]
public class Account
{
/// <summary>
/// Gets or sets the unique login name.
/// </summary>
[Required]
public string LoginName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the hash of the password, preferrably of BCrypt.
/// </summary>
public string PasswordHash { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the security code which is used to confirm character deletion and guild kicks.
/// </summary>
public string SecurityCode { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the e mail address.
/// </summary>
public string EMail { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the iso code (ISO 639-2/3) of the preferred language of the player.
/// </summary>
[DefaultValue("en")]
public string LanguageIsoCode { get; set; } = "en";
/// <summary>
/// Gets or sets the date and time until which the chat ban is in effect.
/// </summary>
public DateTime? ChatBanUntil { get; set; }
/// <summary>
/// Gets or sets the unlocked character classes which are locked by default.
/// </summary>
/// <remarks>
/// Some classes are only available when the player reached a certain level before, or when he paid for some unlock ticket.
/// </remarks>
[HiddenAtCreation]
public virtual ICollection<CharacterClass> UnlockedCharacterClasses { get; protected set; } = null!;
/// <summary>
/// Gets or sets the registration date.
/// </summary>
public DateTime RegistrationDate { get; set; } = DateTime.UtcNow;
/// <summary>
/// Gets or sets the state.
/// </summary>
public AccountState State { get; set; }
/// <summary>
/// Gets or sets the timezone of the player, difference to UTC.
/// </summary>
public short TimeZone { get; set; }
/// <summary>
/// Gets or sets the vault password.
/// </summary>
[HiddenAtCreation]
public string VaultPassword { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the vault.
/// </summary>
[MemberOfAggregate]
[HiddenAtCreation]
public virtual ItemStorage? Vault { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is vault extended.
/// </summary>
public bool IsVaultExtended { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is a template account
/// and therefore read-only within the game server.
/// </summary>
public bool IsTemplate { get; set; }
/// <summary>
/// Gets or sets the characters.
/// </summary>
[MemberOfAggregate]
[HiddenAtCreation]
public virtual ICollection<Character> Characters { get; protected set; } = null!;
/// <summary>
/// Gets or sets the stat attributes which are applied across all characters of the account.
/// </summary>
/// <remarks>
/// Please note, that it's not possible to add stat attribute with the same
/// attribute definition to the <see cref="Account.Attributes"/> and the <see cref="Character.Attributes"/>.
/// </remarks>
[MemberOfAggregate]
public virtual ICollection<StatAttribute> Attributes { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return this.LoginName;
}
}

View File

@@ -0,0 +1,46 @@
// <copyright file="AppearanceData.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// The appearance data of a character.
/// </summary>
public class AppearanceData : IAppearanceData
{
/// <summary>
/// Occurs when the appearance of the player changed.
/// </summary>
/// <remarks>
/// This never happens in this implementation.
/// </remarks>
public event EventHandler? AppearanceChanged;
/// <summary>
/// Gets or sets the character class.
/// </summary>
public virtual CharacterClass? CharacterClass { get; set; }
/// <summary>
/// Gets or sets the character status.
/// </summary>
public CharacterStatus CharacterStatus { get; set; }
/// <inheritdoc />
public CharacterPose Pose { get; set; }
/// <inheritdoc />
public bool FullAncientSetEquipped { get; set; }
/// <summary>
/// Gets or sets the equipped items.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemAppearance> EquippedItems { get; protected set; } = null!;
/// <inheritdoc />
IEnumerable<ItemAppearance> IAppearanceData.EquippedItems => this.EquippedItems;
}

View File

@@ -0,0 +1,271 @@
// <copyright file="Character.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// The hero state of a player. Given enough time, the state converges to <see cref="Normal"/>.
/// </summary>
public enum HeroState
{
/// <summary>
/// The character is new.
/// </summary>
New,
/// <summary>
/// The character is a hero.
/// </summary>
Hero,
/// <summary>
/// The character is a hero, but the hero state is almost gone.
/// </summary>
LightHero,
/// <summary>
/// The normal state.
/// </summary>
Normal,
/// <summary>
/// The character killed another character, and has a kill warning.
/// </summary>
PlayerKillWarning,
/// <summary>
/// The character killed two characters, and has some restrictions.
/// </summary>
PlayerKiller1stStage,
/// <summary>
/// The character killed more than two characters, and has hard restrictions.
/// </summary>
PlayerKiller2ndStage,
}
/// <summary>
/// The Character Status of a player.
/// </summary>
public enum CharacterStatus
{
/// <summary>
/// The character is normal.
/// </summary>
Normal = 0,
/// <summary>
/// The character is banned.
/// </summary>
Banned = 1,
/// <summary>
/// The character is a GameMaster (have mu logo on the head).
/// </summary>
GameMaster = 32,
}
/// <summary>
/// The character pose.
/// </summary>
public enum CharacterPose : byte
{
/// <summary>
/// The character is standing (normal).
/// </summary>
Standing = 0,
/// <summary>
/// The character is sitting on an object.
/// </summary>
Sitting = 2,
/// <summary>
/// The character is leaning towards something (wall etc).
/// </summary>
Leaning = 3,
/// <summary>
/// The character is hanging on something.
/// </summary>
Hanging = 4,
}
/// <summary>
/// The character of a player.
/// </summary>
public class Character
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
[Required]
public string Name { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the character class.
/// </summary>
[Required]
public virtual CharacterClass? CharacterClass { get; set; }
/// <summary>
/// Gets or sets the character slot in the account.
/// </summary>
public byte CharacterSlot { get; set; }
/// <summary>
/// Gets or sets the create date.
/// </summary>
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
/// <summary>
/// Gets or sets the experience.
/// </summary>
public long Experience { get; set; }
/// <summary>
/// Gets or sets the master experience.
/// </summary>
public long MasterExperience { get; set; }
/// <summary>
/// Gets or sets the remaining level up points which can be spent on increasable stat attributes.
/// </summary>
public int LevelUpPoints { get; set; }
/// <summary>
/// Gets or sets the master level up points which can be spent on master skills.
/// </summary>
public int MasterLevelUpPoints { get; set; }
/// <summary>
/// Gets or sets the current game map.
/// </summary>
[Required]
public virtual GameMapDefinition? CurrentMap { get; set; }
/// <summary>
/// Gets or sets the x-coordinate of its map position.
/// </summary>
public byte PositionX { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of its map position.
/// </summary>
public byte PositionY { get; set; }
/// <summary>
/// Gets or sets the player kill count.
/// </summary>
public int PlayerKillCount { get; set; }
/// <summary>
/// Gets or sets the remaining seconds for the current hero state, when the player state is not normal.
/// </summary>
public int StateRemainingSeconds { get; set; }
/// <summary>
/// Gets or sets the hero state.
/// </summary>
public HeroState State { get; set; }
/// <summary>
/// Gets or sets the character status.
/// </summary>
public CharacterStatus CharacterStatus { get; set; }
/// <summary>
/// Gets or sets the pose.
/// </summary>
public CharacterPose Pose { get; set; }
/// <summary>
/// Gets or sets the used fruit points.
/// </summary>
public int UsedFruitPoints { get; set; }
/// <summary>
/// Gets or sets the used negative fruit points.
/// </summary>
public int UsedNegFruitPoints { get; set; }
/// <summary>
/// Gets or sets the number of inventory extensions.
/// </summary>
public int InventoryExtensions { get; set; }
/// <summary>
/// Gets or sets the key configuration, which is set by the client and just saved as is.
/// </summary>
public byte[]? KeyConfiguration { get; set; }
/// <summary>
/// Gets or sets the configuration of the mu helper, which is set by the client and just saved as is.
/// </summary>
public byte[]? MuHelperConfiguration { get; set; }
/// <summary>
/// Gets or sets the name of the personal store.
/// </summary>
public string? StoreName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this character has it's store opened.
/// </summary>
public bool IsStoreOpened { get; set; }
/// <summary>
/// Gets or sets the stat attributes.
/// </summary>
/// <remarks>
/// Please note, that it's not possible to add stat attribute with the same
/// attribute definition to the <see cref="Account.Attributes"/> and the <see cref="Character.Attributes"/>.
/// </remarks>
[MemberOfAggregate]
public virtual ICollection<StatAttribute> Attributes { get; protected set; } = null!;
/// <summary>
/// Gets or sets the letters.
/// </summary>
[MemberOfAggregate]
public virtual IList<LetterHeader> Letters { get; protected set; } = null!;
/// <summary>
/// Gets or sets the learned skills.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<SkillEntry> LearnedSkills { get; protected set; } = null!;
/// <summary>
/// Gets or sets the inventory.
/// </summary>
[MemberOfAggregate]
public virtual ItemStorage? Inventory { get; set; }
/// <summary>
/// Gets or sets the drop item groups.
/// </summary>
public virtual ICollection<DropItemGroup> DropItemGroups { get; protected set; } = null!;
/// <summary>
/// Gets or sets the quest states.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<CharacterQuestState> QuestStates { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return this.Name;
}
}

View File

@@ -0,0 +1,46 @@
// <copyright file="CharacterQuestState.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.DataModel.Configuration.Quests;
/// <summary>
/// Keeps the progress state of a started quest.
/// It's only possible to have one quest of the same group active.
/// </summary>
public class CharacterQuestState
{
/// <summary>
/// Gets or sets the group.
/// </summary>
public short Group { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the game client action was performed.
/// </summary>
public bool ClientActionPerformed { get; set; }
/// <summary>
/// Gets or sets the last finished quest of the <see cref="Group"/>.
/// </summary>
public virtual QuestDefinition? LastFinishedQuest { get; set; }
/// <summary>
/// Gets or sets the active quest.
/// </summary>
public virtual QuestDefinition? ActiveQuest { get; set; }
/// <summary>
/// Gets or sets the requirement states for the current <see cref="ActiveQuest"/>.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<QuestMonsterKillRequirementState> RequirementStates { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return $"#{this.Group}, Last finished: '{this.LastFinishedQuest?.Name}', Active: '{this.ActiveQuest?.Name}'";
}
}

View File

@@ -0,0 +1,28 @@
// <copyright file="Guild.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
/// <summary>
/// A guild is a group of players who like to play together.
/// </summary>
[AggregateRoot]
public class Guild : OpenMU.Interfaces.Guild
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the members.
/// </summary>
public virtual ICollection<GuildMember> Members { get; protected set; } = null!;
/// <inheritdoc />
public override string ToString()
{
return this.Name ?? "<Guild>";
}
}

View File

@@ -0,0 +1,44 @@
// <copyright file="GuildMember.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// Information about a guild member.
/// </summary>
public class GuildMember
{
/// <summary>
/// Initializes a new instance of the <see cref="GuildMember"/> class.
/// </summary>
public GuildMember()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GuildMember"/> class.
/// </summary>
/// <param name="id">The identifier.</param>
public GuildMember(Guid id)
{
this.Id = id;
}
/// <summary>
/// Gets or sets the identifier. Should be the same id as the character id to which it belongs.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the guild identifier to which the member belongs.
/// </summary>
public Guid GuildId { get; set; }
/// <summary>
/// Gets or sets the status of the member.
/// </summary>
public GuildPosition Status { get; set; }
}

View File

@@ -0,0 +1,43 @@
// <copyright file="IAppearanceData.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// The interface for the appearance data.
/// </summary>
public interface IAppearanceData
{
/// <summary>
/// Occurs when the appearance of the player changed.
/// </summary>
event EventHandler? AppearanceChanged;
/// <summary>
/// Gets the character class.
/// </summary>
CharacterClass? CharacterClass { get; }
/// <summary>
/// Gets the character status.
/// </summary>
CharacterStatus CharacterStatus { get; }
/// <summary>
/// Gets the current pose.
/// </summary>
CharacterPose Pose { get; }
/// <summary>
/// Gets a value indicating whether a full ancient set is equipped.
/// </summary>
bool FullAncientSetEquipped { get; }
/// <summary>
/// Gets the equipped items.
/// </summary>
IEnumerable<ItemAppearance> EquippedItems { get; }
}

View File

@@ -0,0 +1,226 @@
// <copyright file="Item.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using System.Globalization;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.DataModel.Properties;
/// <summary>
/// The item.
/// </summary>
[Cloneable]
public partial class Item
{
/// <summary>
/// Gets or sets the item slot in the <see cref="ItemStorage"/>.
/// </summary>
public byte ItemSlot { get; set; }
/// <summary>
/// Gets or sets the item definition.
/// </summary>
[Required]
public virtual ItemDefinition? Definition { get; set; }
/// <summary>
/// Gets or sets the currently remaining durability.
/// </summary>
public double Durability { get; set; }
/// <summary>
/// Gets or sets the level of the item.
/// </summary>
public byte Level { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this item instance provides the weapon skill while being equipped.
/// </summary>
public bool HasSkill { get; set; }
/// <summary>
/// Gets or sets the item options.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<ItemOptionLink> ItemOptions { get; protected set; } = null!;
/// <summary>
/// Gets or sets the applied item set groups (Ancient Set).
/// </summary>
public virtual ICollection<ItemOfItemSet> ItemSetGroups { get; protected set; } = null!;
/// <summary>
/// Gets or sets the socket count. This limits the amount of socket options in the <see cref="ItemOptions"/>.
/// </summary>
public int SocketCount { get; set; }
/// <summary>
/// Gets or sets the price which was set by the player for his personal store.
/// </summary>
public int? StorePrice { get; set; }
/// <summary>
/// Gets or sets the pet experience.
/// Only applies, if this item is actually a trainable pet.
/// </summary>
public int PetExperience { get; set; }
/// <summary>
/// Assigns the values of another item to this item.
/// </summary>
/// <param name="otherItem">The other item.</param>
public void AssignValues(Item otherItem)
{
this.Definition = otherItem.Definition;
this.Durability = otherItem.Durability;
this.Level = otherItem.Level;
this.HasSkill = otherItem.HasSkill;
this.SocketCount = otherItem.SocketCount;
this.PetExperience = otherItem.PetExperience;
if (otherItem.ItemOptions != null && otherItem.ItemOptions.Any())
{
this.ItemOptions.Clear();
foreach (var option in otherItem.ItemOptions)
{
this.ItemOptions.Add(this.CloneItemOptionLink(option));
}
}
if (otherItem.ItemSetGroups != null && otherItem.ItemSetGroups.Any())
{
this.ItemSetGroups.Clear();
foreach (var setGroup in otherItem.ItemSetGroups)
{
this.ItemSetGroups.Add(setGroup);
}
}
}
/// <inheritdoc/>
public override string ToString()
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("Slot ").Append(this.ItemSlot).Append(": ");
if (this.ItemOptions.Any(o => o.ItemOption?.OptionType == ItemOptionTypes.Excellent))
{
stringBuilder.Append("Excellent ");
}
var ancientSet = this.ItemSetGroups.FirstOrDefault(s => s.AncientSetDiscriminator != 0)?.ItemSetGroup;
if (ancientSet != null)
{
stringBuilder.Append(ancientSet.Name).Append(" ");
}
var itemName = this.Definition?.GetNameForLevel(this.Level);
stringBuilder.Append(itemName);
if (this.Level > 0)
{
stringBuilder.Append("+").Append(this.Level);
}
foreach (var option in this.ItemOptions
.Where(o => o.ItemOption?.OptionType != ItemOptionTypes.Luck)
.OrderBy(o => o.ItemOption?.OptionType == ItemOptionTypes.Option))
{
var levelOption = option.ItemOption?.LevelDependentOptions.FirstOrDefault(o => o.Level == (option.ItemOption.LevelType == LevelType.ItemLevel ? this.Level : option.Level));
var powerUpDefinition = levelOption?.PowerUpDefinition ?? option.ItemOption?.PowerUpDefinition;
if (powerUpDefinition is not null)
{
stringBuilder.Append("+").Append(powerUpDefinition);
}
}
if (this.HasSkill)
{
stringBuilder.Append("+Skill");
}
if (this.ItemOptions.Any(opt => opt.ItemOption?.OptionType == ItemOptionTypes.Luck))
{
stringBuilder.Append("+Luck");
}
if (this.SocketCount > 0)
{
stringBuilder.Append("+").Append(this.SocketCount).Append("S");
}
return stringBuilder.ToString();
}
/// <summary>
/// Returns a string that represents this item, using the specified culture for localization.
/// </summary>
/// <param name="culture">The culture to use for localization.</param>
/// <returns>The localized string.</returns>
public string ToString(CultureInfo culture)
{
using var cultureHelper = CultureHelper.SetTemporaryCulture(culture);
var stringBuilder = new StringBuilder();
stringBuilder.Append(Resources.Slot).Append(" ").Append(this.ItemSlot).Append(": ");
if (this.ItemOptions.Any(o => o.ItemOption?.OptionType == ItemOptionTypes.Excellent))
{
stringBuilder.Append(Resources.Excellent).Append(" ");
}
var ancientSet = this.ItemSetGroups.FirstOrDefault(s => s.AncientSetDiscriminator != 0)?.ItemSetGroup;
if (ancientSet != null)
{
stringBuilder.Append(ancientSet.Name.ToString()).Append(" ");
}
var itemName = this.Definition?.GetNameForLevel(this.Level);
stringBuilder.Append(itemName);
if (this.Level > 0)
{
stringBuilder.Append("+").Append(this.Level);
}
foreach (var option in this.ItemOptions
.Where(o => o.ItemOption?.OptionType != ItemOptionTypes.Luck)
.OrderBy(o => o.ItemOption?.OptionType == ItemOptionTypes.Option))
{
var levelOption = option.ItemOption?.LevelDependentOptions.FirstOrDefault(o => o.Level == (option.ItemOption.LevelType == LevelType.ItemLevel ? this.Level : option.Level));
var powerUpDefinition = levelOption?.PowerUpDefinition ?? option.ItemOption?.PowerUpDefinition;
if (powerUpDefinition is not null)
{
stringBuilder.Append("+").Append(powerUpDefinition);
}
}
if (this.HasSkill)
{
stringBuilder.Append("+").Append(Resources.Skill);
}
if (this.ItemOptions.Any(opt => opt.ItemOption?.OptionType == ItemOptionTypes.Luck))
{
stringBuilder.Append("+").Append(Resources.Luck);
}
if (this.SocketCount > 0)
{
stringBuilder.Append("+").Append(this.SocketCount).Append(Resources.SocketAbbreviation);
}
return stringBuilder.ToString();
}
/// <summary>
/// Clones the item option link.
/// </summary>
/// <param name="link">The link.</param>
/// <returns>The cloned item option link.</returns>
protected virtual ItemOptionLink CloneItemOptionLink(ItemOptionLink link)
{
return link.Clone();
}
}

View File

@@ -0,0 +1,33 @@
// <copyright file="ItemAppearance.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Appearance of an item.
/// </summary>
public class ItemAppearance
{
/// <summary>
/// Gets or sets the item slot.
/// </summary>
public byte ItemSlot { get; set; }
/// <summary>
/// Gets or sets the definition of the item.
/// </summary>
public virtual ItemDefinition? Definition { get; set; }
/// <summary>
/// Gets or sets the level.
/// </summary>
public byte Level { get; set; }
/// <summary>
/// Gets or sets the visible options.
/// </summary>
public virtual ICollection<ItemOptionType> VisibleOptions { get; protected set; } = null!;
}

View File

@@ -0,0 +1,62 @@
// <copyright file="ItemOptionLink.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Annotations;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// This class defines a link between the item and the concrete item option which the actual item instance possess.
/// </summary>
[Cloneable]
public partial class ItemOptionLink
{
/// <summary>
/// Gets or sets the item option.
/// Link to <see cref="ItemDefinition.PossibleItemOptions"/>, <see cref="ItemOptionDefinition.PossibleOptions"/>.
/// </summary>
[Required]
public virtual IncreasableItemOption? ItemOption { get; set; }
/// <summary>
/// Gets or sets the level.
/// </summary>
public int Level { get; set; }
/// <summary>
/// Gets or sets the index of the option. This is required when the options are sorted, e.g. for socket options.
/// </summary>
public int Index { get; set; }
/// <summary>
/// Clones this instance.
/// </summary>
/// <returns>The cloned instance.</returns>
public virtual ItemOptionLink Clone()
{
var link = new ItemOptionLink();
link.AssignValues(this);
return link;
}
/// <summary>
/// Assigns the values.
/// </summary>
/// <param name="otherLink">The other link.</param>
public void AssignValues(ItemOptionLink otherLink)
{
this.ItemOption = otherLink.ItemOption;
this.Level = otherLink.Level;
this.Index = otherLink.Index;
}
/// <inheritdoc />
public override string ToString()
{
var powerUpDefinition = this.ItemOption?.LevelDependentOptions?.FirstOrDefault(ldo => ldo.Level == this.Level)?.PowerUpDefinition
?? this.ItemOption?.PowerUpDefinition;
return powerUpDefinition?.ToString() ?? "empty";
}
}

View File

@@ -0,0 +1,31 @@
// <copyright file="ItemStorage.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Annotations;
/// <summary>
/// A storage where items can be stored.
/// </summary>
[Cloneable]
public partial class ItemStorage
{
/// <summary>
/// Gets or sets the items which are stored.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<Item> Items { get; protected set; } = null!;
/// <summary>
/// Gets or sets the money which is stored.
/// </summary>
public int Money { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.Items?.Count ?? 0} Items, {this.Money} Money";
}
}

View File

@@ -0,0 +1,52 @@
// <copyright file="LetterBody.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Interfaces;
/// <summary>
/// The body of a letter.
/// </summary>
[AggregateRoot]
public class LetterBody
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the header.
/// </summary>
[Required]
public virtual LetterHeader? Header { get; set; }
/// <summary>
/// Gets or sets the message.
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the rotation of the sender character.
/// </summary>
public byte Rotation { get; set; }
/// <summary>
/// Gets or sets the animation of the sender character.
/// </summary>
public byte Animation { get; set; }
/// <summary>
/// Gets or sets the sender appearance data.
/// </summary>
[MemberOfAggregate]
public virtual AppearanceData? SenderAppearance { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.Header}";
}
}

View File

@@ -0,0 +1,29 @@
// <copyright file="QuestMonsterKillRequirementState.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.DataModel.Configuration.Quests;
/// <summary>
/// Keeps the progress of the <see cref="QuestDefinition.RequiredMonsterKills"/> of the currently active quest.
/// </summary>
public class QuestMonsterKillRequirementState
{
/// <summary>
/// Gets or sets the requirement for which this state is kept.
/// </summary>
public virtual QuestMonsterKillRequirement? Requirement { get; set; }
/// <summary>
/// Gets or sets the monster kill count for this <see cref="Requirement"/>.
/// </summary>
public int KillCount { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.KillCount}/{this.Requirement?.MinimumNumber} {this.Requirement?.Monster}";
}
}

View File

@@ -0,0 +1,110 @@
// <copyright file="SkillEntry.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel.Entities;
using System.ComponentModel;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
/// <summary>
/// An actual entry of a skill in the characters skill list.
/// </summary>
public class SkillEntry : INotifyPropertyChanged
{
private int level;
/// <summary>
/// Occurs when a property changed.
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Gets or sets the skill definition.
/// </summary>
[Required]
public virtual Skill? Skill { get; set; }
/// <summary>
/// Gets or sets the level of the skill, primarily master skill level.
/// </summary>
public int Level
{
get => this.level;
set
{
if (this.level != value)
{
this.level = value;
this.OnPropertyChanged(nameof(this.Level));
}
}
}
/// <summary>
/// Gets or sets the power up element of this skill of this player. It is a "cached" element which will be created on demand and can be applied multiple times.
/// </summary>
[Transient]
public (AttributeDefinition Target, IElement BuffPowerUp)[]? PowerUps { get; set; }
/// <summary>
/// Gets or sets the PvP power up element of this skill of this player. It is a "cached" element which will be created on demand and can be applied multiple times.
/// </summary>
[Transient]
public (AttributeDefinition Target, IElement BuffPowerUp)[]? PowerUpsPvp { get; set; }
/// <summary>
/// Gets or sets the duration of the <see cref="PowerUps"/>.
/// </summary>
/// <remarks>
/// It is an IElement, because the duration can be dependent from the player attributes.
/// </remarks>
[Transient]
public IElement? PowerUpDuration { get; set; }
/// <summary>
/// Gets or sets the duration of the <see cref="PowerUps"/> for PvP.
/// </summary>
/// <remarks>
/// It is an IElement, because the duration can be dependent from the player attributes.
/// </remarks>
[Transient]
public IElement? PowerUpDurationPvp { get; set; }
/// <summary>
/// Gets or sets the chance of applying the <see cref="PowerUps"/>.
/// </summary>
/// <remarks>
/// It is an IElement, because the duration can be dependent from the player attributes.
/// </remarks>
[Transient]
public IElement? PowerUpChance { get; set; }
/// <summary>
/// Gets or sets the chance of applying the <see cref="PowerUps"/> for PvP.
/// </summary>
/// <remarks>
/// It is an IElement, because the duration can be dependent from the player attributes.
/// </remarks>
[Transient]
public IElement? PowerUpChancePvp { get; set; }
/// <summary>
/// Gets or sets the attributes, if this skill has attribute relationships.
/// </summary>
[Transient]
public IAttributeSystem? Attributes { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{this.Skill?.Name}{(this.Level > 0 ? ", Level: " + this.Level : string.Empty)}";
}
private void OnPropertyChanged(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

50
src/DataModel/Error.cs Normal file
View File

@@ -0,0 +1,50 @@
// <copyright file="Error.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.DataModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
/// <summary>
/// Static class which offers convenience methods to throw exceptions.
/// </summary>
public static class Error
{
/// <summary>
/// Creates an <see cref="InvalidOperationException"/> because of on uninitialized property.
/// </summary>
/// <param name="parent">The parent object whose property is not initialized.</param>
/// <param name="propertyName">The property name.</param>
/// <returns>The created <see cref="InvalidOperationException"/>.</returns>
public static Exception NotInitializedProperty(object parent, [CallerMemberName] string propertyName = "")
{
return new InvalidOperationException($"Property '{propertyName}' of {parent} is not initialized yet.");
}
/// <summary>
/// Creates and throws an <see cref="InvalidOperationException"/> because of on uninitialized property.
/// </summary>
/// <param name="parent">The parent object whose property is not initialized.</param>
/// <param name="propertyName">The property name.</param>
[DoesNotReturn]
public static void ThrowNotInitializedProperty(this object parent, string propertyName)
{
throw new InvalidOperationException($"Property '{propertyName}' of {parent} is not initialized yet.");
}
/// <summary>
/// Creates and throws an <see cref="InvalidOperationException"/> if <paramref name="propertyIsNull"/> is <see langword="true"/>.
/// </summary>
/// <param name="parent">The parent object whose property is not initialized.</param>
/// <param name="propertyIsNull">The flag, if the property is null.</param>
/// <param name="propertyName">The property name.</param>
public static void ThrowNotInitializedProperty(this object parent, [DoesNotReturnIf(true)] bool propertyIsNull, string propertyName)
{
if (propertyIsNull)
{
throw new InvalidOperationException($"Property '{propertyName}' of {parent} is not initialized yet.");
}
}
}

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