Repository navigation
feat(items): add the talisman of mobility #1458
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| // <copyright file="TalismanOfMobilityConsumeHandlerPlugIn.cs" company="MUnique"> | ||
| // Licensed under the MIT License. See LICENSE file in the project root for full license information. | ||
| // </copyright> | ||
|
|
||
| namespace MUnique.OpenMU.GameLogic.PlayerActions.ItemConsumeActions; | ||
|
|
||
| using System.Runtime.InteropServices; | ||
| using MUnique.OpenMU.AttributeSystem; | ||
| using MUnique.OpenMU.DataModel.Entities; | ||
| using MUnique.OpenMU.GameLogic.Attributes; | ||
| using MUnique.OpenMU.PlugIns; | ||
|
|
||
| /// <summary> | ||
| /// Consume handler for the Talisman of Mobility. It has two usages: the first one saves the place of the character, | ||
| /// the second one warps the character back to the saved place. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The game client describes it as "Saves the location with the right-mouse-click. Returns to the saved location by a click.". | ||
| /// The place is saved at the character, so it's kept when the character leaves the game. | ||
| /// It can't be used in mini games and duels, because these have their own maps and spawn places. | ||
| /// </remarks> | ||
| [Guid("23071045-4323-4B5F-B26B-5283EEB130C1")] | ||
| [PlugIn] | ||
| [Display(Name = nameof(PlugInResources.TalismanOfMobilityConsumeHandlerPlugIn_Name), Description = nameof(PlugInResources.TalismanOfMobilityConsumeHandlerPlugIn_Description), ResourceType = typeof(PlugInResources))] | ||
| public class TalismanOfMobilityConsumeHandlerPlugIn : BaseConsumeHandlerPlugIn | ||
| { | ||
| /// <summary> | ||
| /// The durability of an unused talisman. With it, the place is saved; with less, the character returns to it. | ||
| /// </summary> | ||
| private const double UnusedDurability = 2; | ||
|
icheat2win marked this conversation as resolved.
Resolved
|
||
|
|
||
| /// <inheritdoc /> | ||
| public override ItemIdentifier Key => ItemConstants.TalismanOfMobility; | ||
|
|
||
| /// <inheritdoc /> | ||
| public override async ValueTask<bool> ConsumeItemAsync(Player player, Item item, Item? targetItem, FruitUsage fruitUsage) | ||
| { | ||
| if (!this.CheckPreconditions(player, item) | ||
| || player.SelectedCharacter is not { } character | ||
| || player.CurrentMap is not { } currentMap | ||
| || player.CurrentMiniGame is not null | ||
| || player.DuelRoom is not null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| if (item.Durability >= UnusedDurability) | ||
| { | ||
| var position = player.Position; | ||
| SetValue(player, character, Stats.MobilityTalismanMap, currentMap.MapId + 1); | ||
| SetValue(player, character, Stats.MobilityTalismanX, position.X); | ||
| SetValue(player, character, Stats.MobilityTalismanY, position.Y); | ||
| await this.ConsumeSourceItemAsync(player, item).ConfigureAwait(false); | ||
| await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.MobilityTalismanPlaceSaved)).ConfigureAwait(false); | ||
| return true; | ||
| } | ||
|
|
||
| if (await GetSavedGateAsync(player, character).ConfigureAwait(false) is not { } gate) | ||
| { | ||
| await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.MobilityTalismanNoPlaceSaved)).ConfigureAwait(false); | ||
| return false; | ||
| } | ||
|
|
||
| SetValue(player, character, Stats.MobilityTalismanMap, 0); | ||
|
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.
When a character uses two full talismans once before returning with either, both items reach durability 1 and share the character's latest bookmark. Returning with the first talisman clears that shared bookmark, so the second subsequently fails in Useful? React with 👍 / 👎.
Member
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. I think that's acceptable for now. |
||
| await this.ConsumeSourceItemAsync(player, item).ConfigureAwait(false); | ||
| await player.WarpToAsync(gate).ConfigureAwait(false); | ||
|
icheat2win marked this conversation as resolved.
Resolved
|
||
| return true; | ||
| } | ||
|
|
||
| private static async ValueTask<ExitGate?> GetSavedGateAsync(Player player, Character character) | ||
| { | ||
| var mapNumber = (int)GetValue(character, Stats.MobilityTalismanMap) - 1; | ||
| if (mapNumber < 0 | ||
| || player.GameContext.Configuration.Maps.FirstOrDefault(map => map.Number == mapNumber) is not { IsActive: true } mapDefinition) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| var x = (byte)GetValue(character, Stats.MobilityTalismanX); | ||
| var y = (byte)GetValue(character, Stats.MobilityTalismanY); | ||
|
|
||
| // The map may be hosted by another game server; the warp then moves the player to it. | ||
| var map = await player.GameContext.GetMapAsync((ushort)mapNumber).ConfigureAwait(false); | ||
| return new ExitGate | ||
| { | ||
| Map = map?.Definition ?? mapDefinition, | ||
|
Comment on lines
+83
to
+86
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.
If the saved map remains active in configuration but its hosting server is stopped, a character on another server gets a null result from Useful? React with 👍 / 👎.
Member
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. agree |
||
| X1 = x, | ||
| X2 = x, | ||
| Y1 = y, | ||
| Y2 = y, | ||
| }; | ||
| } | ||
|
|
||
| private static float GetValue(Character character, AttributeDefinition attribute) | ||
| { | ||
| return character.Attributes.FirstOrDefault(a => a.Definition?.Id == attribute.Id)?.Value ?? 0; | ||
| } | ||
|
|
||
| private static void SetValue(Player player, Character character, AttributeDefinition attribute, float value) | ||
| { | ||
| if (character.Attributes.FirstOrDefault(a => a.Definition?.Id == attribute.Id) is { } existing) | ||
| { | ||
| existing.Value = value; | ||
| return; | ||
| } | ||
|
|
||
| var definition = player.GameContext.Configuration.Attributes?.FirstOrDefault(a => a.Id == attribute.Id) ?? attribute; | ||
| character.Attributes.Add(player.PersistenceContext.CreateNew<StatAttribute>(definition, value)); | ||
| } | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.