baseline: OpenMU upstream b5a0961 (fresh source)
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
// <copyright file="AddAreaSkillSettingsUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This adds the items required to enter the kalima map.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("D01DA745-BF72-40C4-BD90-D2D637AEDF99")]
|
||||
public class AddAreaSkillSettingsUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Area Skill Settings";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Adds the new area skill settings for skills like evil spirit, etc. to make them work properly again.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddAreaSkillSettings;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 10, 25, 19, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Flame, false, default, default, default, true, TimeSpan.Zero, TimeSpan.FromMilliseconds(500), 0, 2, default, 0.5f, targetAreaDiameter: 2, useTargetAreaFilter: true);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Twister, true, 1.5f, 1.5f, 4f, true, TimeSpan.FromMilliseconds(300), TimeSpan.FromMilliseconds(1000), 0, 2, default, 0.7f);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.EvilSpirit, false, default, default, default, true, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(1000), 0, 2, default, 0.7f, newRange: 7);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.AquaBeam, true, 1.5f, 1.5f, 8f);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Cometfall, false, default, default, default, targetAreaDiameter: 2, useTargetAreaFilter: true);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.TripleShot, true, 1f, 4.5f, 7f, true, TimeSpan.FromMilliseconds(50), maximumHitsPerTarget: 3, maximumHitsPerAttack: 3);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.IceStorm, false, default, default, default, true, TimeSpan.Zero, TimeSpan.FromMilliseconds(200), targetAreaDiameter: 3, useTargetAreaFilter: true);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Penetration, true, 1.1f, 1.2f, 8f, useDeferredHits: true, delayPerOneDistance: TimeSpan.FromMilliseconds(50));
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.FireSlash, true, 1.5f, 2, 2);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.PowerSlash, true, 1.0f, 6.0f, 6.0f);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.ElectricSpike, true, 1.5f, 1.5f, 12f, useDeferredHits: true, delayPerOneDistance: TimeSpan.FromMilliseconds(10));
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.ForceWave, true, 1f, 1f, 4f);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Stun, true, 1.5f, 1.5f, 3f);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.FireScream, true, 2f, 3f, 6f);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.MultiShot, true, 1f, 6f, 7f);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.FlameStrike, true, 5f, 2f, 4f);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.ChaoticDiseier, true, 1.5f, 1.5f, 6f);
|
||||
|
||||
// Fix master skills as well:
|
||||
foreach (var skill in gameConfiguration.Skills.OrderBy(s => s.Number))
|
||||
{
|
||||
var replacedSkill = skill.MasterDefinition?.ReplacedSkill;
|
||||
if (replacedSkill?.AreaSkillSettings is not { } areaSkillSettings)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
skill.AreaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
var id = skill.AreaSkillSettings.GetId();
|
||||
skill.AreaSkillSettings.AssignValuesOf(areaSkillSettings, gameConfiguration);
|
||||
skill.AreaSkillSettings.SetGuid(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAreaSkillSettings(
|
||||
GameConfiguration gameConfiguration,
|
||||
IContext context,
|
||||
SkillNumber skillNumber,
|
||||
bool useFrustumFilter,
|
||||
float frustumStartWidth,
|
||||
float frustumEndWidth,
|
||||
float frustumDistance,
|
||||
bool useDeferredHits = false,
|
||||
TimeSpan delayPerOneDistance = default,
|
||||
TimeSpan delayBetweenHits = default,
|
||||
int minimumHitsPerTarget = 1,
|
||||
int maximumHitsPerTarget = 1,
|
||||
int maximumHitsPerAttack = default,
|
||||
float hitChancePerDistanceMultiplier = 1.0f,
|
||||
bool useTargetAreaFilter = false,
|
||||
float targetAreaDiameter = default,
|
||||
short? newRange = null)
|
||||
{
|
||||
var skill = gameConfiguration.Skills.First(s => s.Number == (short)skillNumber);
|
||||
var areaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
skill.AreaSkillSettings = areaSkillSettings;
|
||||
skill.SkillType = SkillType.AreaSkillAutomaticHits;
|
||||
|
||||
if (newRange.HasValue)
|
||||
{
|
||||
skill.Range = newRange.Value;
|
||||
}
|
||||
|
||||
areaSkillSettings.UseFrustumFilter = useFrustumFilter;
|
||||
areaSkillSettings.FrustumStartWidth = frustumStartWidth;
|
||||
areaSkillSettings.FrustumEndWidth = frustumEndWidth;
|
||||
areaSkillSettings.FrustumDistance = frustumDistance;
|
||||
areaSkillSettings.UseTargetAreaFilter = useTargetAreaFilter;
|
||||
areaSkillSettings.TargetAreaDiameter = targetAreaDiameter;
|
||||
areaSkillSettings.UseDeferredHits = useDeferredHits;
|
||||
areaSkillSettings.DelayPerOneDistance = delayPerOneDistance;
|
||||
areaSkillSettings.DelayBetweenHits = delayBetweenHits;
|
||||
areaSkillSettings.MinimumNumberOfHitsPerTarget = minimumHitsPerTarget;
|
||||
areaSkillSettings.MaximumNumberOfHitsPerTarget = maximumHitsPerTarget;
|
||||
areaSkillSettings.MaximumNumberOfHitsPerAttack = maximumHitsPerAttack;
|
||||
areaSkillSettings.HitChancePerDistanceMultiplier = hitChancePerDistanceMultiplier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// <copyright file="AddCrestOfMonarchDropGroupUpdateSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the Crest of Monarch drop item group for the Icarus map.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("FF14A478-3EA8-4C41-A298-8E6698D5973D")]
|
||||
public class AddCrestOfMonarchDropGroupUpdateSeason6 : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Crest of Monarch Drop Group";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Adds the Crest of Monarch (Loch's Feather +1) drop item group to Icarus.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddCrestOfMonarchDropGroupSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 03, 13, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CS1998
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
#pragma warning restore CS1998
|
||||
{
|
||||
var map = gameConfiguration.Maps.First(m => m.Number == Icarus.Number && m.Discriminator == 0);
|
||||
var lochsFeather = gameConfiguration.Items.First(item => item.Group == 13 && item.Number == 14);
|
||||
var crestId = GuidHelper.CreateGuid<DropItemGroup>(Icarus.Number, 2);
|
||||
|
||||
var crestGroup = gameConfiguration.DropItemGroups.FirstOrDefault(group => group.GetId() == crestId);
|
||||
if (crestGroup is null)
|
||||
{
|
||||
crestGroup = context.CreateNew<DropItemGroup>();
|
||||
crestGroup.SetGuid(Icarus.Number, 2);
|
||||
gameConfiguration.DropItemGroups.Add(crestGroup);
|
||||
}
|
||||
|
||||
crestGroup.Description = "Crest of Monarch";
|
||||
crestGroup.Chance = 0.001;
|
||||
crestGroup.MinimumMonsterLevel = 82;
|
||||
crestGroup.MaximumMonsterLevel = null;
|
||||
crestGroup.ItemLevel = 1;
|
||||
if (crestGroup.PossibleItems.Count != 1 || crestGroup.PossibleItems.First().GetItemId() != lochsFeather.GetItemId())
|
||||
{
|
||||
crestGroup.PossibleItems.Clear();
|
||||
crestGroup.PossibleItems.Add(lochsFeather);
|
||||
}
|
||||
|
||||
if (!map.DropItemGroups.Any(group => group.GetId() == crestGroup.GetId()))
|
||||
{
|
||||
map.DropItemGroups.Add(crestGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// <copyright file="AddDuelConfigurationPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This updates adds the data for the duel system.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("5DC5638E-581E-4ACC-81E4-D565C625649B")]
|
||||
public class AddDuelConfigurationPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add duel configuration";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds data for the duel configuration";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddDuelConfiguration;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 07, 11, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
if (gameConfiguration.DuelConfiguration is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var invisibleEffect = new InvisibleEffectInitializer(context, gameConfiguration);
|
||||
invisibleEffect.Initialize();
|
||||
|
||||
var duelMap = gameConfiguration.Maps.First(m => m.Number == 64);
|
||||
var mapGates = duelMap.ExitGates.ToDictionary(g => (g.X1, g.Y1), g => g);
|
||||
var targetGates = new Dictionary<short, ExitGate>
|
||||
{
|
||||
{ 294, gameConfiguration.Maps.First(m => m.Number == 63).ExitGates.First() },
|
||||
{ 295, mapGates[(101, 64)] },
|
||||
{ 296, mapGates[(101, 75)] },
|
||||
{ 297, mapGates[(101, 113)] },
|
||||
{ 298, mapGates[(101, 124)] },
|
||||
{ 299, mapGates[(154, 64)] },
|
||||
{ 300, mapGates[(154, 75)] },
|
||||
{ 301, mapGates[(154, 113)] },
|
||||
{ 302, mapGates[(154, 124)] },
|
||||
{ 303, mapGates[(100, 70)] },
|
||||
{ 304, mapGates[(100, 120)] },
|
||||
{ 305, mapGates[(150, 70)] },
|
||||
{ 306, mapGates[(150, 120)] },
|
||||
};
|
||||
|
||||
gameConfiguration.DuelConfiguration = this.CreateDuelConfiguration(context, targetGates);
|
||||
|
||||
var doorkeeper = gameConfiguration.Monsters.First(m => m.Number == 479);
|
||||
doorkeeper.NpcWindow = NpcWindow.DoorkeeperTitusDuelWatch;
|
||||
}
|
||||
|
||||
private DuelConfiguration CreateDuelConfiguration(IContext context, IDictionary<short, ExitGate> targetGates)
|
||||
{
|
||||
var duelConfig = context.CreateNew<DuelConfiguration>();
|
||||
duelConfig.MaximumScore = 10;
|
||||
duelConfig.MinimumCharacterLevel = 30;
|
||||
duelConfig.EntranceFee = 30000;
|
||||
duelConfig.Exit = targetGates[294]; // Vulcanus, see above
|
||||
|
||||
List<(short FirstPlayerGate, short SecondPlayerGate, short SpectatorGate)> duelGateNumbers =
|
||||
[
|
||||
(295, 296, 303),
|
||||
(297, 298, 304),
|
||||
(299, 300, 305),
|
||||
(301, 302, 306),
|
||||
];
|
||||
|
||||
for (short i = 0; i < duelGateNumbers.Count; i++)
|
||||
{
|
||||
var indices = duelGateNumbers[i];
|
||||
var duelArea = context.CreateNew<DuelArea>();
|
||||
duelArea.Index = i;
|
||||
duelArea.FirstPlayerGate = targetGates[indices.FirstPlayerGate];
|
||||
duelArea.SecondPlayerGate = targetGates[indices.SecondPlayerGate];
|
||||
duelArea.SpectatorsGate = targetGates[indices.SpectatorGate];
|
||||
duelConfig.DuelAreas.Add(duelArea);
|
||||
}
|
||||
|
||||
return duelConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="AddGlobalMoneyAmountRateAttributePlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update moves the MoneyAmountRate attribute to global base attributes for version 0.75.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("A1B2C3D4-E5F6-7890-ABCD-EF1234567890")]
|
||||
public class AddGlobalMoneyAmountRateAttributePlugIn075 : AddGlobalMoneyAmountRateAttributePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddGlobalMoneyAmountRateAttribute075;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="AddGlobalMoneyAmountRateAttributePlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update moves the MoneyAmountRate attribute to global base attributes for version 0.95d.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("B2C3D4E5-F6A7-8901-BCDE-F12345678901")]
|
||||
public class AddGlobalMoneyAmountRateAttributePlugIn095d : AddGlobalMoneyAmountRateAttributePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddGlobalMoneyAmountRateAttribute095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// <copyright file="AddGlobalMoneyAmountRateAttributePlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// This update moves the <see cref="Stats.MoneyAmountRate"/> attribute from individual character class
|
||||
/// base attribute values to the global base attribute values of the game configuration.
|
||||
/// </summary>
|
||||
public abstract class AddGlobalMoneyAmountRateAttributePlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Global Money Amount Rate Attribute";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Moves the MoneyAmountRate attribute from character class base attributes to global base attributes.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 03, 20, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var moneyAmountRateDef = gameConfiguration.Attributes.FirstOrDefault(a => a.Id == Stats.MoneyAmountRate.Id);
|
||||
if (moneyAmountRateDef is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gameConfiguration.GlobalBaseAttributeValues.Any(a => a.Definition?.Id == Stats.MoneyAmountRate.Id))
|
||||
{
|
||||
var globalMoneyRate = context.CreateNew<ConstValueAttribute>(1f, moneyAmountRateDef);
|
||||
gameConfiguration.GlobalBaseAttributeValues.Add(globalMoneyRate);
|
||||
}
|
||||
|
||||
foreach (var characterClass in gameConfiguration.CharacterClasses)
|
||||
{
|
||||
var classMoneyRate = characterClass.BaseAttributeValues
|
||||
.FirstOrDefault(a => a.Definition?.Id == Stats.MoneyAmountRate.Id);
|
||||
if (classMoneyRate is not null)
|
||||
{
|
||||
characterClass.BaseAttributeValues.Remove(classMoneyRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="AddGlobalMoneyAmountRateAttributePlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update moves the MoneyAmountRate attribute to global base attributes for Season 6.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("C3D4E5F6-A7B8-9012-CDEF-123456789012")]
|
||||
public class AddGlobalMoneyAmountRateAttributePlugInSeason6 : AddGlobalMoneyAmountRateAttributePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddGlobalMoneyAmountRateAttributeSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
}
|
||||
133
src/Persistence/Initialization/Updates/AddGuardsDataPlugIn.cs
Normal file
133
src/Persistence/Initialization/Updates/AddGuardsDataPlugIn.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
// <copyright file="AddGuardsDataPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.NPC;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This updates adds the data for Guard NPCs.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("1EF50759-0A5F-4301-A5E9-B68A8B7D29F9")]
|
||||
public class AddGuardsDataPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add guard npc data";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds data for guard npcs, so they can attack and move around.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddGuardsData;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 06, 01, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var guard = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == 220);
|
||||
if (guard is { AttackRange: 0 })
|
||||
{
|
||||
guard.Designation = "Guard";
|
||||
guard.ObjectKind = NpcObjectKind.Guard;
|
||||
guard.MoveRange = 3;
|
||||
guard.AttackRange = 2;
|
||||
guard.ViewRange = 8;
|
||||
guard.IntelligenceTypeName = typeof(GuardIntelligence).FullName;
|
||||
guard.MoveDelay = new TimeSpan(400 * TimeSpan.TicksPerMillisecond);
|
||||
guard.AttackDelay = new TimeSpan(1500 * TimeSpan.TicksPerMillisecond);
|
||||
guard.RespawnDelay = new TimeSpan(3 * TimeSpan.TicksPerSecond);
|
||||
guard.NumberOfMaximumItemDrops = 0;
|
||||
var attributes = new Dictionary<AttributeDefinition, float>
|
||||
{
|
||||
{ Stats.Level, 2 },
|
||||
{ Stats.MaximumHealth, 500 },
|
||||
{ Stats.MinimumPhysBaseDmg, 15 },
|
||||
{ Stats.MaximumPhysBaseDmg, 30 },
|
||||
{ Stats.AttackRatePvm, 30 },
|
||||
{ Stats.DefenseRatePvm, 20 },
|
||||
{ Stats.DefenseBase, 70 },
|
||||
};
|
||||
guard.AddAttributes(attributes, context, gameConfiguration);
|
||||
}
|
||||
|
||||
var crossbowGuard = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == 247);
|
||||
if (crossbowGuard is { AttackRange: 0 })
|
||||
{
|
||||
crossbowGuard.Designation = "Crossbow Guard";
|
||||
crossbowGuard.ObjectKind = NpcObjectKind.Guard;
|
||||
crossbowGuard.MoveRange = 3;
|
||||
crossbowGuard.AttackRange = 5;
|
||||
crossbowGuard.ViewRange = 7;
|
||||
crossbowGuard.IntelligenceTypeName = typeof(GuardIntelligence).FullName;
|
||||
crossbowGuard.MoveDelay = new TimeSpan(400 * TimeSpan.TicksPerMillisecond);
|
||||
crossbowGuard.AttackDelay = new TimeSpan(1500 * TimeSpan.TicksPerMillisecond);
|
||||
crossbowGuard.RespawnDelay = new TimeSpan(3 * TimeSpan.TicksPerSecond);
|
||||
crossbowGuard.NumberOfMaximumItemDrops = 0;
|
||||
crossbowGuard.Attribute = 1;
|
||||
var attributes = new Dictionary<AttributeDefinition, float>
|
||||
{
|
||||
{ Stats.Level, 90 },
|
||||
{ Stats.MaximumHealth, 10000 },
|
||||
{ Stats.MinimumPhysBaseDmg, 180 },
|
||||
{ Stats.MaximumPhysBaseDmg, 195 },
|
||||
{ Stats.AttackRatePvm, 300 },
|
||||
{ Stats.DefenseRatePvm, 100 },
|
||||
{ Stats.DefenseBase, 70 },
|
||||
};
|
||||
crossbowGuard.AddAttributes(attributes, context, gameConfiguration);
|
||||
}
|
||||
|
||||
var berdyshGuard = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == 249);
|
||||
if (berdyshGuard is { AttackRange: 0 })
|
||||
{
|
||||
berdyshGuard.Designation = "Berdysh Guard";
|
||||
berdyshGuard.ObjectKind = NpcObjectKind.Guard;
|
||||
berdyshGuard.MoveRange = 3;
|
||||
berdyshGuard.AttackRange = 2;
|
||||
berdyshGuard.ViewRange = 7;
|
||||
berdyshGuard.IntelligenceTypeName = typeof(GuardIntelligence).FullName;
|
||||
berdyshGuard.MoveDelay = new TimeSpan(400 * TimeSpan.TicksPerMillisecond);
|
||||
berdyshGuard.AttackDelay = new TimeSpan(1500 * TimeSpan.TicksPerMillisecond);
|
||||
berdyshGuard.RespawnDelay = new TimeSpan(3 * TimeSpan.TicksPerSecond);
|
||||
berdyshGuard.NumberOfMaximumItemDrops = 0;
|
||||
berdyshGuard.Attribute = 1;
|
||||
var attributes = new Dictionary<AttributeDefinition, float>
|
||||
{
|
||||
{ Stats.Level, 90 },
|
||||
{ Stats.MaximumHealth, 10000 },
|
||||
{ Stats.MinimumPhysBaseDmg, 180 },
|
||||
{ Stats.MaximumPhysBaseDmg, 195 },
|
||||
{ Stats.AttackRatePvm, 300 },
|
||||
{ Stats.DefenseRatePvm, 100 },
|
||||
{ Stats.DefenseBase, 70 },
|
||||
};
|
||||
berdyshGuard.AddAttributes(attributes, context, gameConfiguration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// <copyright file="AddHarmonyOptionWeightsUpdateSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds Jewel of Harmony option weights used for option assignment, fixes some options, and fixes item restore mix.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("E94DE59E-5B3A-4498-A4AF-E7F4F173B754")]
|
||||
public class AddHarmonyOptionWeightsUpdateSeason6 : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add harmony option weights";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds Jewel of Harmony option weights used for option assignment, fixes some options, and fixes item restore mix";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddHarmonyOptionWeightsSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 10, 24, 15, 00, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Fix Restore Item Mix
|
||||
var requiredItem = gameConfiguration.Monsters
|
||||
.Single(m => m.NpcWindow == NpcWindow.RemoveJohOption).ItemCraftings
|
||||
.Single(ic => ic.Number == 35).SimpleCraftingSettings?.RequiredItems
|
||||
.Single();
|
||||
if (requiredItem is { } item)
|
||||
{
|
||||
item.MaximumAmount = 1;
|
||||
item.MaximumItemLevel = 15;
|
||||
}
|
||||
|
||||
// Add JoH option weights
|
||||
byte[] defOptWeights = [50, 40, 40, 30, 20, 20, 20, 10];
|
||||
byte[] physAttackOptWeights = [40, 40, 40, 40, 30, 30, 20, 20, 20, 10];
|
||||
byte[] magicOptWeights = [40, 40, 40, 30, 30, 20, 20, 10];
|
||||
|
||||
var defOptions = gameConfiguration.ItemOptions.Where(io => io.Name == HarmonyOptions.DefenseOptionsName)
|
||||
.FirstOrDefault()?.PossibleOptions.OrderBy(o => o.Number);
|
||||
var physAttackOptions = gameConfiguration.ItemOptions.Where(io => io.Name == HarmonyOptions.PhysicalAttackOptionsName)
|
||||
.FirstOrDefault()?.PossibleOptions.OrderBy(o => o.Number);
|
||||
var wizAttackOptions = gameConfiguration.ItemOptions.Where(io => io.Name == HarmonyOptions.WizardryAttackOptionsName)
|
||||
.FirstOrDefault()?.PossibleOptions.OrderBy(o => o.Number);
|
||||
var curseAttackOptions = gameConfiguration.ItemOptions.Where(io => io.Name == HarmonyOptions.CurseAttackOptionsName)
|
||||
.FirstOrDefault()?.PossibleOptions.OrderBy(o => o.Number);
|
||||
|
||||
if (defOptions?.Count() == defOptWeights.Length)
|
||||
{
|
||||
for (int i = 0; i < defOptWeights.Length; i++)
|
||||
{
|
||||
defOptions.ElementAt(i).Weight = defOptWeights[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (physAttackOptions?.Count() == physAttackOptWeights.Length)
|
||||
{
|
||||
for (int i = 0; i < physAttackOptWeights.Length; i++)
|
||||
{
|
||||
physAttackOptions.ElementAt(i).Weight = physAttackOptWeights[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (wizAttackOptions?.Count() == magicOptWeights.Length)
|
||||
{
|
||||
for (int i = 0; i < magicOptWeights.Length; i++)
|
||||
{
|
||||
wizAttackOptions.ElementAt(i).Weight = magicOptWeights[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (curseAttackOptions?.Count() == magicOptWeights.Length)
|
||||
{
|
||||
for (int i = 0; i < magicOptWeights.Length; i++)
|
||||
{
|
||||
curseAttackOptions.ElementAt(i).Weight = magicOptWeights[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Fix physical base dmg attribute
|
||||
var baseDmgBonusOpt = physAttackOptions?.Single(o => o.Number == 5);
|
||||
var physBaseDmgAttr = gameConfiguration.Attributes.Single(a => a.Id == new Guid("DD1E13E4-BFFD-45B5-9B91-9080710324B2"));
|
||||
|
||||
if (baseDmgBonusOpt?.LevelDependentOptions is ICollection<ItemOptionOfLevel> baseDmgOptLvls)
|
||||
{
|
||||
foreach (var level in baseDmgOptLvls)
|
||||
{
|
||||
if (level.PowerUpDefinition is PowerUpDefinition pud)
|
||||
{
|
||||
pud.TargetAttribute = physBaseDmgAttr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix wiz/curse dmg increase option values and attribute
|
||||
var wizAtkDmgIncOpt = wizAttackOptions?.Single(o => o.Number == 1);
|
||||
var curseAtkDmgIncOpt = curseAttackOptions?.Single(o => o.Number == 1);
|
||||
List<IncreasableItemOption?> magicAtkDmgIncOpts = [wizAtkDmgIncOpt, curseAtkDmgIncOpt];
|
||||
|
||||
var wizBaseDmgAttr = gameConfiguration.Attributes.Single(a => a.Id == new Guid("7F4F3646-33A6-40AC-8DA6-29A0A0F46016"));
|
||||
float[] magicAtkDmgIncValues = [6, 8, 10, 12, 14, 16, 17, 18, 19, 21, 23, 25, 27, 31];
|
||||
|
||||
foreach (var opt in magicAtkDmgIncOpts)
|
||||
{
|
||||
var optLvls = opt?.LevelDependentOptions.OrderBy(ldo => ldo.Level);
|
||||
if (optLvls?.Count() == magicAtkDmgIncValues.Length)
|
||||
{
|
||||
for (int i = 0; i < magicAtkDmgIncValues.Length; i++)
|
||||
{
|
||||
if (optLvls.ElementAt(i).PowerUpDefinition is PowerUpDefinition pud)
|
||||
{
|
||||
pud.TargetAttribute = wizBaseDmgAttr;
|
||||
|
||||
if (pud.Boost?.ConstantValue is SimpleElement element)
|
||||
{
|
||||
element.Value = magicAtkDmgIncValues[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// <copyright file="AddIsQuestItemFlagPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update sets the new <see cref="ItemDefinition.IsQuestItem"/> flag on the existing quest items,
|
||||
/// so that pick-up eligibility can be checked against a character's active quests instead of relying on
|
||||
/// <see cref="ItemDefinition.IsBoundToCharacter"/> alone, which is also used by non-quest items.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("9374E428-CF5C-44B1-AAB9-0369C77AF7C6")]
|
||||
public class AddIsQuestItemFlagPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add IsQuestItem flag";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update flags the existing quest items as such, so that they can only be picked up by characters with a matching active quest.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddIsQuestItemFlag;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 07, 10, 20, 50, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var hashSet = new HashSet<short>
|
||||
{
|
||||
Quest.BrokenSwordNumber,
|
||||
Quest.EyeOfAbyssalNumber,
|
||||
Quest.FeatherOfDarkPhoenixNumber,
|
||||
Quest.FlameOfDeathBeamKnightNumber,
|
||||
Quest.HornOfHellMaineNumber,
|
||||
Quest.ScrollOfEmperorNumber,
|
||||
Quest.SoulShardOfWizardNumber,
|
||||
Quest.TearOfElfNumber,
|
||||
};
|
||||
var questItems = gameConfiguration.Items.Where(item => item.Group == 14 && hashSet.Contains(item.Number));
|
||||
foreach (var item in questItems)
|
||||
{
|
||||
item.IsQuestItem = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// <copyright file="AddItemDropGroupForJewelsUpdate075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update creates a specific item drop group for jewels with a default chance of 5%.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("DCF14924-BB19-4CA2-93EC-397A89AA3EB3")]
|
||||
public class AddItemDropGroupForJewelsUpdate075 : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Create item drop group for jewels";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update creates a specific item drop group for jewels with a default chance of 5%.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddItemDropGroupForJewels075;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 08, 26, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
this.CreateDropItemGroupForJewels(context, gameConfiguration, 4, null, "The jewels drop item group (0.1 % drop chance)");
|
||||
|
||||
this.AddJewelToItemDrop(gameConfiguration, 4, null, "Jewel of Bless");
|
||||
this.AddJewelToItemDrop(gameConfiguration, 4, null, "Jewel of Soul");
|
||||
this.AddJewelToItemDrop(gameConfiguration, 4, null, "Jewel of Chaos");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add jewel to specific drop item group.
|
||||
/// </summary>
|
||||
/// <param name="gameConfiguration">Game configuration context to update.</param>
|
||||
/// <param name="dropId">Drop to which the jewel will be associated.</param>
|
||||
/// <param name="mapNumber">Optionally the map number to asociate drop group item.</param>
|
||||
/// <param name="jewelName">Jewel name to add into specific drop item group.</param>
|
||||
protected void AddJewelToItemDrop(GameConfiguration gameConfiguration, short dropId, short? mapNumber, string jewelName)
|
||||
{
|
||||
var jewelsItemDrop = GetJewelsDropItemGroup(gameConfiguration, dropId, mapNumber);
|
||||
|
||||
if (jewelsItemDrop == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = gameConfiguration.Items.FirstOrDefault(x => x.Name == jewelName);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.DropsFromMonsters == true)
|
||||
{
|
||||
item.DropsFromMonsters = false;
|
||||
}
|
||||
|
||||
var jewelId = item.GetItemId();
|
||||
|
||||
if (!jewelsItemDrop.PossibleItems.Any(x => x.GetItemId() == jewelId))
|
||||
{
|
||||
jewelsItemDrop.PossibleItems.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create drop item group for jewels.
|
||||
/// </summary>
|
||||
/// <param name="context">The persistence context.</param>
|
||||
/// <param name="gameConfiguration">Game configuration context to update.</param>
|
||||
/// <param name="dropId">New drop id.</param>
|
||||
/// <param name="mapNumber">Optionally the map number to associate drop group item.</param>
|
||||
/// <param name="name">Description of drop.</param>
|
||||
/// <returns>Returns new drop item group.</returns>
|
||||
protected DropItemGroup CreateDropItemGroupForJewels(IContext context, GameConfiguration gameConfiguration, short dropId, short? mapNumber, string name)
|
||||
{
|
||||
var jewelsItemDrop = GetJewelsDropItemGroup(gameConfiguration, dropId, mapNumber);
|
||||
|
||||
if (jewelsItemDrop != null)
|
||||
{
|
||||
return jewelsItemDrop;
|
||||
}
|
||||
|
||||
var jewelsDropItemGroup = context.CreateNew<DropItemGroup>();
|
||||
|
||||
if (mapNumber != null)
|
||||
{
|
||||
jewelsDropItemGroup.SetGuid(mapNumber.Value, dropId);
|
||||
}
|
||||
else
|
||||
{
|
||||
jewelsDropItemGroup.SetGuid(dropId);
|
||||
}
|
||||
|
||||
jewelsDropItemGroup.Chance = 0.001;
|
||||
jewelsDropItemGroup.ItemType = SpecialItemType.RandomItem;
|
||||
jewelsDropItemGroup.Description = name;
|
||||
gameConfiguration.DropItemGroups.Add(jewelsDropItemGroup);
|
||||
|
||||
if (mapNumber != null)
|
||||
{
|
||||
var map = gameConfiguration.Maps.First(x => x.Number == mapNumber);
|
||||
map.DropItemGroups.Add(jewelsDropItemGroup);
|
||||
}
|
||||
|
||||
return jewelsDropItemGroup;
|
||||
}
|
||||
|
||||
private static DropItemGroup? GetJewelsDropItemGroup(GameConfiguration gameConfiguration, short dropId, short? mapNumber)
|
||||
{
|
||||
var id = mapNumber != null
|
||||
? GuidHelper.CreateGuid<DropItemGroup>(mapNumber ?? 0, dropId)
|
||||
: GuidHelper.CreateGuid<DropItemGroup>(dropId);
|
||||
|
||||
var jewelsItemDrop = gameConfiguration.DropItemGroups.FirstOrDefault(x => x.GetId() == id);
|
||||
return jewelsItemDrop;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// <copyright file="AddItemDropGroupForJewelsUpdate095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update creates a specific item drop group for jewels with a default chance of 5%.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("D21056E6-E912-416B-A076-3C2D17DA517B")]
|
||||
public class AddItemDropGroupForJewelsUpdate095D : AddItemDropGroupForJewelsUpdate075
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddItemDropGroupForJewels095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.AddJewelToItemDrop(gameConfiguration, 4, null, "Jewel of Life");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// <copyright file="AddItemDropGroupForJewelsUpdateSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update creates a specific item drop group for jewels with a default chance of 5%.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("F958CC5B-C1E6-4F67-B48D-4BF75EC5CAA8")]
|
||||
public class AddItemDropGroupForJewelsUpdateSeason6 : AddItemDropGroupForJewelsUpdate075
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddItemDropGroupForJewelsSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
this.CreateDropItemGroupForJewels(context, gameConfiguration, 1, VersionSeasonSix.Maps.LandOfTrials.Number, "Jewel of Guardian");
|
||||
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.AddJewelToItemDrop(gameConfiguration, 4, null, "Jewel of Creation");
|
||||
this.AddJewelToItemDrop(gameConfiguration, 1, VersionSeasonSix.Maps.LandOfTrials.Number, "Jewel of Guardian");
|
||||
}
|
||||
}
|
||||
113
src/Persistence/Initialization/Updates/AddKalimaPlugIn.cs
Normal file
113
src/Persistence/Initialization/Updates/AddKalimaPlugIn.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
// <copyright file="AddKalimaPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This adds the items required to enter the kalima map.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("0C99155F-1289-4E73-97F0-47CB67C3716F")]
|
||||
public class AddKalimaPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Kalima";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This adds the items required to enter the kalima map.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddKalima;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 06, 09, 18, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CS1998
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
#pragma warning restore CS1998
|
||||
{
|
||||
this.CreateLostMap(context, gameConfiguration);
|
||||
this.CreateSymbolOfKundun(context, gameConfiguration);
|
||||
|
||||
// copy potion girl items to oracle layla:
|
||||
var potionGirl = gameConfiguration.Monsters.First(m => m.Number == 253);
|
||||
var oracleLayla = gameConfiguration.Monsters.First(m => m.Number == 259);
|
||||
if (oracleLayla.NpcWindow is not NpcWindow.Merchant && oracleLayla.MerchantStore is null)
|
||||
{
|
||||
oracleLayla.NpcWindow = NpcWindow.Merchant;
|
||||
oracleLayla.MerchantStore = potionGirl.MerchantStore!.Clone(gameConfiguration);
|
||||
oracleLayla.MerchantStore.SetGuid(oracleLayla.Number);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateLostMap(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var itemDefinition = context.CreateNew<ItemDefinition>();
|
||||
itemDefinition.Name = "Lost Map";
|
||||
itemDefinition.Number = 28;
|
||||
itemDefinition.Group = 14;
|
||||
itemDefinition.DropsFromMonsters = false;
|
||||
itemDefinition.Durability = 1;
|
||||
itemDefinition.Width = 1;
|
||||
itemDefinition.Height = 1;
|
||||
itemDefinition.MaximumItemLevel = 7;
|
||||
itemDefinition.SetGuid(itemDefinition.Group, itemDefinition.Number);
|
||||
gameConfiguration.Items.Add(itemDefinition);
|
||||
}
|
||||
|
||||
private void CreateSymbolOfKundun(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var itemDefinition = context.CreateNew<ItemDefinition>();
|
||||
itemDefinition.Name = "Symbol of Kundun";
|
||||
itemDefinition.Number = 29;
|
||||
itemDefinition.Group = 14;
|
||||
itemDefinition.DropLevel = 0;
|
||||
itemDefinition.DropsFromMonsters = true;
|
||||
itemDefinition.Durability = 5;
|
||||
itemDefinition.Width = 1;
|
||||
itemDefinition.Height = 1;
|
||||
itemDefinition.MaximumItemLevel = 7;
|
||||
itemDefinition.SetGuid(itemDefinition.Group, itemDefinition.Number);
|
||||
gameConfiguration.Items.Add(itemDefinition);
|
||||
|
||||
(byte, byte)[] dropLevels = [(25, 46), (47, 65), (66, 77), (78, 84), (85, 91), (92, 107), (108, 255)];
|
||||
for (byte level = 1; level <= dropLevels.Length; level++)
|
||||
{
|
||||
var dropItemGroup = context.CreateNew<DropItemGroup>();
|
||||
dropItemGroup.SetGuid(14, 29, level);
|
||||
dropItemGroup.ItemLevel = level;
|
||||
dropItemGroup.PossibleItems.Add(itemDefinition);
|
||||
dropItemGroup.Chance = 0.003; // 0.3 Percent
|
||||
dropItemGroup.Description = $"The drop item group for Symbol of Kundun (Level {level})";
|
||||
(dropItemGroup.MinimumMonsterLevel, dropItemGroup.MaximumMonsterLevel) = dropLevels[level - 1];
|
||||
|
||||
gameConfiguration.DropItemGroups.Add(dropItemGroup);
|
||||
gameConfiguration.Maps.ForEach(map => map.DropItemGroups.Add(dropItemGroup));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// <copyright file="AddLorenMarketJuliaWarpPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update turns Market Union Member Julia (547) into a working warp NPC for the Loren Market,
|
||||
/// on both sides. It changes her NPC window so that talking to her opens the warp dialog instead of
|
||||
/// an empty merchant shop, and spawns a second instance of her in Lorencia, which is the entrance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The actual warp is performed server-side by the <c>EnterMarketPlaceHandlerPlugIn</c> when the
|
||||
/// player uses the 'Warp' button of the (client-side) Julia window.
|
||||
/// </remarks>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("ed2f1728-b35c-4a3d-810e-eab5b6e12a82")]
|
||||
public class AddLorenMarketJuliaWarpPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Loren Market Julia warp";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Makes Market Union Member Julia (547) a working warp NPC between Lorencia and the Loren Market, on both sides, instead of an empty merchant.";
|
||||
|
||||
private const short JuliaNpcNumber = 547;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddLorenMarketJuliaWarp;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 06, 24, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var julia = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == JuliaNpcNumber);
|
||||
if (julia is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
julia.NpcWindow = NpcWindow.JuliaWarpMarketServer;
|
||||
julia.MerchantStore = null;
|
||||
|
||||
var lorencia = gameConfiguration.Maps.FirstOrDefault(m => m.Number == Lorencia.Number);
|
||||
if (lorencia is null
|
||||
|| lorencia.MonsterSpawns.Any(s => s.MonsterDefinition?.Number == JuliaNpcNumber))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var juliaSpawn = context.CreateNew<MonsterSpawnArea>();
|
||||
lorencia.MonsterSpawns.Add(juliaSpawn);
|
||||
juliaSpawn.SetGuid(JuliaNpcNumber);
|
||||
juliaSpawn.GameMap = lorencia;
|
||||
juliaSpawn.MonsterDefinition = julia;
|
||||
juliaSpawn.Quantity = 1;
|
||||
juliaSpawn.SpawnTrigger = SpawnTrigger.Automatic;
|
||||
juliaSpawn.Direction = Direction.SouthEast;
|
||||
juliaSpawn.X1 = 139;
|
||||
juliaSpawn.X2 = 139;
|
||||
juliaSpawn.Y1 = 138;
|
||||
juliaSpawn.Y2 = 138;
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// <copyright file="AddMaximumAllianceSizeUpdatePlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the <see cref="Stats.MaximumAllianceSize"/> global base attribute
|
||||
/// (default value 5) to Season 6 game configurations.
|
||||
/// Older game versions (below season 1) do not support alliances and therefore do not
|
||||
/// receive this attribute.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("2C2743B0-1305-47BF-85D9-09F6CA64AD54")]
|
||||
public class AddMaximumAllianceSizeUpdatePlugInSeason6 : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Maximum Alliance Size Attribute (Season 6)";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Adds the MaximumAllianceSize global base attribute with a default value of 5 to the Season 6 game configuration.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddMaximumAllianceSizeSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 04, 03, 18, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CS1998
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
#pragma warning restore CS1998
|
||||
{
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.MaximumAllianceSize);
|
||||
|
||||
var maximumAllianceSizeDef = gameConfiguration.Attributes.FirstOrDefault(a => a.Id == Stats.MaximumAllianceSize.Id);
|
||||
if (maximumAllianceSizeDef is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gameConfiguration.GlobalBaseAttributeValues.Any(a => a.Definition?.Id == Stats.MaximumAllianceSize.Id))
|
||||
{
|
||||
var maximumAllianceSizeValue = context.CreateNew<ConstValueAttribute>(5f, maximumAllianceSizeDef);
|
||||
gameConfiguration.GlobalBaseAttributeValues.Add(maximumAllianceSizeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// <copyright file="AddMissingMerchantStoresPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update gives a merchant store to a merchant NPC which has a merchant window
|
||||
/// but no items assigned, so talking to it opens an empty (non-working) shop.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Affected NPC: Christine the General Goods Merchant (545) in the Loren Market. She is given a
|
||||
/// clone of the general-goods (potion girl) store carried by Thompson the Merchant (231).
|
||||
/// Two other NPCs in that window state are intentionally left out: Moss The Merchant (492) is the
|
||||
/// gambler and Market Union Member Julia (547) is a warp NPC, so a general-goods store would not
|
||||
/// fit either of them.
|
||||
/// </remarks>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("f78d6e1d-1cb5-45f7-912d-54b2cb1220eb")]
|
||||
public class AddMissingMerchantStoresPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add missing merchant stores";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Gives a general-goods store to Christine the General Goods Merchant (545), which had a shop window but no items, cloned from Thompson the Merchant (231).";
|
||||
|
||||
/// <summary>
|
||||
/// The number of the NPC whose store is cloned for the empty merchants.
|
||||
/// </summary>
|
||||
private const short StoreSourceNpcNumber = 231; // Thompson the Merchant - carries the general-goods (potion girl) store
|
||||
|
||||
/// <summary>
|
||||
/// The numbers of the NPCs which have a merchant window but no store.
|
||||
/// </summary>
|
||||
private static readonly short[] EmptyMerchantNpcNumbers = [545];
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddMissingMerchantStores;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 06, 17, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var storeSource = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == StoreSourceNpcNumber);
|
||||
if (storeSource?.MerchantStore is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
foreach (var number in EmptyMerchantNpcNumbers)
|
||||
{
|
||||
var npc = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == number);
|
||||
if (npc is null || npc.MerchantStore is not null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
npc.MerchantStore = storeSource.MerchantStore.Clone(gameConfiguration);
|
||||
npc.MerchantStore.SetGuid(npc.Number);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// <copyright file="AddMovementSpeedAttributesPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Adds movement speed attributes to 0.75 game configurations.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("890E2FCB-EC93-4CC1-84FC-67A1B398D5C8")]
|
||||
public class AddMovementSpeedAttributesPlugIn075 : AddMovementSpeedAttributesPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddMovementSpeedAttributes075;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override int MaximumItemLevel => Version075.Items.Constants.MaximumItemLevel;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// <copyright file="AddMovementSpeedAttributesPlugIn095D.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Adds movement speed attributes to 0.95d game configurations.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("7C38C30F-163B-4625-A82D-5C3A0A9ED883")]
|
||||
public class AddMovementSpeedAttributesPlugIn095D : AddMovementSpeedAttributesPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddMovementSpeedAttributes095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override int MaximumItemLevel => Version095d.Items.Constants.MaximumItemLevel;
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
// <copyright file="AddMovementSpeedAttributesPlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using AtlansMap = MUnique.OpenMU.Persistence.Initialization.Version075.Maps.Atlans;
|
||||
using Doppelgaenger3Map = MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps.Doppelgaenger3;
|
||||
using Kalima1Map = MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps.Kalima1;
|
||||
using Kalima2Map = MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps.Kalima2;
|
||||
using Kalima3Map = MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps.Kalima3;
|
||||
using Kalima4Map = MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps.Kalima4;
|
||||
using Kalima5Map = MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps.Kalima5;
|
||||
using Kalima6Map = MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps.Kalima6;
|
||||
using Kalima7Map = MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps.Kalima7;
|
||||
|
||||
/// <summary>
|
||||
/// Adds movement speed attributes and configuration values.
|
||||
/// </summary>
|
||||
public abstract class AddMovementSpeedAttributesPlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug-in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Movement Speed Attributes";
|
||||
|
||||
/// <summary>
|
||||
/// The plug-in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Adds attribute-based movement speed configuration for players, monsters, items, effects, and underwater maps.";
|
||||
|
||||
private const byte PetItemGroup = (byte)ItemGroups.Misc1;
|
||||
private const byte UniriaNumber = 2;
|
||||
private const byte DinorantNumber = 3;
|
||||
private const byte DarkHorseNumber = 4;
|
||||
private const byte WingsOfDragonNumber = 5;
|
||||
private const byte WingOfStormNumber = 36;
|
||||
private const byte FenrirNumber = 37;
|
||||
private const string RunningMovementSpeedTableName = "Running Movement Speed";
|
||||
private const int BlackFenrirMovementSpeedCombinationBonusNumber = 101;
|
||||
private const int BlackFenrirUnderwaterMovementSpeedCombinationBonusNumber = 102;
|
||||
private const int BlueFenrirMovementSpeedCombinationBonusNumber = 103;
|
||||
private const int BlueFenrirUnderwaterMovementSpeedCombinationBonusNumber = 104;
|
||||
private const int GoldFenrirMovementSpeedCombinationBonusNumber = 105;
|
||||
private const int GoldFenrirUnderwaterMovementSpeedCombinationBonusNumber = 106;
|
||||
|
||||
private static readonly short[] UnderwaterMapNumbers =
|
||||
[
|
||||
AtlansMap.Number,
|
||||
Kalima1Map.Number,
|
||||
Kalima2Map.Number,
|
||||
Kalima3Map.Number,
|
||||
Kalima4Map.Number,
|
||||
Kalima5Map.Number,
|
||||
Kalima6Map.Number,
|
||||
Kalima7Map.Number,
|
||||
Doppelgaenger3Map.Number,
|
||||
];
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 05, 15, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum item level of the target game version.
|
||||
/// </summary>
|
||||
protected abstract int MaximumItemLevel { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.MovementSpeed);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.MovementSpeedUnderwater);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.MovementSpeedFactor);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.IsUnderwater);
|
||||
|
||||
this.AddGlobalMovementSpeedFactor(context, gameConfiguration);
|
||||
this.AddEffectMovementSpeedFactors(context, gameConfiguration);
|
||||
this.AddItemMovementSpeeds(context, gameConfiguration);
|
||||
this.AddUnderwaterMapPowerUps(context, gameConfiguration);
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
private static bool IsWingSlotItem(ItemDefinition item)
|
||||
{
|
||||
return item.ItemSlot?.ItemSlots.Contains(InventoryConstants.WingsSlot) ?? false;
|
||||
}
|
||||
|
||||
private static float GetWingMovementSpeed(ItemDefinition wing)
|
||||
{
|
||||
return wing.Number is WingsOfDragonNumber or WingOfStormNumber
|
||||
? MovementSpeedConstants.FastWingMovementSpeed
|
||||
: MovementSpeedConstants.DefaultWingMovementSpeed;
|
||||
}
|
||||
|
||||
private static float GetPetMovementSpeed(ItemDefinition pet)
|
||||
{
|
||||
return pet.Number switch
|
||||
{
|
||||
UniriaNumber or DinorantNumber => MovementSpeedConstants.BasicMountMovementSpeed,
|
||||
DarkHorseNumber or FenrirNumber => MovementSpeedConstants.HorseOrFenrirMovementSpeed,
|
||||
_ => 0f,
|
||||
};
|
||||
}
|
||||
|
||||
private void AddGlobalMovementSpeedFactor(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
if (gameConfiguration.GlobalBaseAttributeValues.Any(a => a.Definition?.Id == Stats.MovementSpeedFactor.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gameConfiguration.GlobalBaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(1f, Stats.MovementSpeedFactor.GetPersistent(gameConfiguration)));
|
||||
}
|
||||
|
||||
private void AddEffectMovementSpeedFactors(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
foreach (var icedEffect in gameConfiguration.MagicEffects.Where(e => e.Number == (short)MagicEffectNumber.Iced))
|
||||
{
|
||||
this.AddMovementSpeedFactorPowerUp(context, gameConfiguration, icedEffect, MovementSpeedConstants.IcedMovementSpeedFactor);
|
||||
}
|
||||
|
||||
var coldEffect = gameConfiguration.MagicEffects.FirstOrDefault(e => e.Number == (short)MagicEffectNumber.Cold);
|
||||
if (coldEffect is null
|
||||
&& gameConfiguration.Skills.Any(s => s.Number == (short)SkillNumber.StrikeofDestruction))
|
||||
{
|
||||
coldEffect = this.CreateEffect(context, gameConfiguration, ElementalType.Ice, MagicEffectNumber.Cold, Stats.IsIced, 10);
|
||||
}
|
||||
|
||||
if (coldEffect is not null)
|
||||
{
|
||||
this.AddMovementSpeedFactorPowerUp(context, gameConfiguration, coldEffect, MovementSpeedConstants.ColdMovementSpeedFactor);
|
||||
foreach (var skill in gameConfiguration.Skills.Where(s => s.Number == (short)SkillNumber.StrikeofDestruction || s.Number == (short)SkillNumber.StrikeofDestrStr))
|
||||
{
|
||||
skill.MagicEffectDef = coldEffect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMovementSpeedFactorPowerUp(IContext context, GameConfiguration gameConfiguration, MagicEffectDefinition magicEffect, float value)
|
||||
{
|
||||
if (magicEffect.PowerUpDefinitions.Any(p => p.TargetAttribute?.Id == Stats.MovementSpeedFactor.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
magicEffect.PowerUpDefinitions.Add(this.CreatePowerUpDefinition(context, gameConfiguration, Stats.MovementSpeedFactor, value, AggregateType.Multiplicate));
|
||||
}
|
||||
|
||||
private void AddItemMovementSpeeds(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var runningMovementSpeedTable = this.GetOrCreateRunningMovementSpeedTable(context, gameConfiguration);
|
||||
|
||||
foreach (var boots in gameConfiguration.Items.Where(item => item.Group == (byte)ItemGroups.Boots))
|
||||
{
|
||||
this.AddItemBasePowerUp(context, gameConfiguration, boots, Stats.MovementSpeed, 0, AggregateType.Maximum, runningMovementSpeedTable);
|
||||
}
|
||||
|
||||
foreach (var gloves in gameConfiguration.Items.Where(item => item.Group == (byte)ItemGroups.Gloves))
|
||||
{
|
||||
this.AddItemBasePowerUp(context, gameConfiguration, gloves, Stats.MovementSpeedUnderwater, 0, AggregateType.Maximum, runningMovementSpeedTable);
|
||||
}
|
||||
|
||||
foreach (var wing in gameConfiguration.Items.Where(IsWingSlotItem))
|
||||
{
|
||||
this.AddMovementSpeedPowerUps(context, gameConfiguration, wing, GetWingMovementSpeed(wing));
|
||||
}
|
||||
|
||||
foreach (var pet in gameConfiguration.Items.Where(item => item.Group == PetItemGroup))
|
||||
{
|
||||
var speed = GetPetMovementSpeed(pet);
|
||||
|
||||
if (speed > 0)
|
||||
{
|
||||
this.AddMovementSpeedPowerUps(context, gameConfiguration, pet, speed);
|
||||
}
|
||||
}
|
||||
|
||||
this.AddFenrirMovementSpeedCombinationBonuses(context, gameConfiguration);
|
||||
}
|
||||
|
||||
private ItemLevelBonusTable GetOrCreateRunningMovementSpeedTable(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
if (gameConfiguration.ItemLevelBonusTables.FirstOrDefault(t => t.Name == RunningMovementSpeedTableName) is { } existingTable)
|
||||
{
|
||||
return existingTable;
|
||||
}
|
||||
|
||||
var table = context.CreateNew<ItemLevelBonusTable>();
|
||||
gameConfiguration.ItemLevelBonusTables.Add(table);
|
||||
table.Name = RunningMovementSpeedTableName;
|
||||
table.Description = "Defines the running movement speed for boots and underwater gloves from item level 5.";
|
||||
for (int level = MovementSpeedConstants.RunningGearMinimumLevel; level <= this.MaximumItemLevel; level++)
|
||||
{
|
||||
var levelBonus = context.CreateNew<LevelBonus>();
|
||||
levelBonus.Level = level;
|
||||
levelBonus.AdditionalValue = MovementSpeedConstants.RunningGearMovementSpeed;
|
||||
table.BonusPerLevel.Add(levelBonus);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private void AddMovementSpeedPowerUps(IContext context, GameConfiguration gameConfiguration, ItemDefinition item, float speed)
|
||||
{
|
||||
this.AddItemBasePowerUp(context, gameConfiguration, item, Stats.MovementSpeed, speed, AggregateType.Maximum);
|
||||
this.AddItemBasePowerUp(context, gameConfiguration, item, Stats.MovementSpeedUnderwater, speed, AggregateType.Maximum);
|
||||
}
|
||||
|
||||
private void AddItemBasePowerUp(
|
||||
IContext context,
|
||||
GameConfiguration gameConfiguration,
|
||||
ItemDefinition item,
|
||||
AttributeDefinition targetAttribute,
|
||||
float value,
|
||||
AggregateType aggregateType,
|
||||
ItemLevelBonusTable? bonusPerLevelTable = null)
|
||||
{
|
||||
if (item.BasePowerUpAttributes.Any(p => p.TargetAttribute?.Id == targetAttribute.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var powerUp = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
powerUp.TargetAttribute = targetAttribute.GetPersistent(gameConfiguration);
|
||||
powerUp.BaseValue = value;
|
||||
powerUp.AggregateType = aggregateType;
|
||||
powerUp.BonusPerLevelTable = bonusPerLevelTable;
|
||||
item.BasePowerUpAttributes.Add(powerUp);
|
||||
}
|
||||
|
||||
private void AddFenrirMovementSpeedCombinationBonuses(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
this.AddFenrirMovementSpeedCombinationBonus(context, gameConfiguration, ItemOptionTypes.BlackFenrir, BlackFenrirMovementSpeedCombinationBonusNumber, Stats.MovementSpeed);
|
||||
this.AddFenrirMovementSpeedCombinationBonus(context, gameConfiguration, ItemOptionTypes.BlackFenrir, BlackFenrirUnderwaterMovementSpeedCombinationBonusNumber, Stats.MovementSpeedUnderwater);
|
||||
this.AddFenrirMovementSpeedCombinationBonus(context, gameConfiguration, ItemOptionTypes.BlueFenrir, BlueFenrirMovementSpeedCombinationBonusNumber, Stats.MovementSpeed);
|
||||
this.AddFenrirMovementSpeedCombinationBonus(context, gameConfiguration, ItemOptionTypes.BlueFenrir, BlueFenrirUnderwaterMovementSpeedCombinationBonusNumber, Stats.MovementSpeedUnderwater);
|
||||
this.AddFenrirMovementSpeedCombinationBonus(context, gameConfiguration, ItemOptionTypes.GoldFenrir, GoldFenrirMovementSpeedCombinationBonusNumber, Stats.MovementSpeed);
|
||||
this.AddFenrirMovementSpeedCombinationBonus(context, gameConfiguration, ItemOptionTypes.GoldFenrir, GoldFenrirUnderwaterMovementSpeedCombinationBonusNumber, Stats.MovementSpeedUnderwater);
|
||||
}
|
||||
|
||||
private void AddFenrirMovementSpeedCombinationBonus(
|
||||
IContext context,
|
||||
GameConfiguration gameConfiguration,
|
||||
ItemOptionType optionType,
|
||||
int number,
|
||||
AttributeDefinition targetAttribute)
|
||||
{
|
||||
if (gameConfiguration.ItemOptionTypes.FirstOrDefault(t => t == optionType) is not { } persistentOptionType)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameConfiguration.ItemOptionCombinationBonuses.Any(b =>
|
||||
b.Bonus?.TargetAttribute?.Id == targetAttribute.Id
|
||||
&& b.Requirements.Any(r => r.OptionType == persistentOptionType)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var combinationBonus = context.CreateNew<ItemOptionCombinationBonus>();
|
||||
combinationBonus.Number = number;
|
||||
combinationBonus.Description = $"{persistentOptionType.Name}: {targetAttribute.Designation}";
|
||||
combinationBonus.AppliesMultipleTimes = false;
|
||||
combinationBonus.Requirements.Add(this.CreateFenrirMovementSpeedRequirement(context, persistentOptionType));
|
||||
combinationBonus.Bonus = this.CreatePowerUpDefinition(context, gameConfiguration, targetAttribute, MovementSpeedConstants.UpgradedFenrirMovementSpeed, AggregateType.Maximum);
|
||||
gameConfiguration.ItemOptionCombinationBonuses.Add(combinationBonus);
|
||||
}
|
||||
|
||||
private CombinationBonusRequirement CreateFenrirMovementSpeedRequirement(IContext context, ItemOptionType optionType)
|
||||
{
|
||||
var requirement = context.CreateNew<CombinationBonusRequirement>();
|
||||
requirement.OptionType = optionType;
|
||||
requirement.MinimumCount = 1;
|
||||
return requirement;
|
||||
}
|
||||
|
||||
private void AddUnderwaterMapPowerUps(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
foreach (var map in gameConfiguration.Maps.Where(m => UnderwaterMapNumbers.Contains(m.Number)))
|
||||
{
|
||||
if (map.CharacterPowerUpDefinitions.Any(p => p.TargetAttribute?.Id == Stats.IsUnderwater.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
map.CharacterPowerUpDefinitions.Add(this.CreatePowerUpDefinition(context, gameConfiguration, Stats.IsUnderwater, 1, AggregateType.AddRaw));
|
||||
}
|
||||
}
|
||||
|
||||
private PowerUpDefinition CreatePowerUpDefinition(IContext context, GameConfiguration gameConfiguration, AttributeDefinition targetAttribute, float value, AggregateType aggregateType)
|
||||
{
|
||||
var powerUp = context.CreateNew<PowerUpDefinition>();
|
||||
powerUp.TargetAttribute = targetAttribute.GetPersistent(gameConfiguration);
|
||||
powerUp.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUp.Boost.ConstantValue.Value = value;
|
||||
powerUp.Boost.ConstantValue.AggregateType = aggregateType;
|
||||
return powerUp;
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateEffect(IContext context, GameConfiguration gameConfiguration, ElementalType type, MagicEffectNumber effectNumber, AttributeDefinition targetAttribute, float durationInSeconds, float chance = 0)
|
||||
{
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(
|
||||
e => e.Number == (short)effectNumber
|
||||
&& e.SubType == (byte)(0xFF - type)
|
||||
&& Equals(e.Duration?.ConstantValue.Value, durationInSeconds)
|
||||
&& Equals(e.Chance?.ConstantValue.Value, chance)
|
||||
&& e.PowerUpDefinitions.FirstOrDefault()?.TargetAttribute == targetAttribute) is { } existingEffect)
|
||||
{
|
||||
return existingEffect;
|
||||
}
|
||||
|
||||
var effect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(effect);
|
||||
effect.Name = Enum.GetName(effectNumber) ?? string.Empty;
|
||||
effect.InformObservers = true;
|
||||
effect.Number = (short)effectNumber;
|
||||
effect.StopByDeath = true;
|
||||
effect.SubType = (byte)(0xFF - type);
|
||||
effect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
effect.Duration.ConstantValue.Value = durationInSeconds;
|
||||
var powerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
effect.PowerUpDefinitions.Add(powerUpDefinition);
|
||||
powerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUpDefinition.Boost.ConstantValue.Value = 1;
|
||||
powerUpDefinition.TargetAttribute = targetAttribute.GetPersistent(gameConfiguration);
|
||||
if (targetAttribute == Stats.IsIced)
|
||||
{
|
||||
var movementSpeedFactorPowerUp = context.CreateNew<PowerUpDefinition>();
|
||||
effect.PowerUpDefinitions.Add(movementSpeedFactorPowerUp);
|
||||
movementSpeedFactorPowerUp.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
movementSpeedFactorPowerUp.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
|
||||
movementSpeedFactorPowerUp.TargetAttribute = Stats.MovementSpeedFactor.GetPersistent(gameConfiguration);
|
||||
|
||||
if (effectNumber == MagicEffectNumber.Cold)
|
||||
{
|
||||
movementSpeedFactorPowerUp.Boost.ConstantValue.Value = MovementSpeedConstants.ColdMovementSpeedFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
movementSpeedFactorPowerUp.Boost.ConstantValue.Value = MovementSpeedConstants.IcedMovementSpeedFactor;
|
||||
}
|
||||
}
|
||||
|
||||
if (chance > 0)
|
||||
{
|
||||
effect.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
effect.Chance.ConstantValue.Value = chance;
|
||||
}
|
||||
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// <copyright file="AddMovementSpeedAttributesPlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Adds movement speed attributes to season 6 game configurations.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("1D4968DA-9C9C-42A7-AF80-D4811535EC63")]
|
||||
public class AddMovementSpeedAttributesPlugInSeason6 : AddMovementSpeedAttributesPlugInBase
|
||||
{
|
||||
private const int SeasonSixMaximumItemLevel = 15;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddMovementSpeedAttributesSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override int MaximumItemLevel => SeasonSixMaximumItemLevel;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// <copyright file="AddPointsPerResetAttributePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the <see cref="Stats.PointsPerReset"/>.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("6011A1B8-7FA5-48EB-935D-EEAF83017799")]
|
||||
public class AddPointsPerResetAttributePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Adds attribute PointsPerReset";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds the attribute PointsPerReset.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddPointsPerResetByClassAttribute;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 06, 09, 13, 30, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
if (gameConfiguration.Attributes.Contains(Stats.PointsPerReset))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var attribute = Stats.PointsPerReset;
|
||||
var persistentAttribute = context.CreateNew<AttributeDefinition>(attribute.Id, attribute.Designation, attribute.Description);
|
||||
gameConfiguration.Attributes.Add(persistentAttribute);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// <copyright file="AddProjectileCountToTripleShotUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Triple Shot skill to use 3 projectiles for proper arrow direction handling.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("E3A8F7C9-2D4B-4A1E-9F3C-8B5D7A6C1E4F")]
|
||||
public class AddProjectileCountToTripleShotUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Projectile Count to Triple Shot";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Adds the projectile count of 3 to the Triple Shot skill to properly handle arrow directions.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddProjectileCountToTripleShot;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 01, 04, 11, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var tripleShotSkill = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.TripleShot);
|
||||
if (tripleShotSkill?.AreaSkillSettings is { } areaSkillSettings)
|
||||
{
|
||||
areaSkillSettings.ProjectileCount = 3;
|
||||
}
|
||||
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// <copyright file="AddQuestItemLimitPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This updates adds the new <see cref="ItemDefinition.StorageLimitPerCharacter"/> for quest items.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("48D40F2E-2844-4058-B1FA-710EEE55157B")]
|
||||
public class AddQuestItemLimitPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add quest item limits";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds limits to quest items, so that only one item can be picked up.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddQuestItemLimit;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 05, 04, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var hashSet = new HashSet<short>
|
||||
{
|
||||
Quest.BrokenSwordNumber,
|
||||
Quest.EyeOfAbyssalNumber,
|
||||
Quest.FeatherOfDarkPhoenixNumber,
|
||||
Quest.FlameOfDeathBeamKnightNumber,
|
||||
Quest.HornOfHellMaineNumber,
|
||||
Quest.ScrollOfEmperorNumber,
|
||||
Quest.SoulShardOfWizardNumber,
|
||||
Quest.TearOfElfNumber,
|
||||
};
|
||||
var questItems = gameConfiguration.Items.Where(item => item.Group == 14 && hashSet.Contains(item.Number));
|
||||
foreach (var item in questItems)
|
||||
{
|
||||
item.StorageLimitPerCharacter = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="AddRandomExperienceConfigAttributesPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the random experience config attributes for version 0.75.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("5F412933-CC0F-483B-B6AE-7B358A6257FD")]
|
||||
public class AddRandomExperienceConfigAttributesPlugIn075 : AddRandomExperienceConfigAttributesPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddRandomExperienceConfigAttributes075;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="AddRandomExperienceConfigAttributesPlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the random experience config attributes for version 0.95d.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("9A166583-C3E7-4E04-924C-F01FF9840974")]
|
||||
public class AddRandomExperienceConfigAttributesPlugIn095d : AddRandomExperienceConfigAttributesPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddRandomExperienceConfigAttributes095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// <copyright file="AddRandomExperienceConfigAttributesPlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the random experience config attributes to the database
|
||||
/// (RandomExperienceMinMultiplier, RandomExperienceMaxMultiplier)
|
||||
/// and assigns their default values to global base attributes.
|
||||
/// </summary>
|
||||
public abstract class AddRandomExperienceConfigAttributesPlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plugin name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Random Experience Configuration Attributes";
|
||||
|
||||
/// <summary>
|
||||
/// The plugin description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Adds new random experience Configuration attributes to the game configuration.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 04, 27, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var attributesToAdd = new[]
|
||||
{
|
||||
Stats.RandomExperienceMinMultiplier,
|
||||
Stats.RandomExperienceMaxMultiplier,
|
||||
};
|
||||
|
||||
foreach (var attr in attributesToAdd)
|
||||
{
|
||||
if (!gameConfiguration.Attributes.Any(a => a.Id == attr.Id))
|
||||
{
|
||||
var newAttr = context.CreateNew<AttributeDefinition>(attr.Id, attr.Designation, attr.Description);
|
||||
gameConfiguration.Attributes.Add(newAttr);
|
||||
}
|
||||
}
|
||||
|
||||
var randMin = gameConfiguration.Attributes.First(a => a.Id == Stats.RandomExperienceMinMultiplier.Id);
|
||||
var randMax = gameConfiguration.Attributes.First(a => a.Id == Stats.RandomExperienceMaxMultiplier.Id);
|
||||
|
||||
if (!gameConfiguration.GlobalBaseAttributeValues.Any(a => a.Definition?.Id == randMin.Id))
|
||||
{
|
||||
gameConfiguration.GlobalBaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(0.8f, randMin));
|
||||
}
|
||||
|
||||
if (!gameConfiguration.GlobalBaseAttributeValues.Any(a => a.Definition?.Id == randMax.Id))
|
||||
{
|
||||
gameConfiguration.GlobalBaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(1.2f, randMax));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="AddRandomExperienceConfigAttributesPlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the random experience config attributes for season 6.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("D1DC70A2-2614-4CC0-81C0-6C8253781019")]
|
||||
public class AddRandomExperienceConfigAttributesPlugInSeason6 : AddRandomExperienceConfigAttributesPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddRandomExperienceConfigAttributesSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
// <copyright file="AddSummonerBuffSkillsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the Sleep, Innovation, Damage Reflection and Weakness Summoner buff skills. It also fixes the 3rd wing full reflect option.
|
||||
/// </summary>
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[PlugIn]
|
||||
[Guid("B1E2D6C3-1F4A-4D7C-8C2E-3F6D9A7B8E2F")]
|
||||
public class AddSummonerBuffSkillsPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add Summoner Buff Skills";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds the Sleep, Innovation, Damage Reflection and Weakness Summoner buff skills. It also fixes the 3rd wing full reflect option.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddSummonerBuffSkills;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 12, 29, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Add new attributes
|
||||
var innovationDefDecrement = context.CreateNew<AttributeDefinition>(Stats.InnovationDefDecrement.Id, Stats.InnovationDefDecrement.Designation, Stats.InnovationDefDecrement.Description);
|
||||
gameConfiguration.Attributes.Add(innovationDefDecrement);
|
||||
var isAsleep = context.CreateNew<AttributeDefinition>(Stats.IsAsleep.Id, Stats.IsAsleep.Designation, Stats.IsAsleep.Description);
|
||||
gameConfiguration.Attributes.Add(isAsleep);
|
||||
var fullyReflectDamageAfterHitChance = context.CreateNew<AttributeDefinition>(Stats.FullyReflectDamageAfterHitChance.Id, Stats.FullyReflectDamageAfterHitChance.Designation, Stats.FullyReflectDamageAfterHitChance.Description);
|
||||
gameConfiguration.Attributes.Add(fullyReflectDamageAfterHitChance);
|
||||
|
||||
// Fix reflect excellent option
|
||||
var excDefenseOptionsId = new Guid("00000083-0012-0000-0000-000000000000");
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == excDefenseOptionsId) is { } excDefenseOptions)
|
||||
{
|
||||
if (excDefenseOptions.PossibleOptions.FirstOrDefault(p => p.PowerUpDefinition?.TargetAttribute == Stats.DamageReflection) is { } dmgReflection)
|
||||
{
|
||||
dmgReflection.PowerUpDefinition!.Boost!.ConstantValue.Value = 0.05f;
|
||||
}
|
||||
}
|
||||
|
||||
// Update Weakness magic effect
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.Weakness) is { } weaknessEffect)
|
||||
{
|
||||
weaknessEffect.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
weaknessEffect.Chance.ConstantValue.Value = 0.1f; // 10%
|
||||
}
|
||||
|
||||
var innovationEffect = this.CreateInnovationMagicEffect(context, gameConfiguration);
|
||||
var reflectionEffect = this.CreateReflectionMagicEffect(context, gameConfiguration);
|
||||
var sleepEffect = this.CreateSleepMagicEffect(context, gameConfiguration);
|
||||
var weaknessSummonerEffect = this.CreateWeaknessSummonerMagicEffect(context, gameConfiguration);
|
||||
|
||||
// Update 3rd wing reflect option
|
||||
var thirWingOptionDefId = new Guid("00000083-0067-0000-0000-000000000000");
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == thirWingOptionDefId) is { } thirWingOptionDef
|
||||
&& thirWingOptionDef.PossibleOptions.FirstOrDefault(po => po.PowerUpDefinition?.TargetAttribute == Stats.DamageReflection) is { } reflectOpt
|
||||
&& reflectOpt.PowerUpDefinition is not null)
|
||||
{
|
||||
reflectOpt.PowerUpDefinition.TargetAttribute = fullyReflectDamageAfterHitChance;
|
||||
}
|
||||
|
||||
// Update existing skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Innovation) is { } innovation)
|
||||
{
|
||||
innovation.MagicEffectDef = innovationEffect;
|
||||
innovation.SkillType = SkillType.Buff;
|
||||
innovation.AreaSkillSettings =
|
||||
this.AddAreaSkillSettings(context, false, 0, 0, 0, maximumHitsPerAttack: 5, useTargetAreaFilter: true, targetAreaDiameter: 10);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DamageReflection) is { } damageReflection)
|
||||
{
|
||||
damageReflection.MagicEffectDef = reflectionEffect;
|
||||
damageReflection.SkillType = SkillType.Buff;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Sleep) is { } sleep)
|
||||
{
|
||||
sleep.MagicEffectDef = sleepEffect;
|
||||
sleep.SkillType = SkillType.Buff;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Weakness) is { } weakness)
|
||||
{
|
||||
weakness.MagicEffectDef = weaknessSummonerEffect;
|
||||
weakness.SkillType = SkillType.Buff;
|
||||
weakness.AreaSkillSettings =
|
||||
this.AddAreaSkillSettings(context, false, 0, 0, 0, maximumHitsPerAttack: 5, useTargetAreaFilter: true, targetAreaDiameter: 10);
|
||||
}
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)MagicEffectNumber.Innovation;
|
||||
magicEffect.Name = "Innovation Effect";
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = false;
|
||||
magicEffect.StopByDeath = true;
|
||||
magicEffect.DurationDependsOnTargetLevel = true;
|
||||
magicEffect.MonsterTargetLevelDivisor = 20;
|
||||
magicEffect.PlayerTargetLevelDivisor = 150;
|
||||
|
||||
// Chance % = 32 + (Energy / 50) + (Book Rise / 6)
|
||||
magicEffect.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Chance.ConstantValue.Value = 0.32f; // 32%
|
||||
|
||||
var chancePerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
chancePerEnergy.InputOperator = InputOperator.Multiply;
|
||||
chancePerEnergy.InputOperand = 1f / 5000f; // 50 energy adds 1% chance
|
||||
magicEffect.Chance.RelatedValues.Add(chancePerEnergy);
|
||||
|
||||
var chancePerBookRise = context.CreateNew<AttributeRelationship>();
|
||||
chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration);
|
||||
chancePerBookRise.InputOperator = InputOperator.Multiply;
|
||||
chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance
|
||||
magicEffect.Chance.RelatedValues.Add(chancePerBookRise);
|
||||
|
||||
// Chance PvP % = 17 + (Energy / 50) + (Book Rise / 6)
|
||||
magicEffect.ChancePvp = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.ChancePvp.ConstantValue.Value = 0.17f; // 17%
|
||||
|
||||
var chancePerEnergyPvp = context.CreateNew<AttributeRelationship>();
|
||||
chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
chancePerEnergyPvp.InputOperator = InputOperator.Multiply;
|
||||
chancePerEnergyPvp.InputOperand = 1f / 5000f; // 50 energy adds 1% chance
|
||||
magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp);
|
||||
|
||||
var chancePerBookRisePvp = context.CreateNew<AttributeRelationship>();
|
||||
chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration);
|
||||
chancePerBookRisePvp.InputOperator = InputOperator.Multiply;
|
||||
chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance
|
||||
magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp);
|
||||
|
||||
// Duration = 4 + (Energy / 100)
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds
|
||||
magicEffect.Duration.MaximumValue = 44; // 44 Seconds (based on 4k total energy cap)
|
||||
|
||||
var durationPerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
durationPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s
|
||||
magicEffect.Duration.RelatedValues.Add(durationPerEnergy);
|
||||
|
||||
magicEffect.DurationPvp = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds
|
||||
magicEffect.DurationPvp.MaximumValue = 18; // 18 Seconds (based on 4k total energy cap)
|
||||
|
||||
// Duration PvP = 5 + (Energy / 300) + ((Level - Target's Level) / 150)
|
||||
var durationPerEnergyPvp = context.CreateNew<AttributeRelationship>();
|
||||
durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
durationPerEnergyPvp.InputOperator = InputOperator.Multiply;
|
||||
durationPerEnergyPvp.InputOperand = 1f / 300f; // 300 energy adds 1s
|
||||
magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp);
|
||||
|
||||
var durationPerLevelPvp = context.CreateNew<AttributeRelationship>();
|
||||
durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(gameConfiguration);
|
||||
durationPerLevelPvp.InputOperator = InputOperator.Multiply;
|
||||
durationPerLevelPvp.InputOperand = 1f / 150f; // 150 levels adds 1s
|
||||
magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp);
|
||||
|
||||
// Defense decrease % (applies last) = 20 + (Energy / 90)
|
||||
var decDefPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(decDefPowerUpDefinition);
|
||||
decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration);
|
||||
decDefPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
decDefPowerUpDefinition.Boost.ConstantValue.Value = 0.20f; // 20% decrease
|
||||
decDefPowerUpDefinition.Boost.MaximumValue = 0.64f; // 64% decrease (based on 4k total energy cap)
|
||||
|
||||
var decDefPerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
decDefPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
decDefPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
decDefPerEnergy.InputOperand = 1f / 9000f; // 90 energy further decreases 0.01
|
||||
decDefPowerUpDefinition.Boost.RelatedValues.Add(decDefPerEnergy);
|
||||
|
||||
// Defense decrease PvP % (applies last) = 12 + (Energy / 110)
|
||||
var decDefPowerUpDefinitionPvp = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp);
|
||||
decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration);
|
||||
decDefPowerUpDefinitionPvp.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
decDefPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.12f; // 12% decrease
|
||||
decDefPowerUpDefinitionPvp.Boost.MaximumValue = 0.48f; // 48% decrease (based on 4k total energy cap)
|
||||
|
||||
var decDefPerEnergyPvp = context.CreateNew<AttributeRelationship>();
|
||||
decDefPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
decDefPerEnergyPvp.InputOperator = InputOperator.Multiply;
|
||||
decDefPerEnergyPvp.InputOperand = 1f / 11000f; // 110 energy further decreases 0.01
|
||||
decDefPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDefPerEnergyPvp);
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateReflectionMagicEffect(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)MagicEffectNumber.Reflection;
|
||||
magicEffect.Name = "Reflection Effect";
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = false;
|
||||
magicEffect.StopByDeath = true;
|
||||
|
||||
// Duration = 30 + (Energy / 24)
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = 30; // 30 Seconds
|
||||
magicEffect.Duration.MaximumValue = 180;
|
||||
|
||||
var durationPerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
durationPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
durationPerEnergy.InputOperand = 1f / 24; // 24 energy adds 1s
|
||||
magicEffect.Duration.RelatedValues.Add(durationPerEnergy);
|
||||
|
||||
// Reflection % = 30 + (Energy / 42)
|
||||
var incReflectPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(incReflectPowerUpDefinition);
|
||||
incReflectPowerUpDefinition.TargetAttribute = Stats.DamageReflection.GetPersistent(gameConfiguration);
|
||||
incReflectPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
incReflectPowerUpDefinition.Boost.ConstantValue.Value = 0.3f; // 30% increase
|
||||
incReflectPowerUpDefinition.Boost.MaximumValue = 0.6f;
|
||||
|
||||
var incReflectPerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
incReflectPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
incReflectPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
incReflectPerEnergy.InputOperand = 1f / 4200f; // 42 energy further increases 0.01
|
||||
incReflectPowerUpDefinition.Boost.RelatedValues.Add(incReflectPerEnergy);
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateSleepMagicEffect(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)MagicEffectNumber.Sleep;
|
||||
magicEffect.Name = "Sleep Effect";
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = false;
|
||||
magicEffect.StopByDeath = true;
|
||||
magicEffect.DurationDependsOnTargetLevel = true;
|
||||
magicEffect.MonsterTargetLevelDivisor = 20;
|
||||
magicEffect.PlayerTargetLevelDivisor = 100;
|
||||
|
||||
// Chance % = 20 + (Energy / 30) + (Book Rise / 6)
|
||||
magicEffect.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Chance.ConstantValue.Value = 0.2f; // 20%
|
||||
|
||||
var chancePerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
chancePerEnergy.InputOperator = InputOperator.Multiply;
|
||||
chancePerEnergy.InputOperand = 1f / 3000f; // 30 energy adds 1% chance
|
||||
magicEffect.Chance.RelatedValues.Add(chancePerEnergy);
|
||||
|
||||
var chancePerBookRise = context.CreateNew<AttributeRelationship>();
|
||||
chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration);
|
||||
chancePerBookRise.InputOperator = InputOperator.Multiply;
|
||||
chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance
|
||||
magicEffect.Chance.RelatedValues.Add(chancePerBookRise);
|
||||
|
||||
// Chance PvP % = 15 + (Energy / 37) + (Book Rise / 6)
|
||||
magicEffect.ChancePvp = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.ChancePvp.ConstantValue.Value = 0.15f; // 15%
|
||||
|
||||
var chancePerEnergyPvp = context.CreateNew<AttributeRelationship>();
|
||||
chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
chancePerEnergyPvp.InputOperator = InputOperator.Multiply;
|
||||
chancePerEnergyPvp.InputOperand = 1f / 3700f; // 37 energy adds 1% chance
|
||||
magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp);
|
||||
|
||||
var chancePerBookRisePvp = context.CreateNew<AttributeRelationship>();
|
||||
chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration);
|
||||
chancePerBookRisePvp.InputOperator = InputOperator.Multiply;
|
||||
chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance
|
||||
magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp);
|
||||
|
||||
// Duration = 5 + (Energy / 100)
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = 5; // 5 Seconds
|
||||
magicEffect.Duration.MaximumValue = 20; // 20 Seconds
|
||||
|
||||
var durationPerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
durationPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s
|
||||
magicEffect.Duration.RelatedValues.Add(durationPerEnergy);
|
||||
|
||||
// Duration = 4 + (Energy / 250) + ((Level - Target's Level) / 100)
|
||||
magicEffect.DurationPvp = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.DurationPvp.ConstantValue.Value = 4; // 4 Seconds
|
||||
magicEffect.DurationPvp.MaximumValue = 10; // 10 Seconds
|
||||
|
||||
var durationPerEnergyPvp = context.CreateNew<AttributeRelationship>();
|
||||
durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
durationPerEnergyPvp.InputOperator = InputOperator.Multiply;
|
||||
durationPerEnergyPvp.InputOperand = 1f / 250f; // 250 energy adds 1s
|
||||
magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp);
|
||||
|
||||
var durationPerLevelPvp = context.CreateNew<AttributeRelationship>();
|
||||
durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(gameConfiguration);
|
||||
durationPerLevelPvp.InputOperator = InputOperator.Multiply;
|
||||
durationPerLevelPvp.InputOperand = 1f / 100f; // 100 levels adds 1s
|
||||
magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp);
|
||||
|
||||
var isAsleep = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(isAsleep);
|
||||
isAsleep.TargetAttribute = Stats.IsAsleep.GetPersistent(gameConfiguration);
|
||||
isAsleep.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
isAsleep.Boost.ConstantValue.Value = 1;
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateWeaknessSummonerMagicEffect(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)MagicEffectNumber.Weakness; // We will map skill to effect by hand in this update, so we use this number instead of WeaknessSummoner
|
||||
magicEffect.Name = "Weakness Effect (Summoner)";
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = false;
|
||||
magicEffect.StopByDeath = true;
|
||||
magicEffect.DurationDependsOnTargetLevel = true;
|
||||
magicEffect.MonsterTargetLevelDivisor = 20;
|
||||
magicEffect.PlayerTargetLevelDivisor = 150;
|
||||
|
||||
// Chance % = 32 + (Energy / 50) + (Book Rise / 6)
|
||||
magicEffect.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Chance.ConstantValue.Value = 0.32f; // 32%
|
||||
|
||||
var chancePerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
chancePerEnergy.InputOperator = InputOperator.Multiply;
|
||||
chancePerEnergy.InputOperand = 1f / 5000f; // 50 energy adds 1% chance
|
||||
magicEffect.Chance.RelatedValues.Add(chancePerEnergy);
|
||||
|
||||
var chancePerBookRise = context.CreateNew<AttributeRelationship>();
|
||||
chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration);
|
||||
chancePerBookRise.InputOperator = InputOperator.Multiply;
|
||||
chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance
|
||||
magicEffect.Chance.RelatedValues.Add(chancePerBookRise);
|
||||
|
||||
// Chance % = 17 + (Energy / 50) + (Book Rise / 6)
|
||||
magicEffect.ChancePvp = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.ChancePvp.ConstantValue.Value = 0.17f; // 17%
|
||||
|
||||
var chancePerEnergyPvp = context.CreateNew<AttributeRelationship>();
|
||||
chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
chancePerEnergyPvp.InputOperator = InputOperator.Multiply;
|
||||
chancePerEnergyPvp.InputOperand = 1f / 5000f; // 50 energy adds 1% chance
|
||||
magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp);
|
||||
|
||||
var chancePerBookRisePvp = context.CreateNew<AttributeRelationship>();
|
||||
chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration);
|
||||
chancePerBookRisePvp.InputOperator = InputOperator.Multiply;
|
||||
chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance
|
||||
magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp);
|
||||
|
||||
// Duration = 4 + (Energy / 100)
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds
|
||||
magicEffect.Duration.MaximumValue = 44; // 44 Seconds (based on 4k total energy cap)
|
||||
|
||||
var durationPerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
durationPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s
|
||||
magicEffect.Duration.RelatedValues.Add(durationPerEnergy);
|
||||
|
||||
// Duration = 5 + (Energy / 300) + ((Level - Target's Level) / 150)
|
||||
magicEffect.DurationPvp = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds
|
||||
magicEffect.DurationPvp.MaximumValue = 18; // 18 Seconds (based on 4k total energy cap)
|
||||
|
||||
var durationPerEnergyPvp = context.CreateNew<AttributeRelationship>();
|
||||
durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
durationPerEnergyPvp.InputOperator = InputOperator.Multiply;
|
||||
durationPerEnergyPvp.InputOperand = 1f / 300f; // 300 energy adds 1s
|
||||
magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp);
|
||||
|
||||
var durationPerLevelPvp = context.CreateNew<AttributeRelationship>();
|
||||
durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(gameConfiguration);
|
||||
durationPerLevelPvp.InputOperator = InputOperator.Multiply;
|
||||
durationPerLevelPvp.InputOperand = 1f / 150f; // 150 levels adds 1s
|
||||
magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp);
|
||||
|
||||
// Phys damage decrease % = 4 + (Energy / 58)
|
||||
var decDmgPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(decDmgPowerUpDefinition);
|
||||
decDmgPowerUpDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration);
|
||||
decDmgPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
decDmgPowerUpDefinition.Boost.ConstantValue.Value = 0.04f; // 4% decrease
|
||||
decDmgPowerUpDefinition.Boost.MaximumValue = 0.73f; // 73% decrease (based on 4k total energy cap)
|
||||
|
||||
var decDmgPerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
decDmgPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
decDmgPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
decDmgPerEnergy.InputOperand = 1f / 5800f; // 58 energy further decreases 0.01
|
||||
decDmgPowerUpDefinition.Boost.RelatedValues.Add(decDmgPerEnergy);
|
||||
|
||||
// Phys damage decrease PvP % = 3 + (Energy / 93)
|
||||
var decDmgPowerUpDefinitionPvp = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitionsPvp.Add(decDmgPowerUpDefinitionPvp);
|
||||
decDmgPowerUpDefinitionPvp.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration);
|
||||
decDmgPowerUpDefinitionPvp.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
decDmgPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.03f; // 3% decrease
|
||||
decDmgPowerUpDefinitionPvp.Boost.MaximumValue = 0.46f; // 46% decrease (based on 4k total energy cap)
|
||||
|
||||
var decDmgPerEnergyPvp = context.CreateNew<AttributeRelationship>();
|
||||
decDmgPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
decDmgPerEnergyPvp.InputOperator = InputOperator.Multiply;
|
||||
decDmgPerEnergyPvp.InputOperand = 1f / 9300f; // 93 energy further decreases 0.01
|
||||
decDmgPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDmgPerEnergyPvp);
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
|
||||
private AreaSkillSettings AddAreaSkillSettings(
|
||||
IContext context,
|
||||
bool useFrustumFilter,
|
||||
float frustumStartWidth,
|
||||
float frustumEndWidth,
|
||||
float frustumDistance,
|
||||
bool useDeferredHits = false,
|
||||
TimeSpan delayPerOneDistance = default,
|
||||
TimeSpan delayBetweenHits = default,
|
||||
int minimumHitsPerTarget = 1,
|
||||
int maximumHitsPerTarget = 1,
|
||||
int maximumHitsPerAttack = default,
|
||||
float hitChancePerDistanceMultiplier = 1.0f,
|
||||
bool useTargetAreaFilter = false,
|
||||
float targetAreaDiameter = default)
|
||||
{
|
||||
var areaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
|
||||
areaSkillSettings.UseFrustumFilter = useFrustumFilter;
|
||||
areaSkillSettings.FrustumStartWidth = frustumStartWidth;
|
||||
areaSkillSettings.FrustumEndWidth = frustumEndWidth;
|
||||
areaSkillSettings.FrustumDistance = frustumDistance;
|
||||
areaSkillSettings.UseTargetAreaFilter = useTargetAreaFilter;
|
||||
areaSkillSettings.TargetAreaDiameter = targetAreaDiameter;
|
||||
areaSkillSettings.UseDeferredHits = useDeferredHits;
|
||||
areaSkillSettings.DelayPerOneDistance = delayPerOneDistance;
|
||||
areaSkillSettings.DelayBetweenHits = delayBetweenHits;
|
||||
areaSkillSettings.MinimumNumberOfHitsPerTarget = minimumHitsPerTarget;
|
||||
areaSkillSettings.MaximumNumberOfHitsPerTarget = maximumHitsPerTarget;
|
||||
areaSkillSettings.MaximumNumberOfHitsPerAttack = maximumHitsPerAttack;
|
||||
areaSkillSettings.HitChancePerDistanceMultiplier = hitChancePerDistanceMultiplier;
|
||||
|
||||
return areaSkillSettings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// <copyright file="AddWhiteWizardInvasionMobsUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Adds the White Wizard invasion monsters (135-137) and their drop groups
|
||||
/// to existing databases where <see cref="VersionSeasonSix.InvasionMobsInitialization"/>
|
||||
/// has already run but before White Wizard monsters were defined.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("D8F4E2C0-5A6B-4C3D-9E7F-1B2A4C6D8E0F")]
|
||||
public class AddWhiteWizardInvasionMobsUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the plugin name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Add White Wizard Invasion Monsters";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Adds White Wizard (135), Destructive Ogre Soldier (136), and Destructive Ogre Archer (137) and drop groups for existing databases.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.AddWhiteWizardInvasionMobs;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 07, 05, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
if (gameConfiguration.Monsters.Any(m => m.Number == 135))
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
this.AddWhiteWizard(context, gameConfiguration);
|
||||
this.AddDestructiveOgreSoldier(context, gameConfiguration);
|
||||
this.AddDestructiveOgreArcher(context, gameConfiguration);
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
private void AddWhiteWizard(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var monster = context.CreateNew<MonsterDefinition>();
|
||||
gameConfiguration.Monsters.Add(monster);
|
||||
monster.Number = 135;
|
||||
monster.Designation = "White Wizard";
|
||||
monster.MoveRange = 4;
|
||||
monster.AttackRange = 5;
|
||||
monster.AttackSkill = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.MonsterSkill);
|
||||
monster.ViewRange = 6;
|
||||
monster.MoveDelay = new TimeSpan(400 * TimeSpan.TicksPerMillisecond);
|
||||
monster.AttackDelay = new TimeSpan(1400 * TimeSpan.TicksPerMillisecond);
|
||||
monster.RespawnDelay = new TimeSpan(10 * TimeSpan.TicksPerSecond);
|
||||
monster.Attribute = 2;
|
||||
monster.NumberOfMaximumItemDrops = 1;
|
||||
var attributes = new Dictionary<AttributeDefinition, float>
|
||||
{
|
||||
{ Stats.Level, 87 },
|
||||
{ Stats.MaximumHealth, 26000 },
|
||||
{ Stats.MinimumPhysBaseDmg, 370 },
|
||||
{ Stats.MaximumPhysBaseDmg, 410 },
|
||||
{ Stats.DefenseBase, 400 },
|
||||
{ Stats.AttackRatePvm, 550 },
|
||||
{ Stats.DefenseRatePvm, 200 },
|
||||
{ Stats.IceResistance, 15f / 255 },
|
||||
{ Stats.PoisonResistance, 15f / 255 },
|
||||
{ Stats.LightningResistance, 20f / 255 },
|
||||
{ Stats.FireResistance, 15f / 255 },
|
||||
};
|
||||
monster.AddAttributes(attributes, context, gameConfiguration);
|
||||
|
||||
var itemDrop = context.CreateNew<DropItemGroup>();
|
||||
itemDrop.Chance = 1.0;
|
||||
itemDrop.Description = "Jewel of Bless from White Wizard";
|
||||
itemDrop.Monster = monster;
|
||||
itemDrop.PossibleItems.Add(gameConfiguration.Items.First(item => item.Number == ItemConstants.JewelOfBless.Number && item.Group == ItemConstants.JewelOfBless.Group));
|
||||
monster.DropItemGroups.Add(itemDrop);
|
||||
gameConfiguration.DropItemGroups.Add(itemDrop);
|
||||
}
|
||||
|
||||
private void AddDestructiveOgreSoldier(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var monster = context.CreateNew<MonsterDefinition>();
|
||||
gameConfiguration.Monsters.Add(monster);
|
||||
monster.Number = 136;
|
||||
monster.Designation = "Destructive Ogre Soldier";
|
||||
monster.MoveRange = 3;
|
||||
monster.AttackRange = 1;
|
||||
monster.AttackSkill = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.MonsterSkill);
|
||||
monster.ViewRange = 3;
|
||||
monster.MoveDelay = new TimeSpan(400 * TimeSpan.TicksPerMillisecond);
|
||||
monster.AttackDelay = new TimeSpan(1400 * TimeSpan.TicksPerMillisecond);
|
||||
monster.RespawnDelay = new TimeSpan(10 * TimeSpan.TicksPerSecond);
|
||||
monster.Attribute = 2;
|
||||
monster.NumberOfMaximumItemDrops = 1;
|
||||
var attributes = new Dictionary<AttributeDefinition, float>
|
||||
{
|
||||
{ Stats.Level, 70 },
|
||||
{ Stats.MaximumHealth, 9500 },
|
||||
{ Stats.MinimumPhysBaseDmg, 210 },
|
||||
{ Stats.MaximumPhysBaseDmg, 240 },
|
||||
{ Stats.DefenseBase, 180 },
|
||||
{ Stats.AttackRatePvm, 400 },
|
||||
{ Stats.DefenseRatePvm, 125 },
|
||||
{ Stats.IceResistance, 7f / 255 },
|
||||
{ Stats.PoisonResistance, 7f / 255 },
|
||||
{ Stats.LightningResistance, 7f / 255 },
|
||||
{ Stats.FireResistance, 7f / 255 },
|
||||
};
|
||||
monster.AddAttributes(attributes, context, gameConfiguration);
|
||||
|
||||
var itemDrop = context.CreateNew<DropItemGroup>();
|
||||
itemDrop.Chance = 0.8;
|
||||
itemDrop.Description = "Wizard's Ring from Destructive Ogre Soldier";
|
||||
itemDrop.Monster = monster;
|
||||
itemDrop.PossibleItems.Add(gameConfiguration.Items.First(item => item.Number == ItemConstants.WizardsRing.Number && item.Group == ItemConstants.WizardsRing.Group));
|
||||
monster.DropItemGroups.Add(itemDrop);
|
||||
gameConfiguration.DropItemGroups.Add(itemDrop);
|
||||
}
|
||||
|
||||
private void AddDestructiveOgreArcher(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var monster = context.CreateNew<MonsterDefinition>();
|
||||
gameConfiguration.Monsters.Add(monster);
|
||||
monster.Number = 137;
|
||||
monster.Designation = "Destructive Ogre Archer";
|
||||
monster.MoveRange = 3;
|
||||
monster.AttackRange = 5;
|
||||
monster.AttackSkill = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.MonsterSkill);
|
||||
monster.ViewRange = 5;
|
||||
monster.MoveDelay = new TimeSpan(400 * TimeSpan.TicksPerMillisecond);
|
||||
monster.AttackDelay = new TimeSpan(1600 * TimeSpan.TicksPerMillisecond);
|
||||
monster.RespawnDelay = new TimeSpan(10 * TimeSpan.TicksPerSecond);
|
||||
monster.Attribute = 2;
|
||||
monster.NumberOfMaximumItemDrops = 1;
|
||||
var attributes = new Dictionary<AttributeDefinition, float>
|
||||
{
|
||||
{ Stats.Level, 74 },
|
||||
{ Stats.MaximumHealth, 12000 },
|
||||
{ Stats.MinimumPhysBaseDmg, 220 },
|
||||
{ Stats.MaximumPhysBaseDmg, 260 },
|
||||
{ Stats.DefenseBase, 190 },
|
||||
{ Stats.AttackRatePvm, 440 },
|
||||
{ Stats.DefenseRatePvm, 130 },
|
||||
{ Stats.IceResistance, 8f / 255 },
|
||||
{ Stats.PoisonResistance, 8f / 255 },
|
||||
{ Stats.LightningResistance, 8f / 255 },
|
||||
{ Stats.FireResistance, 8f / 255 },
|
||||
};
|
||||
monster.AddAttributes(attributes, context, gameConfiguration);
|
||||
|
||||
var itemDrop = context.CreateNew<DropItemGroup>();
|
||||
itemDrop.Chance = 0.8;
|
||||
itemDrop.Description = "Wizard's Ring from Destructive Ogre Archer";
|
||||
itemDrop.Monster = monster;
|
||||
itemDrop.PossibleItems.Add(gameConfiguration.Items.First(item => item.Number == ItemConstants.WizardsRing.Number && item.Group == ItemConstants.WizardsRing.Group));
|
||||
monster.DropItemGroups.Add(itemDrop);
|
||||
gameConfiguration.DropItemGroups.Add(itemDrop);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// <copyright file="ChainLightningUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update sets the right settings for the chain lightning skill.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("039D09CB-283C-4CBD-ABBC-FFD3F7D5C62F")]
|
||||
public class ChainLightningUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Chain Lightning skill";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update sets the right settings for the chain lightning skill.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.ChainLightningUpdate;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 07, 15, 18, 00, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var chainLightning = gameConfiguration.Skills.First(s => s.Number == (int)SkillNumber.ChainLightning);
|
||||
chainLightning.SkillType = SkillType.AreaSkillExplicitTarget;
|
||||
chainLightning.Target = SkillTarget.Explicit;
|
||||
|
||||
var chainLightningStr = gameConfiguration.Skills.First(s => s.Number == (int)SkillNumber.ChainLightningStr);
|
||||
chainLightningStr.SkillType = SkillType.AreaSkillExplicitTarget;
|
||||
chainLightningStr.Target = SkillTarget.Explicit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// <copyright file="ChaosCastleDataUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Events;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The chaos castle update plugin.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("13059991-F3C8-4050-A201-6D6A67E57541")]
|
||||
public class ChaosCastleDataUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Chaos Castle Data";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update creates the configuration data for the chaos castle event.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.ChaosCastleDataUpdate;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 03, 05, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CS1998
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
#pragma warning restore CS1998
|
||||
{
|
||||
// First check if an update is required.
|
||||
if (gameConfiguration.MiniGameDefinitions.Any(def => def.Type == MiniGameType.ChaosCastle))
|
||||
{
|
||||
// There is already a chaos castle definition, so we can skip this update
|
||||
return;
|
||||
}
|
||||
|
||||
var initializer = new ChaosCastleInitializer(context, gameConfiguration);
|
||||
initializer.Initialize();
|
||||
|
||||
var chaosCastleMaps = gameConfiguration.Maps.Where(map => map.Number is >= 17 and <= 23 or 53);
|
||||
foreach (var chaosCastleMap in chaosCastleMaps)
|
||||
{
|
||||
chaosCastleMap.UpdateTerrainFromResources();
|
||||
}
|
||||
}
|
||||
}
|
||||
116
src/Persistence/Initialization/Updates/DataUpdateService.cs
Normal file
116
src/Persistence/Initialization/Updates/DataUpdateService.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
// <copyright file="DataUpdateService.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Service which applies updates of previously initialized data by a <see cref="IDataInitializationPlugIn"/>.
|
||||
/// </summary>
|
||||
public class DataUpdateService
|
||||
{
|
||||
private readonly IPersistenceContextProvider _contextProvider;
|
||||
private readonly PlugInManager _plugInManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataUpdateService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="contextProvider">The context provider.</param>
|
||||
/// <param name="plugInManager">The plug in manager.</param>
|
||||
public DataUpdateService(IPersistenceContextProvider contextProvider, PlugInManager plugInManager)
|
||||
{
|
||||
this._contextProvider = contextProvider;
|
||||
this._plugInManager = plugInManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when updates have been installed.
|
||||
/// </summary>
|
||||
public event AsyncEventHandler? UpdatesInstalled;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the available updates which are not installed yet.
|
||||
/// </summary>
|
||||
/// <returns>The available plugins.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">The plugin manager is not initialized.</exception>
|
||||
public async ValueTask<IReadOnlyCollection<IConfigurationUpdatePlugIn>> DetermineAvailableUpdatesAsync()
|
||||
{
|
||||
using var context = this._contextProvider.CreateNewContext();
|
||||
var updates = (await context.GetAsync<ConfigurationUpdate>().ConfigureAwait(false)).ToList();
|
||||
|
||||
var initializationKey = await this.DetermineInitializationKeyAsync(context).ConfigureAwait(false);
|
||||
var installedUpdates = updates
|
||||
.Where(up => up.InstalledAt is not null)
|
||||
.Select(up => (UpdateVersion)up.Version)
|
||||
.ToHashSet();
|
||||
|
||||
var updateStrategyProvider = this._plugInManager.GetStrategyProvider<int, IConfigurationUpdatePlugIn>();
|
||||
if (updateStrategyProvider is null)
|
||||
{
|
||||
// it's null when there are no plugins yet ...
|
||||
return [];
|
||||
}
|
||||
|
||||
var availableUpdates = updateStrategyProvider.AvailableStrategies
|
||||
.Where(up => up.DataInitializationKey == initializationKey)
|
||||
.Where(up => !installedUpdates.Contains(up.Version))
|
||||
.OrderBy(up => up.Version)
|
||||
.ToList();
|
||||
|
||||
return availableUpdates;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the updates asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="updates">The updates.</param>
|
||||
/// <param name="progress">The progress provider. Reports the progress back to the caller.</param>
|
||||
public async ValueTask ApplyUpdatesAsync(IReadOnlyList<IConfigurationUpdatePlugIn> updates, IProgress<(UpdateVersion CurrentUpdatingVersion, bool IsCompleted)> progress)
|
||||
{
|
||||
using var context = this._contextProvider.CreateNewContext();
|
||||
var updateStates = await context.GetAsync<ConfigurationUpdateState>().ConfigureAwait(false);
|
||||
var updateState = updateStates.FirstOrDefault() ?? context.CreateNew<ConfigurationUpdateState>();
|
||||
var gameConfiguration = (await context.GetAsync<GameConfiguration>().ConfigureAwait(false)).First();
|
||||
foreach (var update in updates)
|
||||
{
|
||||
progress.Report((update.Version, false));
|
||||
await update.ApplyUpdateAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
|
||||
updateState.CurrentInstalledVersion = Math.Max((int)update.Version, updateState.CurrentInstalledVersion);
|
||||
updateState.InitializationKey = update.DataInitializationKey;
|
||||
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
progress.Report((update.Version, true));
|
||||
}
|
||||
|
||||
progress.Report((UpdateVersion.Undefined, true));
|
||||
this.UpdatesInstalled?.SafeInvokeAsync();
|
||||
}
|
||||
|
||||
private async ValueTask<string> DetermineInitializationKeyAsync(IContext context)
|
||||
{
|
||||
var updateStates = await context.GetAsync<ConfigurationUpdateState>().ConfigureAwait(false);
|
||||
if (updateStates.FirstOrDefault() is { InitializationKey: not null } updateState)
|
||||
{
|
||||
return updateState.InitializationKey;
|
||||
}
|
||||
|
||||
// Now it's getting tricky ...
|
||||
var clientDefinitions = await context.GetAsync<GameClientDefinition>().ConfigureAwait(false);
|
||||
if (clientDefinitions.FirstOrDefault() is not { } clientDefinition)
|
||||
{
|
||||
throw new InvalidOperationException("No data installed");
|
||||
}
|
||||
|
||||
return (clientDefinition.Season, clientDefinition.Episode) switch
|
||||
{
|
||||
(6, 3) => VersionSeasonSix.DataInitialization.Id,
|
||||
(0, 75) => Version075.DataInitialization.Id,
|
||||
(0, 95) => Version095d.DataInitialization.Id,
|
||||
_ => throw new InvalidOperationException($"Unknown client version: {clientDefinition}."),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// <copyright file="FinishDarkKnightMasterTreePlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update completes the dark knight master tree skills and effects. It also fixes the double wield damage calculations.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("B8F3E2C1-4D5A-6F78-9B0C-2E7D1A3F5B6C")]
|
||||
public class FinishDarkKnightMasterTreePlugIn075 : FinishDarkKnightMasterTreePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FinishDarkKnightMasterTree075;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// <copyright file="FinishDarkKnightMasterTreePlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update completes the dark knight master tree skills and effects. It also fixes the double wield damage calculations.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("D4F7A9C2-1B3E-56D8-9F0A-7C2E4B1D5A8F")]
|
||||
public class FinishDarkKnightMasterTreePlugIn095D : FinishDarkKnightMasterTreePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FinishDarkKnightMasterTree095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
// <copyright file="FinishDarkKnightMasterTreePlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// This update completes the dark knight master tree skills and effects. It also fixes the double wield damage calculations.
|
||||
/// </summary>
|
||||
public abstract class FinishDarkKnightMasterTreePlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Double Wield Damage Calculations";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes the double wield damage calculations.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 6, 22, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Create new Stats
|
||||
var maceMasteryStunChance = context.CreateNew<AttributeDefinition>(Stats.MaceMasteryStunChance.Id, Stats.MaceMasteryStunChance.Designation, Stats.MaceMasteryStunChance.Description);
|
||||
gameConfiguration.Attributes.Add(maceMasteryStunChance);
|
||||
var ragefulBlowMasteryDurabilityDecChance = context.CreateNew<AttributeDefinition>(Stats.RagefulBlowMasteryDurabilityDecChance.Id, Stats.RagefulBlowMasteryDurabilityDecChance.Designation, Stats.RagefulBlowMasteryDurabilityDecChance.Description);
|
||||
gameConfiguration.Attributes.Add(ragefulBlowMasteryDurabilityDecChance);
|
||||
var durabilityReductionFactor = context.CreateNew<AttributeDefinition>(Stats.DurabilityReductionFactor.Id, Stats.DurabilityReductionFactor.Designation, Stats.DurabilityReductionFactor.Description);
|
||||
gameConfiguration.Attributes.Add(durabilityReductionFactor);
|
||||
var spearMasteryDoubleDamageChance = context.CreateNew<AttributeDefinition>(Stats.SpearMasteryDoubleDamageChance.Id, Stats.SpearMasteryDoubleDamageChance.Designation, Stats.SpearMasteryDoubleDamageChance.Description);
|
||||
gameConfiguration.Attributes.Add(spearMasteryDoubleDamageChance);
|
||||
var swellLifeHealthIncrease = context.CreateNew<AttributeDefinition>(Stats.SwellLifeHealthIncrease.Id, Stats.SwellLifeHealthIncrease.Designation, Stats.SwellLifeHealthIncrease.Description);
|
||||
gameConfiguration.Attributes.Add(swellLifeHealthIncrease);
|
||||
var swellLifeManaIncrease = context.CreateNew<AttributeDefinition>(Stats.SwellLifeManaIncrease.Id, Stats.SwellLifeManaIncrease.Designation, Stats.SwellLifeManaIncrease.Description);
|
||||
gameConfiguration.Attributes.Add(swellLifeManaIncrease);
|
||||
|
||||
// Update attribute combinations
|
||||
var maximumHealth = Stats.MaximumHealth.GetPersistent(gameConfiguration);
|
||||
var maximumMana = Stats.MaximumMana.GetPersistent(gameConfiguration);
|
||||
var obsoleteTempDoubleWieldMultipliers = gameConfiguration.Attributes.Where(a => a.Designation == "Temp Double Wield multiplier"); // we remove this later
|
||||
var hasDoubleWield = Stats.HasDoubleWield.GetPersistent(gameConfiguration);
|
||||
var minimumPhysBaseDmgByWeapon = Stats.MinimumPhysBaseDmgByWeapon.GetPersistent(gameConfiguration);
|
||||
var maximumPhysBaseDmgByWeapon = Stats.MaximumPhysBaseDmgByWeapon.GetPersistent(gameConfiguration);
|
||||
var physicalBaseDmg = Stats.PhysicalBaseDmg.GetPersistent(gameConfiguration);
|
||||
var physicalBaseDmgIncrease = Stats.PhysicalBaseDmgIncrease.GetPersistent(gameConfiguration);
|
||||
var masteryStunChance = Stats.MasteryStunChance.GetPersistent(gameConfiguration);
|
||||
var isMaceEquipped = Stats.IsMaceEquipped.GetPersistent(gameConfiguration);
|
||||
var doubleDamageChance = Stats.DoubleDamageChance.GetPersistent(gameConfiguration);
|
||||
var isSpearEquipped = Stats.IsSpearEquipped.GetPersistent(gameConfiguration);
|
||||
|
||||
gameConfiguration.CharacterClasses.ForEach(charClass =>
|
||||
{
|
||||
// Update common combination attribute
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.PhysicalBaseDmg && attrCombo.InputAttribute == Stats.BaseDamageBonus) is { } baseDmgBonusToPhysicalBaseDmg)
|
||||
{
|
||||
baseDmgBonusToPhysicalBaseDmg.AggregateType = AggregateType.AddFinal;
|
||||
}
|
||||
|
||||
// Add new attribute combinations
|
||||
var swellLifeHealthIncreaseToMaxHealth = context.CreateNew<AttributeRelationship>(
|
||||
maximumHealth,
|
||||
1,
|
||||
swellLifeHealthIncrease,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate);
|
||||
|
||||
var swellLifeManaIncreaseToMaxMana = context.CreateNew<AttributeRelationship>(
|
||||
maximumMana,
|
||||
1,
|
||||
swellLifeManaIncrease,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate);
|
||||
|
||||
charClass.AttributeCombinations.Add(swellLifeHealthIncreaseToMaxHealth);
|
||||
charClass.AttributeCombinations.Add(swellLifeManaIncreaseToMaxMana);
|
||||
charClass.BaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(1, swellLifeHealthIncrease));
|
||||
charClass.BaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(1, swellLifeManaIncrease));
|
||||
charClass.BaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(0.1f, durabilityReductionFactor));
|
||||
|
||||
// Update/add double wield attribute combinations
|
||||
if (charClass.Number == 4 || charClass.Number == 6 || charClass.Number == 7 // DK classes
|
||||
|| charClass.Number == 12 || charClass.Number == 13 // MG classes
|
||||
|| charClass.Number == 24 || charClass.Number == 25) // RF classes
|
||||
{
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.InputOperand == -0.45f) is { } hasDoubleWieldToTempDoubleWieldMultiplier)
|
||||
{
|
||||
charClass.AttributeCombinations.Remove(hasDoubleWieldToTempDoubleWieldMultiplier);
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.PhysicalBaseDmgIncrease
|
||||
&& obsoleteTempDoubleWieldMultipliers.Contains(attrCombo.InputAttribute)) is { } tempDoubleWieldToPhysicalBaseDmgIncrease)
|
||||
{
|
||||
tempDoubleWieldToPhysicalBaseDmgIncrease.InputAttribute = hasDoubleWield;
|
||||
tempDoubleWieldToPhysicalBaseDmgIncrease.InputOperand = 0.55f;
|
||||
tempDoubleWieldToPhysicalBaseDmgIncrease.InputOperator = InputOperator.ExponentiateByAttribute;
|
||||
}
|
||||
|
||||
var hasDoubleWieldToMinimumPhysBaseDmgByWeapon = context.CreateNew<AttributeRelationship>(
|
||||
minimumPhysBaseDmgByWeapon,
|
||||
0.5f,
|
||||
hasDoubleWield,
|
||||
InputOperator.ExponentiateByAttribute,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate);
|
||||
|
||||
var hasDoubleWieldToMaximumPhysBaseDmgByWeapon = context.CreateNew<AttributeRelationship>(
|
||||
maximumPhysBaseDmgByWeapon,
|
||||
0.5f,
|
||||
hasDoubleWield,
|
||||
InputOperator.ExponentiateByAttribute,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate);
|
||||
|
||||
var hasDoubleWieldToPhysicalBaseDmg = context.CreateNew<AttributeRelationship>(
|
||||
physicalBaseDmg,
|
||||
0.5f,
|
||||
hasDoubleWield,
|
||||
InputOperator.ExponentiateByAttribute,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate);
|
||||
|
||||
var hasDoubleWieldToPhysicalBaseDmgIncrease = context.CreateNew<AttributeRelationship>(
|
||||
physicalBaseDmgIncrease,
|
||||
0.5f,
|
||||
hasDoubleWield,
|
||||
InputOperator.ExponentiateByAttribute,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate);
|
||||
|
||||
var hasDoubleWieldToPhysicalBaseDmgIncreaseRaw = context.CreateNew<AttributeRelationship>(
|
||||
physicalBaseDmgIncrease,
|
||||
1,
|
||||
hasDoubleWield,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw);
|
||||
|
||||
charClass.AttributeCombinations.Add(hasDoubleWieldToMinimumPhysBaseDmgByWeapon);
|
||||
charClass.AttributeCombinations.Add(hasDoubleWieldToMaximumPhysBaseDmgByWeapon);
|
||||
charClass.AttributeCombinations.Add(hasDoubleWieldToPhysicalBaseDmg);
|
||||
charClass.AttributeCombinations.Add(hasDoubleWieldToPhysicalBaseDmgIncrease);
|
||||
charClass.AttributeCombinations.Add(hasDoubleWieldToPhysicalBaseDmgIncreaseRaw);
|
||||
|
||||
if (charClass.Number == 4 || charClass.Number == 6 || charClass.Number == 7)
|
||||
{
|
||||
var masteryStunChanceToMaceMasteryStunChance = context.CreateNew<AttributeRelationship>(
|
||||
maceMasteryStunChance,
|
||||
isMaceEquipped,
|
||||
masteryStunChance,
|
||||
AggregateType.AddRaw);
|
||||
|
||||
var spearMasteryDoubleDamageChanceToDoubleDamageChance = context.CreateNew<AttributeRelationship>(
|
||||
doubleDamageChance,
|
||||
isSpearEquipped,
|
||||
spearMasteryDoubleDamageChance,
|
||||
AggregateType.AddRaw);
|
||||
|
||||
charClass.AttributeCombinations.Add(masteryStunChanceToMaceMasteryStunChance);
|
||||
charClass.AttributeCombinations.Add(spearMasteryDoubleDamageChanceToDoubleDamageChance);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Removed obsolete attributes
|
||||
foreach (var obsoleteTempDoubleWield in obsoleteTempDoubleWieldMultipliers.ToList())
|
||||
{
|
||||
gameConfiguration.Attributes.Remove(obsoleteTempDoubleWield);
|
||||
}
|
||||
|
||||
// Update wings physical base dmg option
|
||||
var wingsSlot = gameConfiguration.ItemSlotTypes.First(st => st.ItemSlots.Contains(InventoryConstants.WingsSlot));
|
||||
foreach (var wing in gameConfiguration.Items.Where(i => i.ItemSlot == wingsSlot))
|
||||
{
|
||||
if (wing.PossibleItemOptions.SelectMany(pio => pio.PossibleOptions).FirstOrDefault(po => po.PowerUpDefinition?.TargetAttribute == physicalBaseDmg) is { } pio)
|
||||
{
|
||||
foreach (var levelOption in pio.LevelDependentOptions)
|
||||
{
|
||||
levelOption.PowerUpDefinition!.Boost!.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
// <copyright file="FinishDarkKnightMasterTreePlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update completes the dark knight master tree skills and effects. It also fixes the double wield damage calculations.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("F7B2C9E4-1A3D-56F8-9B0C-4E2D7A1F8B3C")]
|
||||
public class FinishDarkKnightMasterTreePlugInSeason6 : FinishDarkKnightMasterTreePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal new const string PlugInName = "Finish Dark Knight Master Tree PlugIn";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal new const string PlugInDescription = "This update completes the dark knight master tree skills and effects. It also fixes the double wield damage calculations.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FinishDarkKnightMasterTreeSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
|
||||
var masteryStunChance = Stats.MasteryStunChance.GetPersistent(gameConfiguration);
|
||||
var ragefulBlowMasteryDurabilityDecChance = Stats.RagefulBlowMasteryDurabilityDecChance.GetPersistent(gameConfiguration);
|
||||
var spearMasteryDoubleDamageChance = Stats.SpearMasteryDoubleDamageChance.GetPersistent(gameConfiguration);
|
||||
var swellLifeHealthIncrease = Stats.SwellLifeHealthIncrease.GetPersistent(gameConfiguration);
|
||||
var swellLifeManaIncrease = Stats.SwellLifeManaIncrease.GetPersistent(gameConfiguration);
|
||||
var physicalBaseDmg = Stats.PhysicalBaseDmg.GetPersistent(gameConfiguration);
|
||||
|
||||
// Update Life Swell effect
|
||||
var lifeSwellEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.GreaterFortitude);
|
||||
lifeSwellEffect.SubType = 4;
|
||||
lifeSwellEffect.Duration!.MaximumValue = 180;
|
||||
|
||||
if (lifeSwellEffect.Duration.RelatedValues.FirstOrDefault() is { } durationPerEnergy)
|
||||
{
|
||||
durationPerEnergy.InputOperand = 1f / 180f;
|
||||
}
|
||||
|
||||
if (lifeSwellEffect.PowerUpDefinitions.FirstOrDefault() is { } maxHealth)
|
||||
{
|
||||
maxHealth.TargetAttribute = swellLifeHealthIncrease;
|
||||
maxHealth.Boost!.ConstantValue.Value = 0.12f;
|
||||
maxHealth.Boost.ConstantValue.AggregateType = AggregateType.AddRaw;
|
||||
maxHealth.Boost.MaximumValue = 2f;
|
||||
|
||||
foreach (var boostRelatedValue in maxHealth.Boost.RelatedValues)
|
||||
{
|
||||
if (boostRelatedValue.InputAttribute == Stats.TotalEnergy)
|
||||
{
|
||||
boostRelatedValue.InputOperator = InputOperator.Multiply;
|
||||
boostRelatedValue.InputOperand = 1f / 2000;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (boostRelatedValue.InputAttribute == Stats.TotalVitality)
|
||||
{
|
||||
boostRelatedValue.InputOperator = InputOperator.Multiply;
|
||||
boostRelatedValue.InputOperand = 1f / 10000;
|
||||
}
|
||||
}
|
||||
|
||||
var boostPerPartyMember = context.CreateNew<AttributeRelationship>();
|
||||
boostPerPartyMember.InputAttribute = Stats.NearbyPartyMemberCount.GetPersistent(gameConfiguration);
|
||||
boostPerPartyMember.InputOperator = InputOperator.Multiply;
|
||||
boostPerPartyMember.InputOperand = 1f / 100;
|
||||
maxHealth.Boost.RelatedValues.Add(boostPerPartyMember);
|
||||
}
|
||||
|
||||
// Create Life Swell Proficiency Skill Effect
|
||||
var lifeSwellProficiencyEffect = this.CreateLifeSwellProficiencyEffect(context, gameConfiguration);
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.SwellLifeProficiency) is { } skill)
|
||||
{
|
||||
skill.MagicEffectDef = lifeSwellProficiencyEffect;
|
||||
}
|
||||
|
||||
// Restore iced effect (revert bug)
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(e => e.Number == (short)MagicEffectNumber.Iced && e.Chance is { }) is { } originalIced)
|
||||
{
|
||||
originalIced.Chance = null;
|
||||
}
|
||||
|
||||
// Remove existing cold effect
|
||||
var existingColdEffect = gameConfiguration.MagicEffects.FirstOrDefault(e => e.Number == (short)MagicEffectNumber.Cold);
|
||||
if (existingColdEffect is not null)
|
||||
{
|
||||
gameConfiguration.MagicEffects.Remove(existingColdEffect);
|
||||
}
|
||||
|
||||
// Create chain drive cold effect
|
||||
var chainDriveCold = this.CreateEffect(context, gameConfiguration, ElementalType.Ice, MagicEffectNumber.Cold, Stats.IsIced, 10, 0.4f);
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainDrive) is { } chainDrive)
|
||||
{
|
||||
chainDrive.MagicEffectDef = chainDriveCold;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainDriveStrengthener) is { } chainDriveStrengthener)
|
||||
{
|
||||
chainDriveStrengthener.MagicEffectDef = chainDriveCold;
|
||||
}
|
||||
|
||||
// Create strike of destruction cold effect
|
||||
var strikeOfDestructCold = this.CreateEffect(context, gameConfiguration, ElementalType.Ice, MagicEffectNumber.Cold, Stats.IsIced, 10);
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.StrikeofDestruction) is { } strikeOfDestruction)
|
||||
{
|
||||
strikeOfDestruction.SkipElementalModifier = true;
|
||||
strikeOfDestruction.MagicEffectDef = strikeOfDestructCold;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.StrikeofDestrStr) is { } strikeOfDestrStr)
|
||||
{
|
||||
strikeOfDestrStr.SkipElementalModifier = true;
|
||||
strikeOfDestrStr.MagicEffectDef = strikeOfDestructCold;
|
||||
}
|
||||
|
||||
// Update harmony option
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(o => o.Name == "Harmony Physical Attack Options") is { } harmonyPhysAttackOptions
|
||||
&& harmonyPhysAttackOptions.PossibleOptions.FirstOrDefault(o => o.Number == 5) is { } physBaseDmgOpt)
|
||||
{
|
||||
foreach (var level in physBaseDmgOpt.LevelDependentOptions)
|
||||
{
|
||||
level.PowerUpDefinition!.Boost!.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
}
|
||||
}
|
||||
|
||||
// Update gold fenrir option
|
||||
var goldFenrirOptionId = new Guid("00000083-0081-0000-0000-000000000000");
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(o => o.GetId() == goldFenrirOptionId) is { } goldFenrirOption)
|
||||
{
|
||||
foreach (var option in goldFenrirOption.PossibleOptions)
|
||||
{
|
||||
if (option.PowerUpDefinition?.TargetAttribute == physicalBaseDmg)
|
||||
{
|
||||
option.PowerUpDefinition.Boost!.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update master skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.TwistingSlashMastery)?.MasterDefinition is { } twistingSlashMastery)
|
||||
{
|
||||
twistingSlashMastery.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.TwistingSlashStreng);
|
||||
twistingSlashMastery.TargetAttribute = Stats.MasteryMoveTargetChance.GetPersistent(gameConfiguration);
|
||||
twistingSlashMastery.Aggregation = AggregateType.AddRaw;
|
||||
twistingSlashMastery.ValueFormula = $"{twistingSlashMastery.ValueFormula} / 100";
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.RagefulBlowMastery) is { } ragefulBlowMastery)
|
||||
{
|
||||
ragefulBlowMastery.AttributeRelationships.Add(context.CreateNew<AttributeRelationship>(
|
||||
ragefulBlowMasteryDurabilityDecChance,
|
||||
1,
|
||||
ragefulBlowMasteryDurabilityDecChance,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw));
|
||||
|
||||
if (ragefulBlowMastery.MasterDefinition is { } masterDefinition)
|
||||
{
|
||||
masterDefinition.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.RagefulBlowStreng);
|
||||
masterDefinition.TargetAttribute = ragefulBlowMasteryDurabilityDecChance;
|
||||
masterDefinition.Aggregation = AggregateType.AddRaw;
|
||||
masterDefinition.ValueFormula = $"{masterDefinition.ValueFormula} / 100";
|
||||
}
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.MaceMastery)?.MasterDefinition is { } maceMastery)
|
||||
{
|
||||
maceMastery.TargetAttribute = masteryStunChance;
|
||||
maceMastery.Aggregation = AggregateType.AddRaw;
|
||||
maceMastery.ValueFormula = $"{maceMastery.ValueFormula} / 100";
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.SpearMastery)?.MasterDefinition is { } spearMastery)
|
||||
{
|
||||
spearMastery.TargetAttribute = spearMasteryDoubleDamageChance;
|
||||
spearMastery.Aggregation = AggregateType.AddRaw;
|
||||
spearMastery.ValueFormula = $"{spearMastery.ValueFormula} / 100";
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.SwellLifeStrengt)?.MasterDefinition is { } swellLifeStrengt)
|
||||
{
|
||||
swellLifeStrengt.TargetAttribute = swellLifeHealthIncrease;
|
||||
swellLifeStrengt.Aggregation = AggregateType.AddRaw;
|
||||
swellLifeStrengt.ValueFormula = $"{swellLifeStrengt.ValueFormula} / 100";
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.SwellLifeProficiency)?.MasterDefinition is { } swellLifeProficiency)
|
||||
{
|
||||
swellLifeProficiency.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.SwellLifeStrengt);
|
||||
swellLifeProficiency.TargetAttribute = swellLifeManaIncrease;
|
||||
swellLifeProficiency.Aggregation = AggregateType.AddRaw;
|
||||
swellLifeProficiency.ValueFormula = $"{swellLifeProficiency.ValueFormula} / 100";
|
||||
}
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateLifeSwellProficiencyEffect(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (byte)MagicEffectNumber.GreaterFortitudeProficiency;
|
||||
magicEffect.Name = "Life Swell Proficiency Skill Effect";
|
||||
|
||||
var lifeSwellEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.GreaterFortitude);
|
||||
magicEffect.InformObservers = lifeSwellEffect.InformObservers;
|
||||
magicEffect.SubType = lifeSwellEffect.SubType;
|
||||
magicEffect.SendDuration = lifeSwellEffect.SendDuration;
|
||||
magicEffect.StopByDeath = lifeSwellEffect.StopByDeath;
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = lifeSwellEffect.Duration!.ConstantValue.Value;
|
||||
magicEffect.Duration.MaximumValue = lifeSwellEffect.Duration.MaximumValue;
|
||||
|
||||
foreach (var durationRelatedValue in lifeSwellEffect.Duration.RelatedValues)
|
||||
{
|
||||
var durationRelatedValueCopy = context.CreateNew<AttributeRelationship>();
|
||||
durationRelatedValueCopy.InputAttribute = durationRelatedValue.InputAttribute!.GetPersistent(gameConfiguration);
|
||||
durationRelatedValueCopy.InputOperator = durationRelatedValue.InputOperator;
|
||||
durationRelatedValueCopy.InputOperand = durationRelatedValue.InputOperand;
|
||||
magicEffect.Duration.RelatedValues.Add(durationRelatedValueCopy);
|
||||
}
|
||||
|
||||
foreach (var powerUp in lifeSwellEffect.PowerUpDefinitions)
|
||||
{
|
||||
var powerUpCopy = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(powerUpCopy);
|
||||
powerUpCopy.TargetAttribute = powerUp.TargetAttribute!.GetPersistent(gameConfiguration);
|
||||
powerUpCopy.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUpCopy.Boost.ConstantValue.Value = powerUp.Boost!.ConstantValue.Value;
|
||||
powerUpCopy.Boost.MaximumValue = powerUp.Boost.MaximumValue;
|
||||
|
||||
foreach (var boostRelatedValue in powerUp.Boost.RelatedValues)
|
||||
{
|
||||
var boostRelatedValueCopy = context.CreateNew<AttributeRelationship>();
|
||||
boostRelatedValueCopy.InputAttribute = boostRelatedValue.InputAttribute!.GetPersistent(gameConfiguration);
|
||||
boostRelatedValueCopy.InputOperator = boostRelatedValue.InputOperator;
|
||||
boostRelatedValueCopy.InputOperand = boostRelatedValue.InputOperand;
|
||||
powerUpCopy.Boost.RelatedValues.Add(boostRelatedValueCopy);
|
||||
}
|
||||
}
|
||||
|
||||
// one percent per party member in view
|
||||
var boostPerPartyMember = context.CreateNew<AttributeRelationship>();
|
||||
boostPerPartyMember.InputAttribute = Stats.NearbyPartyMemberCount.GetPersistent(gameConfiguration);
|
||||
boostPerPartyMember.InputOperator = InputOperator.Multiply;
|
||||
boostPerPartyMember.InputOperand = 1f / 100;
|
||||
|
||||
var manaPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(manaPowerUpDefinition);
|
||||
manaPowerUpDefinition.TargetAttribute = Stats.SwellLifeManaIncrease.GetPersistent(gameConfiguration);
|
||||
manaPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
manaPowerUpDefinition.Boost.RelatedValues.Add(boostPerPartyMember);
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateEffect(IContext context, GameConfiguration gameConfiguration, ElementalType type, MagicEffectNumber effectNumber, AttributeDefinition targetAttribute, float durationInSeconds, float chance = 0)
|
||||
{
|
||||
var effect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(effect);
|
||||
effect.Name = Enum.GetName(effectNumber) ?? string.Empty;
|
||||
effect.InformObservers = true;
|
||||
effect.Number = (short)effectNumber;
|
||||
effect.StopByDeath = true;
|
||||
effect.SubType = (byte)(0xFF - type);
|
||||
effect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
effect.Duration.ConstantValue.Value = durationInSeconds;
|
||||
var powerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
effect.PowerUpDefinitions.Add(powerUpDefinition);
|
||||
powerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUpDefinition.Boost.ConstantValue.Value = 1;
|
||||
powerUpDefinition.TargetAttribute = targetAttribute.GetPersistent(gameConfiguration);
|
||||
if (targetAttribute == Stats.IsIced)
|
||||
{
|
||||
var movementSpeedFactorPowerUp = context.CreateNew<PowerUpDefinition>();
|
||||
effect.PowerUpDefinitions.Add(movementSpeedFactorPowerUp);
|
||||
movementSpeedFactorPowerUp.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
movementSpeedFactorPowerUp.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
|
||||
movementSpeedFactorPowerUp.TargetAttribute = Stats.MovementSpeedFactor.GetPersistent(gameConfiguration);
|
||||
|
||||
if (effectNumber == MagicEffectNumber.Cold)
|
||||
{
|
||||
movementSpeedFactorPowerUp.Boost.ConstantValue.Value = MovementSpeedConstants.ColdMovementSpeedFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
movementSpeedFactorPowerUp.Boost.ConstantValue.Value = MovementSpeedConstants.IcedMovementSpeedFactor;
|
||||
}
|
||||
}
|
||||
|
||||
if (chance > 0)
|
||||
{
|
||||
effect.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
effect.Chance.ConstantValue.Value = chance;
|
||||
}
|
||||
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
// <copyright file="FinishDarkLordMasterTreePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update completes the dark lord master tree skills and effects.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("1A2B3C4D-5E6F-7890-ABCD-EF1234567890")]
|
||||
public class FinishDarkLordMasterTreePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Finish Dark Lord Master Tree PlugIn";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update completes the dark lord master tree skills and effects.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FinishDarkLordMasterTree;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 4, 14, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Create Critical Damage Increase Mastery Skill Effect
|
||||
var critDmgIncMasteryEffect = this.CreateCritDmgIncMasteryEffect(context, gameConfiguration);
|
||||
|
||||
// Update Stunned effect
|
||||
var stunnedEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.Stunned);
|
||||
stunnedEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
stunnedEffect.Duration.ConstantValue.Value = 2;
|
||||
|
||||
var stunChancePowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
stunnedEffect.PowerUpDefinitions.Add(stunChancePowerUpDefinition);
|
||||
stunChancePowerUpDefinition.TargetAttribute = Stats.MasteryStunChance.GetPersistent(gameConfiguration);
|
||||
stunChancePowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
stunChancePowerUpDefinition.Boost.ConstantValue.Value = 0;
|
||||
|
||||
// Map skills to effects
|
||||
this.MapSkillToEffect(gameConfiguration, SkillNumber.FireBurstMastery, stunnedEffect);
|
||||
this.MapSkillToEffect(gameConfiguration, SkillNumber.EarthshakeMastery, stunnedEffect);
|
||||
this.MapSkillToEffect(gameConfiguration, SkillNumber.CritDmgIncPowUp3, critDmgIncMasteryEffect);
|
||||
|
||||
// Update AreaSkillSettings
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Earthshake, false, 0, 0, 0, useTargetAreaFilter: true, targetAreaDiameter: 10, minimumHitsPerAttack: 9, maximumHitsPerAttack: 15);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.EarthshakeStreng, false, 0, 0, 0, useTargetAreaFilter: true, targetAreaDiameter: 10, minimumHitsPerAttack: 9, maximumHitsPerAttack: 15);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.EarthshakeMastery, false, 0, 0, 0, useTargetAreaFilter: true, targetAreaDiameter: 10, minimumHitsPerAttack: 9, maximumHitsPerAttack: 15);
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChaoticDiseier) is { } chaoticDiseier)
|
||||
{
|
||||
chaoticDiseier.AreaSkillSettings!.MinimumNumberOfHitsPerAttack = 7;
|
||||
}
|
||||
|
||||
// Update master skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.CriticalDmgIncPowUp)?.MasterDefinition is { } fireTomeMastery)
|
||||
{
|
||||
fireTomeMastery.TargetAttribute = Stats.CriticalDamageBonus.GetPersistent(gameConfiguration);
|
||||
fireTomeMastery.Aggregation = AggregateType.AddRaw;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.FireBurstMastery)?.MasterDefinition is { } fireBurstMastery)
|
||||
{
|
||||
fireBurstMastery.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.FireBurstStreng);
|
||||
fireBurstMastery.TargetAttribute = Stats.MasteryStunChance.GetPersistent(gameConfiguration);
|
||||
fireBurstMastery.Aggregation = AggregateType.AddRaw;
|
||||
fireBurstMastery.ValueFormula = $"{fireBurstMastery.ValueFormula} / 100";
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.CritDmgIncPowUp2)?.MasterDefinition is { } critDmgIncPowUp2)
|
||||
{
|
||||
critDmgIncPowUp2.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.CriticalDmgIncPowUp);
|
||||
critDmgIncPowUp2.ExtendsDuration = true;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.EarthshakeMastery)?.MasterDefinition is { } earthshakeMastery)
|
||||
{
|
||||
earthshakeMastery.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.EarthshakeStreng);
|
||||
earthshakeMastery.TargetAttribute = Stats.MasteryStunChance.GetPersistent(gameConfiguration);
|
||||
earthshakeMastery.Aggregation = AggregateType.AddRaw;
|
||||
earthshakeMastery.ValueFormula = $"{earthshakeMastery.ValueFormula} / 100";
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.CritDmgIncPowUp3)?.MasterDefinition is { } critDmgIncPowUp3)
|
||||
{
|
||||
critDmgIncPowUp3.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.CritDmgIncPowUp2);
|
||||
critDmgIncPowUp3.TargetAttribute = Stats.CriticalDamageChance.GetPersistent(gameConfiguration);
|
||||
critDmgIncPowUp3.Aggregation = AggregateType.AddRaw;
|
||||
critDmgIncPowUp3.ValueFormula = $"{critDmgIncPowUp3.ValueFormula} / 100";
|
||||
}
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateCritDmgIncMasteryEffect(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (byte)MagicEffectNumber.CriticalDamageIncreaseMastery;
|
||||
magicEffect.Name = "Critical Damage Increase Mastery Skill Effect";
|
||||
|
||||
var critDmgIncEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.CriticalDamageIncrease);
|
||||
critDmgIncEffect.Duration?.MaximumValue = 180;
|
||||
critDmgIncEffect.SubType = 17;
|
||||
magicEffect.InformObservers = critDmgIncEffect.InformObservers;
|
||||
magicEffect.SubType = critDmgIncEffect.SubType;
|
||||
magicEffect.SendDuration = critDmgIncEffect.SendDuration;
|
||||
magicEffect.StopByDeath = critDmgIncEffect.StopByDeath;
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = critDmgIncEffect.Duration!.ConstantValue.Value;
|
||||
magicEffect.Duration.MaximumValue = critDmgIncEffect.Duration.MaximumValue;
|
||||
|
||||
foreach (var durationRelatedValue in critDmgIncEffect.Duration.RelatedValues)
|
||||
{
|
||||
var durationRelatedValueCopy = context.CreateNew<AttributeRelationship>();
|
||||
durationRelatedValueCopy.InputAttribute = durationRelatedValue.InputAttribute!.GetPersistent(gameConfiguration);
|
||||
durationRelatedValueCopy.InputOperator = durationRelatedValue.InputOperator;
|
||||
durationRelatedValueCopy.InputOperand = durationRelatedValue.InputOperand;
|
||||
magicEffect.Duration.RelatedValues.Add(durationRelatedValueCopy);
|
||||
}
|
||||
|
||||
foreach (var powerUp in critDmgIncEffect.PowerUpDefinitions)
|
||||
{
|
||||
var powerUpCopy = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(powerUpCopy);
|
||||
powerUpCopy.TargetAttribute = powerUp.TargetAttribute!.GetPersistent(gameConfiguration);
|
||||
powerUpCopy.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUpCopy.Boost.ConstantValue.Value = powerUp.Boost!.ConstantValue.Value;
|
||||
|
||||
foreach (var boostRelatedValue in powerUp.Boost.RelatedValues)
|
||||
{
|
||||
var boostRelatedValueCopy = context.CreateNew<AttributeRelationship>();
|
||||
boostRelatedValueCopy.InputAttribute = boostRelatedValue.InputAttribute!.GetPersistent(gameConfiguration);
|
||||
boostRelatedValueCopy.InputOperator = boostRelatedValue.InputOperator;
|
||||
boostRelatedValueCopy.InputOperand = boostRelatedValue.InputOperand;
|
||||
powerUpCopy.Boost.RelatedValues.Add(boostRelatedValueCopy);
|
||||
}
|
||||
}
|
||||
|
||||
var critChancePowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(critChancePowerUpDefinition);
|
||||
critChancePowerUpDefinition.TargetAttribute = Stats.CriticalDamageChance.GetPersistent(gameConfiguration);
|
||||
critChancePowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
critChancePowerUpDefinition.Boost.ConstantValue.Value = 0;
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
|
||||
private void MapSkillToEffect(GameConfiguration gameConfiguration, SkillNumber skillNumber, MagicEffectDefinition magicEffect)
|
||||
{
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)skillNumber) is { } skill)
|
||||
{
|
||||
skill.MagicEffectDef = magicEffect;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAreaSkillSettings(
|
||||
GameConfiguration gameConfiguration,
|
||||
IContext context,
|
||||
SkillNumber skillNumber,
|
||||
bool useFrustumFilter,
|
||||
float frustumStartWidth,
|
||||
float frustumEndWidth,
|
||||
float frustumDistance,
|
||||
bool useDeferredHits = false,
|
||||
TimeSpan delayPerOneDistance = default,
|
||||
TimeSpan delayBetweenHits = default,
|
||||
int minimumHitsPerTarget = 1,
|
||||
int maximumHitsPerTarget = 1,
|
||||
int minimumHitsPerAttack = default,
|
||||
int maximumHitsPerAttack = default,
|
||||
float hitChancePerDistanceMultiplier = 1.0f,
|
||||
bool useTargetAreaFilter = false,
|
||||
float targetAreaDiameter = default,
|
||||
int projectileCount = 1,
|
||||
int effectRange = default)
|
||||
{
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)skillNumber) is not { } skill)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
skill.SkillType = SkillType.AreaSkillAutomaticHits;
|
||||
var areaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
skill.AreaSkillSettings = areaSkillSettings;
|
||||
|
||||
areaSkillSettings.UseFrustumFilter = useFrustumFilter;
|
||||
areaSkillSettings.FrustumStartWidth = frustumStartWidth;
|
||||
areaSkillSettings.FrustumEndWidth = frustumEndWidth;
|
||||
areaSkillSettings.FrustumDistance = frustumDistance;
|
||||
areaSkillSettings.UseTargetAreaFilter = useTargetAreaFilter;
|
||||
areaSkillSettings.TargetAreaDiameter = targetAreaDiameter;
|
||||
areaSkillSettings.UseDeferredHits = useDeferredHits;
|
||||
areaSkillSettings.DelayPerOneDistance = delayPerOneDistance;
|
||||
areaSkillSettings.DelayBetweenHits = delayBetweenHits;
|
||||
areaSkillSettings.MinimumNumberOfHitsPerTarget = minimumHitsPerTarget;
|
||||
areaSkillSettings.MaximumNumberOfHitsPerTarget = maximumHitsPerTarget;
|
||||
areaSkillSettings.MinimumNumberOfHitsPerAttack = minimumHitsPerAttack;
|
||||
areaSkillSettings.MaximumNumberOfHitsPerAttack = maximumHitsPerAttack;
|
||||
areaSkillSettings.HitChancePerDistanceMultiplier = hitChancePerDistanceMultiplier;
|
||||
areaSkillSettings.ProjectileCount = projectileCount;
|
||||
areaSkillSettings.EffectRange = effectRange;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// <copyright file="FixAncientDiscriminatorsUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the discriminators of some ancient items.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("AB664421-1CA6-4FCE-A150-0007971017E1")]
|
||||
public class FixAncientDiscriminatorsUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Warrior Morning Star";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes the discriminators of some ancient items.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixAncientDiscriminators;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 08, 25, 17, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
ChangeDiscriminator(gameConfiguration, "Anonymous", ItemGroups.Shields, 0, 1);
|
||||
|
||||
ChangeDiscriminator(gameConfiguration, "Mist", ItemGroups.Gloves, 0, 1);
|
||||
ChangeDiscriminator(gameConfiguration, "Mist", ItemGroups.Helm, 0, 1);
|
||||
|
||||
ChangeDiscriminator(gameConfiguration, "Berserker", ItemGroups.Gloves, 6, 1);
|
||||
ChangeDiscriminator(gameConfiguration, "Berserker", ItemGroups.Boots, 6, 1);
|
||||
|
||||
ChangeDiscriminator(gameConfiguration, "Cloud", ItemGroups.Helm, 8, 1);
|
||||
|
||||
ChangeDiscriminator(gameConfiguration, "Rave", ItemGroups.Helm, 9, 1);
|
||||
ChangeDiscriminator(gameConfiguration, "Rave", ItemGroups.Pants, 9, 1);
|
||||
|
||||
ChangeDiscriminator(gameConfiguration, "Barnake", ItemGroups.Boots, 2, 1);
|
||||
|
||||
ChangeDiscriminator(gameConfiguration, "Sylion", ItemGroups.Gloves, 4, 1);
|
||||
ChangeDiscriminator(gameConfiguration, "Sylion", ItemGroups.Helm, 4, 1);
|
||||
|
||||
ChangeDiscriminator(gameConfiguration, "Drake", ItemGroups.Armor, 10, 1);
|
||||
|
||||
ChangeDiscriminator(gameConfiguration, "Fase", ItemGroups.Boots, 11, 1);
|
||||
}
|
||||
|
||||
private static void ChangeDiscriminator(GameConfiguration gameConfiguration, string setName, ItemGroups itemGroup, byte itemNumber, byte discriminator)
|
||||
{
|
||||
var itemSetGroup = gameConfiguration.ItemSetGroups.First(set => set.Name == setName);
|
||||
var itemOfItemSet = itemSetGroup.Items.FirstOrDefault(item => item.ItemDefinition?.Group == (byte)itemGroup && item.ItemDefinition?.Number == itemNumber);
|
||||
if (itemOfItemSet != null)
|
||||
{
|
||||
itemOfItemSet.AncientSetDiscriminator = discriminator;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// <copyright file="FixAreaSkillsUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes area skills' range, effect radius, and delay issues.
|
||||
/// Hellfire, Decay, and Ice Storm had missing area skill settings causing incorrect hit radius.
|
||||
/// Ice Storm had an incorrect 200ms delay between hits.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("B9938E4D-8F63-48DF-AE45-6739D1E2A8C7")]
|
||||
public class FixAreaSkillsUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Area Skills";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Fixes Hellfire, Decay, and Ice Storm skills' range, effect radius, and delay.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixAreaSkills;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 04, 09, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Fix Hellfire Strengthener
|
||||
var hellfireStrengthener = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.HellfireStrengthener);
|
||||
if (hellfireStrengthener != null)
|
||||
{
|
||||
hellfireStrengthener.Range = 4;
|
||||
hellfireStrengthener.AttackDamage = 3;
|
||||
|
||||
if (hellfireStrengthener.AreaSkillSettings == null)
|
||||
{
|
||||
var areaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
hellfireStrengthener.AreaSkillSettings = areaSkillSettings;
|
||||
areaSkillSettings.EffectRange = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Decay Strengthener
|
||||
var decayStrengthener = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DecayStrengthener);
|
||||
if (decayStrengthener != null)
|
||||
{
|
||||
decayStrengthener.Range = 6;
|
||||
|
||||
if (decayStrengthener.AreaSkillSettings == null)
|
||||
{
|
||||
var areaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
decayStrengthener.AreaSkillSettings = areaSkillSettings;
|
||||
areaSkillSettings.EffectRange = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix base Decay skill
|
||||
var decay = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Decay);
|
||||
if (decay != null)
|
||||
{
|
||||
if (decay.AreaSkillSettings == null)
|
||||
{
|
||||
var areaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
decay.AreaSkillSettings = areaSkillSettings;
|
||||
areaSkillSettings.EffectRange = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Ice Storm - remove incorrect 200ms delay
|
||||
var iceStorm = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.IceStorm);
|
||||
if (iceStorm?.AreaSkillSettings != null)
|
||||
{
|
||||
iceStorm.AreaSkillSettings.DelayBetweenHits = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
// <copyright file="FixAttackSpeedCalculationUpdate.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
using static MUnique.OpenMU.Persistence.Initialization.CharacterClasses.CharacterClassHelper;
|
||||
|
||||
/// <summary>
|
||||
/// This adds attributes and relations for attack speed. Adds effects for Ale and Potion of Soul.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("F9977AA7-F52A-4F42-BD6C-98DE700B5980")]
|
||||
public class FixAttackSpeedCalculationUpdate : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Attack Speed Calculation";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Adds attributes and relations for attack speed. Adds effects for Ale and Potion of Soul.";
|
||||
|
||||
private static readonly Dictionary<int, int> AttackSpeedByGloveNumber = new()
|
||||
{
|
||||
{ 0, 4 }, // Bronze Gloves
|
||||
{ 1, 6 }, // Dragon Gloves
|
||||
{ 5, 8 }, // Leather Gloves
|
||||
{ 6, 10 }, // Scale Gloves
|
||||
{ 8, 8 }, // Brass Gloves
|
||||
{ 9, 4 }, // Plate Gloves
|
||||
{ 10, 4 }, // Vine Gloves
|
||||
{ 11, 8 }, // Silk Gloves
|
||||
{ 12, 10 }, // Wind Gloves
|
||||
{ 13, 4 }, // Spirit Gloves
|
||||
{ 14, 6 }, // Guardian Gloves
|
||||
{ 15, 6 }, // Storm Crow Gloves
|
||||
{ 16, 6 }, // Black Dragon Gloves
|
||||
{ 17, 6 }, // Dark Phoenix Gloves
|
||||
{ 18, 5 }, // Grand Soul Gloves
|
||||
{ 19, 6 }, // Divine Gloves
|
||||
{ 20, 7 }, // Thunder Hawk Gloves
|
||||
{ 21, 6 }, // Great Dragon Gloves
|
||||
{ 22, 6 }, // Dark Soul Gloves
|
||||
{ 23, 7 }, // Hurricane Gloves
|
||||
{ 24, 6 }, // Red Spirit Gloves
|
||||
{ 25, 7 }, // Light Plate Gloves
|
||||
{ 26, 6 }, // Adamantine Gloves
|
||||
{ 27, 5 }, // Dark Steel Gloves
|
||||
{ 28, 4 }, // Dark Master Gloves
|
||||
{ 29, 7 }, // Dragon Knight Gloves
|
||||
{ 30, 7 }, // Venom Mist Gloves
|
||||
{ 31, 7 }, // Sylphid Ray Gloves
|
||||
{ 32, 7 }, // Volcano Gloves
|
||||
{ 33, 5 }, // Sunlight Gloves
|
||||
{ 34, 6 }, // Ashcrow Gloves
|
||||
{ 35, 6 }, // Eclipse Gloves
|
||||
{ 36, 6 }, // Iris Gloves
|
||||
{ 37, 7 }, // Valiant Gloves
|
||||
{ 38, 5 }, // Glorious Gloves
|
||||
{ 39, 6 }, // Violent Wind Gloves
|
||||
{ 40, 8 }, // Red Wing Gloves
|
||||
{ 41, 7 }, // Ancient Gloves
|
||||
{ 42, 6 }, // Demonic Gloves
|
||||
{ 43, 6 }, // Storm Blitz Gloves
|
||||
{ 45, 7 }, // Titan Gloves
|
||||
{ 46, 7 }, // Brave Gloves
|
||||
{ 47, 7 }, // Phantom Gloves
|
||||
{ 48, 7 }, // Destroy Gloves
|
||||
{ 49, 7 }, // Seraphim Gloves
|
||||
{ 50, 7 }, // Divine Gloves
|
||||
{ 51, 7 }, // Royal Gloves
|
||||
{ 52, 7 }, // Hades Gloves
|
||||
};
|
||||
|
||||
private static readonly Dictionary<int, int> WalkSpeedByBootNumber = new()
|
||||
{
|
||||
{ 0, 10 }, // Bronze Boots
|
||||
{ 1, 2 }, // Dragon Boots
|
||||
{ 2, 10 }, // Pad Boots
|
||||
{ 3, 0 }, // Legendary Boots
|
||||
{ 4, 6 }, // Bone Boots
|
||||
{ 5, 12 }, // Leather Boots
|
||||
{ 6, 8 }, // Scale Boots
|
||||
{ 7, 8 }, // Sphinx Boots
|
||||
{ 8, 6 }, // Brass Boots
|
||||
{ 9, 4 }, // Plate Boots
|
||||
{ 15, 2 }, // Storm Crow Boots
|
||||
{ 16, 2 }, // Black Dragon Boots
|
||||
{ 17, 2 }, // Dark Phoenix Boots
|
||||
{ 20, 2 }, // Thunder Hawk Boots
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixAttackSpeedCalculation;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 10, 19, 14, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
if (gameConfiguration.Attributes.Contains(Stats.MagicSpeed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.MagicSpeed);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.AttackSpeedByWeapon);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.AreTwoWeaponsEquipped);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.EquippedWeaponCount);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.WalkSpeed);
|
||||
|
||||
Stats.AttackSpeed.GetPersistent(gameConfiguration).MaximumValue = Stats.AttackSpeed.MaximumValue;
|
||||
Stats.MagicSpeed.GetPersistent(gameConfiguration).MaximumValue = Stats.MagicSpeed.MaximumValue;
|
||||
|
||||
foreach (var characterClass in gameConfiguration.CharacterClasses)
|
||||
{
|
||||
var attributeRelationships = characterClass.AttributeCombinations;
|
||||
attributeRelationships.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.AttackSpeed, 1, Stats.AttackSpeedByWeapon));
|
||||
attributeRelationships.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.MagicSpeed, 1, Stats.AttackSpeedByWeapon));
|
||||
|
||||
// If two weapons are equipped we subtract the half of the sum of the speeds again from the attack speed
|
||||
attributeRelationships.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.AreTwoWeaponsEquipped, 1, Stats.EquippedWeaponCount));
|
||||
var tempSpeed = context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Temp Half weapon attack speed", string.Empty);
|
||||
gameConfiguration.Attributes.Add(tempSpeed);
|
||||
attributeRelationships.Add(CreateAttributeRelationship(context, gameConfiguration, tempSpeed, -0.5f, Stats.AttackSpeedByWeapon));
|
||||
attributeRelationships.Add(CreateConditionalRelationship(context, gameConfiguration, Stats.AttackSpeed, Stats.AreTwoWeaponsEquipped, tempSpeed));
|
||||
attributeRelationships.Add(CreateConditionalRelationship(context, gameConfiguration, Stats.MagicSpeed, Stats.AreTwoWeaponsEquipped, tempSpeed));
|
||||
|
||||
characterClass.BaseAttributeValues.Add(CreateConstValueAttribute(context, gameConfiguration, -1, Stats.AreTwoWeaponsEquipped));
|
||||
}
|
||||
|
||||
foreach (var darkKnight in gameConfiguration.CharacterClasses.Where(c => ((CharacterClassNumber)c.Number) is CharacterClassNumber.BladeKnight or CharacterClassNumber.BladeMaster or CharacterClassNumber.DarkKnight))
|
||||
{
|
||||
darkKnight.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.MagicSpeed, 1.0f / 20, Stats.TotalAgility));
|
||||
}
|
||||
|
||||
foreach (var darkLord in gameConfiguration.CharacterClasses.Where(c => ((CharacterClassNumber)c.Number) is CharacterClassNumber.DarkLord or CharacterClassNumber.LordEmperor))
|
||||
{
|
||||
darkLord.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.AttackSpeed, 1.0f / 10, Stats.TotalAgility));
|
||||
darkLord.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.MagicSpeed, 1.0f / 10, Stats.TotalAgility));
|
||||
}
|
||||
|
||||
foreach (var darkWizard in gameConfiguration.CharacterClasses.Where(c => ((CharacterClassNumber)c.Number) is CharacterClassNumber.DarkWizard or CharacterClassNumber.SoulMaster or CharacterClassNumber.GrandMaster))
|
||||
{
|
||||
darkWizard.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.AttackSpeed, 1.0f / 20, Stats.TotalAgility));
|
||||
darkWizard.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.MagicSpeed, 1.0f / 10, Stats.TotalAgility));
|
||||
}
|
||||
|
||||
foreach (var elf in gameConfiguration.CharacterClasses.Where(c => ((CharacterClassNumber)c.Number) is CharacterClassNumber.FairyElf or CharacterClassNumber.MuseElf or CharacterClassNumber.HighElf))
|
||||
{
|
||||
elf.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.AttackSpeed, 1.0f / 50, Stats.TotalAgility));
|
||||
elf.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.MagicSpeed, 1.0f / 50, Stats.TotalAgility));
|
||||
}
|
||||
|
||||
foreach (var magicGladiator in gameConfiguration.CharacterClasses.Where(c => ((CharacterClassNumber)c.Number) is CharacterClassNumber.MagicGladiator or CharacterClassNumber.DuelMaster))
|
||||
{
|
||||
magicGladiator.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.MagicSpeed, 1.0f / 20, Stats.TotalAgility));
|
||||
}
|
||||
|
||||
foreach (var rageFighter in gameConfiguration.CharacterClasses.Where(c => ((CharacterClassNumber)c.Number) is CharacterClassNumber.RageFighter or CharacterClassNumber.FistMaster))
|
||||
{
|
||||
rageFighter.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.AttackSpeed, 1.0f / 9, Stats.TotalAgility));
|
||||
rageFighter.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.MagicSpeed, 1.0f / 9, Stats.TotalAgility));
|
||||
}
|
||||
|
||||
foreach (var summoner in gameConfiguration.CharacterClasses.Where(c => ((CharacterClassNumber)c.Number) is CharacterClassNumber.Summoner or CharacterClassNumber.BloodySummoner or CharacterClassNumber.DimensionMaster))
|
||||
{
|
||||
summoner.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.MagicSpeed, 1.0f / 20, Stats.TotalAgility));
|
||||
}
|
||||
|
||||
// attack speed from gloves:
|
||||
var glovesWithSpeed = gameConfiguration.Items.Where(item => item.Group == (byte)ItemGroups.Gloves)
|
||||
.Select(item => (item, AttackSpeedByGloveNumber.GetValueOrDefault(item.Number)))
|
||||
.Where(pair => pair.Item2 > 0);
|
||||
foreach (var (gloves, attackSpeed) in glovesWithSpeed)
|
||||
{
|
||||
gloves.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(context, gameConfiguration, Stats.AttackSpeed, attackSpeed, AggregateType.AddRaw));
|
||||
gloves.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(context, gameConfiguration, Stats.MagicSpeed, attackSpeed, AggregateType.AddRaw));
|
||||
}
|
||||
|
||||
// walk speed from boots:
|
||||
var bootsWithSpeed = gameConfiguration.Items.Where(item => item.Group == (byte)ItemGroups.Boots)
|
||||
.Select(item => (item, WalkSpeedByBootNumber.GetValueOrDefault(item.Number)))
|
||||
.Where(pair => pair.Item2 > 0);
|
||||
foreach (var (boots, walkSpeed) in bootsWithSpeed)
|
||||
{
|
||||
boots.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(context, gameConfiguration, Stats.WalkSpeed, walkSpeed, AggregateType.AddRaw));
|
||||
}
|
||||
|
||||
new AlcoholEffectInitializer(context, gameConfiguration).Initialize();
|
||||
new BlessPotionEffectInitializer(context, gameConfiguration).Initialize();
|
||||
new SoulPotionEffectInitializer(context, gameConfiguration).Initialize();
|
||||
|
||||
this.SetItemEffect(gameConfiguration, ItemConstants.Alcohol, MagicEffectNumber.Alcohol);
|
||||
var siegePotion = gameConfiguration.Items.First(item => item.Number == ItemConstants.SiegePotion.Number && item.Group == ItemConstants.SiegePotion.Group);
|
||||
siegePotion.Name = "Potion of Bless;Potion of Soul";
|
||||
siegePotion.Durability = 10;
|
||||
siegePotion.MaximumItemLevel = 1;
|
||||
|
||||
var jackOlanternBlessingEffect = gameConfiguration.MagicEffects.First(item => item.Number == (int)MagicEffectNumber.JackOlanternBlessing);
|
||||
var powerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
jackOlanternBlessingEffect.PowerUpDefinitions.Add(powerUpDefinition);
|
||||
powerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUpDefinition.Boost.ConstantValue.Value = 10;
|
||||
powerUpDefinition.TargetAttribute = Stats.MagicSpeed.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
private void SetItemEffect(GameConfiguration gameConfiguration, ItemIdentifier itemIdentifier, MagicEffectNumber effectNumber)
|
||||
{
|
||||
var item = gameConfiguration.Items.First(i => i.Number == itemIdentifier.Number && i.Group == itemIdentifier.Group);
|
||||
item.ConsumeEffect = gameConfiguration.MagicEffects.First(effect => effect.Number == (int)effectNumber);
|
||||
}
|
||||
|
||||
private ItemBasePowerUpDefinition CreateItemBasePowerUpDefinition(IContext context, GameConfiguration gameConfiguration, AttributeDefinition attributeDefinition, float value, AggregateType aggregateType)
|
||||
{
|
||||
var powerUpDefinition = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = attributeDefinition.GetPersistent(gameConfiguration);
|
||||
powerUpDefinition.BaseValue = value;
|
||||
powerUpDefinition.AggregateType = aggregateType;
|
||||
return powerUpDefinition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// <copyright file="FixBloodCastleMonsterAttributesUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Swaps attribute values between BC7 (monsters 138-143) and BC8 (monsters 428-433)
|
||||
/// so the difficulty progression is correct: BC6 → BC7 → BC8.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("D4E5F6A0-1B2C-3D4E-5F6A-7B8C9D0E1F2A")]
|
||||
public class FixBloodCastleMonsterAttributesUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the plugin name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Blood Castle 7/8 Monster Attributes";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Swaps attribute values between BC7 (monsters 138-143) and BC8 (monsters 428-433) so progression is correct: BC6 → BC7 → BC8.";
|
||||
|
||||
private static readonly Guid LevelId = new("560931AD-0901-4342-B7F4-FD2E2FCC0563");
|
||||
private static readonly Guid MaximumHealthId = new("A6C39A5C-295F-415E-A314-5E9F9A748D27");
|
||||
private static readonly Guid MinimumPhysBaseDmgId = new("3E8D6A02-E973-4AE4-9DF3-CDDC3D3183B3");
|
||||
private static readonly Guid MaximumPhysBaseDmgId = new("8A918EA2-893A-48B2-A684-3E71526CA71F");
|
||||
private static readonly Guid DefenseBaseId = new("EB098C46-60D4-4CA6-BBD4-5B6270A1407B");
|
||||
private static readonly Guid AttackRatePvmId = new("1129442A-E1C7-4240-8866-B781C2838C25");
|
||||
private static readonly Guid DefenseRatePvmId = new("C520DD2D-1B06-4392-95EE-3C41F33E68DA");
|
||||
private static readonly Guid PoisonResistanceId = new("3D50D0B7-63A2-4DA9-8855-12173EAE6B39");
|
||||
private static readonly Guid IceResistanceId = new("47235C36-41BB-44B4-8823-6FC415709F59");
|
||||
private static readonly Guid FireResistanceId = new("9AE4D80D-5706-48B9-AD11-EAC4FE088A81");
|
||||
private static readonly Guid LightningResistanceId = new("3E339393-2D17-452E-81D9-3987947A407F");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixBloodCastleMonsterAttributes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 05, 17, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var monsters = gameConfiguration.Monsters;
|
||||
|
||||
UpdateMonster(monsters, 138, 105, 29000, 475, 510, 440, 570, 250, 7f / 255, 7f / 255, 7f / 255, 7f / 255);
|
||||
UpdateMonster(monsters, 139, 106, 32000, 510, 555, 480, 640, 260, 7f / 255, 7f / 255, 7f / 255, 7f / 255);
|
||||
UpdateMonster(monsters, 140, 110, 37000, 600, 650, 500, 710, 300, 7f / 255, 7f / 255, 7f / 255, 7f / 255);
|
||||
UpdateMonster(monsters, 141, 112, 45000, 645, 690, 540, 780, 310, 7f / 255, 7f / 255, 7f / 255, 7f / 255);
|
||||
UpdateMonster(monsters, 142, 119, 55000, 780, 820, 600, 850, 360, 7f / 255, 7f / 255, 7f / 255, 7f / 255);
|
||||
UpdateMonster(monsters, 143, 125, 60000, 830, 865, 680, 920, 370, 10f / 255, 10f / 255, 10f / 255, 10f / 255);
|
||||
|
||||
UpdateMonster(monsters, 428, 114, 173500, 745, 800, 600, 640, 426, 8f / 255, 8f / 255, 8f / 255, 8f / 255);
|
||||
UpdateMonster(monsters, 429, 117, 175000, 825, 872, 615, 690, 440, 8f / 255, 8f / 255, 8f / 255, 8f / 255);
|
||||
UpdateMonster(monsters, 430, 125, 184000, 890, 915, 622, 760, 465, 8f / 255, 8f / 255, 8f / 255, 8f / 255);
|
||||
UpdateMonster(monsters, 431, 129, 208000, 920, 946, 635, 830, 510, 8f / 255, 8f / 255, 8f / 255, 8f / 255);
|
||||
UpdateMonster(monsters, 432, 132, 208700, 995, 1120, 648, 900, 585, 8f / 255, 8f / 255, 8f / 255, 8f / 255);
|
||||
UpdateMonster(monsters, 433, 140, 215000, 1500, 1780, 690, 950, 750, 11f / 255, 11f / 255, 11f / 255, 11f / 255);
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
private static void UpdateMonster(ICollection<MonsterDefinition> monsters, short number, int level, int maxHealth, int minDmg, int maxDmg, int defense, int attackRate, int defenseRate, float poisonRes, float iceRes, float fireRes, float lightningRes)
|
||||
{
|
||||
var monster = monsters.FirstOrDefault(m => m.Number == number);
|
||||
if (monster is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetAttribute(monster, LevelId, level);
|
||||
SetAttribute(monster, MaximumHealthId, maxHealth);
|
||||
SetAttribute(monster, MinimumPhysBaseDmgId, minDmg);
|
||||
SetAttribute(monster, MaximumPhysBaseDmgId, maxDmg);
|
||||
SetAttribute(monster, DefenseBaseId, defense);
|
||||
SetAttribute(monster, AttackRatePvmId, attackRate);
|
||||
SetAttribute(monster, DefenseRatePvmId, defenseRate);
|
||||
SetAttribute(monster, PoisonResistanceId, poisonRes);
|
||||
SetAttribute(monster, IceResistanceId, iceRes);
|
||||
SetAttribute(monster, FireResistanceId, fireRes);
|
||||
SetAttribute(monster, LightningResistanceId, lightningRes);
|
||||
}
|
||||
|
||||
private static void SetAttribute(MonsterDefinition monster, Guid attributeId, float value)
|
||||
{
|
||||
var attribute = monster.Attributes.FirstOrDefault(a => a.AttributeDefinition?.Id == attributeId);
|
||||
if (attribute is not null)
|
||||
{
|
||||
attribute.Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// <copyright file="FixChaosMixesPlugIn095D.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the Chaos Weapon, First Wings, Dinorant, and Item Level Upgrade craftings' settings.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("68BC1F35-FC9A-468F-89FB-0940485AC107")]
|
||||
public class FixChaosMixesPlugIn095D : FixChaosMixesPlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
private new const string PlugInDescription = "This update fixes the Chaos Weapon, First Wings, Dinorant, and Item Level Upgrade crafting settings.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixChaosMixes095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
|
||||
var craftings = gameConfiguration.Monsters.First(m => m.NpcWindow == NpcWindow.ChaosMachine).ItemCraftings;
|
||||
this.ApplyFirstWingsCraftingUpdate(craftings);
|
||||
this.ApplyDinorantCraftingUpdate(craftings);
|
||||
this.ApplyDinorantOptionsUpdate(gameConfiguration);
|
||||
|
||||
// Item Level Upgrade craftings
|
||||
int[] itemLevelUpgradeCraftingNos = [3, 4];
|
||||
|
||||
for (int i = 0; i < itemLevelUpgradeCraftingNos.Length; i++)
|
||||
{
|
||||
if (craftings.Single(c => c.Number == itemLevelUpgradeCraftingNos[i])?.SimpleCraftingSettings is { } craftingSettings)
|
||||
{
|
||||
craftingSettings.Money = 2_000_000 * (10 + i - 9);
|
||||
craftingSettings.SuccessPercent = (byte)(10 + i == 10 ? 50 : 45);
|
||||
craftingSettings.SuccessPercentageAdditionForLuck = 25;
|
||||
craftingSettings.SuccessPercentageAdditionForExcellentItem = 0;
|
||||
craftingSettings.SuccessPercentageAdditionForAncientItem = 0;
|
||||
craftingSettings.SuccessPercentageAdditionForSocketItem = 0;
|
||||
|
||||
foreach (var item in craftingSettings.RequiredItems)
|
||||
{
|
||||
item.FailResult = MixResult.Disappear;
|
||||
if (item.MaximumAmount == 0)
|
||||
{
|
||||
item.MaximumAmount = item.MinimumAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// <copyright file="FixChaosMixesPlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.PlayerActions.Craftings;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the Chaos Weapon crafting settings.
|
||||
/// </summary>
|
||||
public abstract class FixChaosMixesPlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Chaos Mixes Settings";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes Chaos Weapon crafting settings.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 12, 10, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Misc item fixes
|
||||
Guid crystalSwordId = new("00000080-0002-0005-0000-000000000000");
|
||||
if (gameConfiguration.Items.FirstOrDefault(id => id.GetId() == crystalSwordId) is { } crystalSword)
|
||||
{
|
||||
crystalSword.Width = 2;
|
||||
}
|
||||
|
||||
Guid powerWaveScrollId = new("00000080-000f-000a-0000-000000000000");
|
||||
if (gameConfiguration.Items.FirstOrDefault(id => id.GetId() == powerWaveScrollId) is { } powerWaveScroll)
|
||||
{
|
||||
powerWaveScroll.Value = 1100;
|
||||
}
|
||||
|
||||
// Fix Chaos Weapon crafting
|
||||
var craftings = gameConfiguration.Monsters.First(m => m.NpcWindow == NpcWindow.ChaosMachine).ItemCraftings;
|
||||
|
||||
if (craftings.Single(c => c.Number == 1) is { } chaosWeaponCrafting)
|
||||
{
|
||||
chaosWeaponCrafting.ItemCraftingHandlerClassName = typeof(ChaosWeaponAndFirstWingsCrafting).FullName!;
|
||||
|
||||
if (chaosWeaponCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.SuccessPercent = 0;
|
||||
settings.NpcPriceDivisor = 20_000;
|
||||
|
||||
foreach (var requiredItem in settings.RequiredItems)
|
||||
{
|
||||
requiredItem.AddPercentage = 0;
|
||||
if (requiredItem.FailResult != MixResult.Disappear)
|
||||
{
|
||||
requiredItem.NpcPriceDivisor = 0;
|
||||
requiredItem.FailResult = MixResult.ChaosWeaponAndFirstWingsDowngradedRandom;
|
||||
}
|
||||
}
|
||||
|
||||
settings.ResultItemLuckOptionChance = 0;
|
||||
settings.ResultItemSkillChance = 0;
|
||||
|
||||
foreach (var resultItem in settings.ResultItems)
|
||||
{
|
||||
resultItem.RandomMinimumLevel = 0;
|
||||
resultItem.RandomMaximumLevel = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies First Wings crafting settings update.
|
||||
/// </summary>
|
||||
/// <param name="craftings">The craftings collection.</param>
|
||||
protected void ApplyFirstWingsCraftingUpdate(ICollection<ItemCrafting> craftings)
|
||||
{
|
||||
if (craftings.Single(c => c.Number == 11) is { } firstWingsCrafting)
|
||||
{
|
||||
firstWingsCrafting.ItemCraftingHandlerClassName = typeof(ChaosWeaponAndFirstWingsCrafting).FullName!;
|
||||
|
||||
if (firstWingsCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.SuccessPercent = 0;
|
||||
settings.NpcPriceDivisor = 20_000;
|
||||
|
||||
foreach (var requiredItem in settings.RequiredItems)
|
||||
{
|
||||
requiredItem.AddPercentage = 0;
|
||||
requiredItem.NpcPriceDivisor = 0;
|
||||
if (requiredItem.MaximumAmount == 1)
|
||||
{
|
||||
// Chaos weapon
|
||||
requiredItem.FailResult = MixResult.ChaosWeaponAndFirstWingsDowngradedRandom;
|
||||
}
|
||||
else
|
||||
{
|
||||
requiredItem.FailResult = MixResult.Disappear;
|
||||
}
|
||||
}
|
||||
|
||||
settings.ResultItemLuckOptionChance = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies Dinorant crafting settings update.
|
||||
/// </summary>
|
||||
/// <param name="craftings">The craftings collection.</param>
|
||||
protected void ApplyDinorantCraftingUpdate(ICollection<ItemCrafting> craftings)
|
||||
{
|
||||
if (craftings.Single(c => c.Number == 5) is { } dinorantCrafting)
|
||||
{
|
||||
dinorantCrafting.ItemCraftingHandlerClassName = typeof(DinorantCrafting).FullName!;
|
||||
|
||||
if (dinorantCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.ResultItemExcellentOptionChance = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies Dinorant options update.
|
||||
/// </summary>
|
||||
/// <param name="gameConfiguration">The game configuration.</param>
|
||||
protected void ApplyDinorantOptionsUpdate(GameConfiguration gameConfiguration)
|
||||
{
|
||||
if (gameConfiguration.ItemOptions.Single(iod => iod.Name == "Dinorant Options") is { } dinoOpts
|
||||
&& gameConfiguration.ItemOptionTypes.Single(iot => iot == ItemOptionTypes.Option) is { } itemOption)
|
||||
{
|
||||
dinoOpts.AddChance = 0.3f;
|
||||
|
||||
foreach (var opt in dinoOpts.PossibleOptions)
|
||||
{
|
||||
opt.OptionType = itemOption;
|
||||
opt.Number = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
// <copyright file="FixChaosMixesPlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.ItemCrafting;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.PlayerActions.Craftings;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the Chaos Weapon, First Wings, Dinorant, Item Level Upgrade, Second Wings, Third Wings, Cape, SD Potions, Guardian Option, and Secromicon crafting settings; Blue Fenrir (Protect) damage decrease option value; Wizard's Ring wizardry option; lvl 380 item guardian options for Summoner and Rage Fighter.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("EFD7EA69-56AE-48A3-ACE2-1C3B5B87780A")]
|
||||
public class FixChaosMixesPlugInSeason6 : FixChaosMixesPlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
private new const string PlugInName = "Fix Chaos Mixes Settings And Several Options";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
private new const string PlugInDescription = "This update fixes the Chaos Weapon, First Wings, Dinorant, Item Level Upgrade, Second Wings, Third Wings, Cape, SD Potions, Guardian Option, and Secromicon crafting settings; Blue Fenrir (Protect) damage decrease option value; Wizard's Ring wizardry option; lvl 380 item guardian options for Summoner and Rage Fighter.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixChaosMixesSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Dark horse spirit and raven spirit drop item groups id fix (do this first because it persists changes to DB)
|
||||
if (gameConfiguration.Items.Single(id => id.Name == "Spirit") is { } spirit)
|
||||
{
|
||||
spirit.MaximumItemLevel = 1;
|
||||
var maps = gameConfiguration.Maps;
|
||||
if (gameConfiguration.DropItemGroups.Single(dig => dig.Description == "Dark Horse Spirit") is { } oldHorseGroup
|
||||
&& gameConfiguration.DropItemGroups.Single(dig => dig.Description == "Dark Raven Spirit") is { } oldRavenGroup)
|
||||
{
|
||||
await DeleteDropItemGroupAsync(oldHorseGroup).ConfigureAwait(false);
|
||||
await DeleteDropItemGroupAsync(oldRavenGroup).ConfigureAwait(false);
|
||||
CreateDropItemGroup(0, "Dark Horse Spirit", 102);
|
||||
CreateDropItemGroup(1, "Dark Raven Spirit", 96);
|
||||
}
|
||||
|
||||
async ValueTask DeleteDropItemGroupAsync(DropItemGroup group)
|
||||
{
|
||||
group.PossibleItems.Clear();
|
||||
foreach (var map in maps)
|
||||
{
|
||||
if (map.DropItemGroups.FirstOrDefault(dig => dig.GetId() == group.GetId()) is { } mapDropItemGroup)
|
||||
{
|
||||
map.DropItemGroups.Remove(mapDropItemGroup);
|
||||
}
|
||||
}
|
||||
|
||||
gameConfiguration.DropItemGroups.Remove(group);
|
||||
await context.DeleteAsync(group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
void CreateDropItemGroup(int itemLevel, string description, short minimumMonsterLevel)
|
||||
{
|
||||
var group = context.CreateNew<DropItemGroup>();
|
||||
group.SetGuid(NumberConversionExtensions.MakeWord(13, 31).ToSigned(), (short)itemLevel, 1);
|
||||
group.ItemLevel = (byte)itemLevel;
|
||||
group.Chance = 0.001;
|
||||
group.Description = description;
|
||||
group.PossibleItems.Add(spirit);
|
||||
group.MinimumMonsterLevel = (byte)minimumMonsterLevel;
|
||||
|
||||
gameConfiguration.DropItemGroups.Add(group);
|
||||
foreach (var map in maps)
|
||||
{
|
||||
map.DropItemGroups.Add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
|
||||
var goblinCraftings = gameConfiguration.Monsters.Single(m => m.NpcWindow == NpcWindow.ChaosMachine).ItemCraftings;
|
||||
var petTrainercraftings = gameConfiguration.Monsters.Single(m => m.NpcWindow == NpcWindow.PetTrainer).ItemCraftings;
|
||||
this.ApplyFirstWingsCraftingUpdate(goblinCraftings);
|
||||
this.ApplyDinorantCraftingUpdate(goblinCraftings);
|
||||
this.ApplyDinorantOptionsUpdate(gameConfiguration);
|
||||
|
||||
// Fenrir dmg decrease fix
|
||||
Guid fenrirOptionsId = new("00000083-0081-0000-0000-000000000000");
|
||||
if (gameConfiguration.ItemOptions.Single(iod => iod.GetId() == fenrirOptionsId) is { } fenrirOpts
|
||||
&& fenrirOpts.PossibleOptions.Single(iio => iio.PowerUpDefinition?.TargetAttribute == Stats.DamageReceiveDecrement) is { } dmgDecreaseOpt
|
||||
&& dmgDecreaseOpt.PowerUpDefinition?.Boost is { } boost)
|
||||
{
|
||||
boost.ConstantValue.Value = 0.90f;
|
||||
}
|
||||
|
||||
// Wizard's Ring wizardry option fix
|
||||
Guid wizardsRingId = new("00000080-000d-0014-0000-000000000000");
|
||||
if (gameConfiguration.Items.Single(id => id.GetId() == wizardsRingId) is { } wizardsRing)
|
||||
{
|
||||
wizardsRing.Durability = 30;
|
||||
|
||||
if (wizardsRing.PossibleItemOptions.FirstOrDefault() is { } wizardsRingOpts)
|
||||
{
|
||||
wizardsRingOpts.MaximumOptionsPerItem = 3;
|
||||
|
||||
var increaseWizardryDamage = context.CreateNew<IncreasableItemOption>();
|
||||
increaseWizardryDamage.SetGuid(ItemOptionDefinitionNumbers.WizardRing, 3);
|
||||
increaseWizardryDamage.PowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
increaseWizardryDamage.PowerUpDefinition.TargetAttribute = Stats.WizardryAttackDamageIncrease.GetPersistent(gameConfiguration);
|
||||
increaseWizardryDamage.PowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
increaseWizardryDamage.PowerUpDefinition.Boost.ConstantValue.Value = 0.1f;
|
||||
wizardsRingOpts.PossibleOptions.Add(increaseWizardryDamage);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove Raven's lvl requirement
|
||||
Guid darkRavenId = new("00000080-000d-0005-0000-000000000000");
|
||||
if (gameConfiguration.Items.Single(id => id.GetId() == darkRavenId) is { } darkRaven)
|
||||
{
|
||||
darkRaven.Requirements.Clear();
|
||||
}
|
||||
|
||||
// Aura/Storm Blitz Set definitions fix
|
||||
for (int i = 7; i <= 11; i++)
|
||||
{
|
||||
if (gameConfiguration.Items.FirstOrDefault(id => id.Group == i && id.Number == 43) is { } auraItem)
|
||||
{
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(io => io.PossibleOptions.Any(po => po.OptionType == ItemOptionTypes.GuardianOption && po.Number == i)) is { } guardOpt)
|
||||
{
|
||||
auraItem.PossibleItemOptions.Add(guardOpt);
|
||||
}
|
||||
|
||||
if (auraItem.Requirements.FirstOrDefault(r => r.Attribute == Stats.Level) is { } auraLvlRequirement)
|
||||
{
|
||||
auraLvlRequirement.MinimumValue = 380;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phoenix Soul Set definitions fix
|
||||
for (int i = 7; i <= 11; i++)
|
||||
{
|
||||
if (gameConfiguration.Items.FirstOrDefault(id => id.Group == i && id.Number == 73) is { } phoenixSoulItem)
|
||||
{
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(io => io.PossibleOptions.Any(po => po.OptionType == ItemOptionTypes.GuardianOption && po.Number == i)) is { } guardOpt)
|
||||
{
|
||||
phoenixSoulItem.PossibleItemOptions.Add(guardOpt);
|
||||
}
|
||||
|
||||
if (phoenixSoulItem.Requirements.FirstOrDefault(r => r.Attribute == Stats.Level) is { } psLvlRequirement)
|
||||
{
|
||||
psLvlRequirement.MinimumValue = 380;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phoenix Soul Star definition fix
|
||||
Guid phoenixSoulStarId = new("00000080-0000-0023-0000-000000000000");
|
||||
if (gameConfiguration.Items.FirstOrDefault(id => id.GetId() == phoenixSoulStarId) is { } phoenixSoulStar)
|
||||
{
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(io => io.PossibleOptions.Any(po => po.OptionType == ItemOptionTypes.GuardianOption && po.Number == (int)ItemGroups.Weapon)) is { } weapGuardOpt)
|
||||
{
|
||||
phoenixSoulStar.PossibleItemOptions.Add(weapGuardOpt);
|
||||
}
|
||||
|
||||
if (phoenixSoulStar.Requirements.FirstOrDefault(r => r.Attribute == Stats.Level) is { } pssLvlRequirement)
|
||||
{
|
||||
pssLvlRequirement.MinimumValue = 380;
|
||||
}
|
||||
}
|
||||
|
||||
// ---> Fix chaos mixes settings
|
||||
// Item Level Upgrade craftings
|
||||
int[] itemLevelUpgradeCraftingNos = [3, 4, 22, 23, 49, 50];
|
||||
for (int i = 0; i < itemLevelUpgradeCraftingNos.Length; i++)
|
||||
{
|
||||
if (goblinCraftings.Single(c => c.Number == itemLevelUpgradeCraftingNos[i])?.SimpleCraftingSettings is { } craftingSettings)
|
||||
{
|
||||
craftingSettings.Money = 2_000_000 * (10 + i - 9);
|
||||
craftingSettings.SuccessPercentageAdditionForLuck = 25;
|
||||
craftingSettings.SuccessPercentageAdditionForAncientItem = -10;
|
||||
craftingSettings.SuccessPercentageAdditionForGuardianItem = -10;
|
||||
|
||||
foreach (var item in craftingSettings.RequiredItems)
|
||||
{
|
||||
item.FailResult = MixResult.Disappear;
|
||||
if (item.MaximumAmount == 0)
|
||||
{
|
||||
item.MaximumAmount = item.MinimumAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second Wings crafting
|
||||
if (goblinCraftings.Single(c => c.Number == 7) is { } secondWingsCrafting)
|
||||
{
|
||||
secondWingsCrafting.ItemCraftingHandlerClassName = typeof(SecondWingsCrafting).FullName!;
|
||||
|
||||
if (secondWingsCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.MoneyPerFinalSuccessPercentage = 0;
|
||||
|
||||
foreach (var requiredItem in settings.RequiredItems)
|
||||
{
|
||||
if (requiredItem.FailResult != MixResult.Disappear)
|
||||
{
|
||||
// 1st level wings or exc item
|
||||
requiredItem.FailResult = MixResult.Disappear;
|
||||
}
|
||||
else
|
||||
{
|
||||
// chaos or feather
|
||||
requiredItem.AddPercentage = 0;
|
||||
requiredItem.MaximumAmount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
settings.ResultItemLuckOptionChance = 20;
|
||||
}
|
||||
}
|
||||
|
||||
// Thirds Wings, Stage 1 crafting
|
||||
if (goblinCraftings.Single(c => c.Number == 38) is { } thirdWingsS1Crafting)
|
||||
{
|
||||
if (thirdWingsS1Crafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.SuccessPercent = 1;
|
||||
|
||||
foreach (var requiredItem in settings.RequiredItems)
|
||||
{
|
||||
if (requiredItem.FailResult != MixResult.Disappear)
|
||||
{
|
||||
// 2nd level wings or anc item
|
||||
requiredItem.FailResult = MixResult.ThirdWingsDowngradedRandom;
|
||||
if (requiredItem.MinimumItemLevel == 9)
|
||||
{
|
||||
// 2nd lvl wings
|
||||
requiredItem.NpcPriceDivisor = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Thirds Wings, Stage 2 crafting
|
||||
if (goblinCraftings.Single(c => c.Number == 39) is { } thirdWingsS2Crafting)
|
||||
{
|
||||
thirdWingsS2Crafting.ItemCraftingHandlerClassName = typeof(ThirdWingsCrafting).FullName!;
|
||||
|
||||
if (thirdWingsS2Crafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.Money = 0;
|
||||
settings.MoneyPerFinalSuccessPercentage = 200_000;
|
||||
settings.SuccessPercent = 1;
|
||||
|
||||
foreach (var requiredItem in settings.RequiredItems)
|
||||
{
|
||||
if (requiredItem.FailResult != MixResult.Disappear)
|
||||
{
|
||||
// exc item
|
||||
requiredItem.MinimumAmount = 1;
|
||||
requiredItem.FailResult = MixResult.ThirdWingsDowngradedRandom;
|
||||
}
|
||||
else
|
||||
{
|
||||
requiredItem.MaximumAmount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
settings.ResultItemLuckOptionChance = 5;
|
||||
settings.ResultItemExcellentOptionChance = 0;
|
||||
settings.ResultItemMaxExcOptionCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Cape crafting
|
||||
if (goblinCraftings.Single(c => c.Number == 24) is { } capeCrafting)
|
||||
{
|
||||
capeCrafting.ItemCraftingHandlerClassName = typeof(SecondWingsCrafting).FullName!;
|
||||
|
||||
if (capeCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.MoneyPerFinalSuccessPercentage = 0;
|
||||
|
||||
foreach (var requiredItem in settings.RequiredItems)
|
||||
{
|
||||
if (requiredItem.FailResult != MixResult.Disappear)
|
||||
{
|
||||
// 1st level wings or exc item
|
||||
requiredItem.FailResult = MixResult.Disappear;
|
||||
|
||||
if (requiredItem.NpcPriceDivisor == 4_000_000)
|
||||
{
|
||||
// 1st level wings
|
||||
requiredItem.MinimumItemLevel = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// chaos or crest
|
||||
requiredItem.AddPercentage = 0;
|
||||
requiredItem.MaximumAmount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
settings.ResultItemLuckOptionChance = 20;
|
||||
settings.ResultItemExcellentOptionChance = 20;
|
||||
settings.ResultItemMaxExcOptionCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fruit crafting
|
||||
if (goblinCraftings.Single(c => c.Number == 6) is { } fruitCrafting)
|
||||
{
|
||||
fruitCrafting.ItemCraftingHandlerClassName = typeof(SecondWingsCrafting).FullName!;
|
||||
|
||||
if (fruitCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.MoneyPerFinalSuccessPercentage = 0;
|
||||
|
||||
foreach (var resultItem in settings.ResultItems)
|
||||
{
|
||||
resultItem.RandomMaximumLevel = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Small, Medium, and Large Shield Potion craftings
|
||||
for (int i = 30; i <= 32; i++)
|
||||
{
|
||||
if (goblinCraftings.Single(c => c.Number == i) is { } smallShieldPotCrafting)
|
||||
{
|
||||
if (smallShieldPotCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
foreach (var resultItem in settings.ResultItems)
|
||||
{
|
||||
resultItem.Durability = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Guardian Option crafting
|
||||
if (goblinCraftings.Single(c => c.Number == 36) is { } guardianOptionCrafting)
|
||||
{
|
||||
if (guardianOptionCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
foreach (var requiredItem in settings.RequiredItems.OrderBy(i => i.MinimumItemLevel))
|
||||
{
|
||||
if (requiredItem.MinimumItemLevel < 10)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (requiredItem.MinimumItemLevel == 10)
|
||||
{
|
||||
requiredItem.MaximumItemLevel = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.RequiredItems.Remove(requiredItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Secromicon crafting
|
||||
if (goblinCraftings.Single(c => c.Number == 46) is { } secromiconCrafting)
|
||||
{
|
||||
if (secromiconCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.Money = 1_000_000;
|
||||
}
|
||||
}
|
||||
|
||||
// Dark Horse crafting
|
||||
if (petTrainercraftings.Single(c => c.Number == 13) is { } darkHorseCrafting)
|
||||
{
|
||||
if (darkHorseCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
if (settings.ResultItems.First() is { } darkHorseResult)
|
||||
{
|
||||
darkHorseResult.RandomMinimumLevel = 1;
|
||||
darkHorseResult.RandomMaximumLevel = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dark Raven crafting
|
||||
if (petTrainercraftings.Single(c => c.Number == 14) is { } darkRavenCrafting)
|
||||
{
|
||||
if (darkRavenCrafting.SimpleCraftingSettings is { } settings)
|
||||
{
|
||||
settings.ResultItemSkillChance = 0;
|
||||
if (settings.ResultItems.First() is { } darkRavenResult)
|
||||
{
|
||||
darkRavenResult.RandomMinimumLevel = 1;
|
||||
darkRavenResult.RandomMaximumLevel = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="FixChaosMixesUpdatePlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the Chaos Weapon crafting settings.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("04A5F236-117F-422A-8C38-28D09DE911D7")]
|
||||
public class FixChaosMixesUpdatePlugIn075 : FixChaosMixesPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixChaosMixes075;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="FixCharStatsForceWavePlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes DW agility to defense multiplier stat.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("D7CD05B7-06EE-4D9F-BAD0-65267F3A9FE8")]
|
||||
public class FixCharStatsForceWavePlugIn075 : FixCharStatsForceWavePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixCharStatsForceWave075;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// <copyright file="FixCharStatsForceWavePlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes agility to defense multiplier (DW) and base energy (MG) stats.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("14DFF317-B4E6-424A-A8D1-6D1D5195E970")]
|
||||
public class FixCharStatsForceWavePlugIn095D : FixCharStatsForceWavePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal new const string PlugInName = "Fix DW and MG Char Stats";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal new const string PlugInDescription = "This update fixes agility to defense multiplier (DW) and base energy (MG) stats.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixCharStatsForceWave095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.UpdateMagicGladiatorClassesStats(gameConfiguration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// <copyright file="FixCharStatsForceWavePlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes DW agility to defense multiplier stat.
|
||||
/// </summary>
|
||||
public abstract class FixCharStatsForceWavePlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix DW Defense Multiplier";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes DW agility to defense multiplier stat.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 03, 24, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var wizardClasses = gameConfiguration.CharacterClasses.Where(charClass => charClass.Number == 0 || charClass.Number == 2 || charClass.Number == 3);
|
||||
foreach (var wizardClass in wizardClasses)
|
||||
{
|
||||
if (wizardClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.DefenseBase && attrCombo.InputAttribute == Stats.TotalAgility) is { } totalAgilityToDefenseBase)
|
||||
{
|
||||
totalAgilityToDefenseBase.InputOperand = 0.25f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable SA1600, CS1591 // Elements should be documented.
|
||||
protected void UpdateMagicGladiatorClassesStats(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicGladiatorClasses = gameConfiguration.CharacterClasses.Where(charClass => charClass.Number == 12 || charClass.Number == 13);
|
||||
foreach (var magicGladiatorClass in magicGladiatorClasses)
|
||||
{
|
||||
magicGladiatorClass.StatAttributes.First(attr => attr.Attribute == Stats.BaseEnergy).BaseValue = 26;
|
||||
}
|
||||
}
|
||||
#pragma warning restore SA1600, CS1591 // Elements should be documented.
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// <copyright file="FixCharStatsForceWavePlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes several character stats values and DL Force Wave Strengthener master skill.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("0C1995AB-A1CC-42A8-9EFC-E5FE8F360C53")]
|
||||
public class FixCharStatsForceWavePlugInSeason6 : FixCharStatsForceWavePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal new const string PlugInName = "Fix Char Stats and DL Force Wave Str skill";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal new const string PlugInDescription = "This update fixes several character stats values and DL Force Wave Strengthener master skill.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixCharStatsForceWaveSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.UpdateMagicGladiatorClassesStats(gameConfiguration);
|
||||
|
||||
var totalLevel = Stats.TotalLevel.GetPersistent(gameConfiguration);
|
||||
|
||||
gameConfiguration.CharacterClasses.ForEach(charClass =>
|
||||
{
|
||||
foreach (var attrCombo in charClass.AttributeCombinations)
|
||||
{
|
||||
if (attrCombo.TargetAttribute == Stats.AttackRatePvm && attrCombo.InputAttribute == Stats.Level)
|
||||
{
|
||||
attrCombo.InputAttribute = totalLevel;
|
||||
}
|
||||
else if (attrCombo.TargetAttribute == Stats.AttackRatePvp && attrCombo.InputAttribute == Stats.Level)
|
||||
{
|
||||
attrCombo.InputAttribute = totalLevel;
|
||||
}
|
||||
else if (attrCombo.TargetAttribute == Stats.DefenseRatePvp && attrCombo.InputAttribute == Stats.Level)
|
||||
{
|
||||
attrCombo.InputAttribute = totalLevel;
|
||||
}
|
||||
else if (attrCombo.TargetAttribute == Stats.MaximumMana && attrCombo.InputAttribute == Stats.Level)
|
||||
{
|
||||
attrCombo.InputAttribute = totalLevel;
|
||||
}
|
||||
else if (attrCombo.TargetAttribute == Stats.MaximumHealth && attrCombo.InputAttribute == Stats.Level)
|
||||
{
|
||||
attrCombo.InputAttribute = totalLevel;
|
||||
}
|
||||
else if (attrCombo.TargetAttribute == Stats.MaximumShieldTemp && attrCombo.InputAttribute == Stats.Level)
|
||||
{
|
||||
attrCombo.InputAttribute = totalLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// Lord classes.
|
||||
if (charClass.Number == 16 || charClass.Number == 17)
|
||||
{
|
||||
charClass.StatAttributes.First(attr => attr.Attribute == Stats.CurrentHealth).BaseValue = 90;
|
||||
charClass.StatAttributes.First(attr => attr.Attribute == Stats.CurrentMana).BaseValue = 40;
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.MaximumMana && attrCombo.InputAttribute == Stats.TotalLevel) is { } totalLevelToMaximumMana)
|
||||
{
|
||||
totalLevelToMaximumMana.InputOperand = 1;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.MaximumHealth && attrCombo.InputAttribute == Stats.TotalLevel) is { } totalLevelToMaximumHealth)
|
||||
{
|
||||
totalLevelToMaximumHealth.InputOperand = 1.5f;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.MaximumHealth && attrCombo.InputAttribute == Stats.TotalVitality) is { } totalVitalityoMaximumHealth)
|
||||
{
|
||||
totalVitalityoMaximumHealth.InputOperand = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// RF classes.
|
||||
else if (charClass.Number == 24 || charClass.Number == 25)
|
||||
{
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.DefenseRatePvp && attrCombo.InputAttribute == Stats.TotalAgility) is { } totalAgilityToDefenseRatePvp)
|
||||
{
|
||||
totalAgilityToDefenseRatePvp.InputOperand = 0.2f;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.DefenseRatePvp && attrCombo.InputAttribute == Stats.TotalLevel) is { } totalLevelToDefenseRatePvp)
|
||||
{
|
||||
totalLevelToDefenseRatePvp.InputOperand = 1.5f;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.AttackRatePvm && attrCombo.InputAttribute == Stats.TotalAgility) is { } totalAgilityToAttackRatePvm)
|
||||
{
|
||||
totalAgilityToAttackRatePvm.InputOperand = 1.25f;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.AttackRatePvm && attrCombo.InputAttribute == Stats.TotalStrength) is { } totalStrengthToAttackRatePvm)
|
||||
{
|
||||
totalStrengthToAttackRatePvm.InputOperand = 1.0f / 6;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.AttackRatePvp && attrCombo.InputAttribute == Stats.TotalAgility) is { } totalAgilityToAttackRatePvp)
|
||||
{
|
||||
totalAgilityToAttackRatePvp.InputOperand = 3.6f;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.AttackRatePvp && attrCombo.InputAttribute == Stats.TotalLevel) is { } totalLevelToAttackRatePvp)
|
||||
{
|
||||
totalLevelToAttackRatePvp.InputOperand = 2.6f;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.AttackRatePvp && attrCombo.InputAttribute == Stats.TotalEnergy) is { } totalEnergyToAttackRatePvp)
|
||||
{
|
||||
charClass.AttributeCombinations.Remove(totalEnergyToAttackRatePvp);
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.FenrirBaseDmg && attrCombo.InputAttribute == Stats.TotalStrength) is { } totalStrengthToFenrirBaseDmg)
|
||||
{
|
||||
totalStrengthToFenrirBaseDmg.InputOperand = 1.0f / 5;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.FenrirBaseDmg && attrCombo.InputAttribute == Stats.TotalVitality) is { } totalVitalityToFenrirBaseDmg)
|
||||
{
|
||||
totalVitalityToFenrirBaseDmg.InputOperand = 1.0f / 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Summoner classes.
|
||||
else if (charClass.Number == 20 || charClass.Number == 22 || charClass.Number == 23)
|
||||
{
|
||||
charClass.StatAttributes.First(attr => attr.Attribute == Stats.CurrentMana).BaseValue = 40;
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.DefenseBase && attrCombo.InputAttribute == Stats.TotalAgility) is { } totalAgilityToDefenseBase)
|
||||
{
|
||||
totalAgilityToDefenseBase.InputOperand = 1.0f / 3;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.DefenseRatePvp && attrCombo.InputAttribute == Stats.TotalAgility) is { } totalAgilityToDefenseRatePvp)
|
||||
{
|
||||
totalAgilityToDefenseRatePvp.InputOperand = 0.5f;
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.AttackRatePvp && attrCombo.InputAttribute == Stats.TotalAgility) is { } totalAgilityToAttackRatePvp)
|
||||
{
|
||||
totalAgilityToAttackRatePvp.InputOperand = 3.5f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
});
|
||||
|
||||
// Update Force Wave Strengthener skill
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ForceWave) is { } forceWave
|
||||
&& gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ForceWaveStreng) is { } forceWaveStr)
|
||||
{
|
||||
forceWaveStr.AttackDamage = forceWave.AttackDamage;
|
||||
forceWaveStr.DamageType = forceWave.DamageType;
|
||||
forceWaveStr.ElementalModifierTarget = forceWave.ElementalModifierTarget;
|
||||
forceWaveStr.ImplicitTargetRange = forceWave.ImplicitTargetRange;
|
||||
forceWaveStr.MovesTarget = forceWave.MovesTarget;
|
||||
forceWaveStr.MovesToTarget = forceWave.MovesToTarget;
|
||||
forceWaveStr.SkillType = forceWave.SkillType;
|
||||
forceWaveStr.Target = forceWave.Target;
|
||||
forceWaveStr.TargetRestriction = forceWave.TargetRestriction;
|
||||
forceWaveStr.MagicEffectDef = forceWave.MagicEffectDef;
|
||||
|
||||
if (forceWave.AreaSkillSettings is { } areaSkillSettings)
|
||||
{
|
||||
forceWaveStr.AreaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
var id = forceWaveStr.AreaSkillSettings.GetId();
|
||||
forceWaveStr.AreaSkillSettings.AssignValuesOf(areaSkillSettings, gameConfiguration);
|
||||
forceWaveStr.AreaSkillSettings.SetGuid(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
// <copyright file="FixDamageAbsorbItemsUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the damage absorption settings for items and skills.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("280ACE93-2B96-476C-A4AF-4FDA7611D5D5")]
|
||||
public class FixDamageAbsorbItemsUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fixed damage absorb items/skills";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes the damage absorbtion settings for items and skills.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDamageAbsorbItems;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 08, 16, 14, 00, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
FixSoulBarrierEffect(gameConfiguration);
|
||||
FixGuardianAngel(gameConfiguration);
|
||||
FixImp(gameConfiguration);
|
||||
FixDinorant(gameConfiguration);
|
||||
FixDemon(gameConfiguration);
|
||||
FixSpiritOfGuardian(gameConfiguration);
|
||||
FixPanda(gameConfiguration);
|
||||
FixUnicorn(gameConfiguration);
|
||||
FixSkeleton(gameConfiguration, context);
|
||||
|
||||
FixHarmonyDefenseOption(gameConfiguration);
|
||||
|
||||
FixWings(gameConfiguration, 12, 0);
|
||||
FixWings(gameConfiguration, 12, 1);
|
||||
FixWings(gameConfiguration, 12, 2);
|
||||
FixWings(gameConfiguration, 12, 41);
|
||||
|
||||
FixWings(gameConfiguration, 12, 3);
|
||||
FixWings(gameConfiguration, 12, 4);
|
||||
FixWings(gameConfiguration, 12, 5);
|
||||
FixWings(gameConfiguration, 12, 6);
|
||||
FixWings(gameConfiguration, 12, 42);
|
||||
|
||||
FixWings(gameConfiguration, 12, 49);
|
||||
FixWings(gameConfiguration, 13, 30);
|
||||
|
||||
FixWings(gameConfiguration, 12, 36);
|
||||
FixWings(gameConfiguration, 12, 37);
|
||||
FixWings(gameConfiguration, 12, 38);
|
||||
FixWings(gameConfiguration, 12, 39);
|
||||
FixWings(gameConfiguration, 12, 40);
|
||||
FixWings(gameConfiguration, 12, 43);
|
||||
FixWings(gameConfiguration, 12, 50);
|
||||
|
||||
FixDamageIncreaseTable(gameConfiguration, 12, 0); // First and Third Wings
|
||||
FixDamageIncreaseTable(gameConfiguration, 12, 3); // Second Wings
|
||||
FixDamageAbsorbTable(gameConfiguration); // One for all
|
||||
}
|
||||
|
||||
private static void FixDamageIncreaseTable(GameConfiguration gameConfiguration, short group, short number)
|
||||
{
|
||||
var wings = gameConfiguration.Items.FirstOrDefault(item => item.Group == group && item.Number == number);
|
||||
if (wings is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageIncreaseTable = wings.BasePowerUpAttributes
|
||||
.FirstOrDefault(a => a.TargetAttribute == Stats.AttackDamageIncrease)
|
||||
?.BonusPerLevelTable;
|
||||
if (damageIncreaseTable is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var row in damageIncreaseTable.BonusPerLevel)
|
||||
{
|
||||
if (row.AdditionalValue < 1)
|
||||
{
|
||||
row.AdditionalValue += 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixDamageAbsorbTable(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var wings = gameConfiguration.Items.FirstOrDefault(item => item.Group == 12 && item.Number == 0);
|
||||
if (wings is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageDecreaseTable = wings.BasePowerUpAttributes
|
||||
.FirstOrDefault(a => a.TargetAttribute == Stats.DamageReceiveDecrement)
|
||||
?.BonusPerLevelTable;
|
||||
if (damageDecreaseTable is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var row in damageDecreaseTable.BonusPerLevel)
|
||||
{
|
||||
if (row.AdditionalValue <= 0)
|
||||
{
|
||||
row.AdditionalValue += 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixHarmonyDefenseOption(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var harmonyOption = gameConfiguration.ItemOptions
|
||||
.FirstOrDefault(io => io.Name == HarmonyOptions.DefenseOptionsName)
|
||||
?.PossibleOptions.FirstOrDefault(o => o.Number == 7);
|
||||
if (harmonyOption is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var options = harmonyOption.LevelDependentOptions.OrderBy(o => o.RequiredItemLevel).ToList();
|
||||
float[] values = [0.97f, 0.96f, 0.95f, 0.94f, 0.93f];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
var option = options[i];
|
||||
var boost = option.PowerUpDefinition?.Boost;
|
||||
if (boost is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
boost.ConstantValue.Value = values[i];
|
||||
boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixWings(GameConfiguration gameConfiguration, short group, short number)
|
||||
{
|
||||
var wings = gameConfiguration.Items.FirstOrDefault(item => item.Group == group && item.Number == number);
|
||||
if (wings is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageDecrease = wings.BasePowerUpAttributes
|
||||
.FirstOrDefault(a => a.TargetAttribute == Stats.DamageReceiveDecrement && a.AggregateType != AggregateType.Multiplicate);
|
||||
if (damageDecrease is not null)
|
||||
{
|
||||
damageDecrease.BaseValue += 1f;
|
||||
damageDecrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
var damageIncrease = wings.BasePowerUpAttributes
|
||||
.FirstOrDefault(a => a.TargetAttribute == Stats.AttackDamageIncrease && a.AggregateType != AggregateType.Multiplicate);
|
||||
if (damageIncrease is not null)
|
||||
{
|
||||
damageIncrease.BaseValue += 1f;
|
||||
damageIncrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixGuardianAngel(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var angel = gameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 0 });
|
||||
if (angel is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageDecrease = angel.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.DamageReceiveDecrement);
|
||||
if (damageDecrease is not null)
|
||||
{
|
||||
damageDecrease.BaseValue = 0.8f;
|
||||
damageDecrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixImp(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var imp = gameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 1 });
|
||||
if (imp is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageIncrease = imp.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.AttackDamageIncrease);
|
||||
if (damageIncrease is not null)
|
||||
{
|
||||
damageIncrease.BaseValue = 1.3f;
|
||||
damageIncrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixDemon(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var demon = gameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 64 });
|
||||
if (demon is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageIncrease = demon.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.AttackDamageIncrease);
|
||||
if (damageIncrease is not null)
|
||||
{
|
||||
damageIncrease.BaseValue = 1.4f;
|
||||
damageIncrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixSpiritOfGuardian(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var spiritOfGuardian = gameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 65 });
|
||||
if (spiritOfGuardian is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageDecrease = spiritOfGuardian.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.DamageReceiveDecrement);
|
||||
if (damageDecrease is not null)
|
||||
{
|
||||
damageDecrease.BaseValue = 0.7f;
|
||||
damageDecrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixDinorant(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var dinorant = gameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 2 });
|
||||
if (dinorant is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageDecrease = dinorant.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.DamageReceiveDecrement);
|
||||
if (damageDecrease is not null)
|
||||
{
|
||||
damageDecrease.BaseValue = 0.9f;
|
||||
damageDecrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
var damageIncrease = dinorant.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.AttackDamageIncrease);
|
||||
if (damageIncrease is not null)
|
||||
{
|
||||
damageIncrease.BaseValue = 1.15f;
|
||||
damageIncrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixPanda(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var panda = gameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 80 });
|
||||
if (panda is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var exp = panda.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.ExperienceRate);
|
||||
if (exp is not null)
|
||||
{
|
||||
exp.BaseValue = 1.5f;
|
||||
exp.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
var masterExp = panda.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.MasterExperienceRate);
|
||||
if (masterExp is not null)
|
||||
{
|
||||
masterExp.BaseValue = 1.5f;
|
||||
masterExp.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixUnicorn(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var panda = gameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 106 });
|
||||
if (panda is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var moneyRate = panda.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.MoneyAmountRate);
|
||||
if (moneyRate is not null)
|
||||
{
|
||||
moneyRate.BaseValue = 1.5f;
|
||||
moneyRate.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixSkeleton(GameConfiguration gameConfiguration, IContext context)
|
||||
{
|
||||
var skeleton = gameConfiguration.Items.FirstOrDefault(item => item is { Group: 13, Number: 123 });
|
||||
if (skeleton is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var damageIncrease = skeleton.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.AttackDamageIncrease);
|
||||
if (damageIncrease is not null)
|
||||
{
|
||||
damageIncrease.BaseValue = 1.2f;
|
||||
damageIncrease.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
var exp = skeleton.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.ExperienceRate);
|
||||
if (exp is not null)
|
||||
{
|
||||
exp.BaseValue = 1.3f;
|
||||
exp.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
var masterExp = skeleton.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.MasterExperienceRate);
|
||||
if (masterExp is null)
|
||||
{
|
||||
masterExp = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
masterExp.TargetAttribute = Stats.MasterExperienceRate.GetPersistent(gameConfiguration);
|
||||
skeleton.BasePowerUpAttributes.Add(masterExp);
|
||||
}
|
||||
|
||||
masterExp.BaseValue = 1.3f;
|
||||
masterExp.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
private static void FixSoulBarrierEffect(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var soulBarrierEffect = gameConfiguration.MagicEffects.FirstOrDefault(effect => effect.Number == (short)MagicEffectNumber.SoulBarrier);
|
||||
if (soulBarrierEffect is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var powerUp = soulBarrierEffect.PowerUpDefinitions.FirstOrDefault();
|
||||
if (powerUp is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var boost = powerUp.Boost;
|
||||
if (boost is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boost.ConstantValue.Value = 0.90f;
|
||||
boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
|
||||
|
||||
var boostPerEnergy = boost.RelatedValues.FirstOrDefault(value => value.InputAttribute == Stats.TotalEnergy);
|
||||
if (boostPerEnergy is not null)
|
||||
{
|
||||
boostPerEnergy.InputOperand = 1 - (0.01f / 200f);
|
||||
boostPerEnergy.InputOperator = InputOperator.ExponentiateByAttribute;
|
||||
}
|
||||
|
||||
var boostPerAgility = boost.RelatedValues.FirstOrDefault(value => value.InputAttribute == Stats.TotalAgility);
|
||||
if (boostPerAgility is not null)
|
||||
{
|
||||
boostPerAgility.InputOperand = 1 - (0.01f / 50f);
|
||||
boostPerAgility.InputOperator = InputOperator.ExponentiateByAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// <copyright file="FixDamageCalcsPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes character stats, magic effects, items, and options related to damage.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("42B1582B-667F-4098-A339-DDA8560157E3")]
|
||||
public class FixDamageCalcsPlugIn075 : FixDamageCalcsPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDamageCalcs075;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.UpdateWeaponItems(context, gameConfiguration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// <copyright file="FixDamageCalcsPlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes character stats, skills, magic effects, items, and options related to damage.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("A4410B7B-7E5F-409C-9F6F-4E216208829A")]
|
||||
public class FixDamageCalcsPlugIn095D : FixDamageCalcsPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDamageCalcs095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.UpdateExcellentOptions(gameConfiguration);
|
||||
this.UpdateAmmoItems(context, gameConfiguration, [0, 0.03f, 0.05f]);
|
||||
this.UpdateWeaponItems(context, gameConfiguration);
|
||||
this.AddDinorantBasePowerUp(context, gameConfiguration);
|
||||
this.UpdateMGStaffsItemSlot(gameConfiguration);
|
||||
}
|
||||
}
|
||||
1101
src/Persistence/Initialization/Updates/FixDamageCalcsPlugInBase.cs
Normal file
1101
src/Persistence/Initialization/Updates/FixDamageCalcsPlugInBase.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,450 @@
|
||||
// <copyright file="FixDamageCalcsPlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes character stats, skills, magic effects, items, and options related to damage. It also adds the Berserker magic effect.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("077BA63D-F201-41BE-8A65-CFB859482A1B")]
|
||||
public class FixDamageCalcsPlugInSeason6 : FixDamageCalcsPlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal new const string PlugInDescription = "This update fixes character stats, skills, magic effects, items, and options related to damage. It also adds the Berserker magic effect.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDamageCalcsSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.UpdateExcellentOptions(gameConfiguration);
|
||||
this.UpdateAmmoItems(context, gameConfiguration, [0, 0.03f, 0.05f, 0.07f], true);
|
||||
this.AddDinorantBasePowerUp(context, gameConfiguration);
|
||||
this.UpdateMGStaffsItemSlot(gameConfiguration);
|
||||
|
||||
// Create Berserker magic effect
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)MagicEffectNumber.Berserker;
|
||||
magicEffect.Name = "Berserker Buff Skill Effect";
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = false;
|
||||
magicEffect.StopByDeath = true;
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = 30; // 30 Seconds
|
||||
|
||||
var durationPerEnergy = context.CreateNew<AttributeRelationship>();
|
||||
durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
durationPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
durationPerEnergy.InputOperand = 1f / 20f; // 20 energy adds 1 second duration
|
||||
magicEffect.Duration.RelatedValues.Add(durationPerEnergy);
|
||||
|
||||
// Mana (and damage) multiplier (buff)
|
||||
var manaPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(manaPowerUpDefinition);
|
||||
manaPowerUpDefinition.TargetAttribute = Stats.BerserkerManaMultiplier.GetPersistent(gameConfiguration);
|
||||
manaPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
|
||||
var manaMultiplier = context.CreateNew<AttributeRelationship>();
|
||||
manaMultiplier.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
manaMultiplier.InputOperator = InputOperator.Multiply;
|
||||
manaMultiplier.InputOperand = 1f / 3000f;
|
||||
manaPowerUpDefinition.Boost.RelatedValues.Add(manaMultiplier);
|
||||
|
||||
// Health (and defense) multiplier factor (debuff)
|
||||
var healthPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(healthPowerUpDefinition);
|
||||
healthPowerUpDefinition.TargetAttribute = Stats.BerserkerHealthDecrement.GetPersistent(gameConfiguration);
|
||||
healthPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
healthPowerUpDefinition.Boost.ConstantValue.Value = -0.4f;
|
||||
|
||||
var healthMultiplier = context.CreateNew<AttributeRelationship>();
|
||||
healthMultiplier.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
healthMultiplier.InputOperator = InputOperator.Multiply;
|
||||
healthMultiplier.InputOperand = 1f / 6000f;
|
||||
healthPowerUpDefinition.Boost.RelatedValues.Add(healthMultiplier);
|
||||
|
||||
// Min physical damage bonus (later gets multiplied by the mana multiplier)
|
||||
var minPhysPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(minPhysPowerUpDefinition);
|
||||
minPhysPowerUpDefinition.TargetAttribute = Stats.BerserkerMinPhysDmgBonus.GetPersistent(gameConfiguration);
|
||||
minPhysPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
minPhysPowerUpDefinition.Boost.ConstantValue.Value = 140f;
|
||||
|
||||
var minDmgPerStrengthAndAgility = context.CreateNew<AttributeRelationship>();
|
||||
minDmgPerStrengthAndAgility.InputAttribute = Stats.TotalStrengthAndAgility.GetPersistent(gameConfiguration);
|
||||
minDmgPerStrengthAndAgility.InputOperator = InputOperator.Multiply;
|
||||
minDmgPerStrengthAndAgility.InputOperand = 1.0f / 50;
|
||||
minPhysPowerUpDefinition.Boost.RelatedValues.Add(minDmgPerStrengthAndAgility);
|
||||
|
||||
// Max physical damage bonus (later gets multiplied by the mana multiplier)
|
||||
var maxPhysPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(maxPhysPowerUpDefinition);
|
||||
maxPhysPowerUpDefinition.TargetAttribute = Stats.BerserkerMaxPhysDmgBonus.GetPersistent(gameConfiguration);
|
||||
maxPhysPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
maxPhysPowerUpDefinition.Boost.ConstantValue.Value = 160f;
|
||||
|
||||
var maxDmgPerStrengthAndAgility = context.CreateNew<AttributeRelationship>();
|
||||
maxDmgPerStrengthAndAgility.InputAttribute = Stats.TotalStrengthAndAgility.GetPersistent(gameConfiguration);
|
||||
maxDmgPerStrengthAndAgility.InputOperator = InputOperator.Multiply;
|
||||
maxDmgPerStrengthAndAgility.InputOperand = 1.0f / 30;
|
||||
maxPhysPowerUpDefinition.Boost.RelatedValues.Add(maxDmgPerStrengthAndAgility);
|
||||
|
||||
// Placeholder for the Berserker Strengthener master skill
|
||||
var strengthenerCurseDmgMultiplier = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(strengthenerCurseDmgMultiplier);
|
||||
strengthenerCurseDmgMultiplier.TargetAttribute = Stats.BerserkerCurseMultiplier.GetPersistent(gameConfiguration);
|
||||
strengthenerCurseDmgMultiplier.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
|
||||
// Placeholder for the Berserker Proficiency master skill
|
||||
var proficiencyDmgMultiplier = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(proficiencyDmgMultiplier);
|
||||
proficiencyDmgMultiplier.TargetAttribute = Stats.BerserkerProficiencyMultiplier.GetPersistent(gameConfiguration);
|
||||
proficiencyDmgMultiplier.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
|
||||
var berserkers = gameConfiguration.Skills.Where(s =>
|
||||
s.Number == (short)SkillNumber.Berserker ||
|
||||
s.Number == (short)SkillNumber.BerserkerStrengthener ||
|
||||
s.Number == (short)SkillNumber.BerserkerProficiency);
|
||||
foreach (var berserker_ in berserkers)
|
||||
{
|
||||
berserker_.SkillType = SkillType.Buff;
|
||||
berserker_.TargetRestriction = SkillTargetRestriction.Self;
|
||||
berserker_.MagicEffectDef = magicEffect;
|
||||
}
|
||||
|
||||
// Update Infinite Arrow magic effect
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.InfiniteArrow) is { } infiniteArrowEffect)
|
||||
{
|
||||
infiniteArrowEffect.Duration!.ConstantValue.Value = 600;
|
||||
|
||||
var powerUps = infiniteArrowEffect.PowerUpDefinitions.ToList();
|
||||
foreach (var powerUp in powerUps)
|
||||
{
|
||||
// SkillExtraManaCost is the old ManaLossAfterHit.
|
||||
if (powerUp.TargetAttribute == Stats.SkillExtraManaCost || powerUp.TargetAttribute == Stats.BaseDamageBonus)
|
||||
{
|
||||
infiniteArrowEffect.PowerUpDefinitions.Remove(powerUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update options
|
||||
var ancientSetsOpts = gameConfiguration.ItemOptions.Where(io => io.Name.ValueInNeutralLanguageAsSpan.EndsWith("(Ancient Set)"));
|
||||
var finalDamageBonus = Stats.FinalDamageBonus.GetPersistent(gameConfiguration);
|
||||
var wizardryBaseDmgIncrease = Stats.WizardryBaseDmgIncrease.GetPersistent(gameConfiguration);
|
||||
foreach (var ancientSetOpts in ancientSetsOpts)
|
||||
{
|
||||
if (ancientSetOpts.PossibleOptions.FirstOrDefault(o => o.PowerUpDefinition?.TargetAttribute == Stats.BaseDamageBonus) is { } dmgBonusAncOpt)
|
||||
{
|
||||
dmgBonusAncOpt.PowerUpDefinition!.TargetAttribute = finalDamageBonus;
|
||||
}
|
||||
|
||||
if (ancientSetOpts.PossibleOptions.FirstOrDefault(o => o.PowerUpDefinition?.TargetAttribute == Stats.WizardryAttackDamageIncrease) is { } wizDmgIncAncOpt)
|
||||
{
|
||||
wizDmgIncAncOpt.PowerUpDefinition!.TargetAttribute = wizardryBaseDmgIncrease;
|
||||
wizDmgIncAncOpt.PowerUpDefinition.Boost!.ConstantValue.Value += 1f;
|
||||
wizDmgIncAncOpt.PowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
}
|
||||
|
||||
var armorDamageDecrease = Stats.ArmorDamageDecrease.GetPersistent(gameConfiguration);
|
||||
var harmonyDefOptions = gameConfiguration.ItemOptions.FirstOrDefault(o => o.Name == "Harmony Defense Options");
|
||||
if (harmonyDefOptions is not null
|
||||
&& harmonyDefOptions.PossibleOptions.FirstOrDefault(o => o.Number == 7) is { } dmgDecOpt)
|
||||
{
|
||||
foreach (var level in dmgDecOpt.LevelDependentOptions)
|
||||
{
|
||||
level.PowerUpDefinition!.TargetAttribute = armorDamageDecrease;
|
||||
level.PowerUpDefinition.Boost!.ConstantValue.Value = 1f - level.PowerUpDefinition!.Boost!.ConstantValue.Value;
|
||||
level.PowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddRaw;
|
||||
}
|
||||
}
|
||||
|
||||
var socketOptionsFireId = new Guid("00000083-0032-0000-0000-000000000000");
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(o => o.GetId() == socketOptionsFireId) is { } fireSocketOptions
|
||||
&& fireSocketOptions.PossibleOptions.FirstOrDefault(o => o.Number == 0) is { } lvlDmgSockOpt)
|
||||
{
|
||||
var totalLevel = Stats.TotalLevel.GetPersistent(gameConfiguration);
|
||||
var baseDamageBonus = Stats.BaseDamageBonus.GetPersistent(gameConfiguration);
|
||||
foreach (var level in lvlDmgSockOpt.LevelDependentOptions)
|
||||
{
|
||||
level.PowerUpDefinition!.TargetAttribute = baseDamageBonus;
|
||||
|
||||
if (level.PowerUpDefinition!.Boost!.RelatedValues.FirstOrDefault() is { } relatedValue)
|
||||
{
|
||||
relatedValue.InputAttribute = totalLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var socketOptionsWaterId = new Guid("00000083-0033-0000-0000-000000000000");
|
||||
if (gameConfiguration.ItemOptions.FirstOrDefault(o => o.GetId() == socketOptionsWaterId) is { } waterSocketOptions
|
||||
&& waterSocketOptions.PossibleOptions.FirstOrDefault(o => o.Number == 3) is { } dmgDecSockOpt)
|
||||
{
|
||||
foreach (var level in dmgDecSockOpt.LevelDependentOptions)
|
||||
{
|
||||
level.PowerUpDefinition!.TargetAttribute = armorDamageDecrease;
|
||||
level.PowerUpDefinition.Boost!.ConstantValue.Value = 1f - level.PowerUpDefinition.Boost.ConstantValue.Value;
|
||||
level.PowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddRaw;
|
||||
}
|
||||
}
|
||||
|
||||
// Update RF glove weapons item slot
|
||||
var leftOrRightHandSlot = gameConfiguration.ItemSlotTypes.First(t => t.ItemSlots.Contains(0) && t.ItemSlots.Contains(1));
|
||||
if (gameConfiguration.Items.Where(i => i.Group == (byte)ItemGroups.Swords && i.Width == 1 && i.Number >= 32) is { } gloveWeapons)
|
||||
{
|
||||
foreach (var gloveWeapon in gloveWeapons)
|
||||
{
|
||||
gloveWeapon.ItemSlot = leftOrRightHandSlot;
|
||||
}
|
||||
}
|
||||
|
||||
// Add Stats.IsBookEquipped attribute to books
|
||||
if (gameConfiguration.Items.Where(i => i.Group == (byte)ItemGroups.Staff && i.Height == 2) is { } bookItems)
|
||||
{
|
||||
var isBookEquipped = Stats.IsBookEquipped.GetPersistent(gameConfiguration);
|
||||
foreach (var bookItem in bookItems)
|
||||
{
|
||||
if (bookItem.BasePowerUpAttributes.FirstOrDefault(p => p.TargetAttribute == isBookEquipped) is null)
|
||||
{
|
||||
var powerUpDefinition = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = isBookEquipped;
|
||||
powerUpDefinition.BaseValue = 1;
|
||||
powerUpDefinition.AggregateType = AggregateType.AddRaw;
|
||||
bookItem.BasePowerUpAttributes.Add(powerUpDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Lance) is { } lance)
|
||||
{
|
||||
lance.DamageType = DamageType.Wizardry;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.PowerSlash) is { } powerSlash
|
||||
&& powerSlash.Requirements.FirstOrDefault(req => req.Attribute == Stats.TotalEnergy) is { } energyRequirement)
|
||||
{
|
||||
powerSlash.Requirements.Remove(energyRequirement);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DrainLife) is { } drainLife)
|
||||
{
|
||||
drainLife.DamageType = DamageType.Wizardry;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainLightning) is { } chainLightning)
|
||||
{
|
||||
chainLightning.DamageType = DamageType.Wizardry;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Berserker) is { } berserker)
|
||||
{
|
||||
berserker.DamageType = DamageType.None;
|
||||
berserker.SkillType = SkillType.Buff;
|
||||
berserker.TargetRestriction = SkillTargetRestriction.Self;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.LightningShock) is { } lightningShock)
|
||||
{
|
||||
lightningShock.DamageType = DamageType.Wizardry;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainLightningStr) is { } chainLightningStr)
|
||||
{
|
||||
chainLightningStr.DamageType = DamageType.Wizardry;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.LightningShockStr) is { } lightningShockStr)
|
||||
{
|
||||
lightningShockStr.DamageType = DamageType.Wizardry;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DrainLifeStrengthener) is { } drainLifeStr)
|
||||
{
|
||||
drainLifeStr.DamageType = DamageType.Wizardry;
|
||||
}
|
||||
|
||||
// Create Skill attribute relationships
|
||||
AddAttributeRelationship(SkillNumber.Nova, Stats.SkillDamageBonus, 1.0f / 2, Stats.TotalStrength);
|
||||
AddAttributeRelationship(SkillNumber.Nova, Stats.SkillDamageBonus, 1, Stats.NovaStageDamage);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.Earthshake, Stats.SkillDamageBonus, 1.0f / 10, Stats.TotalStrength);
|
||||
AddAttributeRelationship(SkillNumber.Earthshake, Stats.SkillDamageBonus, 1.0f / 5, Stats.TotalLeadership);
|
||||
AddAttributeRelationship(SkillNumber.Earthshake, Stats.SkillDamageBonus, 10, Stats.HorseLevel);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.ElectricSpike, Stats.SkillDamageBonus, 50, Stats.NearbyPartyMemberCount);
|
||||
AddAttributeRelationship(SkillNumber.ElectricSpike, Stats.SkillDamageBonus, 1.0f / 10, Stats.TotalLeadership);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.ChaoticDiseier, Stats.SkillDamageBonus, 1.0f / 30, Stats.TotalStrength);
|
||||
AddAttributeRelationship(SkillNumber.ChaoticDiseier, Stats.SkillDamageBonus, 1.0f / 55, Stats.TotalEnergy);
|
||||
|
||||
SkillNumber[] lordSkills = [SkillNumber.Force, SkillNumber.FireBlast, SkillNumber.FireBurst, SkillNumber.ForceWave, SkillNumber.FireScream];
|
||||
foreach (var lordSkillNumber in lordSkills)
|
||||
{
|
||||
AddAttributeRelationship(lordSkillNumber, Stats.SkillDamageBonus, 1.0f / 25, Stats.TotalStrength);
|
||||
AddAttributeRelationship(lordSkillNumber, Stats.SkillDamageBonus, 1.0f / 50, Stats.TotalEnergy);
|
||||
}
|
||||
|
||||
AddAttributeRelationship(SkillNumber.MultiShot, Stats.SkillMultiplier, 0.8f, Stats.SkillMultiplier, AggregateType.Multiplicate);
|
||||
|
||||
void AddAttributeRelationship(SkillNumber skillNumber, AttributeDefinition targetAttribute, float multiplier, AttributeDefinition sourceAttribute, AggregateType aggregateType = AggregateType.AddRaw)
|
||||
{
|
||||
var skill = gameConfiguration.Skills.First(s => s.Number == (int)skillNumber);
|
||||
var relationship = CharacterClassHelper.CreateAttributeRelationship(context, gameConfiguration, targetAttribute, multiplier, sourceAttribute, aggregateType: aggregateType);
|
||||
skill.AttributeRelationships.Add(relationship);
|
||||
}
|
||||
|
||||
// Update master skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.WeaponMasteryBladeMaster)?.MasterDefinition is { } weaponMasteryBladeMaster)
|
||||
{
|
||||
weaponMasteryBladeMaster.TargetAttribute = Stats.MasterSkillPhysBonusDmg.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.TwoHandedSwordMaster)?.MasterDefinition is { } twoHandedSwordMaster)
|
||||
{
|
||||
twoHandedSwordMaster.TargetAttribute = Stats.TwoHandedSwordMasteryBonusDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.OneHandedSwordMaster)?.MasterDefinition is { } oneHandedSwordMaster)
|
||||
{
|
||||
oneHandedSwordMaster.TargetAttribute = Stats.WeaponMasteryAttackSpeed.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.OneHandedStaffStrengthener)?.MasterDefinition is { } oneHandedtaffStr)
|
||||
{
|
||||
oneHandedtaffStr.TargetAttribute = Stats.OneHandedStaffBonusBaseDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.TwoHandedStaffStrengthener)?.MasterDefinition is { } twoHandedStaffStr)
|
||||
{
|
||||
twoHandedStaffStr.TargetAttribute = Stats.TwoHandedStaffBonusBaseDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.OneHandedStaffMaster)?.MasterDefinition is { } oneHandedStaffMaster)
|
||||
{
|
||||
oneHandedStaffMaster.TargetAttribute = Stats.WeaponMasteryAttackSpeed.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.TwoHandedStaffMaster)?.MasterDefinition is { } twoHandedStaffMaster)
|
||||
{
|
||||
twoHandedStaffMaster.TargetAttribute = Stats.TwoHandedStaffMasteryBonusDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.WeaponMasteryHighElf)?.MasterDefinition is { } weaponMasteryHighElf)
|
||||
{
|
||||
weaponMasteryHighElf.TargetAttribute = Stats.MasterSkillPhysBonusDmg.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BowStrengthener)?.MasterDefinition is { } bowStr)
|
||||
{
|
||||
bowStr.TargetAttribute = Stats.BowStrBonusDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.CrossbowStrengthener)?.MasterDefinition is { } crossbowStr)
|
||||
{
|
||||
crossbowStr.TargetAttribute = Stats.CrossBowStrBonusDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BowMastery)?.MasterDefinition is { } bowMastery)
|
||||
{
|
||||
bowMastery.TargetAttribute = Stats.WeaponMasteryAttackSpeed.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.CrossbowMastery)?.MasterDefinition is { } crossbowMastery)
|
||||
{
|
||||
crossbowMastery.TargetAttribute = Stats.CrossBowMasteryBonusDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.InfinityArrowStr)?.MasterDefinition is { } infinityArrowStr)
|
||||
{
|
||||
infinityArrowStr.ValueFormula = $"1 + {infinityArrowStr.ValueFormula}";
|
||||
infinityArrowStr.Aggregation = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.MagicMasterySummoner)?.MasterDefinition is { } magicMasterySummoner)
|
||||
{
|
||||
magicMasterySummoner.TargetAttribute = Stats.WizardryAndCurseBaseDmgBonus.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.OtherWorldTomeStreng)?.MasterDefinition is { } otherWorldTomeStr)
|
||||
{
|
||||
otherWorldTomeStr.TargetAttribute = Stats.BookBonusBaseDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.StickMastery)?.MasterDefinition is { } stickMastery)
|
||||
{
|
||||
stickMastery.TargetAttribute = Stats.StickMasteryBonusDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.OtherWorldTomeMastery)?.MasterDefinition is { } otherWorldTomeMastery)
|
||||
{
|
||||
otherWorldTomeMastery.TargetAttribute = Stats.WeaponMasteryAttackSpeed.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BerserkerStrengthener)?.MasterDefinition is { } berserkerStr)
|
||||
{
|
||||
berserkerStr.TargetAttribute = Stats.BerserkerCurseMultiplier.GetPersistent(gameConfiguration);
|
||||
berserkerStr.ValueFormula += " / 100";
|
||||
berserkerStr.Aggregation = AggregateType.AddRaw;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BerserkerProficiency)?.MasterDefinition is { } berserkerProf)
|
||||
{
|
||||
berserkerProf.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.BerserkerStrengthener);
|
||||
berserkerProf.TargetAttribute = Stats.BerserkerProficiencyMultiplier.GetPersistent(gameConfiguration);
|
||||
berserkerProf.ValueFormula += " / 100";
|
||||
berserkerProf.Aggregation = AggregateType.AddRaw;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.MinimumWizCurseInc)?.MasterDefinition is { } minimumWizCurseInc)
|
||||
{
|
||||
minimumWizCurseInc.TargetAttribute = Stats.MinWizardryAndCurseDmgBonus.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.WeaponMasteryDuelMaster)?.MasterDefinition is { } weaponMasteryDuelMaster)
|
||||
{
|
||||
weaponMasteryDuelMaster.TargetAttribute = Stats.MasterSkillPhysBonusDmg.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.WeaponMasteryLordEmperor)?.MasterDefinition is { } weaponMasteryLordEmperor)
|
||||
{
|
||||
weaponMasteryLordEmperor.TargetAttribute = Stats.MasterSkillPhysBonusDmg.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ScepterMastery)?.MasterDefinition is { } scepterMastery)
|
||||
{
|
||||
scepterMastery.TargetAttribute = Stats.ScepterMasteryBonusDamage.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.WeaponMasteryFistMaster)?.MasterDefinition is { } weaponMasteryFistMaster)
|
||||
{
|
||||
weaponMasteryFistMaster.TargetAttribute = Stats.MasterSkillPhysBonusDmg.GetPersistent(gameConfiguration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// <copyright file="FixDefenseCalcsPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes character stats, magic effects, and options related to defense.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("683A8F8F-EFE9-4EF4-B536-21048E195A87")]
|
||||
public class FixDefenseCalcsPlugIn075 : FixDefenseCalcsPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDefenseCalcs075;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// <copyright file="FixDefenseCalcsPlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes character stats, magic effects, and options related to defense.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("C6945ADC-0313-47AC-AFAE-61D0544C8935")]
|
||||
public class FixDefenseCalcsPlugIn095D : FixDefenseCalcsPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDefenseCalcs095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// <copyright file="FixDefenseCalcsPlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes character stats, magic effects, and options related to defense.
|
||||
/// </summary>
|
||||
public abstract class FixDefenseCalcsPlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Defense Calculations";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes character stats, magic effects, and options related to defense.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 05, 23, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Update attributes
|
||||
var defenseFinal = context.CreateNew<AttributeDefinition>(Stats.DefenseFinal.Id, Stats.DefenseFinal.Designation, Stats.DefenseFinal.Description);
|
||||
gameConfiguration.Attributes.Add(defenseFinal);
|
||||
var bonusDefenseRateWithShield = context.CreateNew<AttributeDefinition>(Stats.BonusDefenseRateWithShield.Id, Stats.BonusDefenseRateWithShield.Designation, Stats.BonusDefenseRateWithShield.Description);
|
||||
gameConfiguration.Attributes.Add(bonusDefenseRateWithShield);
|
||||
var defenseShield = context.CreateNew<AttributeDefinition>(Stats.DefenseShield.Id, Stats.DefenseShield.Designation, Stats.DefenseShield.Description);
|
||||
gameConfiguration.Attributes.Add(defenseShield);
|
||||
var shieldItemDefenseIncrease = context.CreateNew<AttributeDefinition>(Stats.ShieldItemDefenseIncrease.Id, Stats.ShieldItemDefenseIncrease.Designation, Stats.ShieldItemDefenseIncrease.Description);
|
||||
gameConfiguration.Attributes.Add(shieldItemDefenseIncrease);
|
||||
|
||||
var shieldBlockDamageDecrementId = new Guid("DAC6690B-5922-4446-BCE5-5E701BE62EC1");
|
||||
if (gameConfiguration.Attributes.FirstOrDefault(a => a.Id == shieldBlockDamageDecrementId) is { } shieldBlockDamageDecrement)
|
||||
{
|
||||
gameConfiguration.Attributes.Remove(shieldBlockDamageDecrement);
|
||||
}
|
||||
|
||||
// Update attribute combinations
|
||||
var defenseBase = Stats.DefenseBase.GetPersistent(gameConfiguration);
|
||||
var defensePvm = Stats.DefensePvm.GetPersistent(gameConfiguration);
|
||||
var defensePvp = Stats.DefensePvp.GetPersistent(gameConfiguration);
|
||||
var bonusDefenseWithShield = Stats.BonusDefenseWithShield.GetPersistent(gameConfiguration);
|
||||
var isShieldEquipped = Stats.IsShieldEquipped.GetPersistent(gameConfiguration);
|
||||
var defenseRatePvm = Stats.DefenseRatePvm.GetPersistent(gameConfiguration);
|
||||
var isHorseEquipped = Stats.IsHorseEquipped.GetPersistent(gameConfiguration);
|
||||
var bonusDefenseWithHorse = Stats.BonusDefenseWithHorse.GetPersistent(gameConfiguration);
|
||||
|
||||
gameConfiguration.CharacterClasses.ForEach(charClass =>
|
||||
{
|
||||
var attrCombos = charClass.AttributeCombinations.ToList();
|
||||
foreach (var attrCombo in attrCombos)
|
||||
{
|
||||
if ((attrCombo.TargetAttribute == Stats.DefensePvm && attrCombo.InputAttribute == Stats.DefenseBase)
|
||||
|| (attrCombo.TargetAttribute == Stats.DefensePvp && attrCombo.InputAttribute == Stats.DefenseBase))
|
||||
{
|
||||
charClass.AttributeCombinations.Remove(attrCombo);
|
||||
}
|
||||
}
|
||||
|
||||
if (attrCombos.FirstOrDefault(attrCombo =>
|
||||
attrCombo.TargetAttribute == Stats.DefenseBase
|
||||
&& attrCombo.InputAttribute == Stats.BonusDefenseWithShield
|
||||
&& attrCombo.OperandAttribute == Stats.IsShieldEquipped) is { } bonusDefenseWithShieldToDefenseBase)
|
||||
{
|
||||
charClass.AttributeCombinations.Remove(bonusDefenseWithShieldToDefenseBase);
|
||||
}
|
||||
|
||||
var shieldDefenseToDefenseBase = context.CreateNew<AttributeRelationship>(
|
||||
defenseBase,
|
||||
1,
|
||||
defenseShield,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw);
|
||||
|
||||
var defenseBaseToDefenseFinal = context.CreateNew<AttributeRelationship>(
|
||||
defenseFinal,
|
||||
0.5f,
|
||||
defenseBase,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw);
|
||||
|
||||
var defenseFinalToDefensePvm = context.CreateNew<AttributeRelationship>(
|
||||
defensePvm,
|
||||
1,
|
||||
defenseFinal,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw);
|
||||
|
||||
var defenseFinalToDefensePvp = context.CreateNew<AttributeRelationship>(
|
||||
defensePvp,
|
||||
1,
|
||||
defenseFinal,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw);
|
||||
|
||||
if (attrCombos.FirstOrDefault(attrCombo => attrCombo.InputAttribute == Stats.DefenseIncreaseWithEquippedShield
|
||||
&& attrCombo.OperandAttribute == Stats.IsShieldEquipped) is { } defenseIncWithEquippedShieldToTempDefense)
|
||||
{
|
||||
var tempDefenseToDefenseFinal = context.CreateNew<AttributeRelationship>(
|
||||
defenseFinal,
|
||||
1,
|
||||
defenseIncWithEquippedShieldToTempDefense.TargetAttribute,
|
||||
InputOperator.Add,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate);
|
||||
|
||||
charClass.AttributeCombinations.Add(tempDefenseToDefenseFinal);
|
||||
}
|
||||
|
||||
var shieldItemDefenseIncreaseToDefenseFinal = context.CreateNew<AttributeRelationship>(
|
||||
defenseFinal,
|
||||
defenseShield,
|
||||
shieldItemDefenseIncrease,
|
||||
AggregateType.AddRaw);
|
||||
|
||||
var bonusDefenseWithShieldToDefenseFinal = context.CreateNew<AttributeRelationship>(
|
||||
defenseFinal,
|
||||
isShieldEquipped,
|
||||
bonusDefenseWithShield,
|
||||
AggregateType.AddFinal);
|
||||
|
||||
var bonusDefenseRateWithShieldToDefenseRatePvm = context.CreateNew<AttributeRelationship>(
|
||||
defenseRatePvm,
|
||||
isShieldEquipped,
|
||||
bonusDefenseRateWithShield,
|
||||
AggregateType.AddFinal);
|
||||
|
||||
charClass.AttributeCombinations.Add(shieldDefenseToDefenseBase);
|
||||
charClass.AttributeCombinations.Add(defenseBaseToDefenseFinal);
|
||||
charClass.AttributeCombinations.Add(defenseFinalToDefensePvm);
|
||||
charClass.AttributeCombinations.Add(defenseFinalToDefensePvp);
|
||||
charClass.AttributeCombinations.Add(shieldItemDefenseIncreaseToDefenseFinal);
|
||||
charClass.AttributeCombinations.Add(bonusDefenseWithShieldToDefenseFinal);
|
||||
charClass.AttributeCombinations.Add(bonusDefenseRateWithShieldToDefenseRatePvm);
|
||||
|
||||
// Lord classes.
|
||||
if (charClass.Number == 16 || charClass.Number == 17)
|
||||
{
|
||||
var bonusDefenseWithHorseToDefenseFinal = context.CreateNew<AttributeRelationship>(
|
||||
defenseFinal,
|
||||
isHorseEquipped,
|
||||
bonusDefenseWithHorse,
|
||||
AggregateType.AddFinal);
|
||||
|
||||
charClass.AttributeCombinations.Add(bonusDefenseWithHorseToDefenseFinal);
|
||||
}
|
||||
});
|
||||
|
||||
// Update magic effects
|
||||
var defenseEffect = gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.ShieldSkill);
|
||||
if (defenseEffect?.Duration is not null)
|
||||
{
|
||||
defenseEffect.Duration.ConstantValue.Value = 4;
|
||||
}
|
||||
|
||||
var greaterDefenseEffect = gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.GreaterDefense);
|
||||
if (greaterDefenseEffect?.PowerUpDefinitions.FirstOrDefault(p => p.TargetAttribute == defenseBase) is { } greaterDefenseEffectPowerUp)
|
||||
{
|
||||
greaterDefenseEffectPowerUp.TargetAttribute = defenseFinal;
|
||||
greaterDefenseEffectPowerUp.Boost!.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
}
|
||||
|
||||
// Update shields
|
||||
var shields = gameConfiguration.Items.Where(i => i.Group == (byte)ItemGroups.Shields);
|
||||
foreach (var shield in shields)
|
||||
{
|
||||
if (shield.BasePowerUpAttributes.FirstOrDefault(bpua => bpua.TargetAttribute == Stats.DefenseBase) is { } defensePowerUp)
|
||||
{
|
||||
defensePowerUp.TargetAttribute = defenseShield;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
// <copyright file="FixDefenseCalcsPlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes character stats, magic effects, and options related to defense.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("447FA95B-091B-4950-B1F3-F4EB6D20DE19")]
|
||||
public class FixDefenseCalcsPlugInSeason6 : FixDefenseCalcsPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDefenseCalcsSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Update Anonymous Leather ancient set obsolete option prior to its removal
|
||||
var shieldBlockDamageDecrementId = new Guid("DAC6690B-5922-4446-BCE5-5E701BE62EC1");
|
||||
var anonymousLeatherOptsId = new Guid("00000083-0029-0002-0100-000000000000");
|
||||
var anonymousLeatherOpts = gameConfiguration.ItemOptions.FirstOrDefault(i => i.GetId() == anonymousLeatherOptsId);
|
||||
if (gameConfiguration.Attributes.FirstOrDefault(a => a.Id == shieldBlockDamageDecrementId) is { } shieldBlockDamageDecrement
|
||||
&& anonymousLeatherOpts?.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == shieldBlockDamageDecrement) is { } shieldBlockDamageDecrementOpt)
|
||||
{
|
||||
shieldBlockDamageDecrementOpt.PowerUpDefinition!.TargetAttribute = Stats.DefenseIncreaseWithEquippedShield.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
|
||||
var defenseBase = Stats.DefenseBase.GetPersistent(gameConfiguration);
|
||||
var defenseFinal = Stats.DefenseFinal.GetPersistent(gameConfiguration);
|
||||
var defensePvm = Stats.DefensePvm.GetPersistent(gameConfiguration);
|
||||
var defensePvp = Stats.DefensePvp.GetPersistent(gameConfiguration);
|
||||
var shieldItemDefenseIncrease = Stats.ShieldItemDefenseIncrease.GetPersistent(gameConfiguration);
|
||||
var bonusDefenseRateWithShield = Stats.BonusDefenseRateWithShield.GetPersistent(gameConfiguration);
|
||||
var excellentDamageChance = Stats.ExcellentDamageChance.GetPersistent(gameConfiguration);
|
||||
var excellentDamageBonus = Stats.ExcellentDamageBonus.GetPersistent(gameConfiguration);
|
||||
|
||||
// Update attribute combinations
|
||||
gameConfiguration.CharacterClasses.ForEach(charClass =>
|
||||
{
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attrCombo => attrCombo.TargetAttribute == Stats.MaximumShield && attrCombo.InputAttribute == Stats.DefenseBase) is { } defenseBaseToMaximumShield)
|
||||
{
|
||||
defenseBaseToMaximumShield.InputOperand = 1;
|
||||
defenseBaseToMaximumShield.InputAttribute = defenseFinal;
|
||||
}
|
||||
});
|
||||
|
||||
// Update magic effects
|
||||
var defenseReductionEffect = gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.DefenseReduction);
|
||||
if (defenseReductionEffect is not null)
|
||||
{
|
||||
defenseReductionEffect.PowerUpDefinitions.Clear();
|
||||
|
||||
var reducePvmDefenseEffect = context.CreateNew<PowerUpDefinition>();
|
||||
defenseReductionEffect.PowerUpDefinitions.Add(reducePvmDefenseEffect);
|
||||
reducePvmDefenseEffect.TargetAttribute = defensePvm;
|
||||
reducePvmDefenseEffect.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
reducePvmDefenseEffect.Boost.ConstantValue.Value = 0.9f;
|
||||
reducePvmDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
|
||||
|
||||
var reducePvpDefenseEffect = context.CreateNew<PowerUpDefinition>();
|
||||
defenseReductionEffect.PowerUpDefinitions.Add(reducePvpDefenseEffect);
|
||||
reducePvpDefenseEffect.TargetAttribute = defensePvp;
|
||||
reducePvpDefenseEffect.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
reducePvpDefenseEffect.Boost.ConstantValue.Value = 0.9f;
|
||||
reducePvpDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
var jackOlanternCry = gameConfiguration.Items.FirstOrDefault(m => m.Group == 14 && m.Number == 48);
|
||||
if (jackOlanternCry?.ConsumeEffect is { } effect)
|
||||
{
|
||||
foreach (var powerUp in effect.PowerUpDefinitions)
|
||||
{
|
||||
powerUp.TargetAttribute = defenseFinal;
|
||||
powerUp.Boost!.ConstantValue.Value = 100 / 2;
|
||||
powerUp.Boost.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
}
|
||||
}
|
||||
|
||||
// Update options
|
||||
var ancientSetsOpts = gameConfiguration.ItemOptions.Where(io => io.Name.ValueInNeutralLanguageAsSpan.EndsWith("(Ancient Set)"));
|
||||
foreach (var ancientSetOpts in ancientSetsOpts)
|
||||
{
|
||||
if (ancientSetOpts.PossibleOptions.FirstOrDefault(o => o.PowerUpDefinition?.TargetAttribute == defenseBase) is { } defenseBaseAncOpt)
|
||||
{
|
||||
defenseBaseAncOpt.PowerUpDefinition!.Boost!.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
}
|
||||
}
|
||||
|
||||
var anubisLegendaryOptsId = new Guid("00000083-0029-0013-0100-000000000000");
|
||||
var anubisLegendaryOpts = gameConfiguration.ItemOptions.FirstOrDefault(i => i.GetId() == anubisLegendaryOptsId);
|
||||
if (anubisLegendaryOpts?.PossibleOptions.FirstOrDefault(opt =>
|
||||
opt.PowerUpDefinition?.TargetAttribute == excellentDamageChance
|
||||
&& opt.PowerUpDefinition.Boost?.ConstantValue.Value == 20.0f) is { } excellentDamageChanceOpt)
|
||||
{
|
||||
excellentDamageChanceOpt.PowerUpDefinition!.TargetAttribute = excellentDamageBonus;
|
||||
}
|
||||
|
||||
var pantsGuardianOptions = gameConfiguration.ItemOptions.FirstOrDefault(o => o.Name == "Guardian Option (Pants)");
|
||||
if (pantsGuardianOptions is not null
|
||||
&& pantsGuardianOptions.PossibleOptions.FirstOrDefault(o => o.PowerUpDefinition?.TargetAttribute == defenseBase) is { } defenseOpt)
|
||||
{
|
||||
defenseOpt.PowerUpDefinition!.TargetAttribute = defensePvp;
|
||||
defenseOpt.PowerUpDefinition.Boost!.ConstantValue.Value = 200 / 2;
|
||||
}
|
||||
|
||||
var harmonyDefOptions = gameConfiguration.ItemOptions.FirstOrDefault(o => o.Name == "Harmony Defense Options");
|
||||
if (harmonyDefOptions is not null
|
||||
&& harmonyDefOptions.PossibleOptions.FirstOrDefault(o => o.Number == 1) is { } defenseBaseOpt)
|
||||
{
|
||||
foreach (var level in defenseBaseOpt.LevelDependentOptions)
|
||||
{
|
||||
level.PowerUpDefinition!.Boost!.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
}
|
||||
}
|
||||
|
||||
var socketOptionsWaterId = new Guid("00000083-0033-0000-0000-000000000000");
|
||||
var waterSocketOptions = gameConfiguration.ItemOptions.FirstOrDefault(o => o.GetId() == socketOptionsWaterId);
|
||||
if (waterSocketOptions is not null)
|
||||
{
|
||||
if (waterSocketOptions.PossibleOptions.FirstOrDefault(o => o.Number == 1) is { } defenseBaseSockOpt)
|
||||
{
|
||||
foreach (var level in defenseBaseSockOpt.LevelDependentOptions)
|
||||
{
|
||||
level.PowerUpDefinition!.TargetAttribute = defenseFinal;
|
||||
}
|
||||
}
|
||||
|
||||
if (waterSocketOptions.PossibleOptions.FirstOrDefault(o => o.Number == 2) is { } shieldItemdefenseSockOpt)
|
||||
{
|
||||
foreach (var level in shieldItemdefenseSockOpt.LevelDependentOptions)
|
||||
{
|
||||
level.PowerUpDefinition!.TargetAttribute = shieldItemDefenseIncrease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var socketBonusOptionsArmorsId = new Guid("00000083-0031-0003-0000-000000000000");
|
||||
var socketArmorsBonusOptions = gameConfiguration.ItemOptions.FirstOrDefault(o => o.GetId() == socketBonusOptionsArmorsId);
|
||||
if (socketArmorsBonusOptions is not null
|
||||
&& socketArmorsBonusOptions.PossibleOptions.FirstOrDefault(o => o.Number == 4) is { } defenseBaseSockBonusOpt)
|
||||
{
|
||||
defenseBaseSockBonusOpt.PowerUpDefinition!.TargetAttribute = defenseFinal;
|
||||
}
|
||||
|
||||
// Update master skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DefenseIncrease)?.MasterDefinition is { } defenseIncrease)
|
||||
{
|
||||
defenseIncrease.Aggregation = AggregateType.AddFinal;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.IncreasesDefense)?.MasterDefinition is { } increasesDefense)
|
||||
{
|
||||
increasesDefense.Aggregation = AggregateType.AddFinal;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DefenseSuccessRateInc)?.MasterDefinition is { } defenseSuccessRateInc)
|
||||
{
|
||||
string formula120 = "(1 + (((((((level - 30) ^ 3) + 25000) / 499) / 50) * 100) / 12))";
|
||||
defenseSuccessRateInc.ValueFormula = $"1 + {formula120} / 100";
|
||||
defenseSuccessRateInc.Aggregation = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ShieldMasteryGrandMaster)?.MasterDefinition is { } shieldMasteryGrandMaster)
|
||||
{
|
||||
shieldMasteryGrandMaster.TargetAttribute = bonusDefenseRateWithShield;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ShieldMasteryHighElf)?.MasterDefinition is { } shieldMasteryHighElf)
|
||||
{
|
||||
shieldMasteryHighElf.TargetAttribute = bonusDefenseRateWithShield;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ShieldMastery)?.MasterDefinition is { } shieldMastery)
|
||||
{
|
||||
shieldMastery.TargetAttribute = bonusDefenseRateWithShield;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// <copyright file="FixDrainLifeSkillUpdate.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This adds the items required to enter the kalima map.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("A8827A3C-7F52-47CF-9EA5-562A9C06B986")]
|
||||
public class FixDrainLifeSkillUpdate : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Drain Life Skill";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Updates the attributes of the summoner's Drain Life skill to make it work properly.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDrainLifeSkill;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 08, 29, 18, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var drainLife = gameConfiguration.Skills.First(x => x.Number == (short)SkillNumber.DrainLife);
|
||||
drainLife.SkillType = SkillType.AreaSkillExplicitTarget;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// <copyright file="FixDuelArenaSafezoneMapUpdate.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This Sets the safezone of duel arena to lorencia.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("27714BB3-43F9-4D90-920F-98EF0EC20232")]
|
||||
public class FixDuelArenaSafezoneMapUpdate : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Duel Arena Safezone Map";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Sets the safezone of duel arena to lorencia.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixDuelArenaSafezoneMap;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 10, 28, 18, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var duelArena = gameConfiguration.Maps.First(x => x.Number == DuelArena.Number);
|
||||
duelArena.SafezoneMap = gameConfiguration.Maps.First(m => m.Number == Lorencia.Number);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// <copyright file="FixEventItemsDropFromMonstersUpdatePlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes event items that have DropsFromMonsters set to true,
|
||||
/// but should use dedicated DropItemGroups instead.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("B9C6D3E2-4F5A-6B7C-8D9E-0F1A2B3C4D5E")]
|
||||
public class FixEventItemsDropFromMonstersUpdatePlugIn095d : FixEventItemsDropFromMonstersUpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Event Items DropsFromMonsters 0.95d";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes event items that have DropsFromMonsters set to true, causing them to drop at level 0 instead of using their dedicated DropItemGroups.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixEventItemsDropFromMonsters095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// <copyright file="FixEventItemsDropFromMonstersUpdatePlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes event items that have DropsFromMonsters set to true,
|
||||
/// but should use dedicated DropItemGroups instead.
|
||||
/// </summary>
|
||||
public abstract class FixEventItemsDropFromMonstersUpdatePlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Event Items DropsFromMonsters";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes event items that have DropsFromMonsters set to true, causing them to drop at level 0 instead of using their dedicated DropItemGroups.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 09, 01, 10, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CS1998
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
#pragma warning restore CS1998
|
||||
{
|
||||
// Find event items that have DropsFromMonsters = true but also have dedicated DropItemGroups
|
||||
var eventItemsWithDropGroups = gameConfiguration.DropItemGroups
|
||||
.Where(dig => dig.PossibleItems.Any())
|
||||
.SelectMany(dig => dig.PossibleItems)
|
||||
.Where(item => item.Group is 13 or 14 && item.DropsFromMonsters)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
foreach (var item in eventItemsWithDropGroups)
|
||||
{
|
||||
item.DropsFromMonsters = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// <copyright file="FixEventItemsDropFromMonstersUpdatePlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes event items that have DropsFromMonsters set to true,
|
||||
/// but should use dedicated DropItemGroups instead.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("A8B5C2D1-3E4F-5A6B-7C8D-9E0F1A2B3C4D")]
|
||||
public class FixEventItemsDropFromMonstersUpdatePlugInSeason6 : FixEventItemsDropFromMonstersUpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Event Items DropsFromMonsters Season 6";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes event items that have DropsFromMonsters set to true, causing them to drop at level 0 instead of using their dedicated DropItemGroups.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixEventItemsDropFromMonstersSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
// <copyright file="FixHorseFenrirOptionsSoulBarrierPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.PlayerActions.Craftings;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds Dark Horse options, fixes Gold Fenrir options and Soul Barrier effects.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("3E362629-AAF3-40E0-BC6D-32230285FB03")]
|
||||
public class FixHorseFenrirOptionsSoulBarrierPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Dark Horse and Gold Fenrir Options, and Soul Barrier Effects";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds Dark Horse options, fixes Gold Fenrir options and Soul Barrier effects.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 03, 12, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixHorseFenrirOptionsSoulBarrierPlugIn;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Update dark horse item crafting
|
||||
var petTrainerCraftings = gameConfiguration.Monsters.Single(m => m.NpcWindow == NpcWindow.PetTrainer).ItemCraftings;
|
||||
if (petTrainerCraftings.Single(c => c.Number == 13) is { } darkHorseCrafting)
|
||||
{
|
||||
darkHorseCrafting.ItemCraftingHandlerClassName = typeof(DarkHorseCrafting).FullName!;
|
||||
}
|
||||
|
||||
// Add new Stats
|
||||
var totalLevel = context.CreateNew<AttributeDefinition>(Stats.TotalLevel.Id, Stats.TotalLevel.Designation, Stats.TotalLevel.Description);
|
||||
gameConfiguration.Attributes.Add(totalLevel);
|
||||
var dmgReceiveHorseDec = context.CreateNew<AttributeDefinition>(Stats.DamageReceiveHorseDecrement.Id, Stats.DamageReceiveHorseDecrement.Designation, Stats.DamageReceiveHorseDecrement.Description);
|
||||
gameConfiguration.Attributes.Add(dmgReceiveHorseDec);
|
||||
var soulBarrierReceiveDec = context.CreateNew<AttributeDefinition>(Stats.SoulBarrierReceiveDecrement.Id, Stats.SoulBarrierReceiveDecrement.Designation, Stats.SoulBarrierReceiveDecrement.Description);
|
||||
gameConfiguration.Attributes.Add(soulBarrierReceiveDec);
|
||||
var soulBarrierManaTollPerHit = context.CreateNew<AttributeDefinition>(Stats.SoulBarrierManaTollPerHit.Id, Stats.SoulBarrierManaTollPerHit.Designation, Stats.SoulBarrierManaTollPerHit.Description);
|
||||
gameConfiguration.Attributes.Add(soulBarrierManaTollPerHit);
|
||||
|
||||
// Add total level relationships class attributes
|
||||
var level = Stats.Level.GetPersistent(gameConfiguration);
|
||||
var masterLevel = Stats.MasterLevel.GetPersistent(gameConfiguration);
|
||||
gameConfiguration.CharacterClasses.ForEach(charClass =>
|
||||
{
|
||||
charClass.AttributeCombinations.Add(context.CreateNew<AttributeRelationship>(
|
||||
totalLevel,
|
||||
1,
|
||||
level,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw));
|
||||
|
||||
charClass.AttributeCombinations.Add(context.CreateNew<AttributeRelationship>(
|
||||
totalLevel,
|
||||
1,
|
||||
masterLevel,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw));
|
||||
});
|
||||
|
||||
// Add DL horse dmg receive base value and relationship to dmg receive
|
||||
var lordClasses = gameConfiguration.CharacterClasses.Where(c => c.Number == 16 || c.Number == 17);
|
||||
var damageReceiveDec = Stats.DamageReceiveDecrement.GetPersistent(gameConfiguration);
|
||||
|
||||
foreach (var lordClass in lordClasses)
|
||||
{
|
||||
lordClass.BaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(
|
||||
1,
|
||||
dmgReceiveHorseDec));
|
||||
|
||||
lordClass.AttributeCombinations.Add(context.CreateNew<AttributeRelationship>(
|
||||
damageReceiveDec,
|
||||
1,
|
||||
dmgReceiveHorseDec,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate));
|
||||
}
|
||||
|
||||
// Add new dark horse option type
|
||||
var darkHorseOptionType = context.CreateNew<ItemOptionType>();
|
||||
darkHorseOptionType.Description = ItemOptionTypes.DarkHorse.Description;
|
||||
darkHorseOptionType.Id = ItemOptionTypes.DarkHorse.Id;
|
||||
darkHorseOptionType.Name = ItemOptionTypes.DarkHorse.Name;
|
||||
darkHorseOptionType.IsVisible = ItemOptionTypes.DarkHorse.IsVisible;
|
||||
gameConfiguration.ItemOptionTypes.Add(darkHorseOptionType);
|
||||
|
||||
// Add dark horse options
|
||||
var horse = gameConfiguration.Items.Single(i => i.Group == 13 && i.Number == 4);
|
||||
var horseOptionDefinition = context.CreateNew<ItemOptionDefinition>();
|
||||
horseOptionDefinition.SetGuid(ItemOptionDefinitionNumbers.Horse);
|
||||
gameConfiguration.ItemOptions.Add(horseOptionDefinition);
|
||||
horseOptionDefinition.Name = "Dark Horse Options";
|
||||
horseOptionDefinition.PossibleOptions.Add(this.CreateRelatedPetOption(context, gameConfiguration, ItemOptionTypes.DarkHorse, 1, Stats.DamageReceiveHorseDecrement, AggregateType.AddRaw, ItemOptionDefinitionNumbers.Horse, -0.15f, (Stats.HorseLevel, -0.005f)));
|
||||
horseOptionDefinition.PossibleOptions.Add(this.CreateRelatedPetOption(context, gameConfiguration, ItemOptionTypes.DarkHorse, 2, Stats.DefenseBase, AggregateType.AddRaw, ItemOptionDefinitionNumbers.Horse, 5, (Stats.HorseLevel, 2), (Stats.TotalAgility, 1f / 20)));
|
||||
horse.PossibleItemOptions.Add(horseOptionDefinition);
|
||||
|
||||
// Update gold fenrir options
|
||||
var fenrirOptionDef = gameConfiguration.ItemOptions.First(o => o.PossibleOptions.Any(opt => opt.OptionType == ItemOptionTypes.GoldFenrir));
|
||||
var goldFenrirOptions = fenrirOptionDef.PossibleOptions.Where(opt => opt.OptionType == ItemOptionTypes.GoldFenrir).ToList();
|
||||
foreach (var goldFenrirOpt in goldFenrirOptions)
|
||||
{
|
||||
AttributeDefinition? targetAttribute = null;
|
||||
float multiplier = 0;
|
||||
if (goldFenrirOpt.PowerUpDefinition!.TargetAttribute == Stats.MaximumHealth)
|
||||
{
|
||||
targetAttribute = Stats.MaximumHealth;
|
||||
multiplier = 0.5f;
|
||||
}
|
||||
else if (goldFenrirOpt.PowerUpDefinition.TargetAttribute == Stats.MaximumMana)
|
||||
{
|
||||
targetAttribute = Stats.MaximumMana;
|
||||
multiplier = 0.5f;
|
||||
}
|
||||
else if (goldFenrirOpt.PowerUpDefinition.TargetAttribute == Stats.MaximumPhysBaseDmg)
|
||||
{
|
||||
targetAttribute = Stats.PhysicalBaseDmg;
|
||||
multiplier = 1f / 12f;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetAttribute = Stats.WizardryBaseDmg;
|
||||
multiplier = 1f / 25f;
|
||||
}
|
||||
|
||||
goldFenrirOpt.PowerUpDefinition.TargetAttribute = targetAttribute.GetPersistent(gameConfiguration);
|
||||
goldFenrirOpt.PowerUpDefinition.Boost!.ConstantValue.Value = 0;
|
||||
goldFenrirOpt.PowerUpDefinition.Boost.RelatedValues.Add(
|
||||
this.CreateAttributeRelationship(context, gameConfiguration, targetAttribute, ItemOptionDefinitionNumbers.Fenrir, (Stats.TotalLevel, multiplier)));
|
||||
}
|
||||
|
||||
// Update soul barrier magic effect. Soul barrier dmg decrease % = 10 + (Agility/50) + (Energy/200)
|
||||
var soulBarrierMagicEffect = gameConfiguration.MagicEffects.FirstOrDefault(me => me.Number == (short)MagicEffectNumber.SoulBarrier);
|
||||
if (soulBarrierMagicEffect is not null)
|
||||
{
|
||||
if (soulBarrierMagicEffect.Duration is { } duration)
|
||||
{
|
||||
duration.RelatedValues.First().InputOperand = 1f / 40f;
|
||||
}
|
||||
|
||||
if (soulBarrierMagicEffect.PowerUpDefinitions.FirstOrDefault() is { } powerUp)
|
||||
{
|
||||
powerUp.TargetAttribute = soulBarrierReceiveDec;
|
||||
powerUp.Boost!.ConstantValue.Value = 0.1f;
|
||||
powerUp.Boost!.ConstantValue.AggregateType = AggregateType.AddRaw;
|
||||
|
||||
var boostPerEnergy = powerUp.Boost.RelatedValues.First(v => v.InputOperand == 1 - (0.01f / 200f));
|
||||
boostPerEnergy.InputOperator = InputOperator.Multiply;
|
||||
boostPerEnergy.InputOperand = 1f / 20000f;
|
||||
|
||||
var boostPerAgility = powerUp.Boost.RelatedValues.First(v => v.InputOperand == 1 - (0.01f / 50f));
|
||||
boostPerAgility.InputOperator = InputOperator.Multiply;
|
||||
boostPerAgility.InputOperand = 1f / 5000f;
|
||||
}
|
||||
|
||||
var manaTollPerHit = context.CreateNew<PowerUpDefinition>();
|
||||
soulBarrierMagicEffect.PowerUpDefinitions.Add(manaTollPerHit);
|
||||
manaTollPerHit.TargetAttribute = soulBarrierManaTollPerHit;
|
||||
manaTollPerHit.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
|
||||
var manaToll = context.CreateNew<AttributeRelationship>();
|
||||
manaToll.InputAttribute = Stats.MaximumMana.GetPersistent(gameConfiguration);
|
||||
manaToll.InputOperator = InputOperator.Multiply;
|
||||
manaToll.InputOperand = 0.02f; // two percent of total mana
|
||||
manaTollPerHit.Boost.RelatedValues.Add(manaToll);
|
||||
}
|
||||
|
||||
// Update Soul Barrier Streng master skill definition
|
||||
var soulBarrierStreng = gameConfiguration.Skills.First(s => s.GetId() == new Guid("00000400-0193-0000-0000-000000000000"));
|
||||
soulBarrierStreng.MasterDefinition!.ValueFormula = $"{soulBarrierStreng.MasterDefinition.ValueFormula} / 100";
|
||||
soulBarrierStreng.MasterDefinition.TargetAttribute = soulBarrierReceiveDec;
|
||||
soulBarrierStreng.MasterDefinition.Aggregation = AggregateType.AddRaw;
|
||||
|
||||
// Update Soul Barrier Profic master skill definition
|
||||
var soulBarrierProfic = gameConfiguration.Skills.First(s => s.GetId() == new Guid("00000400-0194-0000-0000-000000000000"));
|
||||
soulBarrierProfic.MasterDefinition!.ExtendsDuration = true;
|
||||
soulBarrierProfic.MasterDefinition.ReplacedSkill = soulBarrierStreng;
|
||||
soulBarrierProfic.AttackDamage = soulBarrierStreng.AttackDamage;
|
||||
soulBarrierProfic.DamageType = soulBarrierStreng.DamageType;
|
||||
soulBarrierProfic.ElementalModifierTarget = soulBarrierStreng.ElementalModifierTarget;
|
||||
soulBarrierProfic.ImplicitTargetRange = soulBarrierStreng.ImplicitTargetRange;
|
||||
soulBarrierProfic.MovesTarget = soulBarrierStreng.MovesTarget;
|
||||
soulBarrierProfic.MovesToTarget = soulBarrierStreng.MovesToTarget;
|
||||
soulBarrierProfic.SkillType = soulBarrierStreng.SkillType;
|
||||
soulBarrierProfic.Target = soulBarrierStreng.Target;
|
||||
soulBarrierProfic.TargetRestriction = soulBarrierStreng.TargetRestriction;
|
||||
soulBarrierProfic.MagicEffectDef = soulBarrierStreng.MagicEffectDef;
|
||||
}
|
||||
|
||||
private IncreasableItemOption CreateRelatedPetOption(IContext context, GameConfiguration gameConfiguration, ItemOptionType optionType, int number, AttributeDefinition targetAttribute, AggregateType aggregateType, short optionNumber, float baseValue = 0, params (AttributeDefinition SourceAttribute, float Multiplier)[] relatedAttributes)
|
||||
{
|
||||
var itemOption = context.CreateNew<IncreasableItemOption>();
|
||||
itemOption.SetGuid(optionNumber, targetAttribute.Id.ExtractFirstTwoBytes());
|
||||
itemOption.OptionType = gameConfiguration.ItemOptionTypes.First(t => t == optionType);
|
||||
itemOption.Number = number;
|
||||
itemOption.PowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
itemOption.PowerUpDefinition.TargetAttribute = targetAttribute.GetPersistent(gameConfiguration);
|
||||
itemOption.PowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
itemOption.PowerUpDefinition.Boost.ConstantValue.Value = baseValue;
|
||||
itemOption.PowerUpDefinition.Boost.ConstantValue.AggregateType = aggregateType;
|
||||
|
||||
for (int i = 0; i < relatedAttributes.Length; i++)
|
||||
{
|
||||
itemOption.PowerUpDefinition.Boost.RelatedValues.Add(this.CreateAttributeRelationship(context, gameConfiguration, targetAttribute, optionNumber, relatedAttributes[i], i));
|
||||
}
|
||||
|
||||
return itemOption;
|
||||
}
|
||||
|
||||
private AttributeRelationship CreateAttributeRelationship(IContext context, GameConfiguration gameConfiguration, AttributeDefinition targetAttribute, short optionNumber, (AttributeDefinition SourceAttribute, float Multiplier) relatedAttribute, int i = 0)
|
||||
{
|
||||
var attributeRelationship = context.CreateNew<AttributeRelationship>();
|
||||
attributeRelationship.SetGuid(optionNumber, targetAttribute.Id.ExtractFirstTwoBytes(), (byte)i);
|
||||
attributeRelationship.InputAttribute = relatedAttribute.SourceAttribute.GetPersistent(gameConfiguration);
|
||||
attributeRelationship.InputOperator = InputOperator.Multiply;
|
||||
attributeRelationship.InputOperand = relatedAttribute.Multiplier;
|
||||
return attributeRelationship;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// <copyright file="FixIgnoreDefenseSkillUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update sets the right settings for the ignore defense skill.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("8DEC7BC2-E6A0-4E46-B123-C92CB43B9ED5")]
|
||||
public class FixIgnoreDefenseSkillUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fixed ignore defense skill";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds the magic effect definition for the Ignore Defense skill.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixIgnoreDefenseSkill;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 08, 08, 20, 00, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var skill = gameConfiguration.Skills.First(s => s.Number == (int)SkillNumber.IgnoreDefense);
|
||||
skill.SkillType = SkillType.Buff;
|
||||
skill.Target = SkillTarget.ImplicitPlayer;
|
||||
skill.MagicEffectDef = gameConfiguration.MagicEffects.First(m => m.Number == (int)MagicEffectNumber.IgnoreDefense);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// <copyright file="FixItemOptionsAndAttackSpeedPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes some item options (damage, defense rate) and weapons attack speed.
|
||||
/// It also refactors attack speed attributes for simplification.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("7733CDA9-6F4B-48D2-94F1-796C937F032A")]
|
||||
public class FixItemOptionsAndAttackSpeedPlugIn075 : FixItemOptionsAndAttackSpeedPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixItemOptionsAndAttackSpeed075;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.FixWeaponsAttackSpeedStat(gameConfiguration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// <copyright file="FixItemOptionsAndAttackSpeedPlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes some item options (damage, defense rate) and weapons attack speed.
|
||||
/// It also refactors attack speed attributes for simplification.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("C7F90EDB-EC00-467D-826F-9DEFFEA1206A")]
|
||||
public class FixItemOptionsAndAttackSpeedPlugIn095D : FixItemOptionsAndAttackSpeedPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixItemOptionsAndAttackSpeed095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.FixWeaponsAttackSpeedStat(gameConfiguration);
|
||||
this.ChangeDinorantAttackSpeedOption(gameConfiguration);
|
||||
this.UpdateExcellentAttackSpeedAndBaseDmgOptions(gameConfiguration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
// <copyright file="FixItemOptionsAndAttackSpeedPlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes some item options (damage, defense rate) and weapons attack speed.
|
||||
/// It also refactors attack speed attributes for simplification.
|
||||
/// </summary>
|
||||
public abstract class FixItemOptionsAndAttackSpeedPlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Item Options And Attack Speed";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes some item options (damage, defense rate) and weapons attack speed.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 03, 05, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var attackSpeedByWeapon = Stats.AttackSpeedByWeapon.GetPersistent(gameConfiguration);
|
||||
var physicalBaseDmg = Stats.PhysicalBaseDmg.GetPersistent(gameConfiguration);
|
||||
var wizardryBaseDmg = Stats.WizardryBaseDmg.GetPersistent(gameConfiguration);
|
||||
|
||||
// Add AttackSpeedAny stat
|
||||
var attackSpeedAny = context.CreateNew<AttributeDefinition>(Stats.AttackSpeedAny.Id, Stats.AttackSpeedAny.Designation, Stats.AttackSpeedAny.Description);
|
||||
gameConfiguration.Attributes.Add(attackSpeedAny);
|
||||
|
||||
// Change attack speed-related combination class attributes
|
||||
gameConfiguration.CharacterClasses.ForEach(charClass =>
|
||||
{
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(rel => rel.TargetAttribute == Stats.AttackSpeed && rel.InputAttribute == Stats.AttackSpeedByWeapon) is { } attackSpeedByWeaponToAttackSpeed
|
||||
&& charClass.AttributeCombinations.FirstOrDefault(rel => rel.TargetAttribute == Stats.MagicSpeed && rel.InputAttribute == Stats.AttackSpeedByWeapon) is { } attackSpeedByWeaponToMagicSpeed
|
||||
&& charClass.AttributeCombinations.FirstOrDefault(rel => rel.TargetAttribute == Stats.AttackSpeed && rel.OperandAttribute == Stats.AreTwoWeaponsEquipped) is { } areTwoWeaponsEquippedToAttackSpeedConditional
|
||||
&& charClass.AttributeCombinations.FirstOrDefault(rel => rel.TargetAttribute == Stats.MagicSpeed && rel.OperandAttribute == Stats.AreTwoWeaponsEquipped) is { } areTwoWeaponsEquippedToMagicSpeedConditional)
|
||||
{
|
||||
attackSpeedByWeaponToAttackSpeed.InputAttribute = attackSpeedAny;
|
||||
attackSpeedByWeaponToMagicSpeed.InputAttribute = attackSpeedAny;
|
||||
|
||||
charClass.AttributeCombinations.Add(context.CreateNew<AttributeRelationship>(
|
||||
attackSpeedAny,
|
||||
1,
|
||||
attackSpeedByWeapon,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw));
|
||||
|
||||
areTwoWeaponsEquippedToAttackSpeedConditional.TargetAttribute = attackSpeedAny;
|
||||
charClass.AttributeCombinations.Remove(areTwoWeaponsEquippedToMagicSpeedConditional);
|
||||
}
|
||||
});
|
||||
|
||||
// Change gloves attack speed stats
|
||||
var gloves = gameConfiguration.Items.Where(i => i.Group == (int)ItemGroups.Gloves);
|
||||
foreach (var glovesItem in gloves)
|
||||
{
|
||||
if (glovesItem.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.AttackSpeed) is { } glovesAttackSpeed
|
||||
&& glovesItem.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.MagicSpeed) is { } glovesMagicSpeed)
|
||||
{
|
||||
glovesAttackSpeed.TargetAttribute = attackSpeedAny;
|
||||
glovesItem.BasePowerUpAttributes.Remove(glovesMagicSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
// Change alcohol attack speed magic effect
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(me => me.Number == (short)MagicEffectNumber.Alcohol) is { } alcoholMagicEffect
|
||||
&& alcoholMagicEffect.PowerUpDefinitions.FirstOrDefault(pu => pu.TargetAttribute == Stats.AttackSpeed) is { } alcoholAttackSpeed
|
||||
&& alcoholMagicEffect.PowerUpDefinitions.FirstOrDefault(pu => pu.TargetAttribute == Stats.MagicSpeed) is { } alcoholMagicSpeed)
|
||||
{
|
||||
alcoholAttackSpeed.TargetAttribute = attackSpeedAny;
|
||||
alcoholMagicEffect.PowerUpDefinitions.Remove(alcoholMagicSpeed);
|
||||
}
|
||||
|
||||
// Fix Item Options (all weapon and wing damage options)
|
||||
var itemOptions = gameConfiguration.ItemOptions.Where(io => io.PossibleOptions.Any(po => po.OptionType == ItemOptionTypes.Option));
|
||||
foreach (var itemOption in itemOptions)
|
||||
{
|
||||
foreach (var opt in itemOption.PossibleOptions)
|
||||
{
|
||||
if (opt.PowerUpDefinition?.TargetAttribute == Stats.MaximumPhysBaseDmg)
|
||||
{
|
||||
opt.PowerUpDefinition.TargetAttribute = physicalBaseDmg;
|
||||
}
|
||||
else if (opt.PowerUpDefinition?.TargetAttribute == Stats.MaximumWizBaseDmg)
|
||||
{
|
||||
opt.PowerUpDefinition.TargetAttribute = wizardryBaseDmg;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gameConfiguration.PhysicalDamageOption().Name = Stats.PhysicalBaseDmg.Designation + " Option";
|
||||
gameConfiguration.WizardryDamageOption().Name = Stats.WizardryBaseDmg.Designation + " Option";
|
||||
|
||||
// Add shield defense rate item option
|
||||
gameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(context, gameConfiguration, Stats.DefenseRatePvm, ItemOptionDefinitionNumbers.DefenseRateOption, 5));
|
||||
|
||||
// Fix all shields item option
|
||||
var shields = gameConfiguration.Items.Where(i => i.Group == (int)ItemGroups.Shields);
|
||||
var defenseOption = gameConfiguration.GetDefenseOption();
|
||||
var defenseRateOption = gameConfiguration.GetDefenseRateOption();
|
||||
foreach (var shield in shields)
|
||||
{
|
||||
if (shield.PossibleItemOptions.FirstOrDefault(io => io == defenseOption) is { } defOpt)
|
||||
{
|
||||
shield.PossibleItemOptions.Remove(defOpt);
|
||||
}
|
||||
|
||||
shield.PossibleItemOptions.Add(defenseRateOption);
|
||||
}
|
||||
|
||||
// Fix 3rd wings defense bonus per level table
|
||||
var thirdWingsDefenseTable = gameConfiguration.ItemLevelBonusTables.FirstOrDefault(t => t.Name == "Defense Bonus (3rd Wings)");
|
||||
if (thirdWingsDefenseTable is not null)
|
||||
{
|
||||
var thirdWings = gameConfiguration.Items.Where(i =>
|
||||
i.Group == (int)ItemGroups.Orbs
|
||||
&& i.BasePowerUpAttributes.Any(pua => pua.TargetAttribute == Stats.CanFly)
|
||||
&& i.Requirements.Any(r => r.Attribute == Stats.Level && r.MinimumValue == 400));
|
||||
|
||||
foreach (var wing in thirdWings)
|
||||
{
|
||||
if (wing.BasePowerUpAttributes.First(pua => pua.TargetAttribute == Stats.DefenseBase) is { } defensePowerUp)
|
||||
{
|
||||
defensePowerUp.BonusPerLevelTable = thirdWingsDefenseTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable SA1600, CS1591 // Elements should be documented.
|
||||
protected ItemOptionDefinition CreateOptionDefinition(IContext context, GameConfiguration gameConfiguration, AttributeDefinition attributeDefinition, short number, byte baseValue)
|
||||
{
|
||||
var definition = context.CreateNew<ItemOptionDefinition>();
|
||||
definition.SetGuid(number);
|
||||
definition.Name = attributeDefinition.Designation + " Option";
|
||||
definition.AddChance = 0.25f;
|
||||
definition.AddsRandomly = true;
|
||||
definition.MaximumOptionsPerItem = 1;
|
||||
|
||||
var itemOption = context.CreateNew<IncreasableItemOption>();
|
||||
itemOption.SetGuid(number);
|
||||
itemOption.OptionType = gameConfiguration.ItemOptionTypes.FirstOrDefault(o => o == ItemOptionTypes.Option);
|
||||
itemOption.PowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
itemOption.PowerUpDefinition.TargetAttribute = gameConfiguration.Attributes.First(a => a == attributeDefinition);
|
||||
itemOption.PowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
itemOption.PowerUpDefinition.Boost.ConstantValue!.Value = baseValue;
|
||||
for (short level = 2; level <= 4; level++)
|
||||
{
|
||||
var levelDependentOption = context.CreateNew<ItemOptionOfLevel>();
|
||||
levelDependentOption.Level = level;
|
||||
var powerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = itemOption.PowerUpDefinition.TargetAttribute;
|
||||
powerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUpDefinition.Boost.ConstantValue!.Value = level * baseValue;
|
||||
levelDependentOption.PowerUpDefinition = powerUpDefinition;
|
||||
itemOption.LevelDependentOptions.Add(levelDependentOption);
|
||||
}
|
||||
|
||||
definition.PossibleOptions.Add(itemOption);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
protected void FixWeaponsAttackSpeedStat(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var attackSpeedByWeapon = Stats.AttackSpeedByWeapon.GetPersistent(gameConfiguration);
|
||||
var weapons = gameConfiguration.Items.Where(i => i.Group >= (int)ItemGroups.Swords && i.Group <= (int)ItemGroups.Staff);
|
||||
foreach (var weapon in weapons)
|
||||
{
|
||||
if (weapon.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.AttackSpeed) is { } weaponAttackSpeed)
|
||||
{
|
||||
weaponAttackSpeed.TargetAttribute = attackSpeedByWeapon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void ChangeDinorantAttackSpeedOption(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var dinorantOption = gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == new Guid("00000083-0080-0000-0000-000000000000"));
|
||||
dinorantOption ??= gameConfiguration.ItemOptions.FirstOrDefault(io => io.Name == "Dinorant Options"); // 0.95d
|
||||
|
||||
if (dinorantOption is not null
|
||||
&& dinorantOption.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.AttackSpeed) is { } dinoAttackSpeed)
|
||||
{
|
||||
dinoAttackSpeed.PowerUpDefinition!.TargetAttribute = Stats.AttackSpeedAny.GetPersistent(gameConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateExcellentAttackSpeedAndBaseDmgOptions(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var excPhysAttackOpts = gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == new Guid("00000083-0013-0000-0000-000000000000"));
|
||||
var excWizAttackOpts = gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == new Guid("00000083-0014-0000-0000-000000000000"));
|
||||
var excCurseAttackOpts = gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == new Guid("00000083-0015-0000-0000-000000000000"));
|
||||
var attackSpeedAny = Stats.AttackSpeedAny.GetPersistent(gameConfiguration);
|
||||
var physicalBaseDmg = Stats.PhysicalBaseDmg.GetPersistent(gameConfiguration);
|
||||
var wizardryBaseDmg = Stats.PhysicalBaseDmg.GetPersistent(gameConfiguration);
|
||||
|
||||
if (excPhysAttackOpts?.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.AttackSpeed) is { } physOptAttackSpeed)
|
||||
{
|
||||
physOptAttackSpeed.PowerUpDefinition!.TargetAttribute = attackSpeedAny;
|
||||
}
|
||||
|
||||
if (excPhysAttackOpts?.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.MaximumPhysBaseDmg && opt.Number == 5) is { } maxPhysBaseDmg)
|
||||
{
|
||||
maxPhysBaseDmg.PowerUpDefinition!.TargetAttribute = physicalBaseDmg;
|
||||
}
|
||||
|
||||
if (excWizAttackOpts?.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.AttackSpeed) is { } wizOptAttackSpeed)
|
||||
{
|
||||
wizOptAttackSpeed.PowerUpDefinition!.TargetAttribute = attackSpeedAny;
|
||||
}
|
||||
|
||||
if (excWizAttackOpts?.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.MaximumWizBaseDmg && opt.Number == 5) is { } maxWizBaseDmg)
|
||||
{
|
||||
maxWizBaseDmg.PowerUpDefinition!.TargetAttribute = wizardryBaseDmg;
|
||||
}
|
||||
|
||||
if (excCurseAttackOpts?.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.AttackSpeed) is { } curseOptAttackSpeed)
|
||||
{
|
||||
curseOptAttackSpeed.PowerUpDefinition!.TargetAttribute = attackSpeedAny;
|
||||
}
|
||||
|
||||
if (excCurseAttackOpts?.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.MaximumCurseBaseDmg && opt.Number == 5) is { } maxCurseBaseDmg)
|
||||
{
|
||||
maxCurseBaseDmg.PowerUpDefinition!.TargetAttribute = wizardryBaseDmg; // Yes, wizardry. This should not be in use, but just in case.
|
||||
}
|
||||
}
|
||||
#pragma warning restore SA1600, CS1591 // Elements should be documented.
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
// <copyright file="FixItemOptionsAndAttackSpeedPlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes some item options (damage, defense rate), weapons attack speed, third wings defense, and some AA weapon values.
|
||||
/// It also refactors attack speed attributes for simplification.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("EEEAA884-4704-48DE-825A-8E588A47E2CC")]
|
||||
public class FixItemOptionsAndAttackSpeedPlugInSeason6 : FixItemOptionsAndAttackSpeedPlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal new const string PlugInDescription = "This update fixes some item options (damage, defense rate), weapons attack speed, third wings defense, and some AA weapon values.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixItemOptionsAndAttackSpeedSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
this.ChangeDinorantAttackSpeedOption(gameConfiguration);
|
||||
this.UpdateExcellentAttackSpeedAndBaseDmgOptions(gameConfiguration);
|
||||
|
||||
var attackSpeedAny = Stats.AttackSpeedAny.GetPersistent(gameConfiguration);
|
||||
|
||||
// Add CurseBaseDmg stat
|
||||
var curseBaseDmg = context.CreateNew<AttributeDefinition>(Stats.CurseBaseDmg.Id, Stats.CurseBaseDmg.Designation, Stats.CurseBaseDmg.Description);
|
||||
gameConfiguration.Attributes.Add(curseBaseDmg);
|
||||
|
||||
// Fix Curse Dmg Item Options (all weapon and wing damage options)
|
||||
var itemOptions = gameConfiguration.ItemOptions.Where(io =>
|
||||
io.PossibleOptions.Any(po =>
|
||||
po.OptionType == ItemOptionTypes.Option
|
||||
&& po.PowerUpDefinition?.TargetAttribute == Stats.MaximumCurseBaseDmg));
|
||||
foreach (var itemOption in itemOptions)
|
||||
{
|
||||
if (itemOption.PossibleOptions.First().PowerUpDefinition is { } curseDmgOpt)
|
||||
{
|
||||
curseDmgOpt.TargetAttribute = curseBaseDmg;
|
||||
}
|
||||
}
|
||||
|
||||
gameConfiguration.ItemOptions
|
||||
.First(iod => iod.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == curseBaseDmg))
|
||||
.Name = curseBaseDmg.Designation + " Option";
|
||||
|
||||
// Change attack speed magic effects
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(me => me.Number == (short)MagicEffectNumber.PotionOfSoul) is { } soulPotionMagicEffect
|
||||
&& soulPotionMagicEffect.PowerUpDefinitions.FirstOrDefault(pu => pu.TargetAttribute == Stats.AttackSpeed) is { } soulPotionAttackSpeed
|
||||
&& soulPotionMagicEffect.PowerUpDefinitions.FirstOrDefault(pu => pu.TargetAttribute == Stats.MagicSpeed) is { } soulPotionMagicSpeed)
|
||||
{
|
||||
soulPotionAttackSpeed.TargetAttribute = attackSpeedAny;
|
||||
soulPotionMagicEffect.PowerUpDefinitions.Remove(soulPotionMagicSpeed);
|
||||
}
|
||||
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(me => me.Number == (short)MagicEffectNumber.JackOlanternBlessing) is { } jackMagicEffect
|
||||
&& jackMagicEffect.PowerUpDefinitions.FirstOrDefault(pu => pu.TargetAttribute == Stats.AttackSpeed) is { } jackAttackSpeed
|
||||
&& jackMagicEffect.PowerUpDefinitions.FirstOrDefault(pu => pu.TargetAttribute == Stats.MagicSpeed) is { } jackMagicSpeed)
|
||||
{
|
||||
jackAttackSpeed.TargetAttribute = attackSpeedAny;
|
||||
jackMagicEffect.PowerUpDefinitions.Remove(jackMagicSpeed);
|
||||
}
|
||||
|
||||
// Change wizard's ring, and fire socket attack speed option
|
||||
var wizardsRingOption = gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == new Guid("00000083-0073-0000-0000-000000000000"));
|
||||
if (wizardsRingOption is not null
|
||||
&& wizardsRingOption.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.AttackSpeed) is { } wizardsRingAttackSpeed)
|
||||
{
|
||||
wizardsRingAttackSpeed.PowerUpDefinition!.TargetAttribute = attackSpeedAny;
|
||||
}
|
||||
|
||||
var fireSocketOption = gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == new Guid("00000083-0032-0000-0000-000000000000"));
|
||||
if (fireSocketOption is not null
|
||||
&& fireSocketOption.PossibleOptions.FirstOrDefault(opt => opt.PowerUpDefinition?.TargetAttribute == Stats.AttackSpeed) is { } fireSocketAttackSpeed)
|
||||
{
|
||||
fireSocketAttackSpeed.PowerUpDefinition!.TargetAttribute = attackSpeedAny;
|
||||
}
|
||||
|
||||
// Add physical and attack damage item option
|
||||
gameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(context, gameConfiguration, Stats.BaseDamageBonus, ItemOptionDefinitionNumbers.PhysicalAndWizardryAttack, 4));
|
||||
|
||||
// Fix magic swords options
|
||||
var magicSwords = gameConfiguration.Items.Where(i => i.Group == (int)ItemGroups.Swords
|
||||
&& (i.Number == 21 // Dark Reign Blade
|
||||
|| i.Number == 23 // Explosion Blade
|
||||
|| i.Number == 25 // Sword Dancer
|
||||
|| i.Number == 28 // Imperial Sword
|
||||
|| i.Number == 31)); // Rune Blade
|
||||
var wizItemOption = gameConfiguration.WizardryDamageOption();
|
||||
var physAndWizItemOption = gameConfiguration.PhysicalAndWizardryDamageOption();
|
||||
var excWizAttackOption = gameConfiguration.ExcellentWizardryAttackOptions();
|
||||
var excPhysAttackOption = gameConfiguration.ExcellentPhysicalAttackOptions();
|
||||
var harmonyWizAttackOption = gameConfiguration.ItemOptions.First(o => o.Name == HarmonyOptions.WizardryAttackOptionsName);
|
||||
var harmonyPhysAttackOption = gameConfiguration.ItemOptions.First(o => o.Name == HarmonyOptions.PhysicalAttackOptionsName);
|
||||
foreach (var magicSword in magicSwords)
|
||||
{
|
||||
magicSword.PossibleItemOptions.Remove(wizItemOption);
|
||||
magicSword.PossibleItemOptions.Remove(excWizAttackOption);
|
||||
magicSword.PossibleItemOptions.Remove(harmonyWizAttackOption);
|
||||
magicSword.PossibleItemOptions.Add(physAndWizItemOption);
|
||||
magicSword.PossibleItemOptions.Add(excPhysAttackOption);
|
||||
magicSword.PossibleItemOptions.Add(harmonyPhysAttackOption);
|
||||
}
|
||||
|
||||
// Remove staffs two handed weapon powerup & add missing powerup for soul master/mg staffs
|
||||
var staffs = gameConfiguration.Items.Where(i => i.Group == (int)ItemGroups.Staff);
|
||||
foreach (var staff in staffs)
|
||||
{
|
||||
if (staff.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.IsTwoHandedWeaponEquipped) is { } twoHandedWeapon)
|
||||
{
|
||||
staff.BasePowerUpAttributes.Remove(twoHandedWeapon);
|
||||
}
|
||||
|
||||
// Dragon Soul Staff, Kundun Staff, Grand Viper Staff, Platina Staff, Deadly Staff, Imperial Staff, Chromatic Staff.
|
||||
if (staff.Number == 9
|
||||
|| staff.Number == 11
|
||||
|| staff.Number == 12
|
||||
|| staff.Number == 13
|
||||
|| staff.Number == 30
|
||||
|| staff.Number == 31
|
||||
|| staff.Number == 33)
|
||||
{
|
||||
staff.BasePowerUpAttributes.Add(CreateNewBasePowerUpDefinition(Stats.IsOneHandedStaffEquipped));
|
||||
}
|
||||
}
|
||||
|
||||
ItemBasePowerUpDefinition CreateNewBasePowerUpDefinition(AttributeDefinition attribute)
|
||||
{
|
||||
var powerUpDefinition = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = attribute.GetPersistent(gameConfiguration);
|
||||
powerUpDefinition.BaseValue = 1;
|
||||
powerUpDefinition.AggregateType = AggregateType.AddRaw;
|
||||
return powerUpDefinition;
|
||||
}
|
||||
|
||||
// Add crystal sword two handed sword powerup
|
||||
var crystalSword = gameConfiguration.Items.FirstOrDefault(i => i.Group == (int)ItemGroups.Scepters && i.Number == 5);
|
||||
if (crystalSword is not null)
|
||||
{
|
||||
crystalSword.BasePowerUpAttributes.Add(CreateNewBasePowerUpDefinition(Stats.IsTwoHandedSwordEquipped));
|
||||
}
|
||||
|
||||
// Fix AA weapons values
|
||||
var archangelSword = gameConfiguration.Items.FirstOrDefault(i => i.Group == (int)ItemGroups.Swords && i.Number == 19);
|
||||
var archangelScepter = gameConfiguration.Items.FirstOrDefault(i => i.Group == (int)ItemGroups.Scepters && i.Number == 13);
|
||||
var archangelCrossbow = gameConfiguration.Items.FirstOrDefault(i => i.Group == (int)ItemGroups.Bows && i.Number == 18);
|
||||
|
||||
if (archangelSword is not null)
|
||||
{
|
||||
if (archangelSword.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.MinimumPhysBaseDmgByWeapon) is { } minPhysDmg)
|
||||
{
|
||||
minPhysDmg.BaseValue = 220;
|
||||
}
|
||||
|
||||
if (archangelSword.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.MaximumPhysBaseDmgByWeapon) is { } maxPhysDmg)
|
||||
{
|
||||
maxPhysDmg.BaseValue = 230;
|
||||
}
|
||||
|
||||
if (archangelSword.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.AttackSpeedByWeapon) is { } attackSpeed)
|
||||
{
|
||||
attackSpeed.BaseValue = 45;
|
||||
}
|
||||
}
|
||||
|
||||
if (archangelScepter is not null)
|
||||
{
|
||||
if (archangelScepter.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.MinimumPhysBaseDmgByWeapon) is { } minPhysDmg)
|
||||
{
|
||||
minPhysDmg.BaseValue = 200;
|
||||
}
|
||||
|
||||
if (archangelScepter.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.MaximumPhysBaseDmgByWeapon) is { } maxPhysDmg)
|
||||
{
|
||||
maxPhysDmg.BaseValue = 223;
|
||||
}
|
||||
|
||||
if (archangelScepter.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.ScepterRise) is { } rise)
|
||||
{
|
||||
rise.BaseValue = 138 / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (archangelCrossbow is not null)
|
||||
{
|
||||
if (archangelCrossbow.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.MinimumPhysBaseDmgByWeapon) is { } minPhysDmg)
|
||||
{
|
||||
minPhysDmg.BaseValue = 224;
|
||||
}
|
||||
|
||||
if (archangelCrossbow.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.MaximumPhysBaseDmgByWeapon) is { } maxPhysDmg)
|
||||
{
|
||||
maxPhysDmg.BaseValue = 246;
|
||||
}
|
||||
|
||||
if (archangelCrossbow.BasePowerUpAttributes.FirstOrDefault(pu => pu.TargetAttribute == Stats.AttackSpeedByWeapon) is { } attackSpeed)
|
||||
{
|
||||
attackSpeed.BaseValue = 45;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// <copyright file="FixItemRequirementsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Updates some item requirements for elf bows that were initialized wrongly.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("9E8DB2CB-1972-40D3-9129-6964ABFEB4DC")]
|
||||
public class FixItemRequirementsPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plugin name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Item Requirements (Elf Bows)";
|
||||
|
||||
/// <summary>
|
||||
/// The plugin description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Updates some item requirements for elf bows which were initialized wrongly.";
|
||||
|
||||
private static readonly List<(int Group, int Number, int StrengthRequirement, int AgilityRequirement, int EnergyRequirement, int VitalityRequirement)> RequirementCorrections =
|
||||
[
|
||||
(4, 0, 20, 80, 0, 0), // Short Bow
|
||||
(4, 1, 30, 90, 0, 0), // Bow
|
||||
(4, 2, 30, 90, 0, 0), // Elven Bow
|
||||
(4, 3, 30, 90, 0, 0), // Battle Bow
|
||||
(4, 4, 30, 100, 0, 0), // Tiger Bow
|
||||
(4, 5, 30, 100, 0, 0), // Silver Bow
|
||||
(4, 6, 40, 150, 0, 0), // Chaos Nature Bow
|
||||
|
||||
/*
|
||||
These are handled different, so they don't need to be corrected.
|
||||
See ItemExtensions.GetRequirement.
|
||||
(15, 0, 0, 0, 100, 0), // Scroll of Poison
|
||||
(15, 1, 0, 0, 100, 0), // Scroll of Meteorite
|
||||
(15, 2, 0, 0, 100, 0), // Scroll of Lighting
|
||||
(15, 3, 0, 0, 100, 0), // Scroll of Fire Ball
|
||||
(15, 4, 0, 0, 100, 0), // Scroll of Flame
|
||||
(15, 5, 0, 0, 100, 0), // Scroll of Teleport
|
||||
(15, 6, 0, 0, 100, 0), // Scroll of Ice
|
||||
(15, 7, 0, 0, 100, 0), // Scroll of Twister
|
||||
(15, 8, 0, 0, 100, 0), // Scroll of Evil Spirit
|
||||
(15, 9, 0, 0, 100, 0), // Scroll of Hellfire
|
||||
(15, 10, 0, 0, 100, 0), // Scroll of Power Wave
|
||||
(15, 11, 0, 0, 110, 0), // Scroll of Aqua Beam
|
||||
(15, 12, 0, 0, 150, 0), // Scroll of Cometfall
|
||||
(15, 13, 0, 0, 200, 0), // Scroll of Inferno
|
||||
(15, 14, 0, 0, 188, 0), // Scroll of Teleport Ally
|
||||
(15, 15, 0, 0, 126, 0), // Scroll of Soul Barrier
|
||||
(15, 16, 0, 0, 243, 0), // Scroll of Decay
|
||||
(15, 17, 0, 0, 223, 0), // Scroll of Ice Storm
|
||||
(15, 18, 0, 0, 258, 0), // Scroll of Nova
|
||||
|
||||
(15, 19, 0, 0, 75, 0), // Chain Lightning Parchment
|
||||
(15, 20, 0, 0, 93, 0), // Drain Life Parchment
|
||||
(15, 21, 0, 0, 216, 0), // Lightning Shock Parchment
|
||||
(15, 22, 0, 0, 111, 0), // Damage Reflection Parchment
|
||||
(15, 23, 0, 0, 181, 0), // Berserker Parchment
|
||||
(15, 24, 0, 0, 100, 0), // Sleep Parchment
|
||||
(15, 26, 0, 0, 173, 0), // Weakness Parchment
|
||||
(15, 27, 0, 0, 201, 0), // Innovation Parchment
|
||||
(15, 28, 0, 0, 118, 0), // Scroll of Wizardry Enhance
|
||||
(15, 29, 0, 0, 118, 0), // Scroll of Gigantic Storm
|
||||
(15, 34, 0, 0, ?, 0), // Ignore Defense Parchment
|
||||
(15, 35, 0, 0, ?, 0), // Increase Health Parchment
|
||||
(15, 36, 0, 0, ?, 0), // Increase Block Parchment
|
||||
*/
|
||||
];
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixItemRequirements;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 11, 03, 18, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
foreach (var reqUpdate in RequirementCorrections)
|
||||
{
|
||||
var item = gameConfiguration.Items.First(x => x.Number == reqUpdate.Number && x.Group == reqUpdate.Group);
|
||||
UpdateRequirement(Stats.TotalStrengthRequirementValue, reqUpdate.StrengthRequirement);
|
||||
UpdateRequirement(Stats.TotalAgilityRequirementValue, reqUpdate.AgilityRequirement);
|
||||
UpdateRequirement(Stats.TotalEnergyRequirementValue, reqUpdate.EnergyRequirement);
|
||||
UpdateRequirement(Stats.TotalVitalityRequirementValue, reqUpdate.VitalityRequirement);
|
||||
|
||||
void UpdateRequirement(AttributeDefinition stat, int newValue)
|
||||
{
|
||||
var requirement = item.Requirements.FirstOrDefault(r => r.Attribute == stat);
|
||||
if (requirement is null && newValue == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (requirement is null)
|
||||
{
|
||||
requirement = context.CreateNew<AttributeRequirement>();
|
||||
requirement.Attribute = stat.GetPersistent(gameConfiguration);
|
||||
item.Requirements.Add(requirement);
|
||||
}
|
||||
|
||||
requirement.MinimumValue = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// <copyright file="FixItemRequirementsPlugIn2.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Updates some item requirements for elf bows which were initialized wrongly.
|
||||
/// This plugin fixes configurations that were created after the initial fix but before the base data was corrected.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("A7C9D4E1-8F2B-4A3C-9E6D-7B8F9A0E1C2D")]
|
||||
public class FixItemRequirementsPlugIn2 : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plugin name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Item Requirements (Elf Bows) v2";
|
||||
|
||||
/// <summary>
|
||||
/// The plugin description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Updates some item requirements for elf bows which were initialized wrongly. This fixes configurations created after the initial fix but before the base data was corrected.";
|
||||
|
||||
private static readonly List<(int Group, int Number, int StrengthRequirement, int AgilityRequirement, int EnergyRequirement, int VitalityRequirement)> RequirementCorrections =
|
||||
[
|
||||
(4, 0, 20, 80, 0, 0), // Short Bow
|
||||
(4, 1, 30, 90, 0, 0), // Bow
|
||||
(4, 2, 30, 90, 0, 0), // Elven Bow
|
||||
(4, 3, 30, 90, 0, 0), // Battle Bow
|
||||
(4, 4, 30, 100, 0, 0), // Tiger Bow
|
||||
(4, 5, 30, 100, 0, 0), // Silver Bow
|
||||
(4, 6, 40, 150, 0, 0), // Chaos Nature Bow
|
||||
];
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixItemRequirements2;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 09, 16, 19, 44, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
foreach (var reqUpdate in RequirementCorrections)
|
||||
{
|
||||
var item = gameConfiguration.Items.FirstOrDefault(x => x.Number == reqUpdate.Number && x.Group == reqUpdate.Group);
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UpdateRequirement(Stats.TotalStrengthRequirementValue, reqUpdate.StrengthRequirement);
|
||||
UpdateRequirement(Stats.TotalAgilityRequirementValue, reqUpdate.AgilityRequirement);
|
||||
UpdateRequirement(Stats.TotalEnergyRequirementValue, reqUpdate.EnergyRequirement);
|
||||
UpdateRequirement(Stats.TotalVitalityRequirementValue, reqUpdate.VitalityRequirement);
|
||||
|
||||
void UpdateRequirement(AttributeDefinition stat, int newValue)
|
||||
{
|
||||
var requirement = item.Requirements.FirstOrDefault(r => r.Attribute == stat);
|
||||
if (requirement is null && newValue == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (requirement is null)
|
||||
{
|
||||
requirement = context.CreateNew<AttributeRequirement>();
|
||||
requirement.Attribute = stat.GetPersistent(gameConfiguration);
|
||||
item.Requirements.Add(requirement);
|
||||
}
|
||||
|
||||
requirement.MinimumValue = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="FixJeweleryPetsDamageCalcsPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes jewelery items and pet options related to damage.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("46F50226-B0A2-4FE7-B708-AEB3F306A7C0")]
|
||||
public class FixJeweleryPetsDamageCalcsPlugIn075 : FixJeweleryPetsDamageCalcsPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixJeweleryPetsDamageCalcs075;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="FixJeweleryPetsDamageCalcsPlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes jewelery items and pet options related to damage.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("BF56A0E2-D7B3-4456-81F7-249440489607")]
|
||||
public class FixJeweleryPetsDamageCalcsPlugIn095D : FixJeweleryPetsDamageCalcsPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixJeweleryPetsDamageCalcs095d;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// <copyright file="FixJeweleryPetsDamageCalcsPlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes jewelery items resistance values.
|
||||
/// </summary>
|
||||
public abstract class FixJeweleryPetsDamageCalcsPlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Jewelery Resistance Calculations";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes jewelery items resistance values.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 09, 30, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Add new attributes
|
||||
var bonusExperienceRate = context.CreateNew<AttributeDefinition>(Stats.BonusExperienceRate.Id, Stats.BonusExperienceRate.Designation, Stats.BonusExperienceRate.Description);
|
||||
gameConfiguration.Attributes.Add(bonusExperienceRate);
|
||||
var isPetSkeletionEquipped = context.CreateNew<AttributeDefinition>(Stats.IsPetSkeletonEquipped.Id, Stats.IsPetSkeletonEquipped.Designation, Stats.IsPetSkeletonEquipped.Description);
|
||||
gameConfiguration.Attributes.Add(isPetSkeletionEquipped);
|
||||
|
||||
var resistancesTable = gameConfiguration.ItemLevelBonusTables.First(t => t.Name == "Elemental resistances (Jewelery)");
|
||||
resistancesTable.Description = "Defines the elemental resistances for jewelery. It's 1 per item level.";
|
||||
|
||||
float[] resistanceIncreaseByLevel = [0, 1, 2, 3, 4];
|
||||
foreach (var levelBonus in resistancesTable.BonusPerLevel)
|
||||
{
|
||||
levelBonus.AdditionalValue = resistanceIncreaseByLevel[levelBonus.Level];
|
||||
}
|
||||
|
||||
// Update jewelery
|
||||
var jeweleryItemSlots = gameConfiguration.ItemSlotTypes.Where(st => st.ItemSlots.Contains(9) || st.ItemSlots.Contains(10));
|
||||
var jewelery = gameConfiguration.Items.Where(i => i.Group == (byte)ItemGroups.Misc1 && jeweleryItemSlots.Contains(i.ItemSlot));
|
||||
|
||||
var xfmRing = jewelery.First(i => i.Number == 10); // Transformation Ring
|
||||
this.AddLevelRequirement(context, gameConfiguration, xfmRing, 20);
|
||||
|
||||
foreach (var jeweleryItem in jewelery)
|
||||
{
|
||||
if (jeweleryItem.BasePowerUpAttributes.FirstOrDefault(p =>
|
||||
p.TargetAttribute == Stats.IceResistance ||
|
||||
p.TargetAttribute == Stats.PoisonResistance ||
|
||||
p.TargetAttribute == Stats.LightningResistance ||
|
||||
p.TargetAttribute == Stats.FireResistance ||
|
||||
p.TargetAttribute == Stats.EarthResistance ||
|
||||
p.TargetAttribute == Stats.WindResistance ||
|
||||
p.TargetAttribute == Stats.WaterResistance) is { } resistancePowerUp)
|
||||
{
|
||||
resistancePowerUp.BaseValue = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable SA1600, CS1591
|
||||
protected void AddLevelRequirement(IContext context, GameConfiguration gameConfiguration, ItemDefinition item, int level)
|
||||
{
|
||||
var requirement = context.CreateNew<AttributeRequirement>();
|
||||
requirement.Attribute = Stats.Level.GetPersistent(gameConfiguration);
|
||||
requirement.MinimumValue = level;
|
||||
item.Requirements.Add(requirement);
|
||||
}
|
||||
#pragma warning restore SA1600, CS1591
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// <copyright file="FixJeweleryPetsDamageCalcsPlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes jewelery items and pet options related to damage, and jewelery resistance values.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("DD5B0424-89DB-4DE4-A1BB-B294F2C1FCE6")]
|
||||
public class FixJeweleryPetsDamageCalcsPlugInSeason6 : FixJeweleryPetsDamageCalcsPlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal new const string PlugInName = "Fix Jewelery and Pets Damage Calculations";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal new const string PlugInDescription = "This update fixes jewelery items and pet options related to damage, and jewelery resistance values.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixJeweleryPetsDamageCalcsSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
|
||||
var jeweleryItemSlots = gameConfiguration.ItemSlotTypes.Where(st => st.ItemSlots.Contains(9) || st.ItemSlots.Contains(10));
|
||||
var petItemSlot = gameConfiguration.ItemSlotTypes.First(st => st.ItemSlots.Contains(8));
|
||||
|
||||
var miscItems = gameConfiguration.Items.Where(i => i.Group == (byte)ItemGroups.Misc1);
|
||||
var jewelery = miscItems.Where(i => jeweleryItemSlots.Contains(i.ItemSlot));
|
||||
var pets = miscItems.Where(i => petItemSlot.Equals(i.ItemSlot));
|
||||
|
||||
// Remove next season rings
|
||||
foreach (var jeweleryItem in jewelery.ToList())
|
||||
{
|
||||
if (jeweleryItem.Number is 163 or 164 or 165)
|
||||
{
|
||||
gameConfiguration.Items.Remove(jeweleryItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Update jewelery
|
||||
#pragma warning disable SA1116, SA1117 // Parameters must be on same line or separate lines
|
||||
var eliteSkeletonRing = jewelery.First(i => i.Number == 39);
|
||||
this.AddLevelRequirement(context, gameConfiguration, eliteSkeletonRing, 10);
|
||||
if (eliteSkeletonRing.PossibleItemOptions.FirstOrDefault() is { } eliteSkeletonRingItemOptDef)
|
||||
{
|
||||
CreateItemBasePowerUps(eliteSkeletonRing,
|
||||
(Stats.DefenseBase, 1.1f, AggregateType.Multiplicate));
|
||||
CreateItemOptionDefinition(eliteSkeletonRingItemOptDef, "Elite Skeleton Transformation Ring", ItemOptionDefinitionNumbers.EliteSkeletonTransformationRing,
|
||||
(Stats.MaximumHealth, 0, AggregateType.AddRaw, (Stats.Level, 1)));
|
||||
}
|
||||
|
||||
var jackOlanternRing = jewelery.First(i => i.Number == 40);
|
||||
this.AddLevelRequirement(context, gameConfiguration, jackOlanternRing, 10);
|
||||
|
||||
var christmasRing = jewelery.First(i => i.Number == 41);
|
||||
CreateItemBasePowerUps(christmasRing,
|
||||
(Stats.BaseDamageBonus, 20, AggregateType.AddRaw));
|
||||
|
||||
var gameMasterRing = jewelery.First(i => i.Number == 42);
|
||||
CreateItemBasePowerUps(gameMasterRing,
|
||||
(Stats.IceDamageBonus, 255, AggregateType.AddRaw),
|
||||
(Stats.PoisonDamageBonus, 255, AggregateType.AddRaw),
|
||||
(Stats.LightningDamageBonus, 255, AggregateType.AddRaw),
|
||||
(Stats.FireDamageBonus, 255, AggregateType.AddRaw),
|
||||
(Stats.EarthDamageBonus, 255, AggregateType.AddRaw),
|
||||
(Stats.WindDamageBonus, 255, AggregateType.AddRaw),
|
||||
(Stats.WaterDamageBonus, 255, AggregateType.AddRaw),
|
||||
(Stats.IceResistance, 255, AggregateType.Maximum),
|
||||
(Stats.PoisonResistance, 255, AggregateType.Maximum),
|
||||
(Stats.LightningResistance, 255, AggregateType.Maximum),
|
||||
(Stats.FireResistance, 255, AggregateType.Maximum),
|
||||
(Stats.EarthResistance, 255, AggregateType.Maximum),
|
||||
(Stats.WindResistance, 255, AggregateType.Maximum),
|
||||
(Stats.WaterResistance, 255, AggregateType.Maximum));
|
||||
|
||||
var snowmanRing = jewelery.First(i => i.Number == 68);
|
||||
this.AddLevelRequirement(context, gameConfiguration, snowmanRing, 10);
|
||||
|
||||
var pandaRing = jewelery.First(i => i.Number == 76);
|
||||
pandaRing.PossibleItemOptions.Clear();
|
||||
CreateItemBasePowerUps(pandaRing,
|
||||
(Stats.BaseDamageBonus, 30, AggregateType.AddRaw),
|
||||
(Stats.CurseBaseDmg, 30, AggregateType.AddRaw),
|
||||
(Stats.MoneyAmountRate, 1.5f, AggregateType.Multiplicate),
|
||||
(Stats.FinalDamageBonus, 30, AggregateType.AddRaw));
|
||||
|
||||
var skeletonRing = jewelery.First(i => i.Number == 122);
|
||||
if (skeletonRing.PossibleItemOptions.FirstOrDefault() is { } skeletonRingItemOptDef)
|
||||
{
|
||||
CreateItemBasePowerUps(skeletonRing,
|
||||
(Stats.BaseDamageBonus, 40, AggregateType.AddRaw),
|
||||
(Stats.CurseBaseDmg, 40, AggregateType.AddRaw));
|
||||
CreateItemOptionDefinition(skeletonRingItemOptDef, "Skeleton Transformation Ring", ItemOptionDefinitionNumbers.SkeletonTransformationRing,
|
||||
(Stats.BonusExperienceRate, 0, AggregateType.AddRaw, (Stats.IsPetSkeletonEquipped, 0.3f)));
|
||||
}
|
||||
|
||||
var wizardsRing = jewelery.First(i => i.Number == 20);
|
||||
wizardsRing.PossibleItemOptions.Clear();
|
||||
CreateItemBasePowerUps(wizardsRing,
|
||||
(Stats.MinimumPhysBaseDmg, 1.1f, AggregateType.Multiplicate),
|
||||
(Stats.MaximumPhysBaseDmg, 1.1f, AggregateType.Multiplicate),
|
||||
(Stats.AttackSpeedAny, 10f, AggregateType.AddRaw),
|
||||
(Stats.MinimumWizBaseDmg, 1.1f, AggregateType.Multiplicate),
|
||||
(Stats.MaximumWizBaseDmg, 1.1f, AggregateType.Multiplicate));
|
||||
|
||||
// Update pets
|
||||
var demon = pets.First(i => i.Number == 64);
|
||||
demon.BasePowerUpAttributes.Clear();
|
||||
CreateItemBasePowerUps(demon,
|
||||
(Stats.MinimumPhysBaseDmg, 1.4f, AggregateType.Multiplicate),
|
||||
(Stats.MaximumPhysBaseDmg, 1.4f, AggregateType.Multiplicate),
|
||||
(Stats.MinimumWizBaseDmg, 1.4f, AggregateType.Multiplicate),
|
||||
(Stats.MaximumWizBaseDmg, 1.4f, AggregateType.Multiplicate),
|
||||
(Stats.MinimumCurseBaseDmg, 1.4f, AggregateType.Multiplicate),
|
||||
(Stats.MaximumCurseBaseDmg, 1.4f, AggregateType.Multiplicate),
|
||||
(Stats.AttackSpeedAny, 10f, AggregateType.AddRaw));
|
||||
|
||||
var panda = pets.First(i => i.Number == 80);
|
||||
panda.BasePowerUpAttributes.Clear();
|
||||
CreateItemBasePowerUps(panda,
|
||||
(Stats.BonusExperienceRate, 0.5f, AggregateType.AddRaw),
|
||||
(Stats.DefenseFinal, 50f, AggregateType.AddRaw));
|
||||
|
||||
var unicorn = pets.First(i => i.Number == 106);
|
||||
unicorn.BasePowerUpAttributes.Clear();
|
||||
CreateItemBasePowerUps(unicorn,
|
||||
(Stats.MoneyAmountRate, 1.5f, AggregateType.Multiplicate),
|
||||
(Stats.DefenseFinal, 50f, AggregateType.AddRaw));
|
||||
|
||||
var skeleton = pets.First(i => i.Number == 123);
|
||||
skeleton.BasePowerUpAttributes.Clear();
|
||||
CreateItemBasePowerUps(skeleton,
|
||||
(Stats.MinimumPhysBaseDmg, 1.2f, AggregateType.Multiplicate),
|
||||
(Stats.MaximumPhysBaseDmg, 1.2f, AggregateType.Multiplicate),
|
||||
(Stats.MinimumWizBaseDmg, 1.2f, AggregateType.Multiplicate),
|
||||
(Stats.MaximumWizBaseDmg, 1.2f, AggregateType.Multiplicate),
|
||||
(Stats.MinimumCurseBaseDmg, 1.2f, AggregateType.Multiplicate),
|
||||
(Stats.MaximumCurseBaseDmg, 1.2f, AggregateType.Multiplicate),
|
||||
(Stats.AttackSpeedAny, 10f, AggregateType.AddRaw),
|
||||
(Stats.BonusExperienceRate, 0.3f, AggregateType.AddRaw),
|
||||
(Stats.IsPetSkeletonEquipped, 1, AggregateType.AddRaw));
|
||||
#pragma warning restore SA1116, SA1117
|
||||
|
||||
void CreateItemBasePowerUps(ItemDefinition item, params (AttributeDefinition AttributeDefinition, float Value, AggregateType AggregateType)[] powerUps)
|
||||
{
|
||||
foreach (var (attributeDefinition, value, aggregateType) in powerUps)
|
||||
{
|
||||
var powerUpDefinition = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = attributeDefinition.GetPersistent(gameConfiguration);
|
||||
powerUpDefinition.BaseValue = value;
|
||||
powerUpDefinition.AggregateType = aggregateType;
|
||||
item.BasePowerUpAttributes.Add(powerUpDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateItemOptionDefinition(ItemOptionDefinition itemOptionDef, string name, short number, (AttributeDefinition TargetOption, float Value, AggregateType AggregateType, (AttributeDefinition SourceAttribute, float Multiplier)) option)
|
||||
{
|
||||
itemOptionDef.Name = name;
|
||||
|
||||
foreach (var possibleOption in itemOptionDef.PossibleOptions.ToList())
|
||||
{
|
||||
if (possibleOption.PowerUpDefinition?.TargetAttribute != option.TargetOption)
|
||||
{
|
||||
itemOptionDef.PossibleOptions.Remove(possibleOption);
|
||||
}
|
||||
}
|
||||
|
||||
if (itemOptionDef.PossibleOptions.Count == 0)
|
||||
{
|
||||
itemOptionDef.PossibleOptions.Add(CreateItemOption(option.TargetOption, option.Value, option.AggregateType, number, (option.Item4.SourceAttribute, option.Item4.Multiplier)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update existing option
|
||||
var possibleOption = itemOptionDef.PossibleOptions.First();
|
||||
possibleOption.PowerUpDefinition!.Boost!.ConstantValue.Value = option.Value;
|
||||
possibleOption.PowerUpDefinition.Boost.ConstantValue.AggregateType = option.AggregateType;
|
||||
|
||||
var attributeRelationship = context.CreateNew<AttributeRelationship>();
|
||||
attributeRelationship.SetGuid(number, option.TargetOption.Id.ExtractFirstTwoBytes(), 0);
|
||||
attributeRelationship.InputAttribute = option.Item4.SourceAttribute.GetPersistent(gameConfiguration);
|
||||
attributeRelationship.InputOperator = InputOperator.Multiply;
|
||||
attributeRelationship.InputOperand = option.Item4.Multiplier;
|
||||
possibleOption.PowerUpDefinition.Boost.RelatedValues.Add(attributeRelationship);
|
||||
}
|
||||
|
||||
// Always add all options "randomly" when it drops ;)
|
||||
itemOptionDef.AddChance = 1.0f;
|
||||
itemOptionDef.AddsRandomly = true;
|
||||
itemOptionDef.MaximumOptionsPerItem = 1;
|
||||
}
|
||||
|
||||
IncreasableItemOption CreateItemOption(AttributeDefinition targetOption, float value, AggregateType aggregateType, short number, (AttributeDefinition? SourceAttribute, float Multiplier) relatedAttribute)
|
||||
{
|
||||
var itemOption = context.CreateNew<IncreasableItemOption>();
|
||||
itemOption.SetGuid(number, targetOption.Id.ExtractFirstTwoBytes());
|
||||
itemOption.PowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
itemOption.PowerUpDefinition.TargetAttribute = targetOption.GetPersistent(gameConfiguration);
|
||||
itemOption.PowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
itemOption.PowerUpDefinition.Boost.ConstantValue.Value = value;
|
||||
itemOption.PowerUpDefinition.Boost.ConstantValue.AggregateType = aggregateType;
|
||||
|
||||
if (relatedAttribute.SourceAttribute is not null)
|
||||
{
|
||||
var attributeRelationship = context.CreateNew<AttributeRelationship>();
|
||||
attributeRelationship.SetGuid(number, targetOption.Id.ExtractFirstTwoBytes(), 0);
|
||||
attributeRelationship.InputAttribute = relatedAttribute.SourceAttribute.GetPersistent(gameConfiguration);
|
||||
attributeRelationship.InputOperator = InputOperator.Multiply;
|
||||
attributeRelationship.InputOperand = relatedAttribute.Multiplier;
|
||||
itemOption.PowerUpDefinition.Boost.RelatedValues.Add(attributeRelationship);
|
||||
}
|
||||
|
||||
return itemOption;
|
||||
}
|
||||
|
||||
// Update skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.PoisonResistanceInc)?.MasterDefinition is { } poisonResistanceInc)
|
||||
{
|
||||
poisonResistanceInc.ValueFormula = poisonResistanceInc.DisplayValueFormula;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.LightningResistanceInc)?.MasterDefinition is { } lightningResistanceInc)
|
||||
{
|
||||
lightningResistanceInc.ValueFormula = lightningResistanceInc.DisplayValueFormula;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.IceResistanceIncrease)?.MasterDefinition is { } iceResistanceIncrease)
|
||||
{
|
||||
iceResistanceIncrease.ValueFormula = iceResistanceIncrease.DisplayValueFormula;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// <copyright file="FixLevelDiv20ExcOptionUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The chaos castle update plugin.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("B0F275DC-B3C2-4826-8263-FFDC8A8AFAEA")]
|
||||
public class FixLevelDiv20ExcOptionUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Excellent Option Level/20 damage fix";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes the excellent option which adds level / 20 as wizardry damage";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixLevelDiv20ExcOptionUpdate;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 04, 03, 20, 5, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CS1998
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
#pragma warning restore CS1998
|
||||
{
|
||||
var level20Relationships = gameConfiguration.ItemOptions
|
||||
.SelectMany(io => io.PossibleOptions.Where(p => p.OptionType == ItemOptionTypes.Excellent))
|
||||
.SelectMany(o => o.PowerUpDefinition?.Boost?.RelatedValues.Where(s => s.InputAttribute == Stats.Level && Math.Abs(s.InputOperand - 20f) < 0.01) ?? Enumerable.Empty<AttributeRelationship>())
|
||||
.ToList();
|
||||
foreach (var relationship in level20Relationships)
|
||||
{
|
||||
relationship.InputOperand = 1f / 20f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// <copyright file="FixLifeSwellEffectUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update sets the right settings for the life swell effect.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("FD521A61-D5B4-4CF2-B203-6FFF12C80E51")]
|
||||
public class FixLifeSwellEffectUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fixed life swell effect";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update sets the right settings for the life swell effect.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixLifeSwellEffect;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 08, 25, 15, 05, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var effect = gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.GreaterFortitude);
|
||||
if (effect is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var boost = effect.PowerUpDefinitions.FirstOrDefault()?.Boost;
|
||||
if (boost is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var relatedValue in boost.RelatedValues)
|
||||
{
|
||||
relatedValue.InputOperator = InputOperator.ExponentiateByAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// <copyright file="FixMaxManaAndAbilityJewelryOptionsUpdateSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the calculation for max mana/AG % increase provided by Ring of Magic/Pendant of Ability options.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("EAC7C809-D4B8-443F-BE52-E56560003483")]
|
||||
public class FixMaxManaAndAbilityJewelryOptionsUpdateSeason6 : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix max mana/AG % increase jewelry options";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes the calculation for max mana/AG % increase provided by Ring of Magic/Pendant of Ability options";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixMaxManaAndAbilityJewelryOptionsSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 09, 26, 10, 00, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var itemOptionGuids = new List<Guid> { new("00000083-d018-8826-0000-000000000000"), new("00000083-d01c-bbba-0000-000000000000") };
|
||||
var itemOptions = gameConfiguration.ItemOptions.Where(io => itemOptionGuids.Contains(io.GetId()));
|
||||
|
||||
foreach (var itemOption in itemOptions)
|
||||
{
|
||||
var optionsOfLevel = itemOption?.PossibleOptions.FirstOrDefault()?.LevelDependentOptions
|
||||
.Where(opt => opt.Level > 1)
|
||||
.OrderBy(opt => opt.Level);
|
||||
|
||||
if (optionsOfLevel is not null)
|
||||
{
|
||||
for (int i = 0; i < optionsOfLevel.Count(); i++)
|
||||
{
|
||||
if (optionsOfLevel.ElementAt(i).PowerUpDefinition?.Boost?.ConstantValue is SimpleElement elmt && elmt.Value > i + 2)
|
||||
{
|
||||
elmt.Value -= i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// <copyright file="FixRageFighterMultipleHitSkillsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the missing multiple hits to the Killing Blow, Beast Uppercut, Chain Drive, Dragon Roar and Phoenix Shot Rage Fighter skills, as well as their magic effects.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("EDDD17F9-BEA5-40F0-A653-8567566C40E7")]
|
||||
public class FixRageFighterMultipleHitSkillsPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Rage Fighter Multiple Hit Skills";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds the missing multiple hits to the Killing Blow, Beast Uppercut, Chain Drive, Dragon Roar and Phoenix Shot Rage Fighter skills, as well as their magic effects.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixRageFighterMultipleHitSkills;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 1, 12, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Update attributes
|
||||
var defenseDecrement = context.CreateNew<AttributeDefinition>(Stats.DefenseDecrement.Id, Stats.DefenseDecrement.Designation, Stats.DefenseDecrement.Description);
|
||||
gameConfiguration.Attributes.Add(defenseDecrement);
|
||||
|
||||
var innovationDefDecrement = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration);
|
||||
|
||||
gameConfiguration.CharacterClasses.ForEach(charClass =>
|
||||
{
|
||||
// Update summoner berserker defense reduction attributes
|
||||
// Summoner classes.
|
||||
if (charClass.Number == 20 || charClass.Number == 22 || charClass.Number == 23)
|
||||
{
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attr => attr.TargetAttribute == Stats.DefensePvm && attr.AggregateType == AggregateType.AddFinal) is { } finalBerserkerHealthDecrementToDefensePvm)
|
||||
{
|
||||
finalBerserkerHealthDecrementToDefensePvm.TargetAttribute = Stats.DefenseFinal.GetPersistent(gameConfiguration);
|
||||
}
|
||||
|
||||
if (charClass.AttributeCombinations.FirstOrDefault(attr => attr.TargetAttribute == Stats.DefensePvp && attr.AggregateType == AggregateType.AddFinal) is { } finalBerserkerHealthDecrementToDefensePvp)
|
||||
{
|
||||
charClass.AttributeCombinations.Remove(finalBerserkerHealthDecrementToDefensePvp);
|
||||
}
|
||||
}
|
||||
|
||||
// Add defense decrement relationships
|
||||
var tempInnovDefDec = context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Temp Innovation defense decrement", string.Empty);
|
||||
gameConfiguration.Attributes.Add(tempInnovDefDec);
|
||||
|
||||
var innovationDefDecrementToTempInnovDefDec = context.CreateNew<AttributeRelationship>(
|
||||
tempInnovDefDec,
|
||||
-1,
|
||||
innovationDefDecrement,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw);
|
||||
|
||||
var tempInnovDefDecToDefenseDecrement = context.CreateNew<AttributeRelationship>(
|
||||
defenseDecrement,
|
||||
1,
|
||||
tempInnovDefDec,
|
||||
InputOperator.Add,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.Multiplicate);
|
||||
|
||||
charClass.AttributeCombinations.Add(innovationDefDecrementToTempInnovDefDec);
|
||||
charClass.AttributeCombinations.Add(tempInnovDefDecToDefenseDecrement);
|
||||
charClass.BaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(1, defenseDecrement));
|
||||
});
|
||||
|
||||
// Update Increase Health (Vitality) magic effect
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.IncreaseHealth) is { } increaseHealthEffect
|
||||
&& increaseHealthEffect.PowerUpDefinitions.FirstOrDefault() is { } increaseHealthPowerUp)
|
||||
{
|
||||
increaseHealthPowerUp.Boost!.MaximumValue = 200f;
|
||||
}
|
||||
|
||||
// Update Defense Reduction (Fire Slash) magic effect
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.DefenseReduction) is { } defenseReductionEffect
|
||||
&& defenseReductionEffect.PowerUpDefinitions.FirstOrDefault() is { } powerUp)
|
||||
{
|
||||
powerUp.TargetAttribute = defenseDecrement;
|
||||
defenseReductionEffect.PowerUpDefinitions.Clear();
|
||||
defenseReductionEffect.PowerUpDefinitions.Add(powerUp);
|
||||
}
|
||||
|
||||
// Add new magic effects
|
||||
var defensereductionBeastUppercut = this.CreateDefenseReductionBeastUppercutMagicEffect(context, gameConfiguration);
|
||||
var decreaseBlockEffect = this.CreateDecreaseBlockMagicEffect(context, gameConfiguration);
|
||||
|
||||
// Apply default value of NumberOfHitsPerAttack to all skills
|
||||
foreach (var skill in gameConfiguration.Skills)
|
||||
{
|
||||
skill.NumberOfHitsPerAttack = 1;
|
||||
}
|
||||
|
||||
// Update existing skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlow) is { } killingBlow)
|
||||
{
|
||||
killingBlow.NumberOfHitsPerAttack = 4;
|
||||
}
|
||||
|
||||
var killingBlowStrengthener = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowStrengthener);
|
||||
if (killingBlowStrengthener is not null)
|
||||
{
|
||||
killingBlowStrengthener.NumberOfHitsPerAttack = 4;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowMastery) is { } killingBlowMastery)
|
||||
{
|
||||
killingBlowMastery.NumberOfHitsPerAttack = 4;
|
||||
killingBlowMastery.MasterDefinition!.ReplacedSkill = killingBlowStrengthener;
|
||||
killingBlowMastery.MasterDefinition.ValueFormula = $"{killingBlowMastery.MasterDefinition.ValueFormula} / 100";
|
||||
killingBlowMastery.MasterDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration);
|
||||
killingBlowMastery.MasterDefinition.Aggregation = AggregateType.AddRaw;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercut) is { } beastUppercut)
|
||||
{
|
||||
beastUppercut.MagicEffectDef = defensereductionBeastUppercut;
|
||||
beastUppercut.NumberOfHitsPerAttack = 2;
|
||||
}
|
||||
|
||||
var beastUppercutStrengthener = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercutStrengthener);
|
||||
if (beastUppercutStrengthener is not null)
|
||||
{
|
||||
beastUppercutStrengthener.MagicEffectDef = defensereductionBeastUppercut;
|
||||
beastUppercutStrengthener.NumberOfHitsPerAttack = 2;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercutMastery) is { } beastUppercutMastery)
|
||||
{
|
||||
beastUppercutMastery.MagicEffectDef = defensereductionBeastUppercut;
|
||||
beastUppercutMastery.NumberOfHitsPerAttack = 2;
|
||||
beastUppercutMastery.MasterDefinition!.ReplacedSkill = beastUppercutStrengthener;
|
||||
beastUppercutMastery.MasterDefinition.ValueFormula = $"{beastUppercutMastery.MasterDefinition.ValueFormula} / -100";
|
||||
beastUppercutMastery.MasterDefinition.TargetAttribute = defenseDecrement;
|
||||
beastUppercutMastery.MasterDefinition.Aggregation = AggregateType.Multiplicate;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainDrive) is { } chainDrive)
|
||||
{
|
||||
chainDrive.MagicEffectDef!.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
chainDrive.MagicEffectDef.Chance.ConstantValue.Value = 0.4f;
|
||||
chainDrive.NumberOfHitsPerAttack = 4;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainDriveStrengthener) is { } chainDriveStrengthener)
|
||||
{
|
||||
chainDriveStrengthener.MagicEffectDef!.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
chainDriveStrengthener.MagicEffectDef.Chance.ConstantValue.Value = 0.4f;
|
||||
chainDriveStrengthener.NumberOfHitsPerAttack = 4;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DragonRoar) is { } dragonRoar)
|
||||
{
|
||||
dragonRoar.SkillType = SkillType.AreaSkillExplicitTarget;
|
||||
dragonRoar.NumberOfHitsPerAttack = 4;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DragonRoarStrengthener) is { } dragonRoarStrengthener)
|
||||
{
|
||||
dragonRoarStrengthener.SkillType = SkillType.AreaSkillExplicitTarget;
|
||||
dragonRoarStrengthener.NumberOfHitsPerAttack = 4;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.PhoenixShot) is { } phoenixShot)
|
||||
{
|
||||
phoenixShot.MagicEffectDef = decreaseBlockEffect;
|
||||
phoenixShot.NumberOfHitsPerAttack = 4;
|
||||
}
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateDecreaseBlockMagicEffect(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)MagicEffectNumber.DecreaseBlock;
|
||||
magicEffect.Name = "Decrease Block Effect (Phoenix Shot)";
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = false;
|
||||
magicEffect.StopByDeath = true;
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = 10; // 10 Seconds
|
||||
magicEffect.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Chance.ConstantValue.Value = 0.1f; // 10%
|
||||
|
||||
var decDefRatePowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(decDefRatePowerUpDefinition);
|
||||
decDefRatePowerUpDefinition.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(gameConfiguration);
|
||||
decDefRatePowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
decDefRatePowerUpDefinition.Boost.ConstantValue.Value = -50f;
|
||||
decDefRatePowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
|
||||
var decDefRatePowerUpDefinitionPvp = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitionsPvp.Add(decDefRatePowerUpDefinitionPvp);
|
||||
decDefRatePowerUpDefinitionPvp.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(gameConfiguration);
|
||||
decDefRatePowerUpDefinitionPvp.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.Value = -20f;
|
||||
decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.AddFinal;
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateDefenseReductionBeastUppercutMagicEffect(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)MagicEffectNumber.DefenseReduction; // We will map skill to effect by hand in this update, so we use this number instead of DefenseReductionBeastUppercut
|
||||
magicEffect.Name = "Defense Reduction Effect (Beast Uppercut)";
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = true;
|
||||
magicEffect.StopByDeath = true;
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = (float)TimeSpan.FromSeconds(10).TotalSeconds;
|
||||
magicEffect.Chance = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Chance.ConstantValue.Value = 0.1f; // 10%
|
||||
|
||||
var reduceDefenseEffect = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(reduceDefenseEffect);
|
||||
reduceDefenseEffect.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration);
|
||||
reduceDefenseEffect.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
reduceDefenseEffect.Boost.ConstantValue.Value = 0.9f; // 10% decrease
|
||||
reduceDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// <copyright file="FixSetBonusesPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This updates adds the new <see cref="SystemConfiguration"/> with default settings.
|
||||
/// </summary>
|
||||
public abstract class FixSetBonusesPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Item Set Bonuses";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes the set bonuses for additional defense of +10~15 sets and the defense rate.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 04, 22, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var itemSetGroups = await context.GetAsync<ItemSetGroup>().ConfigureAwait(false);
|
||||
var unassignedSets = itemSetGroups.Where(s => !gameConfiguration.ItemSetGroups.Contains(s));
|
||||
foreach (var set in unassignedSets)
|
||||
{
|
||||
gameConfiguration.ItemSetGroups.Add(set);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="FixSetBonusesPlugIn"/> for season 6.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("F858E471-B76D-4AAF-8886-8DEB45BC1AB8")]
|
||||
public class Season6 : FixSetBonusesPlugIn
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixSetBonusesSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="FixSetBonusesPlugIn"/> for version 0.95d.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("3D0201C3-D956-4BDD-9D57-3F6FD921EDF7")]
|
||||
public class V095d : FixSetBonusesPlugIn
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixSetBonuses095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="FixSetBonusesPlugIn"/> for version 0.75.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("2E009ADF-1580-4E03-BA59-C9C51DC109BA")]
|
||||
public class V075 : FixSetBonusesPlugIn
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixSetBonuses075;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// <copyright file="FixSkillMultipliersPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes RF skill multipliers and adds several skill-specific multipliers.
|
||||
/// </summary>
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[PlugIn]
|
||||
[Guid("753F01BA-5FCA-42FA-9587-7055631C27B7")]
|
||||
public class FixSkillMultipliersPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Skill Multipliers";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes RF skill multipliers and adds several skill-specific multipliers.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixSkillMultipliers;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2025, 11, 28, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Add SBRD max value
|
||||
Stats.SoulBarrierReceiveDecrement.GetPersistent(gameConfiguration).MaximumValue = 0.7f;
|
||||
|
||||
// Add new attributes
|
||||
var vitalitySkillMultiplier = context.CreateNew<AttributeDefinition>(Stats.VitalitySkillMultiplier.Id, Stats.VitalitySkillMultiplier.Designation, Stats.VitalitySkillMultiplier.Description);
|
||||
gameConfiguration.Attributes.Add(vitalitySkillMultiplier);
|
||||
var explosionBonusDmg = context.CreateNew<AttributeDefinition>(Stats.ExplosionBonusDmg.Id, Stats.ExplosionBonusDmg.Designation, Stats.ExplosionBonusDmg.Description);
|
||||
gameConfiguration.Attributes.Add(explosionBonusDmg);
|
||||
var requiemBonusDmg = context.CreateNew<AttributeDefinition>(Stats.RequiemBonusDmg.Id, Stats.RequiemBonusDmg.Designation, Stats.RequiemBonusDmg.Description);
|
||||
gameConfiguration.Attributes.Add(requiemBonusDmg);
|
||||
var pollutionBonusDmg = context.CreateNew<AttributeDefinition>(Stats.PollutionBonusDmg.Id, Stats.PollutionBonusDmg.Designation, Stats.PollutionBonusDmg.Description);
|
||||
gameConfiguration.Attributes.Add(pollutionBonusDmg);
|
||||
var skillBaseMultiplier = context.CreateNew<AttributeDefinition>(Stats.SkillBaseMultiplier.Id, Stats.SkillBaseMultiplier.Designation, Stats.SkillBaseMultiplier.Description);
|
||||
gameConfiguration.Attributes.Add(skillBaseMultiplier);
|
||||
var skillBaseDamageBonus = context.CreateNew<AttributeDefinition>(Stats.SkillBaseDamageBonus.Id, Stats.SkillBaseDamageBonus.Designation, Stats.SkillBaseDamageBonus.Description);
|
||||
gameConfiguration.Attributes.Add(skillBaseDamageBonus);
|
||||
var skillFinalMultiplier = context.CreateNew<AttributeDefinition>(Stats.SkillFinalMultiplier.Id, Stats.SkillFinalMultiplier.Designation, Stats.SkillFinalMultiplier.Description);
|
||||
gameConfiguration.Attributes.Add(skillFinalMultiplier);
|
||||
var skillFinalDamageBonus = context.CreateNew<AttributeDefinition>(Stats.SkillFinalDamageBonus.Id, Stats.SkillFinalDamageBonus.Designation, Stats.SkillFinalDamageBonus.Description);
|
||||
gameConfiguration.Attributes.Add(skillFinalDamageBonus);
|
||||
|
||||
var totalEnergy = Stats.TotalEnergy.GetPersistent(gameConfiguration);
|
||||
var totalVitality = Stats.TotalVitality.GetPersistent(gameConfiguration);
|
||||
var skillMultiplier = Stats.SkillMultiplier.GetPersistent(gameConfiguration);
|
||||
|
||||
// Fix RF classes skill multiplier
|
||||
gameConfiguration.CharacterClasses.ForEach(charClass =>
|
||||
{
|
||||
// RF classes.
|
||||
if (charClass.Number == 24 || charClass.Number == 25)
|
||||
{
|
||||
if (charClass.BaseAttributeValues.FirstOrDefault(a => a.Definition == Stats.SkillMultiplier) is { } skillMult)
|
||||
{
|
||||
charClass.BaseAttributeValues.Remove(skillMult);
|
||||
charClass.BaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(0.5f, skillMultiplier));
|
||||
}
|
||||
|
||||
var totalEnergyToSkillMultiplier = context.CreateNew<AttributeRelationship>(
|
||||
skillMultiplier,
|
||||
0.001f,
|
||||
totalEnergy,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw);
|
||||
|
||||
var totalVitalityToVitalitySkillMultiplier = context.CreateNew<AttributeRelationship>(
|
||||
vitalitySkillMultiplier,
|
||||
0.001f,
|
||||
totalVitality,
|
||||
InputOperator.Multiply,
|
||||
default(AttributeDefinition?),
|
||||
AggregateType.AddRaw);
|
||||
|
||||
charClass.AttributeCombinations.Add(totalEnergyToSkillMultiplier);
|
||||
charClass.AttributeCombinations.Add(totalVitalityToVitalitySkillMultiplier);
|
||||
charClass.BaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(0.5f, vitalitySkillMultiplier));
|
||||
}
|
||||
});
|
||||
|
||||
// Update Increase Health magic effect
|
||||
if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.IncreaseHealth) is { } increaseHealthEffect)
|
||||
{
|
||||
if (increaseHealthEffect.PowerUpDefinitions.FirstOrDefault() is { } powerUp)
|
||||
{
|
||||
powerUp.TargetAttribute = totalVitality;
|
||||
}
|
||||
}
|
||||
|
||||
// Create Weakness magic effect
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)MagicEffectNumber.Weakness;
|
||||
magicEffect.Name = "Weakness Effect";
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = false;
|
||||
magicEffect.StopByDeath = true;
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = 10; // 10 seconds
|
||||
|
||||
var decDmgPowerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(decDmgPowerUpDefinition);
|
||||
decDmgPowerUpDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration);
|
||||
decDmgPowerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
decDmgPowerUpDefinition.Boost.ConstantValue.Value = 0.05f;
|
||||
decDmgPowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddRaw;
|
||||
|
||||
// Add Wakness magic effect to Killing Blow skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlow) is { } killingBlow)
|
||||
{
|
||||
killingBlow.MagicEffectDef = magicEffect;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowStrengthener) is { } killingBlowStr)
|
||||
{
|
||||
killingBlowStr.MagicEffectDef = magicEffect;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowMastery) is { } killingBlowMastery)
|
||||
{
|
||||
killingBlowMastery.MagicEffectDef = magicEffect;
|
||||
}
|
||||
|
||||
// Add Selupan's skill multiplier
|
||||
if (gameConfiguration.Monsters.FirstOrDefault(m => m.Number == 459) is { } selupan)
|
||||
{
|
||||
selupan.AddAttributes(new Dictionary<AttributeDefinition, float> { { skillMultiplier, 2 } }, context, gameConfiguration);
|
||||
}
|
||||
|
||||
// Update skill attribute relationships
|
||||
foreach (var skill in gameConfiguration.Skills)
|
||||
{
|
||||
if (skill.Number == (short)SkillNumber.Nova
|
||||
|| skill.Number == (short)SkillNumber.Earthshake
|
||||
|| skill.Number == (short)SkillNumber.ElectricSpike
|
||||
|| skill.Number == (short)SkillNumber.ChaoticDiseier
|
||||
|| skill.Number == (short)SkillNumber.Force
|
||||
|| skill.Number == (short)SkillNumber.FireBlast
|
||||
|| skill.Number == (short)SkillNumber.FireBurst
|
||||
|| skill.Number == (short)SkillNumber.ForceWave
|
||||
|| skill.Number == (short)SkillNumber.FireScream)
|
||||
{
|
||||
skill.AttributeRelationships.ForEach(rel => { rel.TargetAttribute = skillBaseDamageBonus; });
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not really a fix, more to standardize
|
||||
if (skill.Number == (short)SkillNumber.MultiShot)
|
||||
{
|
||||
skill.AttributeRelationships.ForEach(rel =>
|
||||
{
|
||||
rel.AggregateType = AggregateType.AddRaw;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add new skill attribute relationships
|
||||
AddAttributeRelationship(SkillNumber.FallingSlash, Stats.SkillFinalMultiplier, 2.0f, Stats.SkillMultiplier, InputOperator.Maximum);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.IceArrow, Stats.SkillFinalMultiplier, 2.0f, Stats.SkillMultiplier);
|
||||
AddAttributeRelationship(SkillNumber.Penetration, Stats.SkillFinalMultiplier, 2.0f, Stats.SkillMultiplier);
|
||||
AddAttributeRelationship(SkillNumber.Starfall, Stats.SkillFinalMultiplier, 2.0f, Stats.SkillMultiplier);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.Explosion223, Stats.SkillFinalDamageBonus, 1.0f, Stats.ExplosionBonusDmg);
|
||||
AddAttributeRelationship(SkillNumber.Requiem, Stats.SkillFinalDamageBonus, 1.0f, Stats.RequiemBonusDmg);
|
||||
AddAttributeRelationship(SkillNumber.Pollution, Stats.SkillFinalDamageBonus, 1.0f, Stats.PollutionBonusDmg);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.PlasmaStorm, Stats.SkillFinalMultiplier, 0.002f, Stats.TotalLevel);
|
||||
AddAttributeRelationship(SkillNumber.PlasmaStorm, Stats.SkillFinalMultiplier, -0.6f, Stats.MaximumHealth, InputOperator.Minimum); // 0.002 * 300(min lvl)
|
||||
AddAttributeRelationship(SkillNumber.PlasmaStorm, Stats.SkillFinalMultiplier, 2.0f, Stats.MaximumHealth, InputOperator.Minimum);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.ChaoticDiseier, Stats.SkillFinalMultiplier, 0.8f, Stats.SkillMultiplier);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.KillingBlow, Stats.SkillFinalMultiplier, 1.0f, Stats.VitalitySkillMultiplier);
|
||||
AddAttributeRelationship(SkillNumber.BeastUppercut, Stats.SkillFinalMultiplier, 1.0f, Stats.VitalitySkillMultiplier);
|
||||
AddAttributeRelationship(SkillNumber.ChainDrive, Stats.SkillFinalMultiplier, 1.0f, Stats.VitalitySkillMultiplier);
|
||||
AddAttributeRelationship(SkillNumber.Charge, Stats.SkillFinalMultiplier, 1.0f, Stats.VitalitySkillMultiplier);
|
||||
AddAttributeRelationship(SkillNumber.PhoenixShot, Stats.SkillFinalMultiplier, 1.0f, Stats.VitalitySkillMultiplier);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.DarkSide, Stats.SkillFinalMultiplier, 0.5f, Stats.SkillMultiplier, InputOperator.Add);
|
||||
AddAttributeRelationship(SkillNumber.DarkSide, Stats.SkillFinalMultiplier, 1.0f / 800, Stats.TotalAgility);
|
||||
|
||||
AddAttributeRelationship(SkillNumber.DragonRoar, Stats.SkillFinalMultiplier, 1.0f, Stats.SkillMultiplier);
|
||||
AddAttributeRelationship(SkillNumber.DragonSlasher, Stats.SkillFinalMultiplier, 1.0f, Stats.SkillMultiplier);
|
||||
|
||||
void AddAttributeRelationship(SkillNumber skillNumber, AttributeDefinition targetAttribute, float multiplier, AttributeDefinition sourceAttribute, InputOperator inputOperator = InputOperator.Multiply, AggregateType aggregateType = AggregateType.AddRaw)
|
||||
{
|
||||
var skill = gameConfiguration.Skills.First(s => s.Number == (int)skillNumber);
|
||||
var relationship = CharacterClassHelper.CreateAttributeRelationship(context, gameConfiguration, targetAttribute, multiplier, sourceAttribute, inputOperator, aggregateType);
|
||||
skill.AttributeRelationships.Add(relationship);
|
||||
}
|
||||
|
||||
// Update sum master skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.FireTomeStrengthener)?.MasterDefinition is { } fireTomeStr)
|
||||
{
|
||||
fireTomeStr.TargetAttribute = explosionBonusDmg;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.WindTomeStrengthener)?.MasterDefinition is { } windTomeStr)
|
||||
{
|
||||
windTomeStr.TargetAttribute = requiemBonusDmg;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.LightningTomeStren)?.MasterDefinition is { } lightningTomeStr)
|
||||
{
|
||||
lightningTomeStr.TargetAttribute = pollutionBonusDmg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// <copyright file="FixSocketSeedCraftingUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update sets the right settings for the socket seed crafting.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("C802EFC2-1D42-4218-871E-8886D115F3ED")]
|
||||
public class FixSocketSeedCraftingUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fixed socket seed crafting";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update sets the right settings for the socket seed crafting.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixSocketSeedCrafting;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 08, 25, 15, 00, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var seedMaster = gameConfiguration.Monsters.FirstOrDefault(m => m.NpcWindow == NpcWindow.SeedMaster);
|
||||
if (seedMaster is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var seedCrafting = seedMaster.ItemCraftings.FirstOrDefault(c => c.Number == 42);
|
||||
if (seedCrafting?.SimpleCraftingSettings is not { } craftingSettings)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var option = gameConfiguration.ItemOptionTypes.First(o => object.Equals(o, ItemOptionTypes.Option));
|
||||
foreach (var requirement in craftingSettings.RequiredItems)
|
||||
{
|
||||
requirement.RequiredItemOptions.Remove(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
// <copyright file="FixSummonerCurseSkillsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds missing area skill settings for summoner curse (book) and lightning shock skills.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("A3B4C8DB-2F39-4C81-A2D9-5E4FA5B9E004")]
|
||||
public class FixSummonerCurseSkillsPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Summoner Curse Skills";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds missing area skill settings for summoner curse (book) and lightning shock skills.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixSummonerCurseSkills;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 3, 30, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// Add new attributes
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.BleedingDamageMultiplier);
|
||||
var bleedingDamageMultiplier = Stats.BleedingDamageMultiplier.GetPersistent(gameConfiguration);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.IsBleeding);
|
||||
var isBleeding = Stats.IsBleeding.GetPersistent(gameConfiguration);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.MasteryStunChance);
|
||||
var stunChance = Stats.MasteryStunChance.GetPersistent(gameConfiguration);
|
||||
this.AddStatIfNotExists(context, gameConfiguration, Stats.MasteryMoveTargetChance);
|
||||
var masteryMoveTargetChance = Stats.MasteryMoveTargetChance.GetPersistent(gameConfiguration);
|
||||
|
||||
// Add new base attribute to summoner classes
|
||||
var summonerClassNumbers = new[] { (int)CharacterClassNumber.Summoner, (int)CharacterClassNumber.BloodySummoner, (int)CharacterClassNumber.DimensionMaster };
|
||||
foreach (var characterClass in gameConfiguration.CharacterClasses.Where(c => summonerClassNumbers.Contains(c.Number)))
|
||||
{
|
||||
characterClass.BaseAttributeValues.Add(context.CreateNew<ConstValueAttribute>(0.6f, bleedingDamageMultiplier));
|
||||
}
|
||||
|
||||
// Create new magic effects
|
||||
var explosionEffect = this.CreateMagicEffect(context, gameConfiguration, MagicEffectNumber.Explosion, "Explosion Effect", isBleeding, duration: 5);
|
||||
var requiemEffect = this.CreateMagicEffect(context, gameConfiguration, MagicEffectNumber.Requiem, "Requiem Effect", isBleeding, duration: 5);
|
||||
this.CreateMagicEffect(context, gameConfiguration, MagicEffectNumber.Stunned, "Stun Effect", Stats.IsStunned.GetPersistent(gameConfiguration));
|
||||
|
||||
this.MapSkillToEffect(gameConfiguration, SkillNumber.Explosion223, explosionEffect);
|
||||
this.MapSkillToEffect(gameConfiguration, SkillNumber.Requiem, requiemEffect);
|
||||
|
||||
// Set elemental modifiers
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Pollution) is { } pollution)
|
||||
{
|
||||
pollution.SkipElementalModifier = true;
|
||||
pollution.MagicEffectDef = this.CreateMagicEffect(context, gameConfiguration, MagicEffectNumber.Iced, "Iced", Stats.IsIced.GetPersistent(gameConfiguration), duration: 2);
|
||||
}
|
||||
|
||||
foreach (var skillNumber in new[]
|
||||
{
|
||||
SkillNumber.ChainDrive, SkillNumber.ChainDriveStrengthener,
|
||||
SkillNumber.LightningShock, SkillNumber.LightningShockStr,
|
||||
SkillNumber.Earthshake, SkillNumber.EarthshakeStreng, SkillNumber.EarthshakeMastery,
|
||||
SkillNumber.Explosion223, SkillNumber.Requiem,
|
||||
})
|
||||
{
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)skillNumber) is { } skill)
|
||||
{
|
||||
skill.SkipElementalModifier = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update AreaSkillSettings
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Explosion223, false, 0, 0, 0, effectRange: 2);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Requiem, false, 0, 0, 0, effectRange: 2);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Pollution, false, 0, 0, 0, minimumHitsPerAttack: 4, maximumHitsPerAttack: 8, effectRange: 3);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.LightningShock, false, 0, 0, 0, minimumHitsPerAttack: 5, maximumHitsPerAttack: 12, useTargetAreaFilter: true, targetAreaDiameter: 14);
|
||||
this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.LightningShockStr, false, 0, 0, 0, minimumHitsPerAttack: 5, maximumHitsPerAttack: 12, useTargetAreaFilter: true, targetAreaDiameter: 14);
|
||||
|
||||
// Update master skills
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.FireTomeMastery)?.MasterDefinition is { } fireTomeMastery)
|
||||
{
|
||||
fireTomeMastery.TargetAttribute = bleedingDamageMultiplier;
|
||||
fireTomeMastery.Aggregation = AggregateType.AddRaw;
|
||||
fireTomeMastery.ValueFormula = $"{fireTomeMastery.ValueFormula} / 100";
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.WindTomeMastery)?.MasterDefinition is { } windTomeMastery)
|
||||
{
|
||||
windTomeMastery.TargetAttribute = stunChance;
|
||||
windTomeMastery.Aggregation = AggregateType.AddRaw;
|
||||
windTomeMastery.ValueFormula = $"{windTomeMastery.ValueFormula} / 100";
|
||||
}
|
||||
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.LightningTomeMastery)?.MasterDefinition is { } lightningTomeMastery)
|
||||
{
|
||||
lightningTomeMastery.TargetAttribute = masteryMoveTargetChance;
|
||||
lightningTomeMastery.Aggregation = AggregateType.AddRaw;
|
||||
lightningTomeMastery.ValueFormula = $"{lightningTomeMastery.ValueFormula} / 100";
|
||||
}
|
||||
}
|
||||
|
||||
private MagicEffectDefinition CreateMagicEffect(IContext context, GameConfiguration gameConfiguration, MagicEffectNumber effectNumber, string name, AttributeDefinition targetAttribute, int? duration = null)
|
||||
{
|
||||
var magicEffect = context.CreateNew<MagicEffectDefinition>();
|
||||
gameConfiguration.MagicEffects.Add(magicEffect);
|
||||
magicEffect.Number = (short)effectNumber;
|
||||
magicEffect.Name = name;
|
||||
magicEffect.InformObservers = true;
|
||||
magicEffect.SendDuration = false;
|
||||
magicEffect.StopByDeath = true;
|
||||
|
||||
if (duration is not null)
|
||||
{
|
||||
magicEffect.Duration = context.CreateNew<PowerUpDefinitionValue>();
|
||||
magicEffect.Duration.ConstantValue.Value = duration.Value;
|
||||
}
|
||||
|
||||
var powerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
magicEffect.PowerUpDefinitions.Add(powerUpDefinition);
|
||||
powerUpDefinition.TargetAttribute = targetAttribute;
|
||||
powerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUpDefinition.Boost.ConstantValue.Value = 1;
|
||||
|
||||
return magicEffect;
|
||||
}
|
||||
|
||||
private void MapSkillToEffect(GameConfiguration gameConfiguration, SkillNumber skillNumber, MagicEffectDefinition magicEffect)
|
||||
{
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)skillNumber) is { } skill)
|
||||
{
|
||||
skill.MagicEffectDef = magicEffect;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAreaSkillSettings(
|
||||
GameConfiguration gameConfiguration,
|
||||
IContext context,
|
||||
SkillNumber skillNumber,
|
||||
bool useFrustumFilter,
|
||||
float frustumStartWidth,
|
||||
float frustumEndWidth,
|
||||
float frustumDistance,
|
||||
bool useDeferredHits = false,
|
||||
TimeSpan delayPerOneDistance = default,
|
||||
TimeSpan delayBetweenHits = default,
|
||||
int minimumHitsPerTarget = 1,
|
||||
int maximumHitsPerTarget = 1,
|
||||
int minimumHitsPerAttack = default,
|
||||
int maximumHitsPerAttack = default,
|
||||
float hitChancePerDistanceMultiplier = 1.0f,
|
||||
bool useTargetAreaFilter = false,
|
||||
float targetAreaDiameter = default,
|
||||
int projectileCount = 1,
|
||||
int effectRange = default)
|
||||
{
|
||||
if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)skillNumber) is not { } skill)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
skill.SkillType = SkillType.AreaSkillAutomaticHits;
|
||||
var areaSkillSettings = context.CreateNew<AreaSkillSettings>();
|
||||
skill.AreaSkillSettings = areaSkillSettings;
|
||||
|
||||
areaSkillSettings.UseFrustumFilter = useFrustumFilter;
|
||||
areaSkillSettings.FrustumStartWidth = frustumStartWidth;
|
||||
areaSkillSettings.FrustumEndWidth = frustumEndWidth;
|
||||
areaSkillSettings.FrustumDistance = frustumDistance;
|
||||
areaSkillSettings.UseTargetAreaFilter = useTargetAreaFilter;
|
||||
areaSkillSettings.TargetAreaDiameter = targetAreaDiameter;
|
||||
areaSkillSettings.UseDeferredHits = useDeferredHits;
|
||||
areaSkillSettings.DelayPerOneDistance = delayPerOneDistance;
|
||||
areaSkillSettings.DelayBetweenHits = delayBetweenHits;
|
||||
areaSkillSettings.MinimumNumberOfHitsPerTarget = minimumHitsPerTarget;
|
||||
areaSkillSettings.MaximumNumberOfHitsPerTarget = maximumHitsPerTarget;
|
||||
areaSkillSettings.MinimumNumberOfHitsPerAttack = minimumHitsPerAttack;
|
||||
areaSkillSettings.MaximumNumberOfHitsPerAttack = maximumHitsPerAttack;
|
||||
areaSkillSettings.HitChancePerDistanceMultiplier = hitChancePerDistanceMultiplier;
|
||||
areaSkillSettings.ProjectileCount = projectileCount;
|
||||
areaSkillSettings.EffectRange = effectRange;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// <copyright file="FixWarpLevelUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// updating LevelWarpRequirementReductionPercent plugin.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("F4342D86-7042-477A-BC3B-475C1F2A79FF")]
|
||||
public class FixWarpLevelUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Warp Level Reduction";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This plugin updates the LevelWarpRequirementReductionPercent for MG, DL, and RF.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWarpLevelUpdate;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 04, 24, 02, 05, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
// warp requirement reduction
|
||||
var characterClasses = gameConfiguration.CharacterClasses
|
||||
.Where(cc => cc.LevelWarpRequirementReductionPercent == 33)
|
||||
.ToList();
|
||||
foreach (var cc in characterClasses)
|
||||
{
|
||||
cc.LevelWarpRequirementReductionPercent = 34;
|
||||
}
|
||||
|
||||
// warp list
|
||||
(string Name, string? NewName, int Costs, int Level)[] warps =
|
||||
{
|
||||
("KanturuRuins", "KanturuRuins1", -1, 160),
|
||||
("KanturuRelics", null, 12000, -1),
|
||||
("Elbeland", "Elveland", -1, -1),
|
||||
("Elbeland2", "Elveland2", -1, -1),
|
||||
("Elbeland3", "Elveland3", -1, -1),
|
||||
("Vulcan", "Vulcanus", -1, -1),
|
||||
("KanturuRuins3", null, 15000, -1),
|
||||
("Karutan2", null, -1, 170),
|
||||
};
|
||||
foreach (var (name, newName, costs, level) in warps)
|
||||
{
|
||||
var warpInfo = gameConfiguration.WarpList.FirstOrDefault(w => w.Name == name);
|
||||
if (warpInfo is not null)
|
||||
{
|
||||
if (newName is not null)
|
||||
{
|
||||
warpInfo.Name = newName;
|
||||
}
|
||||
|
||||
if (costs > 0)
|
||||
{
|
||||
warpInfo.Costs = costs;
|
||||
}
|
||||
|
||||
if (level > 0)
|
||||
{
|
||||
warpInfo.LevelRequirement = level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add LaCleon
|
||||
var laCleon = gameConfiguration.WarpList.FirstOrDefault(w => w.Name == "LaCleon");
|
||||
if (laCleon is null)
|
||||
{
|
||||
laCleon = context.CreateNew<WarpInfo>();
|
||||
laCleon.Index = 48;
|
||||
laCleon.Name = "LaCleon";
|
||||
laCleon.Costs = 15000;
|
||||
laCleon.LevelRequirement = 280;
|
||||
laCleon.Gate = gameConfiguration.Maps
|
||||
.FirstOrDefault(m => m.Name == "LaCleon")
|
||||
?.ExitGates.FirstOrDefault(g => g.X1 == 222);
|
||||
gameConfiguration.WarpList.Add(laCleon);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// <copyright file="FixWarriorMorningStarPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the weapon of the warrior ancient set. The Hand Axe is replaced by the Morning Star.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("65BA79B5-1DBF-4C97-9628-0D8A429A8C88")]
|
||||
public class FixWarriorMorningStarPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Warrior Morning Star";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes the weapon of the warrior ancient set. The Hand Axe is replaced by the Morning Star.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWarriorMorningStar;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 08, 28, 20, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var warriorSet = gameConfiguration.ItemSetGroups.First(set => set.Name == "Warrior");
|
||||
var itemSet = warriorSet.Items.FirstOrDefault(item => item.ItemDefinition?.Group == (byte)ItemGroups.Axes);
|
||||
if (itemSet != null)
|
||||
{
|
||||
itemSet.ItemDefinition = gameConfiguration.Items.First(item => item is { Group: (byte)ItemGroups.Scepters, Number: 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="FixWeaponRisePercentagePlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes weapons (staff) rise percentage.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("5B63534D-E5DF-46B1-992D-C1637B197EE1")]
|
||||
public class FixWeaponRisePercentagePlugIn075 : FixWeaponRisePercentagePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWeaponRisePercentage075;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// <copyright file="FixWeaponRisePercentagePlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes weapons (staff) rise percentage.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("33259706-F3DF-4F4D-9935-3DEF7E53BF81")]
|
||||
public class FixWeaponRisePercentagePlugIn095D : FixWeaponRisePercentagePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWeaponRisePercentage095d;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// <copyright file="FixWeaponRisePercentagePlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes weapons (staff) rise percentage.
|
||||
/// </summary>
|
||||
public abstract class FixWeaponRisePercentagePlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Weapon Rise Percentage";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes weapons (staff) rise percentage";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 11, 11, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var staffRiseBonusTable = gameConfiguration.ItemLevelBonusTables.Single(bt => bt.Name == "Staff Rise");
|
||||
|
||||
// Modify existing table name and description
|
||||
staffRiseBonusTable.Name = "Staff Rise (even)";
|
||||
staffRiseBonusTable.Description = "The staff rise bonus per item level for even magic power staves.";
|
||||
|
||||
// Add new staff odd increase table
|
||||
float[] staffRiseIncreaseByLevelOdd = { 0, 4, 7, 11, 14, 18, 21, 25, 28, 32, 36, 40, 45, 51, 57, 63 };
|
||||
|
||||
var staffOddTable = context.CreateNew<ItemLevelBonusTable>();
|
||||
gameConfiguration.ItemLevelBonusTables.Add(staffOddTable);
|
||||
staffOddTable.Name = "Staff Rise (odd)";
|
||||
staffOddTable.Description = "The staff rise bonus per item level for odd magic power staves.";
|
||||
for (int level = 0; level < staffRiseIncreaseByLevelOdd.Length; level++)
|
||||
{
|
||||
var value = staffRiseIncreaseByLevelOdd[level];
|
||||
if (value != 0)
|
||||
{
|
||||
var levelBonus = context.CreateNew<LevelBonus>();
|
||||
levelBonus.Level = level;
|
||||
levelBonus.AdditionalValue = staffRiseIncreaseByLevelOdd[level];
|
||||
staffOddTable.BonusPerLevel.Add(levelBonus);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Group 5 weapons (staves)
|
||||
var staves = gameConfiguration.Items.Where(i => i.Group == (int)ItemGroups.Staff && i.BasePowerUpAttributes.Any(pua => pua.TargetAttribute == Stats.StaffRise));
|
||||
foreach (var staff in staves)
|
||||
{
|
||||
if (staff.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.StaffRise) is { } staffRiseAttr)
|
||||
{
|
||||
if ((int)staffRiseAttr.BaseValue % 2 != 0)
|
||||
{
|
||||
staffRiseAttr.BonusPerLevelTable = staffOddTable;
|
||||
}
|
||||
|
||||
staffRiseAttr.BaseValue /= 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
// <copyright file="FixWeaponRisePercentagePlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Attributes;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Items;
|
||||
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Items;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
using static MUnique.OpenMU.Persistence.Initialization.CharacterClasses.CharacterClassHelper;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes weapons (staff, stick, book, scepter) rise percentage increase; Summoner weapons and wings wizardry/curse options; and Wing of Dimension (inc/dec), Cape of Overrule (inc/dec), Cape of Emperor (dec) damage rates..
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("58740F26-6496-4CCA-8C90-C4749E09DDB2")]
|
||||
public class FixWeaponRisePercentagePlugInSeason6 : FixWeaponRisePercentagePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
private new const string PlugInName = "Fix Weapon Rise Percentage, Summoner Items Wizardry/Curse Options, and Several 3rd Level Wing Damage Rates";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
private new const string PlugInDescription = "This update fixes weapons (staff, stick, book, scepter) rise percentage; Summoner weapons and wings wizardry/curse options; and Wing of Dimension (inc/dec), Cape of Overrule (inc/dec), Cape of Emperor (dec) damage rates. Also includes fixes for Divine Staff of Archangel and Eternal Wing Stick.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWeaponRisePercentageSeason6;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
|
||||
var scepterRiseBonusTable = gameConfiguration.ItemLevelBonusTables.Single(bt => bt.Name == "Scepter Rise");
|
||||
|
||||
// Modify existing table name and description
|
||||
scepterRiseBonusTable.Name = "Scepter Rise (even)";
|
||||
scepterRiseBonusTable.Description = "The scepter rise bonus per item level for even magic power scepters.";
|
||||
|
||||
// Add new scepter odd increase table
|
||||
float[] scepterRiseIncreaseByLevelOdd = { 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 18, 21, 25, 29, 33 };
|
||||
|
||||
var scepterOddTable = context.CreateNew<ItemLevelBonusTable>();
|
||||
gameConfiguration.ItemLevelBonusTables.Add(scepterOddTable);
|
||||
scepterOddTable.Name = "Scepter Rise (odd)";
|
||||
scepterOddTable.Description = "The scepter rise bonus per item level for odd magic power scepters.";
|
||||
for (int level = 0; level < scepterRiseIncreaseByLevelOdd.Length; level++)
|
||||
{
|
||||
var value = scepterRiseIncreaseByLevelOdd[level];
|
||||
if (value != 0)
|
||||
{
|
||||
var levelBonus = context.CreateNew<LevelBonus>();
|
||||
levelBonus.Level = level;
|
||||
levelBonus.AdditionalValue = scepterRiseIncreaseByLevelOdd[level];
|
||||
scepterOddTable.BonusPerLevel.Add(levelBonus);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix scepters
|
||||
var scepters = gameConfiguration.Items.Where(i => i.Group == (int)ItemGroups.Scepters && i.BasePowerUpAttributes.Any(pua => pua.TargetAttribute == Stats.ScepterRise));
|
||||
foreach (var scepter in scepters)
|
||||
{
|
||||
if (scepter.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.ScepterRise) is { } scepterRiseAttr)
|
||||
{
|
||||
if ((int)scepterRiseAttr.BaseValue % 2 != 0)
|
||||
{
|
||||
scepterRiseAttr.BonusPerLevelTable = scepterOddTable;
|
||||
}
|
||||
|
||||
scepterRiseAttr.BaseValue /= 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Group 5 weapons (Skull & Divine Staves, sticks, and books)
|
||||
var weaponsG5 = gameConfiguration.Items.Where(i => i.Group == 5);
|
||||
var summonerWeapons = weaponsG5.Where(i => i.PossibleItemOptions.Contains(gameConfiguration.ItemOptions.First(io => io.Name == ExcellentOptions.CurseAttackOptionsName))); // Skull Staff included at this point
|
||||
var staffEvenTable = gameConfiguration.ItemLevelBonusTables.Single(bt => bt.Name == "Staff Rise (even)");
|
||||
var staffOddTable = gameConfiguration.ItemLevelBonusTables.Single(bt => bt.Name == "Staff Rise (odd)");
|
||||
|
||||
// -> fix Skull Staff
|
||||
if (weaponsG5.FirstOrDefault(e => e.Number == 0) is { } skullStaff)
|
||||
{
|
||||
skullStaff.PossibleItemOptions.Clear();
|
||||
skullStaff.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.Name == ExcellentOptions.WizardryAttackOptionsName));
|
||||
skullStaff.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.Name == HarmonyOptions.WizardryAttackOptionsName));
|
||||
skullStaff.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Luck)));
|
||||
skullStaff.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == Stats.MaximumWizBaseDmg)));
|
||||
|
||||
var powerUpDefinition = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = Stats.StaffRise.GetPersistent(gameConfiguration);
|
||||
powerUpDefinition.BaseValue = 6 / 2.0f;
|
||||
powerUpDefinition.AggregateType = AggregateType.AddRaw;
|
||||
powerUpDefinition.BonusPerLevelTable = staffEvenTable;
|
||||
skullStaff.BasePowerUpAttributes.Add(powerUpDefinition);
|
||||
}
|
||||
|
||||
// -> fix Divine Staff of Archangel
|
||||
if (weaponsG5.FirstOrDefault(e => e.Number == 10) is { } divineStaff)
|
||||
{
|
||||
var basePowerUps = divineStaff.BasePowerUpAttributes;
|
||||
if (basePowerUps.FirstOrDefault(pu => pu.TargetAttribute == Stats.MinimumPhysBaseDmgByWeapon) is { } minPhysDmgAttr)
|
||||
{
|
||||
minPhysDmgAttr.BaseValue = 153;
|
||||
}
|
||||
|
||||
if (basePowerUps.FirstOrDefault(pu => pu.TargetAttribute == Stats.MaximumPhysBaseDmgByWeapon) is { } maxPhysDmgAttr)
|
||||
{
|
||||
maxPhysDmgAttr.BaseValue = 165;
|
||||
}
|
||||
|
||||
if (basePowerUps.FirstOrDefault(pu => pu.TargetAttribute == Stats.AttackSpeedByWeapon) is { } attackSpeedAttr)
|
||||
{
|
||||
attackSpeedAttr.BaseValue = 30;
|
||||
}
|
||||
|
||||
if (basePowerUps.FirstOrDefault(pu => pu.TargetAttribute == Stats.StaffRise) is { } staffRiseAttr)
|
||||
{
|
||||
staffRiseAttr.BaseValue = 156 / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
var rhOnlySlotType = gameConfiguration.ItemSlotTypes.First(t => !t.ItemSlots.Contains(0) && t.ItemSlots.Contains(1));
|
||||
var books = summonerWeapons.Where(e => e.ItemSlot == rhOnlySlotType);
|
||||
var sticks = summonerWeapons.Except(books);
|
||||
|
||||
// -> fix sticks
|
||||
Dictionary<int, int> sticksMagicPower = new()
|
||||
{
|
||||
[14] = 34,
|
||||
[15] = 46,
|
||||
[16] = 59,
|
||||
[17] = 76,
|
||||
[18] = 92,
|
||||
[19] = 110,
|
||||
[20] = 106,
|
||||
[34] = 130,
|
||||
[36] = 146,
|
||||
};
|
||||
foreach (var stick in sticks)
|
||||
{
|
||||
stick.PossibleItemOptions.Clear();
|
||||
stick.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.Name == ExcellentOptions.WizardryAttackOptionsName));
|
||||
stick.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.Name == HarmonyOptions.WizardryAttackOptionsName));
|
||||
stick.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Luck)));
|
||||
stick.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == Stats.MaximumWizBaseDmg)));
|
||||
if (sticksMagicPower.ContainsKey(stick.Number))
|
||||
{
|
||||
var powerUpDefinition = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = Stats.StaffRise.GetPersistent(gameConfiguration);
|
||||
powerUpDefinition.BaseValue = sticksMagicPower[stick.Number] / 2.0f;
|
||||
powerUpDefinition.AggregateType = AggregateType.AddRaw;
|
||||
powerUpDefinition.BonusPerLevelTable = sticksMagicPower[stick.Number] % 2 == 0 ? staffEvenTable : staffOddTable;
|
||||
stick.BasePowerUpAttributes.Add(powerUpDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
// -> fix Eternal Wing Stick
|
||||
if (weaponsG5.FirstOrDefault(e => e.Number == 20) is { } eternalStick)
|
||||
{
|
||||
eternalStick.DropLevel = 147;
|
||||
var basePowerUps = eternalStick.BasePowerUpAttributes;
|
||||
if (basePowerUps.FirstOrDefault(pu => pu.TargetAttribute == Stats.MinimumPhysBaseDmgByWeapon) is { } minPhysDmgAttr)
|
||||
{
|
||||
minPhysDmgAttr.BaseValue = 66;
|
||||
}
|
||||
|
||||
if (basePowerUps.FirstOrDefault(pu => pu.TargetAttribute == Stats.MaximumPhysBaseDmgByWeapon) is { } maxPhysDmgAttr)
|
||||
{
|
||||
maxPhysDmgAttr.BaseValue = 74;
|
||||
}
|
||||
|
||||
if (basePowerUps.FirstOrDefault(pu => pu.TargetAttribute == Stats.AttackSpeedByWeapon) is { } attackSpeedAttr)
|
||||
{
|
||||
attackSpeedAttr.BaseValue = 30;
|
||||
}
|
||||
}
|
||||
|
||||
// -> fix books
|
||||
if (gameConfiguration.CharacterClasses.FirstOrDefault(cc => cc.Number == (int)CharacterClassNumber.Summoner) is { } summoner)
|
||||
{
|
||||
var bookRiseAttr = context.CreateNew<AttributeDefinition>(Stats.BookRise.Id, Stats.BookRise.Designation, Stats.BookRise.Description);
|
||||
gameConfiguration.Attributes.Add(bookRiseAttr);
|
||||
summoner.AttributeCombinations.Add(CreateAttributeRelationship(context, gameConfiguration, Stats.CurseAttackDamageIncrease, 1.0f / 100, Stats.BookRise));
|
||||
}
|
||||
|
||||
Dictionary<int, int> booksMagicPower = new() { [21] = 46, [22] = 59, [23] = 72 };
|
||||
foreach (var book in books)
|
||||
{
|
||||
book.PossibleItemOptions.Clear();
|
||||
book.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.Name == ExcellentOptions.WizardryAttackOptionsName));
|
||||
book.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.Name == HarmonyOptions.WizardryAttackOptionsName));
|
||||
book.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Luck)));
|
||||
book.PossibleItemOptions.Add(gameConfiguration.ItemOptions.First(io => io.PossibleOptions.Any(o => o.OptionType == ItemOptionTypes.Option && o.PowerUpDefinition?.TargetAttribute == Stats.MaximumCurseBaseDmg)));
|
||||
if (booksMagicPower.ContainsKey(book.Number))
|
||||
{
|
||||
var powerUpDefinition = context.CreateNew<ItemBasePowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = Stats.BookRise.GetPersistent(gameConfiguration);
|
||||
powerUpDefinition.BaseValue = booksMagicPower[book.Number] / 2.0f;
|
||||
powerUpDefinition.AggregateType = AggregateType.AddRaw;
|
||||
powerUpDefinition.BonusPerLevelTable = booksMagicPower[book.Number] % 2 == 0 ? staffEvenTable : staffOddTable;
|
||||
book.BasePowerUpAttributes.Add(powerUpDefinition);
|
||||
}
|
||||
|
||||
if (book.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.IsStickEquipped) is { } stickEquipAttr)
|
||||
{
|
||||
book.BasePowerUpAttributes.Remove(stickEquipAttr);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Wings of Curse options
|
||||
var wingsOfCurse = gameConfiguration.Items.FirstOrDefault(i => i.GetId() == new Guid("00000080-000c-0029-0000-000000000000"));
|
||||
if (wingsOfCurse is not null)
|
||||
{
|
||||
wingsOfCurse.Name = "Wings of Curse";
|
||||
var wingOpts = wingsOfCurse.PossibleItemOptions.First(o => o.Name == "Wing of Curse Options");
|
||||
|
||||
var wizOption = wingOpts.PossibleOptions.First();
|
||||
wizOption.PowerUpDefinition = this.CreatePowerUpDefinition(Stats.MaximumWizBaseDmg, 0, AggregateType.AddRaw, context, gameConfiguration);
|
||||
wizOption.LevelDependentOptions.Clear();
|
||||
for (int level = 1; level <= 4; level++)
|
||||
{
|
||||
var optionOfLevel = context.CreateNew<ItemOptionOfLevel>();
|
||||
optionOfLevel.Level = level;
|
||||
optionOfLevel.PowerUpDefinition = this.CreatePowerUpDefinition(
|
||||
wizOption.PowerUpDefinition.TargetAttribute!,
|
||||
level * 4f,
|
||||
AggregateType.AddRaw,
|
||||
context,
|
||||
gameConfiguration);
|
||||
wizOption.LevelDependentOptions.Add(optionOfLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Wings of Despair options
|
||||
var wingsOfDespair = gameConfiguration.Items.FirstOrDefault(i => i.GetId() == new Guid("00000080-000c-002a-0000-000000000000"));
|
||||
if (wingsOfDespair is not null)
|
||||
{
|
||||
var wingOpts = wingsOfDespair.PossibleItemOptions.First(o => o.Name == "Wings of Despair Options");
|
||||
|
||||
var curseOption = wingOpts.PossibleOptions.First(o => o.Number == 0);
|
||||
curseOption.PowerUpDefinition = this.CreatePowerUpDefinition(Stats.MaximumCurseBaseDmg, 0, AggregateType.AddRaw, context, gameConfiguration);
|
||||
curseOption.LevelDependentOptions.Clear();
|
||||
for (int level = 1; level <= 4; level++)
|
||||
{
|
||||
var optionOfLevel = context.CreateNew<ItemOptionOfLevel>();
|
||||
optionOfLevel.Level = level;
|
||||
optionOfLevel.PowerUpDefinition = this.CreatePowerUpDefinition(
|
||||
curseOption.PowerUpDefinition.TargetAttribute!,
|
||||
level * 4f,
|
||||
AggregateType.AddRaw,
|
||||
context,
|
||||
gameConfiguration);
|
||||
curseOption.LevelDependentOptions.Add(optionOfLevel);
|
||||
}
|
||||
|
||||
var wizOption = wingOpts.PossibleOptions.First(o => o.Number == 2);
|
||||
wizOption.PowerUpDefinition = this.CreatePowerUpDefinition(Stats.MaximumWizBaseDmg, 0, AggregateType.AddRaw, context, gameConfiguration);
|
||||
wizOption.LevelDependentOptions.Clear();
|
||||
for (int level = 1; level <= 4; level++)
|
||||
{
|
||||
var optionOfLevel = context.CreateNew<ItemOptionOfLevel>();
|
||||
optionOfLevel.Level = level;
|
||||
optionOfLevel.PowerUpDefinition = this.CreatePowerUpDefinition(
|
||||
wizOption.PowerUpDefinition.TargetAttribute!,
|
||||
level * 4f,
|
||||
AggregateType.AddRaw,
|
||||
context,
|
||||
gameConfiguration);
|
||||
wizOption.LevelDependentOptions.Add(optionOfLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Wing of Dimension options
|
||||
var wingsOfDimension = gameConfiguration.Items.FirstOrDefault(i => i.GetId() == new Guid("00000080-000c-002b-0000-000000000000"));
|
||||
if (wingsOfDimension is not null)
|
||||
{
|
||||
var wingOpts = wingsOfDimension.PossibleItemOptions.First(o => o.Name == "Wing of Dimension Options");
|
||||
|
||||
var wizOption = wingOpts.PossibleOptions.First(o => o.Number == 3);
|
||||
wizOption.PowerUpDefinition = this.CreatePowerUpDefinition(Stats.MaximumWizBaseDmg, 0, AggregateType.AddRaw, context, gameConfiguration);
|
||||
wizOption.LevelDependentOptions.Clear();
|
||||
for (int level = 1; level <= 4; level++)
|
||||
{
|
||||
var optionOfLevel = context.CreateNew<ItemOptionOfLevel>();
|
||||
optionOfLevel.Level = level;
|
||||
optionOfLevel.PowerUpDefinition = this.CreatePowerUpDefinition(
|
||||
wizOption.PowerUpDefinition.TargetAttribute!,
|
||||
level * 4f,
|
||||
AggregateType.AddRaw,
|
||||
context,
|
||||
gameConfiguration);
|
||||
wizOption.LevelDependentOptions.Add(optionOfLevel);
|
||||
}
|
||||
|
||||
var curseOption = wingOpts.PossibleOptions.First(o => o.Number == 2);
|
||||
curseOption.PowerUpDefinition = this.CreatePowerUpDefinition(Stats.MaximumCurseBaseDmg, 0, AggregateType.AddRaw, context, gameConfiguration);
|
||||
curseOption.LevelDependentOptions.Clear();
|
||||
for (int level = 1; level <= 4; level++)
|
||||
{
|
||||
var optionOfLevel = context.CreateNew<ItemOptionOfLevel>();
|
||||
optionOfLevel.Level = level;
|
||||
optionOfLevel.PowerUpDefinition = this.CreatePowerUpDefinition(
|
||||
curseOption.PowerUpDefinition.TargetAttribute!,
|
||||
level * 4f,
|
||||
AggregateType.AddRaw,
|
||||
context,
|
||||
gameConfiguration);
|
||||
curseOption.LevelDependentOptions.Add(optionOfLevel);
|
||||
}
|
||||
|
||||
if (wingsOfDimension.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.AttackDamageIncrease) is { } dmgInc)
|
||||
{
|
||||
dmgInc.BaseValue = 1f + (39 / 100f);
|
||||
}
|
||||
|
||||
if (wingsOfDimension.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.DamageReceiveDecrement) is { } dmgDec)
|
||||
{
|
||||
dmgDec.BaseValue = 1f - (39 / 100f);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Cape of Overrule dmg increase/decrease rate
|
||||
var capeOfOverrule = gameConfiguration.Items.FirstOrDefault(i => i.GetId() == new Guid("00000080-000c-0032-0000-000000000000"));
|
||||
if (capeOfOverrule is not null)
|
||||
{
|
||||
if (capeOfOverrule.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.AttackDamageIncrease) is { } dmgInc)
|
||||
{
|
||||
dmgInc.BaseValue = 1f + (39 / 100f);
|
||||
}
|
||||
|
||||
if (capeOfOverrule.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.DamageReceiveDecrement) is { } dmgDec)
|
||||
{
|
||||
dmgDec.BaseValue = 1f - (39 / 100f);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Cape of Emperor dmg decrease rate
|
||||
var capeOfEmperor = gameConfiguration.Items.FirstOrDefault(i => i.GetId() == new Guid("00000080-000c-0028-0000-000000000000"));
|
||||
if (capeOfEmperor is not null)
|
||||
{
|
||||
if (capeOfEmperor.BasePowerUpAttributes.FirstOrDefault(pua => pua.TargetAttribute == Stats.DamageReceiveDecrement) is { } dmgDec)
|
||||
{
|
||||
dmgDec.BaseValue = 1f - (24 / 100f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PowerUpDefinition CreatePowerUpDefinition(AttributeDefinition attributeDefinition, float value, AggregateType aggregateType, IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var powerUpDefinition = context.CreateNew<PowerUpDefinition>();
|
||||
powerUpDefinition.TargetAttribute = attributeDefinition.GetPersistent(gameConfiguration);
|
||||
if (value != 0)
|
||||
{
|
||||
powerUpDefinition.Boost = context.CreateNew<PowerUpDefinitionValue>();
|
||||
powerUpDefinition.Boost.ConstantValue.Value = value;
|
||||
powerUpDefinition.Boost.ConstantValue.AggregateType = aggregateType;
|
||||
}
|
||||
|
||||
return powerUpDefinition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// <copyright file="FixWingsAndCapesCraftingsUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update sets the right settings for the wings and capes craftings.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("618A53AF-ED2A-4C78-A103-BAD061FFB0D2")]
|
||||
public class FixWingsAndCapesCraftingsUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fixed wings and capes craftings";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update sets the right settings for the wings and capes craftings.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWingsAndCapesCraftings;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2024, 08, 14, 8, 00, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var chaosGoblin = gameConfiguration.Monsters.FirstOrDefault(m => m.NpcWindow == NpcWindow.ChaosMachine);
|
||||
if (chaosGoblin is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FixSecondWingCrafting(chaosGoblin);
|
||||
FixFlameOfCondorCrafting(chaosGoblin, gameConfiguration);
|
||||
FixCapeOfFighterCrafting(chaosGoblin, gameConfiguration);
|
||||
}
|
||||
|
||||
private static void FixCapeOfFighterCrafting(MonsterDefinition chaosGoblin, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var capeCrafting = chaosGoblin.ItemCraftings.FirstOrDefault(c => c.Number == 24);
|
||||
if (capeCrafting is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Items.FirstOrDefault(item => item.Group == 13 && item.Number == 49) is not { } oldScroll)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Items.FirstOrDefault(item => item.Group == 12 && item.Number == 49) is not { } capeOfFighter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (capeCrafting.SimpleCraftingSettings?.ResultItems.FirstOrDefault(it => object.Equals(it.ItemDefinition, oldScroll)) is { } resultItem)
|
||||
{
|
||||
resultItem.ItemDefinition = capeOfFighter;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixFlameOfCondorCrafting(MonsterDefinition chaosGoblin, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var flameOfCondorCrafting = chaosGoblin.ItemCraftings.FirstOrDefault(c => c.Number == 38);
|
||||
if (flameOfCondorCrafting is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var requiredItems = flameOfCondorCrafting.SimpleCraftingSettings?.RequiredItems
|
||||
.FirstOrDefault(it => it.PossibleItems.Any(item => item.Group == 12 && item.Number == 3));
|
||||
|
||||
if (requiredItems is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameConfiguration.Items.FirstOrDefault(item => item.Group == 12 && item.Number == 49) is { } capeOfFighter
|
||||
&& !requiredItems.PossibleItems.Any(it => object.Equals(it, capeOfFighter)))
|
||||
{
|
||||
requiredItems.PossibleItems.Add(capeOfFighter);
|
||||
}
|
||||
|
||||
if (gameConfiguration.Items.FirstOrDefault(item => item.Group == 13 && item.Number == 30) is { } capeOfLord
|
||||
&& !requiredItems.PossibleItems.Any(it => object.Equals(it, capeOfLord)))
|
||||
{
|
||||
requiredItems.PossibleItems.Add(capeOfLord);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixSecondWingCrafting(MonsterDefinition chaosGoblin)
|
||||
{
|
||||
var secondWingsCrafting = chaosGoblin.ItemCraftings.FirstOrDefault(c => c.Number == 7);
|
||||
if (secondWingsCrafting is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var requiredItems = secondWingsCrafting.SimpleCraftingSettings?.RequiredItems
|
||||
.FirstOrDefault(it => it.PossibleItems.Any(item => item.Group == 12 && item.Number == 0));
|
||||
|
||||
if (requiredItems is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
requiredItems.MinimumItemLevel = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// <copyright file="FixWingsDmgRatesUpdatePlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the wings damage absorption and increase bonus level tables values for a <see cref="CombinedElement"/> (sum) calculation, instead of a compound calculation
|
||||
/// for version 075.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("3821267A-9C37-40E5-B023-BAB1A8E4DAB7")]
|
||||
public class FixWingsDmgRatesUpdatePlugIn075 : FixWingsDmgRatesUpdatePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWingsDmgRatesPlugIn075;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// <copyright file="FixWingsDmgRatesUpdatePlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the wings damage absorption and increase bonus level tables values for a <see cref="CombinedElement"/> (sum) calculation, instead of a compound calculation
|
||||
/// for version 095d.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("F45FA4D0-B19B-48E2-9592-A37F3B36348A")]
|
||||
public class FixWingsDmgRatesUpdatePlugIn095D : FixWingsDmgRatesUpdatePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWingsDmgRatesPlugIn095d;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// <copyright file="FixWingsDmgRatesUpdatePlugInBase.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the wings damage absorption and increase bonus level tables values for a <see cref="CombinedElement"/> (sum) calculation, instead of a compound calculation.
|
||||
/// </summary>
|
||||
public abstract class FixWingsDmgRatesUpdatePlugInBase : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Fix Wings Damage Rates";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update fixes the wings damage absorption and increase bonus level tables values.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 10, 8, 16, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
string dmgAbsorbCommonName = "Wing absorb";
|
||||
string dmgIncreaseCommonName = "Damage Increase (1st and 3rd Wings)";
|
||||
string dmgIncrease2ndWingsName = "Damage Increase (2nd Wings)";
|
||||
|
||||
string[] wingDmgTableNames = [dmgAbsorbCommonName, dmgIncrease2ndWingsName, dmgIncreaseCommonName];
|
||||
|
||||
foreach (var tableName in wingDmgTableNames)
|
||||
{
|
||||
var bonusEntries = gameConfiguration.ItemLevelBonusTables.FirstOrDefault(ilbt => ilbt.Name == tableName)?.BonusPerLevel;
|
||||
|
||||
if (bonusEntries is not null)
|
||||
{
|
||||
foreach (var bonusEntry in bonusEntries)
|
||||
{
|
||||
bonusEntry.AdditionalValue -= 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// <copyright file="FixWingsDmgRatesUpdatePlugInSeason6.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update fixes the wings damage absorption and increase bonus level tables values for a <see cref="CombinedElement"/> (sum) calculation, instead of a compound calculation
|
||||
/// for season 6.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("03F49890-CB0E-40B7-A590-174BBA1962F4")]
|
||||
public class FixWingsDmgRatesUpdatePlugInSeason6 : FixWingsDmgRatesUpdatePlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.FixWingsDmgRatesPlugInSeason6;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// <copyright file="IConfigurationUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// An interface for a plug in which provides data updates for a <see cref="IDataInitializationPlugIn"/>.
|
||||
/// </summary>
|
||||
[Guid("C4DB0C18-84DE-40DE-BD4C-22D884FB3790")]
|
||||
[PlugInPoint("Configuration update", "Provides updates for initialized data.")]
|
||||
public interface IConfigurationUpdatePlugIn : IStrategyPlugIn<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the version number of the update. This must be unique over all <see cref="DataInitializationKey"/>s.
|
||||
/// </summary>
|
||||
UpdateVersion Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data initialization key to which this update belongs.
|
||||
/// </summary>
|
||||
string DataInitializationKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the update.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description about the update.
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this update is mandatory and will be
|
||||
/// installed automatically without asking the user.
|
||||
/// </summary>
|
||||
bool IsMandatory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation date of the update (at development).
|
||||
/// </summary>
|
||||
DateTime CreatedAt { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies this update on the given persistence context.
|
||||
/// </summary>
|
||||
/// <param name="context">The persistence context.</param>
|
||||
/// <param name="gameConfiguration">The game configuration which can be updated.</param>
|
||||
/// <remarks>
|
||||
/// Calling <see cref="IContext.SaveChangesAsync"/> is not required in this implementation.
|
||||
/// It will be called by <see cref="DataUpdateService"/>.
|
||||
/// </remarks>
|
||||
ValueTask ApplyUpdateAsync(IContext context, GameConfiguration gameConfiguration);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// <copyright file="InfinityArrowSkillOnQuestCompletionPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Persistence.Initialization.CharacterClasses;
|
||||
using MUnique.OpenMU.Persistence.Initialization.Skills;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update adds the infinity arrow skill as quest reward for 'Gain Hero Status (Muse Elf)'.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("A78B7540-75AC-494C-9AEC-BC943D929C98")]
|
||||
public class InfinityArrowSkillOnQuestCompletionPlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Adds infinity arrow skill with quest";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "This update adds the infinity arrow skill as quest reward for 'Gain Hero Status (Muse Elf)'.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.InfinityArrowSkillOnQuestCompletion;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2023, 08, 28, 20, 30, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var marlon = gameConfiguration.Monsters.First(m => m.Number == 229);
|
||||
var quest = marlon.Quests.First(q => q.Number == 2 && q.Group == QuestConstants.LegacyQuestGroup && q.QualifiedCharacter?.Number == (byte)CharacterClassNumber.MuseElf);
|
||||
|
||||
if (quest.Rewards.Any(r => r.RewardType == QuestRewardType.Skill))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var skillReward = context.CreateNew<QuestReward>();
|
||||
skillReward.Value = 1;
|
||||
skillReward.SkillReward = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.InfinityArrow);
|
||||
skillReward.RewardType = QuestRewardType.Skill;
|
||||
quest.Rewards.Add(skillReward);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// <copyright file="LimitWhiteWizardDropsUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update limits the Wizard's Ring to one per character for existing databases.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("A7B3E5F1-8C2D-4E6F-9A1B-3D5C7E8F2A4B")]
|
||||
public class LimitWhiteWizardDropsUpdatePlugIn : UpdatePlugInBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The plug in name.
|
||||
/// </summary>
|
||||
internal const string PlugInName = "Limit White Wizard drops";
|
||||
|
||||
/// <summary>
|
||||
/// The plug in description.
|
||||
/// </summary>
|
||||
internal const string PlugInDescription = "Limits the Wizard's Ring to one per character.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => PlugInName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => PlugInDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.LimitWhiteWizardDrops;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsMandatory => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime CreatedAt => new(2026, 07, 05, 2, 20, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var wizardsRing = gameConfiguration.Items.FirstOrDefault(item =>
|
||||
item.Number == ItemConstants.WizardsRing.Number && item.Group == ItemConstants.WizardsRing.Group);
|
||||
|
||||
if (wizardsRing is not null)
|
||||
{
|
||||
wizardsRing.StorageLimitPerCharacter = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// <copyright file="RemoveJewelDropLevelGapPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update removes the existing drop level gap condition for jewels and similar items that should always drop.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("CD958BC1-F17A-4C60-B66D-BD29D49B6ADA")]
|
||||
public class RemoveJewelDropLevelGapPlugIn075 : RemoveJewelDropLevelGapPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version075.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.RemoveJewelDropLevelGap075;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// <copyright file="RemoveJewelDropLevelGapPlugIn095d.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.Initialization.Updates;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// This update removes the existing drop level gap condition for jewels and similar items that should always drop.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = PlugInName, Description = PlugInDescription)]
|
||||
[Guid("6614E91E-5749-478A-96A4-3240E7C1280E")]
|
||||
public class RemoveJewelDropLevelGapPlugIn095D : RemoveJewelDropLevelGapPlugInBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string DataInitializationKey => Version095d.DataInitialization.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UpdateVersion Version => UpdateVersion.RemoveJewelDropLevelGap095d;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
|
||||
{
|
||||
await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user