-
Notifications
You must be signed in to change notification settings - Fork 595
Regens refactoring & add tier 2 chars default movement speeds #888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
sven-n
merged 21 commits into
MUnique:master
from
ze-dom:regens_refactor_default_running_speed_chars
Aug 28, 2026
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
81c3443
First commit
ze-dom 5eaa28a
Fixed mana value
ze-dom 9dd90de
Fixed bad initial interpretation
ze-dom 36432c4
Fix previous wrong commit
ze-dom faaaa1a
Fixed relationships shield
ze-dom 2529abf
Added resting timer
ze-dom 2b776c0
Simplified resting recovery interval value calc
ze-dom c60d5d1
Adjusted indentation and value
ze-dom 0ede60a
Some refactoring based on Claude's input
ze-dom b71641b
Added update plugin
ze-dom 9f6356c
Fixed some update plugin bugs
ze-dom aabd4f7
Some fixes
ze-dom 92a4d1b
plugin fixes
ze-dom 8447be5
Added plugins for different versions
ze-dom 0a80c16
Added conditions for versions without shield (classic PvP)
ze-dom 084f446
Merge branch 'master' into regens_refactor_default_running_speed_chars
ze-dom fdef4fa
Added animation packet pose checks for older versions
ze-dom 4eeb731
Analyzer issues
ze-dom c06e15b
More fixes
ze-dom ab826b9
Removed obsolete method
ze-dom 0b6670e
fixed doc typo
ze-dom File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1173,7 +1173,31 @@ public class Stats | |
| /// Gets the shield recovery everywhere attribute definition. | ||
| /// By default, shield recovery is limited to the safezone only. With this attribute (value >= 1), recovery works everywhere on a map. | ||
| /// </summary> | ||
| public static AttributeDefinition ShieldRecoveryEverywhere { get; } = new(new Guid("3D0A78FF-CCD4-442E-8B4E-64E5082ABD78"), "Is Shield Recovery Active Everwhere", "By default, shield recovery is limited to the safezone only. With this attribute (value >= 1), recovery works everywhere on a map."); | ||
| public static AttributeDefinition ShieldRecoveryEverywhere { get; } = new(new Guid("3D0A78FF-CCD4-442E-8B4E-64E5082ABD78"), "Shield Recovery Active Everywhere", "By default, shield recovery is limited to the safezone only. With this attribute (value >= 1), recovery works everywhere on a map."); | ||
|
|
||
| /// <summary> | ||
| /// Gets the is shield recovery active attribute definition. | ||
| /// </summary> | ||
| public static AttributeDefinition IsShieldRecoveryActive { get; } = new(new Guid("8F2C4D7E-B1A9-4E3F-9C5D-2A1B7E8F3C4D"), "Is Shield Recovery Active", string.Empty); | ||
|
|
||
| /// <summary> | ||
| /// Gets the shield recovery hiatus (in seconds) attribute definition. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Must be equal or greater than <see cref="ShieldRegeneration"/>.HiatusThreshold for shield to regenerate. See <see cref="PlugIns.ShieldRecoveryHiatusPlugIn"/>. | ||
| /// </remarks> | ||
| public static AttributeDefinition ShieldRecoveryHiatus { get; } = new(new Guid("5A7E2B9C-3D1F-4A8E-B6C2-1E7D4A9F2B3C"), "Shield Recovery Hiatus", "The seconds since when shield recovery was last interrupted, either by leaving a safezone, the shield being damaged or maxing out."); | ||
|
|
||
| /// <summary> | ||
| /// Gets the shield recovery ramp factor attribute definition. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The factor by which shield recovery increases. Rises linearly with <see cref="ShieldRecoveryHiatus"/>. Original range is [2,3]. | ||
| /// </remarks> | ||
| public static AttributeDefinition ShieldRecoveryRampFactor { get; } = new(new Guid("C3E8F1A7-4B9D-4C5E-8A2F-3D6E1C9B5A7F"), "Shield Recovery Ramp Factor", "The factor by which shield recovery increases. Rises linearly with the uninterrupted shield recovery duration.") | ||
| { | ||
| MaximumValue = 3, | ||
| }; | ||
|
|
||
| /// <summary> | ||
| /// Gets the ability usage reduction attribute definition. Value ranges from 0 (no reduction) to 1 (full reduction). | ||
|
|
@@ -1437,7 +1461,12 @@ public class Stats | |
| /// <summary> | ||
| /// Gets the <see cref="IsInSafezone"/> attribute which defines if the character is located in a safezone of a game map. | ||
| /// </summary> | ||
| public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover additional health and shield."); | ||
| public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover shield and additional ability."); | ||
|
|
||
| /// <summary> | ||
| /// Gets the <see cref="IsResting"/> attribute which defines if the character is in a resting state: sitting, leaning or hanging. | ||
| /// </summary> | ||
| public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health and mana."); | ||
|
|
||
| /// <summary> | ||
| /// Gets the <see cref="IsUnderwater"/> attribute which defines if the character is located on an underwater game map. | ||
|
|
@@ -1588,13 +1617,43 @@ public static IEnumerable<Regeneration> AfterMonsterKillRegenerationAttributes | |
| } | ||
| } | ||
|
|
||
| private static Regeneration ManaRegeneration { get; } = new(ManaRecoveryMultiplier, MaximumMana, CurrentMana, ManaRecoveryAbsolute); | ||
| /// <summary> | ||
| /// Gets the mana regeneration. | ||
| /// </summary> | ||
| public static Regeneration ManaRegeneration { get; } = new(ManaRecoveryMultiplier, MaximumMana, CurrentMana, ManaRecoveryAbsolute) | ||
| { | ||
| IntervalResting = TimeSpan.FromSeconds(5), | ||
| }; | ||
|
|
||
| private static Regeneration HealthRegeneration { get; } = new(HealthRecoveryMultiplier, MaximumHealth, CurrentHealth, HealthRecoveryAbsolute); | ||
| /// <summary> | ||
| /// Gets the health regeneration. | ||
| /// </summary> | ||
| public static Regeneration HealthRegeneration { get; } = new(HealthRecoveryMultiplier, MaximumHealth, CurrentHealth, HealthRecoveryAbsolute) | ||
| { | ||
| Interval = TimeSpan.FromSeconds(7), | ||
| IntervalResting = TimeSpan.FromSeconds(5), | ||
| }; | ||
|
|
||
| private static Regeneration AbilityRegeneration { get; } = new(AbilityRecoveryMultiplier, MaximumAbility, CurrentAbility, AbilityRecoveryAbsolute); | ||
| /// <summary> | ||
| /// Gets the ability regeneration. | ||
| /// </summary> | ||
| public static Regeneration AbilityRegeneration { get; } = new(AbilityRecoveryMultiplier, MaximumAbility, CurrentAbility, AbilityRecoveryAbsolute); | ||
|
|
||
| private static Regeneration ShieldRegeneration { get; } = new(ShieldRecoveryMultiplier, MaximumShield, CurrentShield, ShieldRecoveryAbsolute); | ||
| /// <summary> | ||
| /// Gets the shield regeneration. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Shield recovery is only possible at safe zone, except the character has a specific attribute which has the effect that it's recovered everywhere. | ||
| /// This attribute is usually provided by level 380 armor with a Guardian Option. | ||
| /// Also, shield has a penalty (hiatus) period before the recovery starts, after which it increases linearly (see <see cref="ShieldRecoveryRampFactor"/>). | ||
| /// </remarks> | ||
| public static Regeneration ShieldRegeneration { get; } = new(ShieldRecoveryMultiplier, MaximumShield, CurrentShield, ShieldRecoveryAbsolute) | ||
| { | ||
| Interval = TimeSpan.FromSeconds(1), | ||
| IntervalResting = TimeSpan.FromSeconds(1), | ||
| EnablerAttribute = IsShieldRecoveryActive, | ||
| HiatusAttribute = ShieldRecoveryHiatus, | ||
| }; | ||
|
|
||
| private static Regeneration ManaRegenerationAfterMonsterKill { get; } = new(ManaAfterMonsterKillMultiplier, MaximumMana, CurrentMana, ManaAfterMonsterKillAbsolute); | ||
|
|
||
|
|
@@ -1613,8 +1672,8 @@ public class Regeneration | |
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="Regeneration" /> class. | ||
| /// At regeneration the value of <paramref name="regenerationMultiplier" /> * <paramref name="maximumAttribute" /> is getting added to | ||
| /// <paramref name="currentAttribute" />, until the value of <paramref name="maximumAttribute" /> is reached. | ||
| /// At regeneration the value of (<paramref name="maximumAttribute" /> * <paramref name="regenerationMultiplier" />) + <paramref name="absoluteAttribute"/> | ||
| /// is getting added to <paramref name="currentAttribute" />, until the value of <paramref name="maximumAttribute" /> is reached. | ||
| /// </summary> | ||
| /// <param name="regenerationMultiplier">The regeneration multiplier.</param> | ||
| /// <param name="maximumAttribute">The maximum attribute.</param> | ||
|
|
@@ -1647,5 +1706,36 @@ public Regeneration(AttributeDefinition regenerationMultiplier, AttributeDefinit | |
| /// Gets the current attribute. | ||
| /// </summary> | ||
| public AttributeDefinition CurrentAttribute { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the interval at which the attribute would complete a full regeneration cycle. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Originally, the game had different regeneration cycles depending on attributes and the player's state (<see cref="IsResting"/>). | ||
| /// To avoid having different timers each with a regeneration interval, we run just one set to <see cref="GameConfiguration.RecoveryInterval"/>. | ||
| /// Then, we apply a compensation factor which is the ratio between elapsed time and <see cref="Interval"/>. | ||
| /// This way we can mimic original regeneration over time while still keeping it configurable. | ||
| /// </remarks> | ||
| public TimeSpan Interval { get; init; } = TimeSpan.FromSeconds(3); | ||
|
|
||
| /// <summary> | ||
| /// Gets the interval at which the attribute would complete a full regeneration cycle when the player is resting (<see cref="IsResting"/>). | ||
| /// </summary> | ||
| public TimeSpan IntervalResting { get; init; } = TimeSpan.FromSeconds(3); | ||
|
Comment on lines
+1719
to
+1724
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| /// <summary> | ||
| /// Gets the attribute which gates the regeneration, if it exists. | ||
| /// </summary> | ||
| public AttributeDefinition? EnablerAttribute { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the attribute which keeps the regeneration hiatus duration, if it exists. | ||
| /// </summary> | ||
| public AttributeDefinition? HiatusAttribute { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the hiatus threshold (in seconds) against which <see cref="HiatusAttribute"/> is checked to allow the regeneration. | ||
| /// </summary> | ||
| public int HiatusThreshold { get; init; } = 10; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,6 @@ namespace MUnique.OpenMU.GameLogic; | |
| using MUnique.OpenMU.GameLogic.MuHelper; | ||
| using MUnique.OpenMU.GameLogic.NPC; | ||
| using MUnique.OpenMU.GameLogic.Pet; | ||
| using MUnique.OpenMU.GameLogic.PlayerActions; | ||
| using MUnique.OpenMU.GameLogic.PlayerActions.Items; | ||
| using MUnique.OpenMU.GameLogic.PlayerActions.Skills; | ||
| using MUnique.OpenMU.GameLogic.PlayerActions.Trade; | ||
|
|
@@ -24,7 +23,6 @@ namespace MUnique.OpenMU.GameLogic; | |
| using MUnique.OpenMU.GameLogic.Views.Guild; | ||
| using MUnique.OpenMU.GameLogic.Views.Inventory; | ||
| using MUnique.OpenMU.GameLogic.Views.MuHelper; | ||
| using MUnique.OpenMU.GameLogic.Views.Pet; | ||
| using MUnique.OpenMU.GameLogic.Views.Quest; | ||
| using MUnique.OpenMU.GameLogic.Views.World; | ||
| using MUnique.OpenMU.Interfaces; | ||
|
|
@@ -240,8 +238,7 @@ public CharacterPose Pose | |
|
|
||
| set | ||
| { | ||
| var character = this._selectedCharacter; | ||
| if (character is null || character.Pose == this.Pose) | ||
| if (this._selectedCharacter is not { } character || character.Pose == value) | ||
| { | ||
| return; | ||
| } | ||
|
|
@@ -574,6 +571,11 @@ public IPetCommandManager? PetCommandManager | |
| /// </summary> | ||
| public DateTime PotionCooldownUntil { get; set; } = DateTime.UtcNow; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the timestamp of when the shield hiatus was last accrued. | ||
| /// </summary> | ||
| public DateTime LastShieldRecoveryHiatusAccrual { get; set; } = DateTime.UtcNow; | ||
|
|
||
| /// <summary> | ||
| /// Gets a value indicating whether opening the player store after entering the game is supported by this instance. | ||
| /// </summary> | ||
|
|
@@ -876,28 +878,41 @@ public async Task RegenerateAsync() | |
| { | ||
| try | ||
| { | ||
| var attributes = this.Attributes; | ||
| if (attributes is null) | ||
| if (this.Attributes is not { } attributes) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| foreach (var r in Stats.IntervalRegenerationAttributes.Where(r => | ||
| attributes[r.RegenerationMultiplier] > 0 || attributes[r.AbsoluteAttribute] > 0)) | ||
| var now = DateTime.UtcNow; | ||
| foreach (var r in Stats.IntervalRegenerationAttributes) | ||
| { | ||
| if (r.CurrentAttribute == Stats.CurrentShield && !this.IsAtSafezone() && | ||
| attributes[Stats.ShieldRecoveryEverywhere] < 1) | ||
| if ((r.EnablerAttribute is { } enabler && attributes[enabler] < 1) | ||
| || (r.HiatusAttribute is { } hiatus && attributes[hiatus] < r.HiatusThreshold)) | ||
| { | ||
| // Shield recovery is only possible at safe-zone, except the character has a specific attribute which has the effect that it's recovered everywhere. | ||
| // This attribute is usually provided by level 380 armor and a Guardian Option. | ||
| continue; | ||
| } | ||
|
|
||
| var factor = 0f; | ||
| var interval = r.Interval; | ||
| if (attributes[Stats.IsResting] > 0) | ||
| { | ||
| interval = r.IntervalResting; | ||
|
|
||
| if (r.CurrentAttribute == Stats.CurrentMana) | ||
| { | ||
| // Mana recovery while resting is on top of regular recovery | ||
| factor += (float)((now - this._lastRegenerate) / r.Interval); | ||
| } | ||
| } | ||
|
|
||
| factor += (float)((now - this._lastRegenerate) / interval); | ||
|
|
||
| attributes[r.CurrentAttribute] = Math.Min( | ||
| attributes[r.CurrentAttribute] + | ||
| ((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + | ||
| attributes[r.AbsoluteAttribute]), | ||
| (((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + attributes[r.AbsoluteAttribute]) * factor), | ||
| attributes[r.MaximumAttribute]); | ||
|
|
||
| // this.Logger.LogDebug($"Regenerated {r.CurrentAttribute} with elapsed time {now - this._lastRegenerate} and factor {factor}"); | ||
| } | ||
|
|
||
| await this.RegenerateHeroStateAsync().ConfigureAwait(false); | ||
|
|
@@ -1202,6 +1217,17 @@ internal async ValueTask AfterKilledPlayerAsync(Player killedPlayer) | |
| await this.ForEachWorldObserverAsync<IUpdateCharacterHeroStatePlugIn>(o => o.UpdateCharacterHeroStateAsync(this), true).ConfigureAwait(false); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sets the current values of the regeneration attributes to their maximum values. | ||
| /// </summary> | ||
| internal void SetReclaimableAttributesToMaximum() | ||
| { | ||
| foreach (var regeneration in Stats.IntervalRegenerationAttributes) | ||
| { | ||
| this.Attributes![regeneration.CurrentAttribute] = this.Attributes[regeneration.MaximumAttribute]; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sets the current map without raising the enter/leave map events, when the player is | ||
| /// removed from the game. | ||
|
|
@@ -1595,6 +1621,7 @@ private async ValueTask OnPlayerEnteredWorldAsync() | |
|
|
||
| this.Attributes = new ItemAwareAttributeSystem(this.Account!, selectedCharacter, this.GameContext.Configuration); | ||
| this.Attributes[Stats.NearbyPartyMemberCount] = 0; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was not a |
||
| this.Attributes[Stats.IsResting] = 0; | ||
| this.LogInvalidInventoryItems(); | ||
|
|
||
| this._storages.CreateForCharacter(selectedCharacter); | ||
|
|
@@ -1619,7 +1646,14 @@ private async ValueTask OnPlayerEnteredWorldAsync() | |
| this.Attributes[Stats.AmmunitionAmount] = (float)(this.Inventory?.EquippedAmmunitionItem?.Durability ?? 0); | ||
| ammoAttribute.ValueChanged += this.OnAmmunitionAmountChanged; | ||
|
|
||
| if (this.Attributes[Stats.MaximumShield] > 0) | ||
| { | ||
| this.Attributes.GetComposableAttribute(Stats.ShieldRecoveryHiatus)?.AddElement(new SimpleElement(0, AggregateType.AddRaw)); | ||
| this.LastShieldRecoveryHiatusAccrual = DateTime.UtcNow; | ||
| } | ||
|
|
||
| await this.ClientReadyAfterMapChangeAsync().ConfigureAwait(false); | ||
| this._lastRegenerate = DateTime.UtcNow; | ||
|
|
||
| await this.InvokeViewPlugInAsync<IUpdateRotationPlugIn>(p => p.UpdateRotationAsync()).ConfigureAwait(false); | ||
| await this.ResetPetBehaviorAsync().ConfigureAwait(false); | ||
|
|
@@ -1669,17 +1703,6 @@ private void SetReclaimableAttributesBeforeEnterGame() | |
| this.Attributes[Stats.CurrentHealth] = Math.Min(this.Attributes[Stats.CurrentHealth], this.Attributes[Stats.MaximumHealth]); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sets the current values of the regeneration attributes to their maximum values. | ||
| /// </summary> | ||
| internal void SetReclaimableAttributesToMaximum() | ||
| { | ||
| foreach (var regeneration in Stats.IntervalRegenerationAttributes) | ||
| { | ||
| this.Attributes![regeneration.CurrentAttribute] = this.Attributes[regeneration.MaximumAttribute]; | ||
| } | ||
| } | ||
|
|
||
| [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Catching all Exceptions.")] | ||
| private async void OnAttributeValueChanged(object? sender, IAttribute attribute) | ||
| { | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interval: zTeamS6.3, emu
IntervalResting (includes mana too): zTeamS6.3, emu