server-master/srcs/_plugins/Plugin.Raids/RaidFactory.cs
2026-02-10 18:21:30 +01:00

336 lines
No EOL
14 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using PhoenixLib.Events;
using PhoenixLib.Logging;
using Plugin.Raids.Extension;
using WingsAPI.Game.Extensions.PacketGeneration;
using WingsAPI.Scripting.Attribute;
using WingsAPI.Scripting.Converter;
using WingsAPI.Scripting.Enum;
using WingsAPI.Scripting.Event;
using WingsAPI.Scripting.Object.Common;
using WingsAPI.Scripting.Object.Common.Map;
using WingsAPI.Scripting.Object.Raid;
using WingsAPI.Scripting.ScriptManager;
using WingsEmu.DTOs.Maps;
using WingsEmu.Game;
using WingsEmu.Game.Entities;
using WingsEmu.Game.Entities.Event;
using WingsEmu.Game.Helpers.Damages;
using WingsEmu.Game.Managers.StaticData;
using WingsEmu.Game.Maps;
using WingsEmu.Game.Monster;
using WingsEmu.Game.Portals;
using WingsEmu.Game.Raids;
using WingsEmu.Packets.Enums;
namespace Plugin.Raids;
public class RaidFactory : IRaidFactory
{
private static readonly IEnumerable<Type> Converters;
private readonly IAsyncEventPipeline _eventPipeline;
private readonly IMapManager _mapManager;
private readonly IMonsterEntityFactory _monsterEntityFactory;
private readonly INpcMonsterManager _npcMonsterManager;
private readonly IPortalFactory _portalFactory;
private readonly IRaidScriptManager _raidScriptManager;
static RaidFactory()
{
Converters = typeof(IScriptedEventConverter).Assembly.GetTypes()
.Concat(typeof(RaidFactory).Assembly.GetTypes())
.Where(x => typeof(IScriptedEventConverter).IsAssignableFrom(x))
.Where(x => !x.IsAbstract && !x.IsInterface);
}
public RaidFactory(IRaidScriptManager raidScriptManager, IMapManager mapManager, IAsyncEventPipeline eventPipeline, IMonsterEntityFactory monsterEntityFactory,
INpcMonsterManager npcMonsterManager, IPortalFactory portalFactory)
{
_raidScriptManager = raidScriptManager;
_mapManager = mapManager;
_eventPipeline = eventPipeline;
_monsterEntityFactory = monsterEntityFactory;
_npcMonsterManager = npcMonsterManager;
_portalFactory = portalFactory;
}
public RaidInstance CreateRaid(RaidParty raidParty)
{
SRaid scriptedRaid = _raidScriptManager.GetScriptedRaid(raidParty.Type.ToSRaidType());
if (scriptedRaid == null)
{
Log.Warn($"Can't create raid {raidParty.Type}, Couldn't find it in RaidScriptManager");
return default;
}
IServiceCollection serviceLocator = new ServiceCollection();
foreach (Type converter in Converters)
{
serviceLocator.AddTransient(typeof(IScriptedEventConverter), converter);
}
var raidSubInstances = new Dictionary<Guid, RaidSubInstance>();
GenerateMaps(scriptedRaid, raidSubInstances);
RaidReward raidReward = InitializeRaidRewards(scriptedRaid);
serviceLocator.AddSingleton(raidSubInstances);
var portals = new Dictionary<Guid, IPortalEntity>();
var monsters = new Dictionary<Guid, IMonsterEntity>();
var buttons = new Dictionary<Guid, ButtonMapItem>();
// Register portals, monsters and buttons to service locator
serviceLocator.AddSingleton(portals);
serviceLocator.AddSingleton(monsters);
serviceLocator.AddSingleton(buttons);
foreach (SMap scriptedMap in scriptedRaid.Maps)
{
RaidSubInstance raidSubInstance = raidSubInstances[scriptedMap.Id];
IMapInstance mapInstance = raidSubInstance.MapInstance;
serviceLocator.AddSingleton(raidParty);
serviceLocator.AddSingleton(raidSubInstance);
serviceLocator.AddSingleton(mapInstance);
// This dictionary will contains all events to attach later
var events = new Dictionary<IEventTriggerContainer, IDictionary<string, IEnumerable<SEvent>>>
{
[raidSubInstance] = scriptedMap.Events
};
// Add Raid Waves
AddWaves(scriptedMap, raidSubInstance);
AddPortals(scriptedMap, mapInstance, raidSubInstances, portals);
AddMonsters(scriptedMap, mapInstance, monsters, events, raidSubInstance);
AddMapObjects(scriptedMap, mapInstance, events, raidSubInstance, buttons);
// Get event converters using service locator
var converters = serviceLocator.BuildServiceProvider()
.GetServices<IScriptedEventConverter>()
.ToDictionary(x => x.EventType, x => x);
// Add all events using event converters
foreach ((IEventTriggerContainer eventContainer, IDictionary<string, IEnumerable<SEvent>> containerEvents) in events)
{
foreach ((string trigger, IEnumerable<SEvent> scriptedEvents) in containerEvents)
{
foreach (SEvent scriptedEvent in scriptedEvents)
{
IAsyncEvent e = converters.GetValueOrDefault(scriptedEvent.GetType())?.Convert(scriptedEvent);
if (e == null)
{
throw new InvalidOperationException($"Failed to convert {scriptedEvent.GetType().Name} to async event");
}
ScriptEventAttribute attribute = scriptedEvent.GetType().GetCustomAttribute<ScriptEventAttribute>();
if (attribute == null)
{
throw new InvalidOperationException($"Failed to find attribute for: {scriptedEvent.GetType().Name}");
}
eventContainer.AddEvent(trigger, e, attribute.IsRemovedOnTrigger);
}
}
}
}
return new RaidInstance(raidSubInstances.Values, raidSubInstances[scriptedRaid.Spawn.MapId], new Position(scriptedRaid.Spawn.Position.X, scriptedRaid.Spawn.Position.Y),
TimeSpan.FromSeconds(scriptedRaid.DurationInSeconds), (byte)raidParty.Members.Count, raidReward);
}
private void AddWaves(SMap scriptedMap, RaidSubInstance raidSubInstance)
{
var raidWaves = new Dictionary<byte, RaidWave>();
byte wave = 0;
foreach (SMonsterWave scriptedRaidWave in scriptedMap.MonsterWaves)
{
var waveMonsters = new List<ToSummon>();
foreach (SMonster mobWave in scriptedRaidWave.Monsters)
{
IMonsterData monsterData = _npcMonsterManager.GetNpc(mobWave.Vnum);
if (monsterData == null)
{
continue;
}
ConcurrentDictionary<byte, Waypoint> waypointsDictionary = null;
if (mobWave.Waypoints != null)
{
byte waypoints = 0;
foreach (SWaypoint waypoint in mobWave.Waypoints)
{
waypointsDictionary ??= new ConcurrentDictionary<byte, Waypoint>();
var newWaypoint = new Waypoint
{
X = waypoint.X,
Y = waypoint.Y,
WaitTime = waypoint.WaitTime
};
waypointsDictionary.TryAdd(waypoints, newWaypoint);
waypoints++;
}
}
var newToSummon = new ToSummon
{
VNum = mobWave.Vnum,
SpawnCell = mobWave.IsRandomPosition ? null : new Position(mobWave.Position.X, mobWave.Position.Y),
IsHostile = true,
IsMoving = monsterData.CanWalk,
SetHitChance = 5,
GoToBossPosition = mobWave.GoToBossPosition == default ? null : new Position(mobWave.GoToBossPosition.X, mobWave.GoToBossPosition.Y),
Waypoints = waypointsDictionary,
SummonType = SummonType.MONSTER_WAVE,
Direction = mobWave.Direction
};
waveMonsters.Add(newToSummon);
}
var newRaidWave = new RaidWave(waveMonsters, scriptedRaidWave.TimeInSeconds);
raidWaves[wave] = newRaidWave;
wave++;
}
raidSubInstance.AddRaidWave(raidWaves);
}
private void AddMapObjects(SMap scriptedMap, IMapInstance mapInstance, Dictionary<IEventTriggerContainer, IDictionary<string, IEnumerable<SEvent>>> events, RaidSubInstance raidSubInstance,
Dictionary<Guid, ButtonMapItem> buttons)
{
foreach (SMapObject scriptedObject in scriptedMap.Objects)
{
if (scriptedObject is not SButton scriptedButton)
{
continue;
}
var button = new ButtonMapItem(scriptedButton.Position.X, scriptedButton.Position.Y, scriptedButton.DeactivatedVnum, scriptedButton.ActivatedVnum, false,
mapInstance, _eventPipeline, isObjective: scriptedButton.IsObjective, onlyOnce: scriptedButton.OnlyOnce == false ? null : false);
events[button] = scriptedButton.Events;
buttons[scriptedObject.Id] = button;
raidSubInstance.AddRaidButton(button);
}
}
private void AddMonsters(SMap scriptedMap, IMapInstance mapInstance, Dictionary<Guid, IMonsterEntity> monsters, Dictionary<IEventTriggerContainer, IDictionary<string, IEnumerable<SEvent>>> events,
RaidSubInstance raidSubInstance)
{
// Add monsters
foreach (SMonster scriptedMonster in scriptedMap.Monsters)
{
var drop = new List<DropChance>();
foreach (SDropChance dropChance in scriptedMonster.Drop)
{
drop.Add(new DropChance(dropChance.Chance, dropChance.ItemVnum, dropChance.Amount));
}
IMonsterEntity monster = _monsterEntityFactory.CreateMonster(scriptedMonster.Vnum, mapInstance,
new MonsterEntityBuilder
{
IsRespawningOnDeath = false,
IsBoss = scriptedMonster.IsBoss,
IsTarget = scriptedMonster.IsTarget,
IsWalkingAround = scriptedMonster.CanMove,
IsHostile = true,
SetHitChance = 5,
HpMultiplier = scriptedMonster.IsBoss ? null : scriptedMonster.IsTarget ? 21f : 6f,
MpMultiplier = scriptedMonster.IsBoss ? null : scriptedMonster.IsTarget ? 21f : 6f,
RaidDrop = drop,
GeneratedGuid = scriptedMonster.Id,
Direction = scriptedMonster.Direction
});
if (scriptedMonster.Waypoints != null)
{
byte waypoints = 0;
foreach (SWaypoint waypoint in scriptedMonster.Waypoints)
{
monster.Waypoints ??= new ConcurrentDictionary<byte, Waypoint>();
var newWaypoint = new Waypoint
{
X = waypoint.X,
Y = waypoint.Y,
WaitTime = waypoint.WaitTime
};
monster.Waypoints.TryAdd(waypoints, newWaypoint);
waypoints++;
}
}
monster.EmitEventAsync(new MapJoinMonsterEntityEvent(monster, scriptedMonster.Position.X, scriptedMonster.Position.Y));
monsters[scriptedMonster.Id] = monster;
events[monster] = scriptedMonster.Events;
raidSubInstance.AddRaidMonster(monster);
}
}
private void AddPortals(SMap scriptedMap, IMapInstance mapInstance, Dictionary<Guid, RaidSubInstance> raidSubInstances, Dictionary<Guid, IPortalEntity> portals)
{
// Add portals
foreach (SPortal scriptedPortal in scriptedMap.Portals)
{
var sourcePos = new Position(scriptedPortal.SourcePosition.X, scriptedPortal.SourcePosition.Y);
var destPos = new Position(scriptedPortal.DestinationPosition.X, scriptedPortal.DestinationPosition.Y);
IPortalEntity portal = _portalFactory.CreatePortal((PortalType)scriptedPortal.Type, mapInstance, sourcePos,
raidSubInstances[scriptedPortal.DestinationId].MapInstance.Id, destPos);
portals[scriptedPortal.Id] = portal;
mapInstance.AddPortalToMap(portal);
}
}
private static RaidReward InitializeRaidRewards(SRaid scriptedRaid)
{
var raidBoxRarities = new List<RaidBoxRarity>();
foreach (SRaidBoxRarity boxRarity in scriptedRaid.Reward.RaidBox.RaidBoxRarity)
{
var newRarity = new RaidBoxRarity(boxRarity.Rarity, boxRarity.Chance);
raidBoxRarities.Add(newRarity);
}
var raidBox = new RaidBox(scriptedRaid.Reward.RaidBox.RewardBox, raidBoxRarities);
var raidReward = new RaidReward(raidBox, scriptedRaid.Reward.DefaultReputation, scriptedRaid.Reward.FixedReputation);
return raidReward;
}
private void GenerateMaps(SRaid scriptedRaid, Dictionary<Guid, RaidSubInstance> raidSubInstances)
{
foreach (SMap scriptedMap in scriptedRaid.Maps)
{
IMapInstance map = scriptedMap.MapType == SMapType.MapId
? _mapManager.GenerateMapInstanceByMapId(scriptedMap.MapIdVnum, MapInstanceType.RaidInstance)
: _mapManager.GenerateMapInstanceByMapVNum(new ServerMapDto
{
Flags = scriptedMap.Flags.Select(mapFlag => (MapFlags)(int)mapFlag).ToList(),
Id = scriptedMap.MapIdVnum,
MapVnum = scriptedMap.MapIdVnum,
NameId = scriptedMap.NameId,
MusicId = scriptedMap.MusicId
}, MapInstanceType.RaidInstance);
raidSubInstances[scriptedMap.Id] = new RaidSubInstance(map, _eventPipeline);
}
}
}