baseline: OpenMU upstream b5a0961 (fresh source)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// <copyright file="MerchantSpawnState.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.WanderingMerchants;
|
||||
|
||||
using MUnique.OpenMU.GameLogic.NPC;
|
||||
|
||||
/// <summary>
|
||||
/// Spawn state of a wandering merchant.
|
||||
/// </summary>
|
||||
public class MerchantSpawnState
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MerchantSpawnState"/> class.
|
||||
/// </summary>
|
||||
/// <param name="merchantDefinition">The merchant definition.</param>
|
||||
/// <param name="possibleSpawns">The possible spawns.</param>
|
||||
public MerchantSpawnState(MonsterDefinition merchantDefinition, List<MonsterSpawnArea> possibleSpawns)
|
||||
{
|
||||
this.MerchantDefinition = merchantDefinition;
|
||||
this.PossibleSpawns = possibleSpawns;
|
||||
this.NextWanderingAt = DateTime.UtcNow.AddSeconds(10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the merchant definition.
|
||||
/// </summary>
|
||||
public MonsterDefinition MerchantDefinition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the possible spawn points of the merchant.
|
||||
/// </summary>
|
||||
public List<MonsterSpawnArea> PossibleSpawns { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the spawned merchant.
|
||||
/// </summary>
|
||||
public NonPlayerCharacter? Merchant { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets time when the next wandering should occur.
|
||||
/// </summary>
|
||||
public DateTime NextWanderingAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// <copyright file="WanderingMerchantsConfiguration.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.WanderingMerchants;
|
||||
|
||||
using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration of wandering merchants.
|
||||
/// </summary>
|
||||
public class WanderingMerchantsConfiguration : PeriodicTaskConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum duration of the spawn.
|
||||
/// </summary>
|
||||
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.WanderingMerchantsConfiguration_MinimumSpawnDuration_Name))]
|
||||
public TimeSpan MinimumSpawnDuration { get; set; } = TimeSpan.FromMinutes(60);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum duration of the spawn.
|
||||
/// </summary>
|
||||
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.WanderingMerchantsConfiguration_MaximumSpawnDuration_Name))]
|
||||
public TimeSpan MaximumSpawnDuration { get; set; } = TimeSpan.FromMinutes(180);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// <copyright file="WanderingMerchantsPlugIn.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.WanderingMerchants;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.NPC;
|
||||
using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This plugins spawns and moves the wandering merchants.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.WanderingMerchantsPlugIn_Name), Description = nameof(PlugInResources.WanderingMerchantsPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("8B2CD316-C4B0-452F-8C7D-CE696356D437")]
|
||||
public class WanderingMerchantsPlugIn : PeriodicTaskBasePlugIn<WanderingMerchantsConfiguration, WanderingMerchantsState>, ISupportDefaultCustomConfiguration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object CreateDefaultConfig()
|
||||
{
|
||||
return new WanderingMerchantsConfiguration
|
||||
{
|
||||
PreStartMessageDelay = TimeSpan.Zero,
|
||||
|
||||
// we check every minute if we have to move a merchant.
|
||||
TaskDuration = TimeSpan.FromMinutes(1),
|
||||
MinimumSpawnDuration = TimeSpan.FromMinutes(60),
|
||||
MaximumSpawnDuration = TimeSpan.FromMinutes(180),
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsItTimeToStart(IGameContext gameContext)
|
||||
{
|
||||
var state = this.GetStateByGameContext(gameContext);
|
||||
if (!state.Merchants.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var minNext = state.Merchants.Min(m => m.NextWanderingAt);
|
||||
return DateTime.UtcNow >= minNext;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask OnStartedAsync(WanderingMerchantsState state)
|
||||
{
|
||||
var wanderingNow = state.Merchants.Where(m => m.NextWanderingAt <= DateTime.UtcNow).ToList();
|
||||
foreach (var merchantState in wanderingNow)
|
||||
{
|
||||
await this.HandleMerchantAsync(state, merchantState).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override WanderingMerchantsState CreateState(IGameContext gameContext)
|
||||
{
|
||||
return new WanderingMerchantsState(gameContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask OnFinishedAsync(WanderingMerchantsState state)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask OnPrepareEventAsync(WanderingMerchantsState state)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask OnPreparedAsync(WanderingMerchantsState state)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
private async Task HandleMerchantAsync(WanderingMerchantsState state, MerchantSpawnState merchantState)
|
||||
{
|
||||
var logger = state.Context.LoggerFactory.CreateLogger(this.GetType().Name);
|
||||
using var scope = logger.BeginScope(state.Context);
|
||||
|
||||
var oldMerchant = merchantState.Merchant;
|
||||
var nextSpawn = merchantState.PossibleSpawns.Count == 1
|
||||
? merchantState.PossibleSpawns.First()
|
||||
: merchantState.PossibleSpawns.Where(s => s != merchantState.Merchant?.SpawnArea).SelectRandom()!;
|
||||
|
||||
if (oldMerchant?.SpawnArea == nextSpawn)
|
||||
{
|
||||
logger.LogDebug("Same spawn area for merchant {merchant}.", oldMerchant);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextSpawn.GameMap is not { } nextMapDefinition)
|
||||
{
|
||||
logger.LogWarning("Spawn area {spawnArea} has no map defined.", nextSpawn);
|
||||
return;
|
||||
}
|
||||
|
||||
var nextMap = await state.Context.GetMapAsync((ushort)nextMapDefinition.Number).ConfigureAwait(false);
|
||||
if (nextMap is null)
|
||||
{
|
||||
logger.LogWarning("Could not create map {map} for next wandering.", nextMapDefinition);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.RemoveOldMerchantIfNeededAsync(oldMerchant, logger, merchantState).ConfigureAwait(false);
|
||||
await this.SpawnMerchantAsync(nextSpawn, merchantState, nextMap, logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task RemoveOldMerchantIfNeededAsync(NonPlayerCharacter? merchant, ILogger logger, MerchantSpawnState merchantState)
|
||||
{
|
||||
if (merchant is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldMap = merchant.CurrentMap;
|
||||
await oldMap.RemoveAsync(merchant).ConfigureAwait(false);
|
||||
await merchant.DisposeAsync().ConfigureAwait(false);
|
||||
logger.LogDebug("Old merchant {merchant} has been removed from {map}.", merchant, oldMap.Definition);
|
||||
merchantState.Merchant = null;
|
||||
}
|
||||
|
||||
private async Task SpawnMerchantAsync(MonsterSpawnArea nextSpawn, MerchantSpawnState merchantState, GameMap nextMap, ILogger logger)
|
||||
{
|
||||
var newMerchant = new NonPlayerCharacter(nextSpawn, merchantState.MerchantDefinition, nextMap);
|
||||
merchantState.Merchant = newMerchant;
|
||||
newMerchant.Initialize();
|
||||
await nextMap.AddAsync(newMerchant).ConfigureAwait(false);
|
||||
merchantState.NextWanderingAt = DateTime.UtcNow.AddMinutes(Rand.NextInt((int)this.Configuration!.MinimumSpawnDuration.TotalMinutes, (int)this.Configuration!.MaximumSpawnDuration.TotalMinutes));
|
||||
logger.LogDebug("New merchant {merchant} has been created on {map}. Next wandering at {next}", newMerchant, nextMap.Definition, merchantState.NextWanderingAt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// <copyright file="WanderingMerchantsState.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.WanderingMerchants;
|
||||
|
||||
using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps track of the state of the wandering merchants of a game server.
|
||||
/// </summary>
|
||||
public class WanderingMerchantsState : PeriodicTaskGameServerState
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WanderingMerchantsState"/> class.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
public WanderingMerchantsState(IGameContext context)
|
||||
: base(context)
|
||||
{
|
||||
var groupedMerchants = context.Configuration.Maps
|
||||
.SelectMany(m => m.MonsterSpawns.Where(s => s.SpawnTrigger == SpawnTrigger.Wandering))
|
||||
.GroupBy(s => s.MonsterDefinition)
|
||||
.Where(s => s.Key is not null)
|
||||
.Select(g => (g.Key, g.ToList()))
|
||||
.ToList();
|
||||
foreach (var (merchant, spawns) in groupedMerchants)
|
||||
{
|
||||
var merchantState = new MerchantSpawnState(merchant!, spawns);
|
||||
this.Merchants.Add(merchantState);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the states of the merchants.
|
||||
/// </summary>
|
||||
public ICollection<MerchantSpawnState> Merchants { get; } = new List<MerchantSpawnState>();
|
||||
}
|
||||
Reference in New Issue
Block a user