Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs-website/docs/server-features/cash-shop.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,19 @@ sell the card.
While the plugin of a special item is deactivated, its packages can't be bought,
and bought items stay in the storage until the plugin is active again.

### Talisman of Mobility

The Talisman of Mobility can be used twice. The first use saves the place of
the character, the second one warps the character back to it and uses the
talisman up. The place is kept when the character leaves the game; saving
another place replaces it. The talisman can't be used in mini games and duels.
The plugin *Talisman of Mobility Consume Handler* can be deactivated on the
**Plugins** page.

The item exists in a new Season 6 database. Existing databases get it with the
optional configuration update *Add the Talisman of Mobility*, which also puts
its packages on sale.

### Invitation to Santa Village

The Invitation to Santa Village can be used ten times. Each use warps the
Expand Down
5 changes: 3 additions & 2 deletions docs/CashShop.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,12 @@ catalog is initialized. They have no item rules (`ItemRules`), because they neve
reach the inventory: a delivery plugin applies them, or they stay in the storage.

Packages with a product that can't be delivered are initialized as not for sale.
That leaves 60 packages for sale: jewel bundles, Kundun boxes, Cherry Blossom
That leaves 62 packages for sale: jewel bundles, Kundun boxes, Cherry Blossom
items and the Talisman of Luck 10% for Goblin Points, and the character cards,
the Magic Backpack, the Vault Expansion Certificate, the free tickets of Devil
Square, Blood Castle and Illusion Temple, the Kalima tickets, the reset fruits,
the elite potions and the Talisman of Luck for WCoin (C) and (P), and the ten
the elite potions, the Talisman of Luck and the Talisman of Mobility for
WCoin (C) and (P), and the ten
lucky item tickets and the Gold Key and Silver Key for WCoin (P). Most cash shop
items, like seals, scrolls and most talismans, aren't defined in the item
configuration yet.
Expand Down
16 changes: 16 additions & 0 deletions src/GameLogic/Attributes/Stats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,22 @@ public class Stats
/// </summary>
public static AttributeDefinition TotalRegisteredRenas { get; } = new(new Guid("56C2D3B4-4F5A-6B7C-8D9E-0F1A2B3C4D5E"), "Total Registered Renas", "Total registered items (all-time counter)");

/// <summary>
/// Gets the attribute definition of the map number of the place which a character saved with the Talisman of Mobility.
/// It's 1 more than the map number, so that 0 means that no place is saved.
/// </summary>
public static AttributeDefinition MobilityTalismanMap { get; } = new(new Guid("1F8D393B-EB1B-4777-A231-B97DAFB8072A"), "Talisman of Mobility Map", "The map number of the place which was saved with the Talisman of Mobility, plus 1. 0 means that no place is saved.");
Comment thread
icheat2win marked this conversation as resolved.
Resolved

/// <summary>
/// Gets the attribute definition of the x coordinate of the place which a character saved with the Talisman of Mobility.
/// </summary>
public static AttributeDefinition MobilityTalismanX { get; } = new(new Guid("B0A203B6-1BD2-4AFD-8916-CCD2AAC47097"), "Talisman of Mobility X", "The x coordinate of the place which was saved with the Talisman of Mobility.");

/// <summary>
/// Gets the attribute definition of the y coordinate of the place which a character saved with the Talisman of Mobility.
/// </summary>
public static AttributeDefinition MobilityTalismanY { get; } = new(new Guid("2CA2ED75-3BC4-47A1-A183-032A005D9CF6"), "Talisman of Mobility Y", "The y coordinate of the place which was saved with the Talisman of Mobility.");

/// <summary>
/// Gets the zen amount rate attribute definition.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/GameLogic/ItemConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public class ItemConstants
/// </summary>
public static ItemIdentifier CommandResetFruit => new(58, 13);

/// <summary>
/// Gets the identifier for the Talisman of Mobility, which saves a place and warps the player back to it.
/// </summary>
public static ItemIdentifier TalismanOfMobility => new(70, 13);

/// <summary>
/// Gets the identifier for the fenrir.
/// </summary>
Expand Down
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;
Comment thread
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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the bookmark for other half-used talismans

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 GetSavedGateAsync and cannot perform its remaining use unless another unused talisman saves a location. Retain the bookmark for other half-used talismans, or prevent multiple outstanding saves rather than silently invalidating another item's paid return use.

Useful? React with 👍 / 👎.

@sven-n sven-n Nov 11, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's acceptable for now.
Prevent consuming a second talisman while there is still one with 1 durability in the inventory.

