diff --git a/src/app/Edelstein.Application.Server/ProgramHost.cs b/src/app/Edelstein.Application.Server/ProgramHost.cs index 114f04573..65c3c9fce 100644 --- a/src/app/Edelstein.Application.Server/ProgramHost.cs +++ b/src/app/Edelstein.Application.Server/ProgramHost.cs @@ -8,6 +8,7 @@ using Edelstein.Common.Gameplay.Game.Combat; using Edelstein.Common.Gameplay.Game.Continents; using Edelstein.Common.Gameplay.Game.Conversations; +using Edelstein.Common.Gameplay.Game.Objects.Mob.Rewards; using Edelstein.Common.Gameplay.Game.Objects.NPC; using Edelstein.Common.Gameplay.Game.Quests; using Edelstein.Common.Gameplay.Handling; @@ -29,6 +30,7 @@ using Edelstein.Protocol.Gameplay.Game.Contexts; using Edelstein.Protocol.Gameplay.Game.Continents; using Edelstein.Protocol.Gameplay.Game.Conversations; +using Edelstein.Protocol.Gameplay.Game.Objects.Mob.Rewards; using Edelstein.Protocol.Gameplay.Game.Objects.NPC; using Edelstein.Protocol.Gameplay.Game.Quests; using Edelstein.Protocol.Gameplay.Login; @@ -178,6 +180,7 @@ public async Task StartAsync(CancellationToken cancellationToken) b.RegisterType().As().SingleInstance(); b.RegisterType().As().SingleInstance(); b.RegisterType().As().SingleInstance(); + b.RegisterType().As().SingleInstance(); b.RegisterType().As().SingleInstance(); b.RegisterType().As().SingleInstance(); b.RegisterType().As().SingleInstance(); diff --git a/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/FieldMob.cs b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/FieldMob.cs index ea445ae64..127eda65a 100644 --- a/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/FieldMob.cs +++ b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/FieldMob.cs @@ -1,7 +1,9 @@ using System.Collections.Immutable; +using Edelstein.Common.Gameplay.Game.Objects.Drop; using Edelstein.Common.Gameplay.Game.Objects.Mob.Stats; using Edelstein.Common.Gameplay.Game.Objects.Mob.Stats.Modify; using Edelstein.Common.Gameplay.Handling; +using Edelstein.Common.Gameplay.Models.Inventories.Items; using Edelstein.Common.Utilities.Packets; using Edelstein.Protocol.Gameplay.Game.Objects; using Edelstein.Protocol.Gameplay.Game.Objects.Mob; @@ -10,6 +12,7 @@ using Edelstein.Protocol.Gameplay.Game.Objects.Mob.Templates; using Edelstein.Protocol.Gameplay.Game.Objects.User; using Edelstein.Protocol.Gameplay.Game.Spatial; +using Edelstein.Protocol.Gameplay.Models.Inventories.Templates; using Edelstein.Protocol.Utilities.Packets; using Edelstein.Protocol.Utilities.Spatial; using Edelstein.Protocol.Utilities.Tickers; @@ -87,10 +90,31 @@ public async Task Damage(int damage, IFieldUser? attacker = null) if (HP <= 0) { - await Field.Leave(this, () => GetLeaveFieldPacket(FieldMobLeaveType.Etc)); - - if (attacker != null) + if (attacker != null) + { + var rewardPool = attacker.StageUser.Context.Managers.MobRewardPool; + var rewards = await rewardPool.CalculateRewards(attacker, this); + + foreach (var reward in rewards) + { + if (reward.ItemID == null) continue; + var template = await attacker.StageUser.Context.Templates.Item.Retrieve(reward.ItemID.Value); + if (template == null) continue; + + var position = Position; + var drop = new FieldDropItem( + position, + template.ToItemSlot(ItemVariationOption.Normal), + sourceID: ObjectID ?? 0 + ); + + await Field.Enter(drop, () => drop.GetEnterFieldPacket(1, position)); + } + _ = attacker.StageUser.Context.Managers.Quest.UpdateMobKill(attacker, Template.ID); + } + + await Field.Leave(this, () => GetLeaveFieldPacket(FieldMobLeaveType.Etc)); } } finally diff --git a/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobReward.cs b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobReward.cs new file mode 100644 index 000000000..dba977598 --- /dev/null +++ b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobReward.cs @@ -0,0 +1,26 @@ +using Edelstein.Protocol.Gameplay.Game.Objects.Mob.Rewards; + +namespace Edelstein.Common.Gameplay.Game.Objects.Mob.Rewards; + +public record MobReward( + int ID +) : IMobReward +{ + public int? ItemID { get; init; } + + public int? Money { get; init; } + + public int? NumberMin { get; init; } + public int? NumberMax { get; init; } + + public int? ReqQuest { get; init; } + + public int? ReqLevelMin { get; init; } + public int? ReqLevelMax { get; init; } + + public int? ReqMobLevelMin { get; init; } + public int? ReqMobLevelMax { get; init; } + + public DateTime? DateStart { get; init; } + public DateTime? DateEnd { get; init; } +} diff --git a/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobRewardPoolManager.cs b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobRewardPoolManager.cs new file mode 100644 index 000000000..06dc9084f --- /dev/null +++ b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobRewardPoolManager.cs @@ -0,0 +1,28 @@ +using System.Collections.Immutable; +using Edelstein.Common.Utilities.Repositories; +using Edelstein.Protocol.Gameplay.Game.Objects.Mob; +using Edelstein.Protocol.Gameplay.Game.Objects.Mob.Rewards; +using Edelstein.Protocol.Gameplay.Game.Objects.User; +using Edelstein.Protocol.Gameplay.Game.Rewards; +using Edelstein.Protocol.Utilities.Repositories; + +namespace Edelstein.Common.Gameplay.Game.Objects.Mob.Rewards; + +public class MobRewardPoolManager : + Repository>, + IMobRewardPoolManager +{ + public IRepository Global { get; } = new Repository(); + + public async Task> CalculateRewards(IFieldUser user, IFieldMob mob) + { + var pool = await Retrieve(mob.Template.ID); + var items = new List(); + + if (pool != null) + items.AddRange(await pool.RetrieveAll()); + items.AddRange(await Global.RetrieveAll()); + + return items.ToImmutableArray(); + } +} diff --git a/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobRewardPoolManagerInit.cs b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobRewardPoolManagerInit.cs new file mode 100644 index 000000000..a30f517fa --- /dev/null +++ b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Rewards/MobRewardPoolManagerInit.cs @@ -0,0 +1,34 @@ +using Edelstein.Common.Gameplay.Game.Objects.Mob.Templates; +using Edelstein.Common.Gameplay.Game.Rewards; +using Edelstein.Protocol.Gameplay.Contracts; +using Edelstein.Protocol.Gameplay.Game.Objects.Mob.Rewards; +using Edelstein.Protocol.Utilities.Pipelines; +using Edelstein.Protocol.Utilities.Templates; + +namespace Edelstein.Common.Gameplay.Game.Objects.Mob.Rewards; + +public class MobRewardPoolManagerInit : IPipelinePlug +{ + private readonly ITemplateManager _templates; + private readonly IMobRewardPoolManager _manager; + + public MobRewardPoolManagerInit( + ITemplateManager templates, + IMobRewardPoolManager manager + ) + { + _templates = templates; + _manager = manager; + } + + public async Task Handle(IPipelineContext ctx, StageStart message) + { + foreach (var rewards in await _templates.RetrieveAll()) + { + var pool = new RewardPool(rewards.ID); + foreach (var item in rewards.Items) + await pool.Insert(item); + await _manager.Insert(pool); + } + } +} diff --git a/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardPoolTemplate.cs b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardPoolTemplate.cs new file mode 100644 index 000000000..7bb6cd6ef --- /dev/null +++ b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardPoolTemplate.cs @@ -0,0 +1,21 @@ +using System.Collections.Immutable; +using Duey.Abstractions; +using Edelstein.Common.Gameplay.Game.Objects.NPC.Templates; +using Edelstein.Protocol.Gameplay.Game.Objects.NPC; +using Edelstein.Protocol.Utilities.Templates; + +namespace Edelstein.Common.Gameplay.Game.Objects.Mob.Templates; + +public class MobRewardPoolTemplate : ITemplate +{ + public int ID { get; } + public ICollection Items { get; } + + public MobRewardPoolTemplate(int id, IDataNode property) + { + ID = id; + Items = property.Children + .Select(p => new MobRewardTemplate(Convert.ToInt32(p.Name), p.Cache())) + .ToImmutableArray(); + } +} diff --git a/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardPoolTemplateLoader.cs b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardPoolTemplateLoader.cs new file mode 100644 index 000000000..83c165719 --- /dev/null +++ b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardPoolTemplateLoader.cs @@ -0,0 +1,33 @@ +using Duey.Abstractions; +using Edelstein.Common.Gameplay.Game.Objects.NPC.Templates; +using Edelstein.Common.Utilities.Templates; +using Edelstein.Protocol.Utilities.Templates; + +namespace Edelstein.Common.Gameplay.Game.Objects.Mob.Templates; + +public class MobRewardPoolTemplateLoader : ITemplateLoader +{ + private readonly IDataNamespace _data; + private readonly ITemplateManager _manager; + + public MobRewardPoolTemplateLoader(IDataNamespace data, ITemplateManager manager) + { + _data = data; + _manager = manager; + } + + public async Task Load() + { + await Task.WhenAll(_data.ResolvePath("Server/Reward.img")?.Children + .Select(async n => + { + var id = Convert.ToInt32(n.Name); + await _manager.Insert(new TemplateProviderEager( + id, + new MobRewardPoolTemplate(id, n.Cache()) + )); + }) ?? Array.Empty()); + + return _manager.Count; + } +} diff --git a/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardTemplate.cs b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardTemplate.cs new file mode 100644 index 000000000..a76a1aeaf --- /dev/null +++ b/src/common/Edelstein.Common.Gameplay.Game/Objects/Mob/Templates/MobRewardTemplate.cs @@ -0,0 +1,40 @@ +using Duey.Abstractions; +using Edelstein.Protocol.Gameplay.Game.Objects.Mob.Rewards; +using Edelstein.Protocol.Utilities.Templates; + +namespace Edelstein.Common.Gameplay.Game.Objects.Mob.Templates; + +public class MobRewardTemplate : ITemplate, IMobReward +{ + public int ID { get; } + + public int? ItemID { get; } + + public int? Money { get; } + + public int? NumberMin { get; } + public int? NumberMax { get; } + + public int? ReqQuest { get; } + + public int? ReqLevelMin { get; } + public int? ReqLevelMax { get; } + + public int? ReqMobLevelMin { get; } + public int? ReqMobLevelMax { get; } + + public DateTime? DateStart { get; } + public DateTime? DateEnd { get; } + + public MobRewardTemplate(int id, IDataNode property) + { + ID = id; + + ItemID = property.ResolveInt("item"); + + Money = property.ResolveInt("money"); + + NumberMin = property.ResolveInt("min"); + NumberMax = property.ResolveInt("max"); + } +} diff --git a/src/common/Edelstein.Common.Gameplay.Game/Rewards/RewardPool.cs b/src/common/Edelstein.Common.Gameplay.Game/Rewards/RewardPool.cs new file mode 100644 index 000000000..f0e8a8689 --- /dev/null +++ b/src/common/Edelstein.Common.Gameplay.Game/Rewards/RewardPool.cs @@ -0,0 +1,14 @@ +using Edelstein.Common.Utilities.Repositories; +using Edelstein.Protocol.Gameplay.Game.Rewards; + +namespace Edelstein.Common.Gameplay.Game.Rewards; + +public class RewardPool : + Repository, + IRewardPool + where TReward : IReward +{ + public int ID { get; } + + public RewardPool(int id) => ID = id; +} diff --git a/src/common/Edelstein.Common.Gameplay.Game/Rewards/RewardPoolManager.cs b/src/common/Edelstein.Common.Gameplay.Game/Rewards/RewardPoolManager.cs new file mode 100644 index 000000000..4a28a660f --- /dev/null +++ b/src/common/Edelstein.Common.Gameplay.Game/Rewards/RewardPoolManager.cs @@ -0,0 +1,11 @@ +using Edelstein.Common.Utilities.Repositories; +using Edelstein.Protocol.Gameplay.Game.Rewards; + +namespace Edelstein.Common.Gameplay.Game.Rewards; + +public class RewardPoolManager : + Repository>, + IRewardPoolManager + where TReward : IReward +{ +} diff --git a/src/plugin/Edelstein.Plugin.Rue/Edelstein.Plugin.Rue.csproj b/src/plugin/Edelstein.Plugin.Rue/Edelstein.Plugin.Rue.csproj index 6fb03fa53..f514fa6dd 100644 --- a/src/plugin/Edelstein.Plugin.Rue/Edelstein.Plugin.Rue.csproj +++ b/src/plugin/Edelstein.Plugin.Rue/Edelstein.Plugin.Rue.csproj @@ -15,4 +15,8 @@ + + + + diff --git a/src/protocol/Edelstein.Protocol.Gameplay.Game/Contexts/GameContextManagers.cs b/src/protocol/Edelstein.Protocol.Gameplay.Game/Contexts/GameContextManagers.cs index 67e520757..16c6b311f 100644 --- a/src/protocol/Edelstein.Protocol.Gameplay.Game/Contexts/GameContextManagers.cs +++ b/src/protocol/Edelstein.Protocol.Gameplay.Game/Contexts/GameContextManagers.cs @@ -2,6 +2,7 @@ using Edelstein.Protocol.Gameplay.Game.Combat; using Edelstein.Protocol.Gameplay.Game.Continents; using Edelstein.Protocol.Gameplay.Game.Conversations; +using Edelstein.Protocol.Gameplay.Game.Objects.Mob.Rewards; using Edelstein.Protocol.Gameplay.Game.Objects.NPC; using Edelstein.Protocol.Gameplay.Game.Quests; using Edelstein.Protocol.Gameplay.Models.Inventories; @@ -18,6 +19,7 @@ public record GameContextManagers( IContiMoveManager ContiMove, INamedConversationManager Conversation, INPCShopManager NPCShop, + IMobRewardPoolManager MobRewardPool, ISkillManager Skill, IQuestManager Quest, IModifiedQuestTimeManager QuestTime diff --git a/src/protocol/Edelstein.Protocol.Gameplay.Game/Objects/Mob/Rewards/IMobReward.cs b/src/protocol/Edelstein.Protocol.Gameplay.Game/Objects/Mob/Rewards/IMobReward.cs new file mode 100644 index 000000000..0b726c8db --- /dev/null +++ b/src/protocol/Edelstein.Protocol.Gameplay.Game/Objects/Mob/Rewards/IMobReward.cs @@ -0,0 +1,24 @@ +using Edelstein.Protocol.Gameplay.Game.Rewards; + +namespace Edelstein.Protocol.Gameplay.Game.Objects.Mob.Rewards; + +public interface IMobReward : IReward +{ + int? ItemID { get; } + + int? Money { get; } + + int? NumberMin { get; } + int? NumberMax { get; } + + int? ReqQuest { get; } + + int? ReqLevelMin { get; } + int? ReqLevelMax { get; } + + int? ReqMobLevelMin { get; } + int? ReqMobLevelMax { get; } + + DateTime? DateStart { get; } + DateTime? DateEnd { get; } +} diff --git a/src/protocol/Edelstein.Protocol.Gameplay.Game/Objects/Mob/Rewards/IMobRewardPoolManager.cs b/src/protocol/Edelstein.Protocol.Gameplay.Game/Objects/Mob/Rewards/IMobRewardPoolManager.cs new file mode 100644 index 000000000..adef7235b --- /dev/null +++ b/src/protocol/Edelstein.Protocol.Gameplay.Game/Objects/Mob/Rewards/IMobRewardPoolManager.cs @@ -0,0 +1,12 @@ +using Edelstein.Protocol.Gameplay.Game.Objects.User; +using Edelstein.Protocol.Gameplay.Game.Rewards; +using Edelstein.Protocol.Utilities.Repositories; + +namespace Edelstein.Protocol.Gameplay.Game.Objects.Mob.Rewards; + +public interface IMobRewardPoolManager : IRewardPoolManager +{ + IRepository Global { get; } + + Task> CalculateRewards(IFieldUser user, IFieldMob mob); +} diff --git a/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IReward.cs b/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IReward.cs new file mode 100644 index 000000000..75f7f42bc --- /dev/null +++ b/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IReward.cs @@ -0,0 +1,7 @@ +using Edelstein.Protocol.Utilities.Repositories; + +namespace Edelstein.Protocol.Gameplay.Game.Rewards; + +public interface IReward : IIdentifiable +{ +} diff --git a/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IRewardPool.cs b/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IRewardPool.cs new file mode 100644 index 000000000..8cfdbc44d --- /dev/null +++ b/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IRewardPool.cs @@ -0,0 +1,10 @@ +using Edelstein.Protocol.Utilities.Repositories; + +namespace Edelstein.Protocol.Gameplay.Game.Rewards; + +public interface IRewardPool : + IIdentifiable, + IRepository + where TReward : IReward +{ +} diff --git a/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IRewardPoolManager.cs b/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IRewardPoolManager.cs new file mode 100644 index 000000000..0674a5fd9 --- /dev/null +++ b/src/protocol/Edelstein.Protocol.Gameplay.Game/Rewards/IRewardPoolManager.cs @@ -0,0 +1,9 @@ +using Edelstein.Protocol.Utilities.Repositories; + +namespace Edelstein.Protocol.Gameplay.Game.Rewards; + +public interface IRewardPoolManager : + IRepository> + where TReward : IReward +{ +}