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
14 changes: 14 additions & 0 deletions docs-website/docs/admin-panel/game-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,20 @@ Route `edit-config-grid/…WarpInfo/`
The entries of the in-game warp list: index, name, target gate, level requirement
and cost.

The [open source client](https://github.com/sven-n/MuMain) gets this list when a
character enters the game and shows it in its warp window, so changes show up
there after the next character selection. The original client shows the warp
list of its data files; a warp which isn't in there can't be selected with it.

*Warpable* only restricts players, and only when it's off: then the warp isn't
in the list of the open source client, and the server refuses it when a player
asks for it, e.g. with the move chat command. Game masters can still use it.
A new season 6 configuration has it off for the warps which the original client
doesn't offer in its warp window: Crywolf (reached by walking), Loren Market
(reached through an NPC) and the second LaCleon warp. For an existing
configuration, the optional update *Restrict warps outside of the original warp
window* does the same.

## Jewel mixes

Route `edit-config-grid/…JewelMix/`
Expand Down
34 changes: 34 additions & 0 deletions docs/Packets/C2-F5-09-WarpListExtended_by-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# C2 F5 09 - WarpListExtended (by server)

## Is sent when

After the character entered the game world, when the client is the open source client.

## Causes the following actions on the client side

The client shows these warps in its warp window (and accepts them for the warp chat command) instead of the warp list of its data files. Warps which the client knows keep the name of its data files; others get the name of this message.

## Structure

| Index | Length | Data Type | Value | Description |
|-------|--------|-----------|-------|-------------|
| 0 | 1 | Byte | 0xC2 | [Packet type](PacketTypes.md) |
| 1 | 2 | Short | | Packet header - length of the packet |
| 3 | 1 | Byte | 0xF5 | Packet header - packet type identifier |
| 4 | 1 | Byte | 0x09 | Packet header - sub packet type identifier |
| 5 | 1 | Byte | | Count |
| 6 | WarpListEntry.Length * Count | Array of WarpListEntry | | Warps |

### WarpListEntry Structure

A warp which the player can select in the warp window.

Length: 44 Bytes

| Index | Length | Data Type | Value | Description |
|-------|--------|-----------|-------|-------------|
| 0 | 2 | ShortLittleEndian | | Index; The index of the warp, which the client sends in the WarpCommandRequest. |
| 2 | 2 | ShortLittleEndian | | MapNumber; The number of the map to which the warp leads. |
| 4 | 2 | ShortLittleEndian | | LevelRequirement; The required character level, as configured. Character classes with a reduced warp level requirement get the reduction on top of it, like in the warp list of the data files. |
| 8 | 4 | IntegerLittleEndian | | Costs; The zen which the warp costs. |
| 12 | 32 | String | | Name; The name of the warp, in the language of the player. |
1 change: 1 addition & 0 deletions docs/Packets/ServerToClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
* [C1 F5 03 - DiscordIntegrationInfo (by server)](C1-F5-03-DiscordIntegrationInfo_by-server.md)
* [C1 F5 05 - DiscordLinkCode (by server)](C1-F5-05-DiscordLinkCode_by-server.md)
* [C2 F5 07 - ExternalChatMessage (by server)](C2-F5-07-ExternalChatMessage_by-server.md)
* [C2 F5 09 - WarpListExtended (by server)](C2-F5-09-WarpListExtended_by-server.md)
* [C1 F6 00 - QuestEventLimitResult (by server)](C1-F6-00-QuestEventLimitResult_by-server.md)
* [C1 F6 03 - QuestEventResponse (by server)](C1-F6-03-QuestEventResponse_by-server.md)
* [C1 F6 04 - QuestStartedByItem (by server)](C1-F6-04-QuestStartedByItem_by-server.md)
Expand Down
11 changes: 11 additions & 0 deletions src/DataModel/Configuration/WarpInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ public partial class WarpInfo
/// </summary>
public int LevelRequirement { get; set; }

/// <summary>
/// Gets or sets a value indicating whether players can use this warp.
/// </summary>
/// <remarks>
/// When it's <c>false</c>, the warp is only available to game masters: it isn't sent to the warp list of
/// the game client, and the warp request of a player is refused. It doesn't affect other ways to get to
/// the map, e.g. walking or the move command of a game master. That's the case of the warps which the
/// original client doesn't offer in its warp window, like Crywolf, which is reached by walking.
/// </remarks>
public bool IsWarpable { get; set; } = true;

/// <summary>
/// Gets or sets the gate.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/DataModel/Properties/ModelResources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3615,4 +3615,10 @@
<data name="ItemRequirementFormula_Attribute_Description" xml:space="preserve">
<value>Das Attribut der Gegenstandsanforderungen, für das diese Formel gilt.</value>
</data>
<data name="WarpInfo_IsWarpable_Caption" xml:space="preserve">
<value>Für Spieler verfügbar</value>
</data>
<data name="WarpInfo_IsWarpable_Description" xml:space="preserve">
<value>Ob Spieler diesen Warp verwenden können. Er schränkt Spieler nur ein, wenn er ausgeschaltet ist: Dann ist der Warp nicht in der Warp-Liste des Clients, und die Warp-Anfrage eines Spielers wird abgelehnt; Game Master können ihn weiterhin verwenden.</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/DataModel/Properties/ModelResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -6523,4 +6523,10 @@
<data name="ItemPriceRounding_Tens_Description" xml:space="preserve">
<value>Rounded down to a multiple of 10, and the durability doesn't reduce the price, e.g. for potions.</value>
</data>
<data name="WarpInfo_IsWarpable_Caption" xml:space="preserve">
<value>Warpable</value>
</data>
<data name="WarpInfo_IsWarpable_Description" xml:space="preserve">
<value>Whether players can use this warp. It only restricts players when it's off: then the warp isn't in the warp list of the client and the warp request of a player is refused, while game masters can still use it.</value>
</data>
</root>
3 changes: 2 additions & 1 deletion src/GameLogic/Bots/BotNavigator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2495,8 +2495,9 @@ private IEnumerable<WarpInfo> GetLegalWarps(GameMapDefinition mapDefinition)
// global warp list, so map-number matching remains the compatibility fallback. When the exact
// definition is present, however, use only its entries to avoid mixing distinct definitions
// which happen to share a number.
// Warps which aren't warpable are refused for players (see WarpAction), so bots don't pick them.
var sameNumberWarps = this._player.GameContext.Configuration.WarpList
.Where(w => w.Gate?.Map?.Number == mapDefinition.Number)
.Where(w => w.IsWarpable && w.Gate?.Map?.Number == mapDefinition.Number)
.ToArray();
var mapDefinitionId = mapDefinition.GetId();
var exactMapWarps = mapDefinitionId == Guid.Empty
Expand Down
6 changes: 6 additions & 0 deletions src/GameLogic/PlayerActions/WarpAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public class WarpAction
/// <param name="warpInfo">The warp information.</param>
public async ValueTask WarpToAsync(Player player, WarpInfo warpInfo)
{
if (!warpInfo.IsWarpable && player.SelectedCharacter?.CharacterStatus != CharacterStatus.GameMaster)
{
await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.WarpOnlyForGameMasters)).ConfigureAwait(false);
return;
}

