Skip to content
Merged
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
12 changes: 12 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,18 @@ 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 Resurrection

When a character dies while it carries a Talisman of Resurrection in its
inventory, it's respawned at the place of its death instead of the safezone, and
one talisman is used up. It doesn't apply in mini games, duels and guild wars,
which have their own spawn places. The plugin *Talisman of Resurrection* 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 Resurrection*, 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 Resurrection 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
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 Resurrection, which respawns a dead player at the place of its death.
/// </summary>
public static ItemIdentifier TalismanOfResurrection => new(69, 13);
Comment thread
sven-n marked this conversation as resolved.
Resolved

/// <summary>
/// Gets the identifier for the fenrir.
/// </summary>
Expand Down
83 changes: 83 additions & 0 deletions src/GameLogic/PlugIns/TalismanOfResurrectionSpawnGatePlugIn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// <copyright file="TalismanOfResurrectionSpawnGatePlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.GameLogic.PlugIns;

using System.Runtime.InteropServices;
using MUnique.OpenMU.GameLogic.CastleSiege;
using MUnique.OpenMU.GameLogic.PlayerActions;
using MUnique.OpenMU.GameLogic.Views.Inventory;
using MUnique.OpenMU.PlugIns;

/// <summary>
/// A plugin which respawns a dead player at the place of its death, when it carries a Talisman of Resurrection
/// in its inventory. One talisman is used up by it.
/// </summary>
/// <remarks>
/// It doesn't apply in mini games, duels, guild wars and to the participants of a running castle siege,
/// which have their own spawn gates. It also doesn't apply when the player doesn't fulfill the requirements
/// of the map anymore, e.g. when it lost its wings on Icarus, so that it's evacuated to the safezone.
/// The item is configurable, by default it's the Talisman of Resurrection (13/69).
/// </remarks>
[PlugIn]
[Display(Name = nameof(MUnique.OpenMU.GameLogic.Properties.PlugInResources.TalismanOfResurrectionSpawnGatePlugIn_Name), Description = nameof(MUnique.OpenMU.GameLogic.Properties.PlugInResources.TalismanOfResurrectionSpawnGatePlugIn_Description), ResourceType = typeof(MUnique.OpenMU.GameLogic.Properties.PlugInResources))]
[Guid("5C057E6E-FECF-4216-AB78-1918AB00224E")]
public class TalismanOfResurrectionSpawnGatePlugIn : IPlayerSpawnGateSelectionPlugIn, ISupportCustomConfiguration<TalismanOfResurrectionSpawnGatePlugInConfiguration>, ISupportDefaultCustomConfiguration
{
/// <inheritdoc />
public TalismanOfResurrectionSpawnGatePlugInConfiguration? Configuration { get; set; }

/// <inheritdoc />
public async ValueTask SelectSpawnGateAsync(Player player, SpawnGateSelectionArgs args)
{
if (args.Gate is not null
|| player.IsAlive
|| player.CurrentMap is not { } map
|| player.CurrentMiniGame is not null
|| player.DuelRoom is not null
|| player.GuildWarContext is not null
Comment on lines +37 to +39

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve Castle Siege's side-specific respawn gates

During an active Castle Siege, participants use a separate CastleSiegeContext, so none of these exclusions applies. If this selector runs before CastleSiegePlugIn, for example after the siege plugin is deactivated and reactivated, it sets args.Gate first. CastleSiegePlugIn.SelectSpawnGateAsync then skips its side-specific gate because it requires args.Gate to be null. A talisman consequently lets attackers respawn behind the castle gates instead of returning to their side's respawn area. Exclude active siege participants or otherwise guarantee that siege gate selection precedes this consumptive fallback.

Useful? React with 👍 / 👎.

|| IsCastleSiegeParticipant(player)
|| map.Definition.TryGetRequirementError(player, out _)
|| player.Inventory is not { } inventory
|| inventory.Items.FirstOrDefault(this.IsTalismanOfResurrection) is not { } talisman)
{
return;
}

var position = player.Position;
args.Gate = new ExitGate
{
Map = map.Definition,
Comment on lines +49 to +51

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 forced safezone evacuation before consuming a talisman

When a lethal hit destroys a character's only flying mount on Icarus, AttackByAsync has already called OnDeathAsync before DecreasePetDurabilityAsync attempts the mandatory safezone evacuation. WarpToSafezoneAsync invokes this selector too, so the dead character's talisman is consumed and the required move to Devias is replaced with its current Icarus position, even though it no longer satisfies the map's CanFly requirement. Restrict this consumption to actual death-respawn requests and preserve mandatory evacuation when the current map's requirements are no longer met.

Useful? React with 👍 / 👎.

X1 = position.X,
X2 = position.X,
Y1 = position.Y,
Y2 = position.Y,
};

var slot = talisman.ItemSlot;
await inventory.RemoveItemAsync(talisman).ConfigureAwait(false);
await player.PersistenceContext.DeleteAsync(talisman).ConfigureAwait(false);
Comment on lines +59 to +60

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 Keep talismans when a dead player disconnects

If a player disconnects during the three-second death delay on an ordinary map, Player.DisconnectAsync first changes its state to Disconnected, then HandleMoveToNextSafezoneAsync cancels the respawn and calls WarpToSafezoneAsync. That cleanup path invokes this selector and deletes a talisman, but PlayerMapTransitions.WarpToAsync immediately returns for disconnected players. The cash-shop item is therefore permanently consumed without performing a resurrection. Reject disconnected/finished players here, or defer consumption until a death respawn is actually executed.

Useful? React with 👍 / 👎.

Comment on lines +59 to +60

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 Consume only one unit of a configured stackable item

When an operator enables stacking for the talisman by increasing its definition's durability, or configures another stackable resurrection item, these calls delete the entire stack for one resurrection. DataModel.ItemExtensions.IsStackable identifies such items, and InventoryProductDelivery.CreateItems stores their quantity in Item.Durability, so a single death permanently discards all remaining units instead of consuming the documented one talisman. Decrement the stack quantity and send a durability update, removing and deleting the item only when its last unit is consumed.

Useful? React with 👍 / 👎.

await player.InvokeViewPlugInAsync<IItemRemovedPlugIn>(p => p.RemoveItemAsync(slot)).ConfigureAwait(false);
}

/// <inheritdoc />
public object CreateDefaultConfig()
{
return new TalismanOfResurrectionSpawnGatePlugInConfiguration();
}

private static bool IsCastleSiegeParticipant(Player player)
{
return CastleSiegeContextResolver.GetContext(player) is { CurrentState: CastleSiegeState.Start } context
&& context.GetPlayerJoinSide(player) != CastleSiegeJoinSide.None;
}

private bool IsTalismanOfResurrection(Item item)
{
var configuration = this.Configuration ??= new TalismanOfResurrectionSpawnGatePlugInConfiguration();
return item.Definition is { } definition
&& definition.Group == configuration.ItemGroup
&& definition.Number == configuration.ItemNumber;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// <copyright file="TalismanOfResurrectionSpawnGatePlugInConfiguration.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.GameLogic.PlugIns;

/// <summary>
/// Configuration for the <see cref="TalismanOfResurrectionSpawnGatePlugIn"/>.
/// </summary>
public class TalismanOfResurrectionSpawnGatePlugInConfiguration
{
/// <summary>
/// Gets or sets the group of the item which respawns a dead player at the place of its death.
/// </summary>
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.TalismanOfResurrectionSpawnGatePlugInConfiguration_ItemGroup_Caption))]
public byte ItemGroup { get; set; } = ItemConstants.TalismanOfResurrection.Group;

