forked from MUnique/OpenMU
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AttributeSystem.cs
215 lines (186 loc) · 7.92 KB
/
AttributeSystem.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// <copyright file="AttributeSystem.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.AttributeSystem;
using System.Globalization;
/// <summary>
/// The attribute system which holds all attributes of a character.
/// </summary>
public class AttributeSystem : IAttributeSystem
{
private readonly IDictionary<AttributeDefinition, IAttribute> _attributes = new Dictionary<AttributeDefinition, IAttribute>();
/// <summary>
/// Initializes a new instance of the <see cref="AttributeSystem" /> class.
/// </summary>
/// <param name="statAttributes">The stat attributes. These attributes are added just as-is and are not wrapped by a <see cref="ComposableAttribute"/>.</param>
/// <param name="baseAttributes">The initial base attributes. These attributes contain the base values which will be wrapped by a <see cref="ComposableAttribute"/>, so additional elements can contribute to the attributes value. Instead of providing them here, you could also add them to the system by calling <see cref="AddElement"/> later.</param>
/// <param name="attributeRelationships">The initial attribute relationships. Instead of providing them here, you could also add them to the system by calling <see cref="AddAttributeRelationship(AttributeRelationship, IAttributeSystem)"/> later.</param>
public AttributeSystem(IEnumerable<IAttribute> statAttributes, IEnumerable<IAttribute> baseAttributes, IEnumerable<AttributeRelationship> attributeRelationships)
{
foreach (var statAttribute in statAttributes)
{
this._attributes.Add(statAttribute.Definition, statAttribute);
}
foreach (var baseAttribute in baseAttributes)
{
this.AddElement(baseAttribute, baseAttribute.Definition);
}
foreach (var combination in attributeRelationships)
{
this.AddAttributeRelationship(combination);
}
}
/// <inheritdoc/>
public float this[AttributeDefinition? attributeDefinition]
{
get => this.GetValueOfAttribute(attributeDefinition);
set => this.SetStatAttribute(attributeDefinition, value);
}
/// <inheritdoc/>
public void AddAttributeRelationship(AttributeRelationship relationship, IAttributeSystem sourceAttributeHolder)
{
if (this.GetOrCreateAttribute(relationship.GetTargetAttribute()) is IComposableAttribute targetAttribute)
{
var relatedElement = this.CreateRelatedAttribute(relationship, sourceAttributeHolder);
targetAttribute.AddElement(relatedElement);
}
}
/// <summary>
/// Creates the related attribute.
/// </summary>
/// <param name="relationship">The relationship.</param>
/// <param name="sourceAttributeHolder">The source attribute holder. May be the attribute system of another player.</param>
/// <returns>The newly created relationship element.</returns>
public IElement CreateRelatedAttribute(AttributeRelationship relationship, IAttributeSystem sourceAttributeHolder)
{
var inputElements = new[] { sourceAttributeHolder.GetOrCreateAttribute(relationship.GetInputAttribute()) };
return new AttributeRelationshipElement(inputElements, relationship.GetOperandElement(sourceAttributeHolder), relationship.InputOperator);
}
/// <summary>
/// Sets the stat attribute, if the <paramref name="attributeDefinition"/> is a stat attribute.
/// </summary>
/// <param name="attributeDefinition">The attribute definition.</param>
/// <param name="newValue">The new value.</param>
/// <returns>The success.</returns>
public bool SetStatAttribute(AttributeDefinition? attributeDefinition, float newValue)
{
if (attributeDefinition is null)
{
return false;
}
if (this._attributes.TryGetValue(attributeDefinition, out var attribute)
&& attribute is StatAttribute statAttribute)
{
statAttribute.Value = newValue;
return true;
}
return false;
}
/// <summary>
/// Gets the composable attribute.
/// </summary>
/// <param name="attributeDefinition">The attribute definition.</param>
/// <returns>The composable attribute.</returns>
public ComposableAttribute? GetComposableAttribute(AttributeDefinition attributeDefinition)
{
return this.GetOrCreateAttribute(attributeDefinition) as ComposableAttribute;
}
/// <inheritdoc/>
public float GetValueOfAttribute(AttributeDefinition? attributeDefinition)
{
var element = this.GetAttribute(attributeDefinition);
if (element != null)
{
return element.Value;
}
return 0;
}
/// <inheritdoc/>
public void AddElement(IElement element, AttributeDefinition targetAttribute)
{
if (!this._attributes.TryGetValue(targetAttribute, out var attribute))
{
attribute = new ComposableAttribute(targetAttribute);
this._attributes.Add(targetAttribute, attribute);
}
if (attribute is IComposableAttribute composableAttribute)
{
composableAttribute.AddElement(element);
}
else
{
throw new ArgumentException($"Attribute {targetAttribute} is not a composable attribute.");
}
}
/// <inheritdoc/>
public void RemoveElement(IElement element, AttributeDefinition targetAttribute)
{
if (this._attributes.TryGetValue(targetAttribute, out var attribute))
{
if (attribute is IComposableAttribute composableAttribute)
{
composableAttribute.RemoveElement(element);
if (!composableAttribute.Elements.Any())
{
this._attributes.Remove(targetAttribute);
}
}
else
{
throw new ArgumentException($"Attribute {targetAttribute} is not a composable attribute.");
}
}
}
/// <inheritdoc/>
public override string ToString()
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Stat Attributes:");
foreach (var statAttribute in this._attributes.Values.OfType<StatAttribute>())
{
stringBuilder.AppendLine($" {statAttribute.Definition}: {statAttribute.Value}");
}
stringBuilder.AppendLine("Others:");
foreach (var attribute in this._attributes.Values.OfType<IComposableAttribute>())
{
stringBuilder.AppendLine($" {attribute.Definition}: {attribute.Value}");
}
return stringBuilder.ToString();
}
/// <summary>
/// Gets or creates the element with the specified attribute.
/// </summary>
/// <param name="attributeDefinition">The attribute definition.</param>
/// <returns>The element of the attribute.</returns>
public IElement GetOrCreateAttribute(AttributeDefinition attributeDefinition)
{
var element = this.GetAttribute(attributeDefinition);
if (element is null)
{
var composableAttribute = new ComposableAttribute(attributeDefinition);
element = composableAttribute;
this._attributes.Add(attributeDefinition, composableAttribute);
}
return element;
}
/// <summary>
/// Adds the attribute relationship.
/// </summary>
/// <param name="combination">The combination.</param>
private void AddAttributeRelationship(AttributeRelationship combination)
{
this.AddAttributeRelationship(combination, this);
}
private IElement? GetAttribute(AttributeDefinition? attributeDefinition)
{
if (attributeDefinition is null)
{
return null;
}
if (this._attributes.TryGetValue(attributeDefinition, out var attribute))
{
return attribute;
}
return null;
}
}