if (warpInfo.Gate is { } gate
&& player.GameContext.PlugInManager.GetPlugInPoint<IWarpGateEnteringPlugIn>() is { } plugInPoint)
{
Expand Down
50 changes: 50 additions & 0 deletions src/GameLogic/PlugIns/ShowWarpListAfterEnterWorldPlugIn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// <copyright file="ShowWarpListAfterEnterWorldPlugIn.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.DataModel.Configuration;
using MUnique.OpenMU.GameLogic.Views.World;
using MUnique.OpenMU.PlugIns;

/// <summary>
/// Shows the warp list of the server to the player when it enters the game, so that the warp window
/// of the client offers the warps of the server instead of the ones of its data files.
/// </summary>
[PlugIn]
[Display(Name = nameof(PlugInResources.ShowWarpListAfterEnterWorldPlugIn_Name), Description = nameof(PlugInResources.ShowWarpListAfterEnterWorldPlugIn_Description), ResourceType = typeof(PlugInResources))]
[Guid("CC3C6192-B0E9-441F-8E7D-D432B562AB86")]
public class ShowWarpListAfterEnterWorldPlugIn : IPlayerStateChangedPlugIn
{
/// <inheritdoc />
public async ValueTask PlayerStateChangedAsync(Player player, State previousState, State currentState)
{
if (previousState != PlayerState.CharacterSelection || currentState != PlayerState.EnteredWorld)
{
return;
}

var warps = GetSelectableWarps(player.GameContext.Configuration.WarpList);
await player.InvokeViewPlugInAsync<IShowWarpListPlugIn>(p => p.ShowWarpListAsync(warps)).ConfigureAwait(false);
}

/// <summary>
/// Gets the warps which a player can select, ordered by their index.
/// </summary>
/// <param name="warpList">The configured warp list.</param>
/// <returns>
/// The warps which lead to a map and which players can use (<see cref="WarpInfo.IsWarpable"/>).
/// Of warps with the same index, only the first one is included, because it's the one which a warp
/// request with that index uses.
/// </returns>
internal static IReadOnlyCollection<WarpInfo> GetSelectableWarps(IEnumerable<WarpInfo> warpList)
{
return warpList
.DistinctBy(warp => warp.Index)
.Where(warp => warp.IsWarpable && warp.Gate?.Map is not null)
.OrderBy(warp => warp.Index)
.ToList();
}
}
9 changes: 9 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.

3 changes: 3 additions & 0 deletions src/GameLogic/Properties/PlayerMessage.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1018,4 +1018,7 @@ Inventarerweiterungen: {18}</value>
<data name="MapIsNotActive" xml:space="preserve">
<value>Diese Karte ist nicht verfügbar.</value>
</data>
<data name="WarpOnlyForGameMasters" xml:space="preserve">
<value>Dieser Warp ist nur für Game Master verfügbar.</value>
</data>
</root>
3 changes: 3 additions & 0 deletions src/GameLogic/Properties/PlayerMessage.pt.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1171,4 +1171,7 @@ Extensões de inventário: {18}</value>
<data name="MiniGameTicketRequired" xml:space="preserve">
<value>Você precisa de um ingresso para entrar neste evento.</value>
</data>
<data name="WarpOnlyForGameMasters" xml:space="preserve">
<value>Este warp está disponível apenas para game masters.</value>
</data>
</root>
3 changes: 3 additions & 0 deletions src/GameLogic/Properties/PlayerMessage.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1171,4 +1171,7 @@ Inventory Extensions: {18}</value>
<data name="MapIsNotActive" xml:space="preserve">
<value>This map is not available.</value>
</data>
<data name="WarpOnlyForGameMasters" xml:space="preserve">
<value>This warp is only available to game masters.</value>
</data>
</root>
Loading
Loading