/// <summary>
/// Gets or sets the number of the item which respawns a dead player at the place of its death.
/// </summary>
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.TalismanOfResurrectionSpawnGatePlugInConfiguration_ItemNumber_Caption))]
public short ItemNumber { get; set; } = ItemConstants.TalismanOfResurrection.Number!.Value;
}
12 changes: 12 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.

12 changes: 12 additions & 0 deletions src/GameLogic/Properties/PlugInResources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2016,6 +2016,18 @@
<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="TalismanOfResurrectionSpawnGatePlugIn_Name" xml:space="preserve">
<value>Talisman der Wiederbelebung</value>
</data>
<data name="TalismanOfResurrectionSpawnGatePlugIn_Description" xml:space="preserve">
<value>Lässt einen toten Spieler am Ort seines Todes wieder erscheinen, wenn er einen Talisman der Wiederbelebung trägt, der dabei verbraucht wird. Gilt nicht in Minispielen, Duellen und Gildenkriegen.</value>
</data>
<data name="TalismanOfResurrectionSpawnGatePlugInConfiguration_ItemGroup_Caption" xml:space="preserve">
<value>Gegenstandsgruppe</value>
</data>
<data name="TalismanOfResurrectionSpawnGatePlugInConfiguration_ItemNumber_Caption" xml:space="preserve">
<value>Gegenstandsnummer</value>
</data>
<data name="LuckyCoinFeaturePlugIn_Name" xml:space="preserve">
<value>Glücksmünzen-Event</value>
</data>
Expand Down
8 changes: 8 additions & 0 deletions src/GameLogic/Properties/PlugInResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2061,6 +2061,14 @@
<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="TalismanOfResurrectionSpawnGatePlugIn_Name" xml:space="preserve"><value>Talisman of Resurrection</value></data>
<data name="TalismanOfResurrectionSpawnGatePlugIn_Description" xml:space="preserve"><value>Respawns a dead player at the place of its death, when it carries a Talisman of Resurrection, which is used up. It doesn't apply in mini games, duels and guild wars.</value></data>
<data name="TalismanOfResurrectionSpawnGatePlugInConfiguration_ItemGroup_Caption" xml:space="preserve">
<value>Item group</value>
</data>
<data name="TalismanOfResurrectionSpawnGatePlugInConfiguration_ItemNumber_Caption" xml:space="preserve">
<value>Item number</value>
</data>
<data name="LuckyCoinFeaturePlugIn_Name" xml:space="preserve">
<value>Lucky coin event</value>
</data>
Expand Down
12 changes: 12 additions & 0 deletions src/GameLogic/Properties/PlugInResources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,12 @@
<data name="SoccerSpawnGatePlugIn_Description" xml:space="preserve">
<value>将正在参加足球比赛的玩家生成在足球场上。</value>
</data>
<data name="TalismanOfResurrectionSpawnGatePlugIn_Name" xml:space="preserve">
<value>复活护身符</value>
</data>
<data name="TalismanOfResurrectionSpawnGatePlugIn_Description" xml:space="preserve">
<value>携带复活护身符的玩家死亡后在死亡地点复活,并消耗该护身符。不适用于小游戏、决斗和战盟战。</value>
</data>
<data name="PetExperiencePlugIn_Name" xml:space="preserve">
<value>宠物经验分配</value>
</data>
Expand Down Expand Up @@ -1804,6 +1810,12 @@
<data name="IllusionTempleAllianceStorageTalkPlugIn_Description" xml:space="preserve"><value>处理玩家与幻影寺院盟军物品仓库的交互。盟军将圣物送至此处可获得一分。</value></data>
<data name="IllusionTempleIllusionStorageTalkPlugIn_Name" xml:space="preserve"><value>幻影寺院幻影物品仓库交互</value></data>
<data name="IllusionTempleIllusionStorageTalkPlugIn_Description" xml:space="preserve"><value>处理玩家与幻影寺院幻影物品仓库的交互。幻影军将圣物送至此处可获得一分。</value></data>
<data name="TalismanOfResurrectionSpawnGatePlugInConfiguration_ItemGroup_Caption" xml:space="preserve">
<value>物品组</value>
</data>
<data name="TalismanOfResurrectionSpawnGatePlugInConfiguration_ItemNumber_Caption" xml:space="preserve">
<value>物品编号</value>
</data>
<data name="LuckyCoinFeaturePlugIn_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,6 +2259,9 @@
<data name="TalismanOfItemProtection" xml:space="preserve">
<value>Talisman des Gegenstandsschutzes</value>
</data>
<data name="TalismanOfResurrection" xml:space="preserve">
<value>Talisman der Wiederbelebung</value>
</data>
<data name="LuckyCoin" xml:space="preserve">
<value>Glücksmünze</value>
</data>
Expand Down
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,6 +2343,9 @@
<data name="TalismanOfItemProtection" xml:space="preserve">
<value>Talisman of Item Protection</value>
</data>
<data name="TalismanOfResurrection" xml:space="preserve">
<value>Talisman of Resurrection</value>
</data>
<data name="LuckyCoin" xml:space="preserve">
<value>Lucky Coin</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2343,6 +2343,9 @@
<data name="TalismanOfItemProtection" xml:space="preserve">
<value>物品保护护身符</value>
</data>
<data name="TalismanOfResurrection" xml:space="preserve">
<value>复活护身符</value>
</data>
<data name="LuckyCoin" 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.

Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,12 @@
<data name="FixCrywolfGateTerrainUpdatePlugIn_Description" xml:space="preserve">
<value>Macht den südlichen Zugang zum Tor der Festung Crywolf im Tal von Loren begehbar, den Spieler außerhalb der Burgbelagerung benutzen. Der nördliche Zugang und alle anderen Felder bleiben unverändert.</value>
</data>
<data name="AddTalismanOfResurrectionUpdatePlugIn_Name" xml:space="preserve">
<value>Talisman der Wiederbelebung hinzufügen</value>
</data>
<data name="AddTalismanOfResurrectionUpdatePlugIn_Description" xml:space="preserve">
<value>Dieses Update fügt den Talisman der Wiederbelebung hinzu, der einen toten Spieler am Ort seines Todes wieder erscheinen lässt. Die Pakete des In-Game-Shops, die ihn enthalten, werden zum Verkauf angeboten.</value>
</data>
<data name="AddLuckyCoinUpdatePlugIn_Name" xml:space="preserve">
<value>Glücksmünze hinzufügen</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@
<data name="AddProtectionTalismansUpdatePlugIn_Description" xml:space="preserve"><value>This update adds the Talisman of Guardian, which protects from the death penalty, and the Talisman of Item Protection, which keeps the durability of the equipped items.</value></data>
<data name="FixCrywolfGateTerrainUpdatePlugIn_Name" xml:space="preserve"><value>Fix the Crywolf entrance in Valley of Loren</value></data>
<data name="FixCrywolfGateTerrainUpdatePlugIn_Description" xml:space="preserve"><value>Makes the south entry of the gate to Crywolf Fortress in Valley of Loren walkable, which players use outside of Castle Siege. The north entry and all other tiles are kept.</value></data>
<data name="AddTalismanOfResurrectionUpdatePlugIn_Name" xml:space="preserve"><value>Add the Talisman of Resurrection</value></data>
<data name="AddTalismanOfResurrectionUpdatePlugIn_Description" xml:space="preserve"><value>This update adds the Talisman of Resurrection, which respawns a dead player at the place of its death. The packages of the in-game shop which contain it are put on sale.</value></data>
<data name="AddLuckyCoinUpdatePlugIn_Name" xml:space="preserve"><value>Add the lucky coin</value></data>
<data name="AddLuckyCoinUpdatePlugIn_Description" xml:space="preserve"><value>This update adds the lucky coin, which is registered or exchanged for items at the NPC Delgado in Devias, and opens his lucky coin dialog.</value></data>
<data name="AddGladiatorsHonorUpdatePlugIn_Name" xml:space="preserve">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,12 @@
</data>
<data name="FixCrywolfGateTerrainUpdatePlugIn_Name" xml:space="preserve"><value>修正罗兰峡谷通往狼魂要塞的入口</value></data>
<data name="FixCrywolfGateTerrainUpdatePlugIn_Description" xml:space="preserve"><value>使罗兰峡谷中狼魂要塞传送门的南侧入口可以行走(攻城战以外的时间玩家使用该入口)。北侧入口及其他所有地块保持不变。</value></data>
<data name="AddTalismanOfResurrectionUpdatePlugIn_Name" xml:space="preserve">
<value>添加复活护身符</value>
</data>
<data name="AddTalismanOfResurrectionUpdatePlugIn_Description" xml:space="preserve">
<value>此更新添加复活护身符,使死亡的玩家在死亡地点复活。包含它的游戏内商城礼包将开始出售。</value>
</data>
<data name="AddLuckyCoinUpdatePlugIn_Name" xml:space="preserve">
<value>添加幸运币</value>
</data>
Expand Down
Loading
Loading