Dans ce tutoriel je vais vous montrer comment refaire le plugin MultiAccount (pas totalement complet), en vous guidant pas-à-pas.
Avant toute chose, veuillez lire la partie "" et "", afin d'être plus à l'aise et savoir comment setup votre projet Visual Studio.
Création du projet et import des dépendances
Créer un nouveau projet Visual Studio portant le nom "Dtwo.Plugins.MultiAccount"
Importer les dll "Dtwo.API.Hybride, Dtwo.API.Retro, Dtwo.API.Dofus2, Dtwo.API.Inputs, Dtwo.Plugins"
Création de la classe MultiAccount_Plugin
using Dtwo.API;
public class MultiAccount_Plugin : Plugin
{
public static MultiAccount_Plugin Instance { get; private set; }
public MultiAccount_Plugin(PluginInfos infos, Assembly assembly) : base(infos, assembly)
{
Instance = this;
MultiAccountManager.Init();
}
}
Création de la classe MultiAccountManager et MultiAccountController
using Dtwo.API;
public static class MultiAccountManager
{
private Dictionary<DofusWindow, MultiAccountController> m_accounts = new()
public IReadonlyDictionary<DofusWindow, MultiAccountController> Accounts => m_accounts;
public void Init()
{
// On s'abonne aux events OnDofusWindowStarted / Stopped
DofusWindow.OnDofusWindowStarted += OnDofusWindowStarted;
DofusWindow.OnDofusWindowStoped += OnDofusWindowStoped;
}
// Une fenêtre commence a être sniffé
private static void OnDofusWindowStarted(DofusWindow dofusWindow)
{
m_accounts.Add(dofusWindow, new MultiAccountController(dofusWindow))
}
// Une fenêtre n'est plus sniffé
private static void OnDofusWindowStoped(DofusWindow dofusWindow)
{
m_accounts.Remove(dofusWindow);
}
}
using Dtwo.API;
public class MultiAccountController
{
public readonly DofusWindow DofusWindow;
public MultiAccountController(DofusWindow dofusWindow)
{
DofusWindow = dofusWindow;
}
}
Raccourcis pour appliquer un clique sur toutes les fenêtres
Créer une classe MultiClickManager
using Dtwo.API;
using Dtwo.API.Inputs;
using Dtwo.API.Inputs.Extensions;
public static class MultiClickManager
{
private static bool m_isStarted;
private static bool m_onMultiClick;
public static void Start()
{
if (m_isStarted)
{
return;
}
// On s'abonne aux event de KeyUp
InputKeyListener.Instance.KeyUp += OnKeyUp;
// On écoute les cliques du milieu
InputKeyListener.Instance.AddKey(0x04); // Middle click
m_isStarted = true;
}
public static void Stop()
{
m_isStarted = false;
}
private static void OnKeyUp(int key)
{
if (m_onMultiClick)
{
// On est déjà en train d'appliquer des clics dans les autres fenêtres
return;
}
if (key == Ox04) // Middle Click
{
OnMultiClick()
}
}
public static void Stop()
{
m_isStarted = false;
// On se désabonne de KeyUp, et on arrête d'écouter le clique du milieu
InputKeyListener.Instance.KeyUp -= OnKeyUp;
InputKeyListener.Instance.RemoveKey(0x04);
}
private static void OnMultiClick()
{
// On démarre un nouveau thread, pour ne pas bloquer l'application
Task.Factory.StartNew(async () =>
{
m_onMultiClick = true;
PInvoke.POINT pos = new PInvoke.POINT();
// On récupère la position de la souris sur l'écran en px
PInvoke.GetCursorPos(out pos);
// On récupère la position relative à la fenêtre sur la-quelle le clique a été fait
// En l'occurence la fenêtre en premier plan.
PInvoke.ScreenToClient(PInvoke.GetForegroundWindow(), ref pos);
for (int i = 0; i < MultiAccountManager.Accounts.Count; i++)
{
var account = MultiAccountManager.Accounts.ElementAt(i).Value;
// Initialization d'un click gauche aux coordonées indiquées
ClickInfo clickInfo = new ClickInfo(pos.X, pos.Y, false);
// On applique le clique sur cette fenêtre dofus
// La position du clique sera relative à la nouvelle fenêtre
// La taille de la fenêtre doit être la même que la fenêtre qui a reçu le clique original
// On attend que le clique se finisse (Click down + Click Up)
await account.DofusWindow.SendClick(clickInfo);
// On attend un peu de temps avant de passer à la fenêtre suivante
Thread.Sleep(API.Random.Range(250,500));
if (m_isStarted == false)
{
// Si on a stop, on s'arrête là
m_onMultiClick = false;
return;
}
}
m_onMultiClick = false;
});
}
}
Dans la classe MultiAccountManager
public void Init()
{
// ...
MultiClickManager.Start()
}
A l'étape où vous en êtes, si vous compilez et ajouter le plugin, vous devriez être capable d'appliquer des cliques sur toutes les fenêtres à l'aide du clique du milieu
Raccourcis pour passer à la fenêtre Suivante / Précédente
TODO
Update de l'initiative
Dans la classe MultiAccount_Plugin :
using Dtwo.API;
using Dtwo.API.Hybride
public class MultiAccount_Plugin : Plugin
{
// ...
// Un joueur rejoint le groupe
[DofusEvent]
public void OnPartyJoinMessage(DofusWindow dofusWindow, PartyJoinMessage message)
{
MultiAccountManager.OnPartyJoin(dofusWindow, message);
}
// Le groupe a été updaté (changement d'initiative, etc ...)
[DofusEvent]
public void OnPartyUpdateMessage(DofusWindow dofusWindow, PartyUpdateMessage message)
{
MultiAccountManager.OnPartyUpdate(dofusWindow, message);
}
}
Dans la classe MultiAccountManager
// Un personnage a été ajouté au groupe
// L'évent est call pour toutes les fenêtres
public static void OnPartyJoin(DofusWindow dofusWindow, PartyJoinMessage message)
{
foreach (var member in message.Members)
{
foreach (var account in m_accounts)
{
// On cherche a update seulement la fenêtre qui a reçu le message
// Afin de ne pas répéter l'opération pour toutes les autres fenêtres
if (account.DofusWindow.Character.Name != member.name)
continue;
// On update l'initiative
account.UpdateInitiative((int)member.initiative);
break;
}
}
// On change l'ordre de la liste
ReOrder();
}
// Le groupe a été update (initiative qui change, etc ...)
public static void OnPartyUpdate(DofusWindow dofusWindow, PartyUpdateMessage message)
{
if (m_accounts.ContainsKey(dofusWindow) == false)
return;
var owner = m_accounts[dofusWindow];
if (wnerController.IsLeader == false)
{
// OnPartyUpdate va être appellé pour chaque fenêtre,
// On ne veut itérer qu'une seule fois
// Donc on se base sur le leader
return;
}
// Find the account with the same name
foreach (var account in m_accounts)
{
// On cherche le joueur qui a été update
if (account.Value.DofusWindow.Character.Name != message.MemberInformations.name)
continue;
// On update l'initiative
account.Value.UpdateInitiative((int)message.MemberInformations.initiative);
// On change l'ordre de la liste
ReOrder();
break;
}
}
public static void ReOrder()
{
// On applique l'ordre d'initiative
m_accounts = m_accounts .OrderByDescending(x => x.Value.Initiative).ToDictionary(p => p.Key, p2 => p2.Value);
}
Dans la classe MultiAccountController
// ...
public int Initiative { get; private set; }
public void UpdateInitiative(int val)
{
Initiative = val;
}
Compiler et Test le plugin
A savoir que dans ce tutoriel nous avons vu le strict minimum, a presque aucun moment nous avons fait de gestion d'erreur.
Vous n'avez plus cas compiler le plugin, et suivre les opérations dans "".