await this.ConsumeSourceItemAsync(player, item).ConfigureAwait(false);
await player.WarpToAsync(gate).ConfigureAwait(false);
Comment thread
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve an available map host before accepting the return gate

If the saved map remains active in configuration but its hosting server is stopped, a character on another server gets a null result from GetMapAsync, yet this fallback still produces a usable gate. The caller then clears the bookmark and consumes the talisman, while PlayerMapTransitions.ClientReadyAfterMapChangeAsync cannot locate the destination and warps the character to its home map instead. Check for an available local or remote host before accepting the gate, leaving the bookmark and item intact when none is available.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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));
}
}
18 changes: 18 additions & 0 deletions src/GameLogic/Properties/PlayerMessage.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/GameLogic/Properties/PlayerMessage.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1021,4 +1021,10 @@ Inventarerweiterungen: {18}</value>
<data name="WarpOnlyForGameMasters" xml:space="preserve">
<value>Dieser Warp ist nur für Game Master verfügbar.</value>
</data>
<data name="MobilityTalismanPlaceSaved" xml:space="preserve">
<value>Der Ort wurde gespeichert. Benutze den Talisman erneut, um zu ihm zurückzukehren.</value>
</data>
<data name="MobilityTalismanNoPlaceSaved" xml:space="preserve">
<value>Mit einem Talisman der Mobilität wurde kein Ort gespeichert.</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/GameLogic/Properties/PlayerMessage.pt.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1174,4 +1174,10 @@ Extensões de inventário: {18}</value>
<data name="WarpOnlyForGameMasters" xml:space="preserve">
<value>Este warp está disponível apenas para game masters.</value>
</data>
<data name="MobilityTalismanPlaceSaved" xml:space="preserve">
<value>O local foi salvo. Use o talismã novamente para voltar a ele.</value>
</data>
<data name="MobilityTalismanNoPlaceSaved" xml:space="preserve">
<value>Nenhum local foi salvo com um Talismã da Mobilidade.</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/GameLogic/Properties/PlayerMessage.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1174,4 +1174,10 @@ Inventory Extensions: {18}</value>
<data name="WarpOnlyForGameMasters" xml:space="preserve">
<value>This warp is only available to game masters.</value>
</data>
<data name="MobilityTalismanPlaceSaved" xml:space="preserve">
<value>The place has been saved. Use the talisman again to return to it.</value>
</data>
<data name="MobilityTalismanNoPlaceSaved" xml:space="preserve">
<value>No place has been saved with a Talisman of Mobility.</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/GameLogic/Properties/PlugInResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/GameLogic/Properties/PlugInResources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2016,4 +2016,10 @@
<data name="ShowWarpListAfterEnterWorldPlugIn_Description" xml:space="preserve">
<value>Teilt dem Client beim Betreten des Spiels mit, welche Warps der Spieler im Warp-Fenster auswählen kann.</value>
</data>
<data name="TalismanOfMobilityConsumeHandlerPlugIn_Name" xml:space="preserve">
<value>Verbrauch des Talismans der Mobilität</value>
</data>
<data name="TalismanOfMobilityConsumeHandlerPlugIn_Description" xml:space="preserve">
<value>Plugin, das den Talisman der Mobilität verarbeitet. Die erste Benutzung speichert den Ort des Charakters, die zweite versetzt ihn an den gespeicherten Ort zurück.</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/GameLogic/Properties/PlugInResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2061,4 +2061,10 @@
<data name="ShowWarpListAfterEnterWorldPlugIn_Description" xml:space="preserve">
<value>Tells the client which warps the player can select in the warp window, when the player enters the game.</value>
</data>
<data name="TalismanOfMobilityConsumeHandlerPlugIn_Name" xml:space="preserve">
<value>Talisman of Mobility Consume Handler</value>
</data>
<data name="TalismanOfMobilityConsumeHandlerPlugIn_Description" xml:space="preserve">
<value>Plugin which handles the Talisman of Mobility. The first usage saves the place of the character, the second one warps it back to the saved place.</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/GameLogic/Properties/PlugInResources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,12 @@
<data name="MediumEliteHealingPotionConsumeHandlerPlugIn_Description" xml:space="preserve">
<value>处理中瓶精华治疗药水的使用和消耗,立即恢复部分生命值。</value>
</data>
<data name="TalismanOfMobilityConsumeHandlerPlugIn_Name" xml:space="preserve">
<value>机动护身符使用</value>
</data>
<data name="TalismanOfMobilityConsumeHandlerPlugIn_Description" xml:space="preserve">
<value>处理机动护身符。第一次使用保存角色的位置,第二次使用将角色传送回保存的位置。</value>
</data>
<data name="StartIllusionTempleEventChatCommandPlugIn_Name" xml:space="preserve"><value>开启幻影寺院活动命令</value></data>
<data name="StartIllusionTempleEventChatCommandPlugIn_Description" xml:space="preserve"><value>处理 /startit 命令,在下一个允许的时机开启幻影寺院活动。</value></data>
<data name="LoginTimeoutPlugIn_Name" xml:space="preserve"><value>登录超时</value></data>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Persistence/Initialization/Properties/ItemNames.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2259,4 +2259,7 @@
<data name="TalismanOfItemProtection" xml:space="preserve">
<value>Talisman des Gegenstandsschutzes</value>
</data>
<data name="TalismanOfMobility" xml:space="preserve">
<value>Talisman der Mobilität</value>
</data>
</root>
3 changes: 3 additions & 0 deletions src/Persistence/Initialization/Properties/ItemNames.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2343,4 +2343,7 @@
<data name="TalismanOfItemProtection" xml:space="preserve">
<value>Talisman of Item Protection</value>
</data>
<data name="TalismanOfMobility" xml:space="preserve">
<value>Talisman of Mobility</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -2343,4 +2343,7 @@
<data name="TalismanOfItemProtection" xml:space="preserve">
<value>物品保护护身符</value>
</data>
<data name="TalismanOfMobility" xml:space="preserve">
<value>机动护身符</value>
</data>
</root>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,12 @@
<data name="AddSealItemDropRateUpdatePlugIn_Description" xml:space="preserve">
<value>Dieses Update fügt dem Siegel des Wohlstands, dem Siegel der Göttlichkeit und dem Meistersiegel des Wohlstands die Item-Droprate von 150 % hinzu, und fügt das zweite Siegel des Wohlstands (13/116) hinzu, das nur die Item-Droprate erhöht.</value>
</data>
<data name="AddTalismanOfMobilityUpdatePlugIn_Name" xml:space="preserve">
<value>Talisman der Mobilität hinzufügen</value>
</data>
<data name="AddTalismanOfMobilityUpdatePlugIn_Description" xml:space="preserve">
<value>Dieses Update fügt den Talisman der Mobilität hinzu, der einen Ort speichert und den Charakter dorthin zurückversetzt. Die Pakete des In-Game-Shops, die ihn enthalten, werden zum Verkauf angeboten.</value>
</data>
<data name="AdjustIceArrowFreezeDurationUpdatePlugIn_Name" xml:space="preserve">
<value>Einfrierdauer des Eis-Pfeils anpassen</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@
<data name="FixKanturuRuins3LandingUpdatePlugIn_Description" xml:space="preserve"><value>Narrows the original Kanturu Ruins 3 warp landing area to walkable terrain. Customized destinations and unrelated settings are kept.</value></data>
<data name="AddSealItemDropRateUpdatePlugIn_Name" xml:space="preserve"><value>Add the item drop rate of seals</value></data>
<data name="AddSealItemDropRateUpdatePlugIn_Description" xml:space="preserve"><value>This update adds the item drop rate of 150 % to the Seals of Wealth and Divinity and the Master Seal of Wealth, and adds the second Seal of Wealth (13/116), which only raises the item drop rate.</value></data>
<data name="AddTalismanOfMobilityUpdatePlugIn_Name" xml:space="preserve"><value>Add the Talisman of Mobility</value></data>
<data name="AddTalismanOfMobilityUpdatePlugIn_Description" xml:space="preserve"><value>This update adds the Talisman of Mobility, which saves a place and warps the character back to it. The packages of the in-game shop which contain it are put on sale.</value></data>
<data name="AdjustIceArrowFreezeDurationUpdatePlugIn_Name" xml:space="preserve"><value>Adjust Ice Arrow freeze duration</value></data>
<data name="AdjustIceArrowFreezeDurationUpdatePlugIn_Description" xml:space="preserve"><value>Extends the default Ice Arrow freeze duration, and adds its reduction on successful hits. Customized durations are left unchanged. Also aligns Ice Arrow Str, Chain Drive Str, Strike of Destruction Str and Poison Str with their base skills magic effects.</value></data>
<data name="FieldMonsterSpawnsUpdatePlugInSeason6_Name" xml:space="preserve"><value>Season 6 monster spawns of Devias, Noria and Elvenland</value></data>
Expand Down
Loading
Loading